Skip to content

Commit

Permalink
fix CI deadlock when testing known vulnerabilities (tlspuffin#306)
Browse files Browse the repository at this point in the history
* test(tlspuffin): add timeout argument to forked execution
* test(tlspuffin): retry several times when testing known vulnerabilities
  • Loading branch information
michaelmera authored Mar 22, 2024
1 parent f80370c commit 482ebfe
Show file tree
Hide file tree
Showing 6 changed files with 345 additions and 216 deletions.
52 changes: 20 additions & 32 deletions puffin/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use log::{error, info, LevelFilter};
use crate::{
algebra::set_deserialize_signature,
codec::Codec,
execution::forked_execution,
experiment::*,
fuzzer::{
harness::{default_put_options, set_default_put_options},
Expand Down Expand Up @@ -389,33 +390,11 @@ fn seed<PB: ProtocolBehavior>(
Ok(())
}

use nix::{
sys::wait::{waitpid, WaitPidFlag},
unistd::{fork, ForkResult},
};

use crate::{
agent::AgentName,
put::{PutDescriptor, PutName},
};

pub fn expect_crash<R>(func: R)
where
R: FnOnce(),
{
match unsafe { fork() } {
Ok(ForkResult::Parent { child, .. }) => {
let status = waitpid(child, Option::from(WaitPidFlag::empty())).unwrap();
info!("Finished executing: {:?}", status);
}
Ok(ForkResult::Child) => {
func();
std::process::exit(0);
}
Err(_) => panic!("Fork failed"),
}
}

fn execute<PB: ProtocolBehavior, P: AsRef<Path>>(input: P, put_registry: &'static PutRegistry<PB>) {
let trace = match Trace::<PB::Matcher>::from_file(input.as_ref()) {
Ok(t) => t,
Expand All @@ -429,16 +408,25 @@ fn execute<PB: ProtocolBehavior, P: AsRef<Path>>(input: P, put_registry: &'stati

// When generating coverage a crash means that no coverage is stored
// By executing in a fork, even when that process crashes, the other executed code will still yield coverage
expect_crash(move || {
let mut ctx = TraceContext::new(put_registry, default_put_options().clone());
if let Err(err) = trace.execute(&mut ctx) {
error!(
"Failed to execute trace {}: {:?}",
input.as_ref().display(),
err
);
}
});
let status = forked_execution(
move || {
let mut ctx = TraceContext::new(put_registry, default_put_options().clone());
if let Err(err) = trace.execute(&mut ctx) {
error!(
"Failed to execute trace {}: {:?}",
input.as_ref().display(),
err
);
std::process::exit(1);
}
},
None,
);

match status {
Ok(s) => info!("execution finished with status {s:?}"),
Err(reason) => panic!("failed to execute trace: {reason}"),
}
}

fn binary_attack<PB: ProtocolBehavior>(
Expand Down
83 changes: 83 additions & 0 deletions puffin/src/execution.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use std::{thread, time::Duration};

use nix::{
sys::{
signal::{kill, Signal},
wait::{
waitpid, WaitPidFlag,
WaitStatus::{Exited, Signaled},
},
},
unistd::{fork, ForkResult, Pid},
};

pub fn forked_execution<R>(func: R, timeout: Option<Duration>) -> Result<ExecutionStatus, String>
where
R: FnOnce(),
{
match unsafe { fork() } {
Ok(ForkResult::Parent { child, .. }) => {
let status = waitpid(child, Option::from(WaitPidFlag::empty())).unwrap();

if let Signaled(_, signal, _) = status {
match signal {
Signal::SIGSEGV | Signal::SIGABRT => return Ok(ExecutionStatus::Crashed),
Signal::SIGUSR2 if timeout.is_some() => return Ok(ExecutionStatus::Timeout),
_ => {
return Err(format!(
"execution process finished with unexpected signal {}",
signal
))
}
}
} else if let Exited(_, code) = status {
if code == 0 {
return Ok(ExecutionStatus::Success);
} else {
return Ok(ExecutionStatus::Failure(code));
}
}

Err(format!(
"execution process finished with unexpected status {:?}",
status
))
}
Ok(ForkResult::Child) => {
if let Some(t) = timeout {
thread::spawn(move || {
thread::sleep(t);
kill(Pid::this(), Signal::SIGUSR2).ok();
});
}

func();
std::process::exit(0);
}
Err(e) => Err(format!("fork failed: {}", e)),
}
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum ExecutionStatus {
Timeout,
Crashed,
Success,
Failure(i32),
}

pub trait AssertExecution {
fn expect_crash(self);
}

impl AssertExecution for Result<ExecutionStatus, String> {
fn expect_crash(self) {
use ExecutionStatus as S;
match self {
Ok(S::Failure(_)) | Ok(S::Crashed) => (),
Ok(S::Timeout) => panic!("trace execution timed out"),
Ok(S::Success) => panic!("expected trace execution to crash, but succeeded"),
Err(reason) => panic!("trace execution error: {reason}"),
}
}
}
1 change: 1 addition & 0 deletions puffin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod claims;
pub mod cli;
pub mod codec;
pub mod error;
pub mod execution;
pub mod experiment;
pub mod fuzzer;
pub mod graphviz;
Expand Down
Loading

0 comments on commit 482ebfe

Please sign in to comment.