Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ jobs:
uses: ./.github/workflows/ubuntu-prepare
- if: runner.os != 'Linux'
uses: dtolnay/rust-toolchain@stable
- name: Fix PATH for Cargo (non-Linux)
if: runner.os != 'Linux'
shell: bash
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install LLVM
if: runner.os == 'MacOS'
run: brew install llvm@${{env.MAIN_LLVM_VERSION}}
Expand Down Expand Up @@ -267,6 +271,9 @@ jobs:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v3
- uses: dtolnay/rust-toolchain@stable
- name: Fix PATH for Cargo
shell: bash
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- uses: taiki-e/install-action@cargo-hack
# Note: We currently only specify minimum rust versions for the default workspace members
- run: just msrv
Expand Down Expand Up @@ -761,6 +768,9 @@ jobs:
- uses: dtolnay/rust-toolchain@nightly
with:
components: rust-src
- name: Fix PATH for Cargo
shell: bash
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Add targets
run: rustup target add arm-linux-androideabi && rustup target add thumbv6m-none-eabi
- uses: actions/checkout@v4
Expand All @@ -780,6 +790,9 @@ jobs:
- uses: dtolnay/rust-toolchain@nightly
with:
components: rust-src
- name: Fix PATH for Cargo
shell: bash
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: add x86 i686 target
run: rustup target add i686-unknown-linux-gnu
- name: Install x86 build dependencies / multilib
Expand All @@ -798,6 +811,9 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rust-src
- name: Fix PATH for Cargo
shell: bash
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Add targets
run: rustup target add arm-linux-androideabi && rustup target add thumbv6m-none-eabi
- uses: actions/checkout@v4
Expand Down Expand Up @@ -976,6 +992,9 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Fix PATH for Cargo
shell: bash
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install nightly
run: rustup toolchain install nightly --component clippy
- name: Install deps
Expand All @@ -997,6 +1016,9 @@ jobs:
runs-on: ubuntu-24.04
steps:
- uses: dtolnay/rust-toolchain@stable
- name: Fix PATH for Cargo
shell: bash
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- uses: nttld/setup-ndk@v1
with:
ndk-version: r25b
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/qemu-fuzzer-tester-prepare/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ runs:
gcc-arm-linux-gnueabi g++-arm-linux-gnueabi cmake
- name: Remove old rust
shell: bash
run: sudo apt purge -y 'rust*' 'cargo*'
run: sudo apt purge -y 'rust*' 'cargo*' 'rustup*'
- uses: dtolnay/rust-toolchain@stable
- name: enable mult-thread for `make`
shell: bash
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/ubuntu-prepare/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ runs:
- name: Uninstall all currently installed Rust
shell: bash
run: |
sudo apt purge -y 'cargo*' 'rust*'
sudo apt purge -y 'cargo*' 'rust*' 'rustup*'

- name: Install and cache deps
shell: bash
Expand All @@ -39,6 +39,10 @@ runs:
components: clippy, rustfmt
# -----------------------------------------

- name: Fix PATH for Cargo
shell: bash
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH

- name: Install just
uses: extractions/setup-just@v3

Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/windows-tester-prepare/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ runs:
- uses: dtolnay/rust-toolchain@stable
with:
components: llvm-tools, clippy, rustfmt
- name: Fix PATH for Cargo
shell: bash
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
- name: Build docs
Expand Down
92 changes: 65 additions & 27 deletions crates/libafl/src/events/launcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ const _AFL_LAUNCHER_CLIENT: &str = "AFL_LAUNCHER_CLIENT";
#[cfg(unix)]
const LIBAFL_DEBUG_OUTPUT: &str = "LIBAFL_DEBUG_OUTPUT";

/// Reasons why a child process exited/crashed
#[derive(Debug, Clone, Copy)]
pub enum LauncherExitReason {
/// Exited with status code
Exited(i32),
/// Killed by signal
Killed(i32),
}

/// Information about this client from the launcher
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientDescription {
Expand Down Expand Up @@ -172,7 +181,6 @@ pub struct Launcher<'a, CF, MT, SP> {
/// Tell the manager to serialize or not the state on restart
serialize_state: ShouldSaveState,
/// If this launcher should use `fork` to spawn a new instance. Otherwise it will try to re-launch the current process with exactly the same parameters.
#[cfg(unix)]
fork: bool,
}

Expand Down Expand Up @@ -204,7 +212,6 @@ pub struct LauncherBuilder<'a, CF, MT, SP> {
multi_machine_node_descriptor: Option<NodeDescriptor<SocketAddr>>,
spawn_broker: bool,
serialize_state: ShouldSaveState,
#[cfg(unix)]
fork: bool,
}

