diff --git a/smitebot/src/commands/bench_exec.rs b/smitebot/src/commands/bench_exec.rs index 8704c7d3..f90882b3 100644 --- a/smitebot/src/commands/bench_exec.rs +++ b/smitebot/src/commands/bench_exec.rs @@ -22,7 +22,7 @@ use crate::commands::build::{BuildInputs, run_build}; use crate::config::CampaignConfig; use crate::latency_stats::{LatencyStats, avg_duration, mean_stddev}; use crate::libnyx::{Libnyx, NyxReturn, PAYLOAD_HEADER_SIZE}; -use crate::utils::{pin_to_cpu, setup_nyx}; +use crate::utils::{afl_bin_dir, pin_to_cpu, setup_nyx}; /// Default number of timed executions when `--iterations` is not given. const DEFAULT_ITERATIONS: u64 = 1000; @@ -487,7 +487,7 @@ fn locate_libnyx(config: &CampaignConfig) -> Option { return None; } - let libnyx_path = config.aflpp_path.join("libnyx.so"); + let libnyx_path = afl_bin_dir(&config.aflpp_path).join("libnyx.so"); if !libnyx_path.exists() { log::error!( "{} not found; build AFL++ with Nyx support (see nyx_mode/README.md)", diff --git a/smitebot/src/commands/corpus.rs b/smitebot/src/commands/corpus.rs index 93fdcdd7..8342e0fe 100644 --- a/smitebot/src/commands/corpus.rs +++ b/smitebot/src/commands/corpus.rs @@ -10,7 +10,7 @@ use std::process::Command; use clap::{Args, Subcommand}; use crate::state::CampaignState; -use crate::utils::{find_in_path, is_executable}; +use crate::utils::{afl_bin_dir, find_in_path, is_executable}; /// Command handler for `smitebot corpus`. pub struct CorpusCommand; @@ -373,7 +373,7 @@ fn output_dir_occupied(output: &Path) -> bool { /// searching `$PATH`. fn find_afl_cmin(aflpp_path: Option<&Path>) -> Option { if let Some(path) = aflpp_path { - let candidate = path.join("afl-cmin"); + let candidate = afl_bin_dir(path).join("afl-cmin"); if is_executable(&candidate) { return Some(candidate); } diff --git a/smitebot/src/commands/doctor.rs b/smitebot/src/commands/doctor.rs index edf06bc2..ab0a912b 100644 --- a/smitebot/src/commands/doctor.rs +++ b/smitebot/src/commands/doctor.rs @@ -10,7 +10,7 @@ use clap::Args; use serde::Serialize; use crate::config::CampaignConfig; -use crate::utils::{find_in_path, is_executable}; +use crate::utils::{afl_bin_dir, find_in_path, is_executable}; /// AFL++ binaries required for campaign execution and corpus minimization. const AFL_TOOLS: &[&str] = &["afl-fuzz", "afl-cmin", "afl-tmin", "afl-whatsup"]; @@ -178,7 +178,7 @@ impl DoctorCommand { None => None, }; let inputs = DoctorInputs::resolve(config.as_ref(), args); - let aflpp_root = &inputs.aflpp_root; + let afl_bin = afl_bin_dir(&inputs.aflpp_root); let smite_dir = &inputs.smite_dir; // Keep a predictable order for operator readability and stable JSON output. @@ -190,14 +190,14 @@ impl DoctorCommand { ), DoctorCheck::new("/dev/kvm accessible", check_kvm_access()), DoctorCheck::new("Docker daemon reachable", check_docker_daemon()), - DoctorCheck::new("AFL++ built with Nyx support", check_libnyx(aflpp_root)), + DoctorCheck::new("AFL++ built with Nyx support", check_libnyx(&afl_bin)), DoctorCheck::new("VMware backdoor enabled", check_vmware_backdoor_enabled()), ]; for &tool in AFL_TOOLS { checks.push(DoctorCheck::new( tool, - require_executable(&aflpp_root.join(tool)), + require_executable(&afl_bin.join(tool)), )); } @@ -315,9 +315,9 @@ fn check_docker_daemon() -> Result<(), CheckFailure> { } } -/// Checks whether `libnyx.so` exists under the AFL++ root used for fuzzing. -fn check_libnyx(aflpp_root: &Path) -> Result<(), CheckFailure> { - if aflpp_root.join("libnyx.so").exists() { +/// Checks whether `libnyx.so` exists in AFL++'s binary directory. +fn check_libnyx(afl_bin: &Path) -> Result<(), CheckFailure> { + if afl_bin.join("libnyx.so").exists() { Ok(()) } else { Err(CheckFailure::LibnyxNotFound) diff --git a/smitebot/src/commands/start.rs b/smitebot/src/commands/start.rs index d6e21731..6e74e9ee 100644 --- a/smitebot/src/commands/start.rs +++ b/smitebot/src/commands/start.rs @@ -16,7 +16,7 @@ use crate::commands::build::{BuildInputs, run_build}; use crate::config::CampaignConfig; use crate::state::{CampaignState, RunnerState, Status}; use crate::tmux; -use crate::utils::{command_stdout, docker_image_id, setup_nyx, shell_quote}; +use crate::utils::{afl_bin_dir, command_stdout, docker_image_id, setup_nyx, shell_quote}; /// How long to wait for `fuzzer_stats` before treating alive runners as started. /// @@ -503,7 +503,7 @@ fn build_runner_shell_cmd( seed_dir: &Path, testcache_mb: Option, ) -> String { - let afl_fuzz = config.aflpp_path.join("afl-fuzz"); + let afl_fuzz = afl_bin_dir(&config.aflpp_path).join("afl-fuzz"); // -L (MOpt) is incompatible with custom mutators; runner_strategy skips it // when an IR scenario or the user's afl_env supplies AFL_CUSTOM_MUTATOR_LIBRARY. let has_custom_mutator = config.scenario.starts_with("ir") diff --git a/smitebot/src/utils.rs b/smitebot/src/utils.rs index d5adae1e..78f90e1e 100644 --- a/smitebot/src/utils.rs +++ b/smitebot/src/utils.rs @@ -23,6 +23,19 @@ pub fn is_executable(path: &Path) -> bool { fs::metadata(path).is_ok_and(|metadata| metadata.permissions().mode() & 0o111 != 0) } +/// Returns the directory holding AFL++'s binaries and `libnyx.so`. +/// +/// A source build keeps them at the tree root; an installed package puts them +/// under `bin/`. +pub fn afl_bin_dir(aflpp_path: &Path) -> PathBuf { + let bin = aflpp_path.join("bin"); + if bin.join("afl-fuzz").is_file() { + bin + } else { + aflpp_path.to_path_buf() + } +} + /// Wraps a string in single quotes for safe interpolation into a shell command. /// /// Embedded single quotes are escaped with the standard `'\''` idiom. Used for @@ -201,6 +214,22 @@ mod tests { assert!(found.is_none()); } + #[test] + fn afl_bin_dir_is_root_for_source_tree() { + let tempdir = tempfile::tempdir().unwrap(); + fs::write(tempdir.path().join("afl-fuzz"), "").unwrap(); + assert_eq!(afl_bin_dir(tempdir.path()), tempdir.path()); + } + + #[test] + fn afl_bin_dir_is_bin_for_installed_package() { + let tempdir = tempfile::tempdir().unwrap(); + let bin = tempdir.path().join("bin"); + fs::create_dir(&bin).unwrap(); + fs::write(bin.join("afl-fuzz"), "").unwrap(); + assert_eq!(afl_bin_dir(tempdir.path()), bin); + } + #[test] fn shell_quote_wraps_in_single_quotes() { assert_eq!(shell_quote("hello"), "'hello'");