Expand Down Expand Up @@ -232,7 +239,6 @@ impl LauncherBuilder<'_, (), (), ()> {
),
spawn_broker: true,
serialize_state: ShouldSaveState::OnRestart,
#[cfg(unix)]
fork: true,
}
}
Expand Down Expand Up @@ -269,7 +275,6 @@ impl<'a, CF, MT, SP> LauncherBuilder<'a, CF, MT, SP> {
multi_machine_node_descriptor: self.multi_machine_node_descriptor,
spawn_broker: self.spawn_broker,
serialize_state: self.serialize_state,
#[cfg(unix)]
fork: self.fork,
}
}
Expand All @@ -295,7 +300,6 @@ impl<'a, CF, MT, SP> LauncherBuilder<'a, CF, MT, SP> {
multi_machine_node_descriptor: self.multi_machine_node_descriptor,
spawn_broker: self.spawn_broker,
serialize_state: self.serialize_state,
#[cfg(unix)]
fork: self.fork,
}
}
Expand Down Expand Up @@ -328,7 +332,6 @@ impl<'a, CF, MT, SP> LauncherBuilder<'a, CF, MT, SP> {
multi_machine_node_descriptor: self.multi_machine_node_descriptor,
spawn_broker: self.spawn_broker,
serialize_state: self.serialize_state,
#[cfg(unix)]
fork: self.fork,
}
}
Expand Down Expand Up @@ -412,7 +415,6 @@ impl<'a, CF, MT, SP> LauncherBuilder<'a, CF, MT, SP> {
}

/// If this launcher should use `fork` to spawn a new instance. Otherwise it will try to re-launch the current process with exactly the same parameters.
#[cfg(unix)]
#[must_use]
pub fn fork(mut self, fork: bool) -> Self {
self.fork = fork;
Expand Down Expand Up @@ -445,7 +447,6 @@ impl<'a, CF, MT, SP> LauncherBuilder<'a, CF, MT, SP> {
.expect("multi_machine_node_descriptor not set"),
spawn_broker: self.spawn_broker,
serialize_state: self.serialize_state,
#[cfg(unix)]
fork: self.fork,
}
}
Expand Down Expand Up @@ -527,7 +528,6 @@ where
multi_machine_node_descriptor: self.multi_machine_node_descriptor,
spawn_broker: self.spawn_broker,
serialize_state: self.serialize_state,
#[cfg(unix)]
fork: self.fork,
}
}
Expand Down Expand Up @@ -652,12 +652,7 @@ where
F: FnMut(&Self, Option<ClientDescription>, Option<MT>) -> Result<(Option<S>, EM), Error>,
CF: FnOnce(Option<S>, EM, ClientDescription) -> Result<(), Error>,
{
#[cfg(unix)]
let use_fork = self.fork;
#[cfg(not(unix))]
let use_fork = false;

if use_fork {
if cfg!(unix) && self.fork {
#[cfg(unix)]
{
if self.cores.ids.is_empty() {
Expand Down Expand Up @@ -762,12 +757,9 @@ where
}
}

Self::wait_for_pids(&handles, self.spawn_broker);
}
// This is the fork part for unix
#[cfg(not(unix))]
{
unreachable!("Forking not supported");
if let Err(err) = Self::wait_for_pids(&handles, self.spawn_broker) {
log::error!("Error waiting for client PIDs: {err:?}");
}
}
} else {
// spawn logic
Expand Down Expand Up @@ -892,13 +884,18 @@ where
spawn_mgr(&self, None, monitor)?;
}

Self::wait_for_child_processes(&mut handles, self.spawn_broker);
if let Err(err) = Self::wait_for_child_processes(&mut handles, self.spawn_broker) {
log::error!("Error waiting for child processes: {err:?}");
}
}
Ok(())
}

#[cfg(unix)]
fn wait_for_pids(handles: &[i32], spawn_broker: bool) {
fn wait_for_pids(handles: &[i32], spawn_broker: bool) -> Result<(), Error> {
let mut crash_count = 0;
let mut reasons = vec![];

if spawn_broker {
// Broker exited. kill all clients and wait for them to avoid zombies.
for handle in handles {
Expand Down Expand Up @@ -927,26 +924,67 @@ where
);
} else {
log::info!("Client with pid {handle} exited with status {status}");
crash_count += 1;
reasons.push(LauncherExitReason::Exited(status));
}
}
}
}

if crash_count > 0 {
return Err(Error::unknown(format!(
"{crash_count} child processes crashed. Reasons: {reasons:?}"
)));
}
Ok(())
}

fn wait_for_child_processes(handles: &mut [std::process::Child], spawn_broker: bool) {
fn wait_for_child_processes(
handles: &mut [std::process::Child],
spawn_broker: bool,
) -> Result<(), Error> {
let mut crash_count = 0;
let mut reasons = vec![];

if spawn_broker {
for handle in &mut *handles {
let _ = handle.kill();
let _ = handle.wait();
}
} else {
for handle in &mut *handles {
let ecode = handle.wait();
if ecode.as_ref().is_ok_and(|e| !e.success()) {
log::info!("Client with handle {handle:?} exited with {ecode:?}");
let ecode = handle.wait()?;
if !ecode.success() {
#[cfg(unix)]
{
use std::os::unix::process::ExitStatusExt;
if let Some(sig) = ecode.signal() {
if sig != libc::SIGINT && sig != libc::SIGTERM {
crash_count += 1;
reasons.push(LauncherExitReason::Killed(sig));
}
} else if let Some(code) = ecode.code() {
crash_count += 1;
reasons.push(LauncherExitReason::Exited(code));
}
}
#[cfg(not(unix))]
{
if let Some(code) = ecode.code() {
crash_count += 1;
reasons.push(LauncherExitReason::Exited(code));
}
}
}
}
}

if crash_count > 0 {
return Err(Error::unknown(format!(
"{crash_count} child processes crashed. Reasons: {reasons:?}"
)));
}
Ok(())
}

/// Launch the common broker logic
Expand Down
Loading