Skip to content

Commit

Permalink
libafl-fuzz: feature-flag nyx mode (AFLplusplus#2712)
Browse files Browse the repository at this point in the history
  • Loading branch information
R9295 authored Nov 20, 2024
1 parent e7f4888 commit 6e707d1
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 17 deletions.
5 changes: 4 additions & 1 deletion fuzzers/forkserver/libafl-fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ memmap2 = "0.9.4"
nix = { version = "0.29.0", features = ["fs"] }
regex = "1.10.5"
serde = { version = "1.0.117", features = ["derive"] }
libafl_nyx = { path = "../../../libafl_nyx" }

[target.'cfg(target_os = "linux")'.dependencies]
libafl_nyx = { path = "../../../libafl_nyx", optional = true }

[features]
default = ["track_hit_feedbacks"]
track_hit_feedbacks = ["libafl/track_hit_feedbacks"]
fuzzbench = []
nyx = ["dep:libafl_nyx"]
8 changes: 8 additions & 0 deletions fuzzers/forkserver/libafl-fuzz/Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ dependencies = [
"test_frida",
"test_qemu",
"test_unicorn_mode",
# nyx
# since we cannot test nyx mode on CI, let's build it
"build_nyx_mode",
# fuzzbench
"test_instr_fuzzbench",
]
Expand Down Expand Up @@ -295,6 +298,11 @@ test -n "$( ls ./test/output-nyx/fuzzer_main/queue/id:000003* 2>/dev/null )" ||
'''
dependencies = ["build_afl", "build_libafl_fuzz"]

# since we cannot test nyx mode on CI, let's build it
[tasks.build_nyx_mode]
script_runner = "@shell"
script = "cargo build --profile ${PROFILE} --features nyx"

[tasks.clean]
linux_alias = "clean_unix"
mac_alias = "clean_unix"
Expand Down
98 changes: 88 additions & 10 deletions fuzzers/forkserver/libafl-fuzz/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub fn check_binary(opt: &mut Opt, shmem_env_var: &str) -> Result<(), Error> {
}
} else {
bin_path = &opt.executable;
#[cfg(target_os = "linux")]
#[cfg(feature = "nyx")]
{
if opt.nyx_mode {
if !bin_path.is_symlink() && bin_path.is_dir() {
Expand Down Expand Up @@ -91,7 +91,7 @@ pub fn check_binary(opt: &mut Opt, shmem_env_var: &str) -> Result<(), Error> {
}

// check if the binary is an ELF file
#[cfg(target_os = "linux")]
#[cfg(feature = "nyx")]
if mmap[0..4] != [0x7f, 0x45, 0x4c, 0x46] {
return Err(Error::illegal_argument(format!(
"Program '{}' is not an ELF binary",
Expand All @@ -117,7 +117,7 @@ pub fn check_binary(opt: &mut Opt, shmem_env_var: &str) -> Result<(), Error> {
&& !opt.forkserver_cs
&& !opt.non_instrumented_mode;

#[cfg(target_os = "linux")]
#[cfg(feature = "nyx")]
let check_instrumentation = check_instrumentation && !opt.nyx_mode;

if check_instrumentation && !is_instrumented(&mmap, shmem_env_var) {
Expand Down Expand Up @@ -252,19 +252,21 @@ fn check_file_found(file: &Path, perm: u32) -> bool {
false
}

#[cfg(feature = "nyx")]
pub enum SupportedExecutors<S, OT, FSV, NYX> {
Forkserver(FSV, PhantomData<(S, OT)>),
#[cfg(target_os = "linux")]
Forkserver(FSV, PhantomData<(S, OT, NYX)>),
Nyx(NYX),
}

#[cfg(feature = "nyx")]
impl<S, OT, FSV, NYX> UsesState for SupportedExecutors<S, OT, FSV, NYX>
where
S: State,
{
type State = S;
}

#[cfg(feature = "nyx")]
impl<S, OT, FSV, NYX, EM, Z> Executor<EM, Z> for SupportedExecutors<S, OT, FSV, NYX>
where
S: State,
Expand All @@ -282,12 +284,13 @@ where
) -> Result<ExitKind, Error> {
match self {
Self::Forkserver(fsrv, _) => fsrv.run_target(fuzzer, state, mgr, input),
#[cfg(target_os = "linux")]
#[cfg(feature = "nyx")]
Self::Nyx(nyx) => nyx.run_target(fuzzer, state, mgr, input),
}
}
}

#[cfg(feature = "nyx")]
impl<S, OT, FSV, NYX> HasObservers for SupportedExecutors<S, OT, FSV, NYX>
where
OT: ObserversTuple<S::Input, S>,
Expand All @@ -300,7 +303,7 @@ where
fn observers(&self) -> RefIndexable<&Self::Observers, Self::Observers> {
match self {
Self::Forkserver(fsrv, _) => fsrv.observers(),
#[cfg(target_os = "linux")]
#[cfg(feature = "nyx")]
Self::Nyx(nyx) => nyx.observers(),
}
}
Expand All @@ -309,12 +312,13 @@ where
fn observers_mut(&mut self) -> RefIndexable<&mut Self::Observers, Self::Observers> {
match self {
Self::Forkserver(fsrv, _) => fsrv.observers_mut(),
#[cfg(target_os = "linux")]
#[cfg(feature = "nyx")]
Self::Nyx(nyx) => nyx.observers_mut(),
}
}
}

#[cfg(feature = "nyx")]
impl<S, OT, FSV, NYX> HasTimeout for SupportedExecutors<S, OT, FSV, NYX>
where
FSV: HasTimeout,
Expand All @@ -323,15 +327,89 @@ where
fn set_timeout(&mut self, timeout: std::time::Duration) {
match self {
Self::Forkserver(fsrv, _) => fsrv.set_timeout(timeout),
#[cfg(target_os = "linux")]
#[cfg(feature = "nyx")]
Self::Nyx(nyx) => nyx.set_timeout(timeout),
}
}
fn timeout(&self) -> std::time::Duration {
match self {
Self::Forkserver(fsrv, _) => fsrv.timeout(),
#[cfg(target_os = "linux")]
#[cfg(feature = "nyx")]
Self::Nyx(nyx) => nyx.timeout(),
}
}
}

#[cfg(not(feature = "nyx"))]
impl<S, OT, FSV> UsesState for SupportedExecutors<S, OT, FSV>
where
S: State,
{
type State = S;
}

#[cfg(not(feature = "nyx"))]
pub enum SupportedExecutors<S, OT, FSV> {
Forkserver(FSV, PhantomData<(S, OT)>),
}

#[cfg(not(feature = "nyx"))]
impl<S, OT, FSV, EM, Z> Executor<EM, Z> for SupportedExecutors<S, OT, FSV>
where
S: State,
Z: UsesState<State = S>,
EM: UsesState<State = S>,
FSV: Executor<EM, Z, State = S>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &S::Input,
) -> Result<ExitKind, Error> {
match self {
Self::Forkserver(fsrv, _) => fsrv.run_target(fuzzer, state, mgr, input),
}
}
}

#[cfg(not(feature = "nyx"))]
impl<S, OT, FSV> HasObservers for SupportedExecutors<S, OT, FSV>
where
OT: ObserversTuple<S::Input, S>,
S: State,
FSV: HasObservers<Observers = OT>,
{
type Observers = OT;
#[inline]
fn observers(&self) -> RefIndexable<&Self::Observers, Self::Observers> {
match self {
Self::Forkserver(fsrv, _) => fsrv.observers(),
}
}

#[inline]
fn observers_mut(&mut self) -> RefIndexable<&mut Self::Observers, Self::Observers> {
match self {
Self::Forkserver(fsrv, _) => fsrv.observers_mut(),
}
}
}

#[cfg(not(feature = "nyx"))]
impl<S, OT, FSV> HasTimeout for SupportedExecutors<S, OT, FSV>
where
FSV: HasTimeout,
{
fn set_timeout(&mut self, timeout: std::time::Duration) {
match self {
Self::Forkserver(fsrv, _) => fsrv.set_timeout(timeout),
}
}
fn timeout(&self) -> std::time::Duration {
match self {
Self::Forkserver(fsrv, _) => fsrv.timeout(),
}
}
}
11 changes: 6 additions & 5 deletions fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ use libafl_bolts::{
tuples::{tuple_list, Handled, Merge},
AsSliceMut,
};
#[cfg(feature = "nyx")]
use libafl_nyx::{executor::NyxExecutor, helper::NyxHelper, settings::NyxSettings};
use libafl_targets::{cmps::AFLppCmpLogMap, AFLppCmpLogObserver, AFLppCmplogTracingStage};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -125,7 +126,7 @@ define_run_client!(state, mgr, fuzzer_dir, core_id, opt, is_main_node, {
let shmem_buf = shmem.as_slice_mut();

// If we are in Nyx Mode, we need to use a different map observer.
#[cfg(target_os = "linux")]
#[cfg(feature = "nyx")]
let (nyx_helper, edges_observer) = {
if opt.nyx_mode {
// main node is the first core id in CentralizedLauncher
Expand All @@ -152,7 +153,7 @@ define_run_client!(state, mgr, fuzzer_dir, core_id, opt, is_main_node, {
(None, observer)
}
};
#[cfg(not(target_os = "linux"))]
#[cfg(not(feature = "nyx"))]
let edges_observer = { unsafe { StdMapObserver::new("edges", shmem_buf) } };

let edges_observer = HitcountsMapObserver::new(edges_observer).track_indices();
Expand Down Expand Up @@ -319,7 +320,7 @@ define_run_client!(state, mgr, fuzzer_dir, core_id, opt, is_main_node, {
std::env::set_var("LD_PRELOAD", &preload);
std::env::set_var("DYLD_INSERT_LIBRARIES", &preload);
}
#[cfg(target_os = "linux")]
#[cfg(feature = "nyx")]
let mut executor = {
if opt.nyx_mode {
SupportedExecutors::Nyx(NyxExecutor::builder().build(
Expand Down Expand Up @@ -349,8 +350,8 @@ define_run_client!(state, mgr, fuzzer_dir, core_id, opt, is_main_node, {
)
}
};
#[cfg(not(target_os = "linux"))]
let executor = {
#[cfg(not(feature = "nyx"))]
let mut executor = {
// Create the base Executor
let mut executor_builder = base_forkserver_builder(opt, &mut shmem_provider, fuzzer_dir);
// Set a custom exit code to be interpreted as a Crash if configured.
Expand Down
3 changes: 2 additions & 1 deletion fuzzers/forkserver/libafl-fuzz/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,8 @@ struct Opt {
/// use binary-only instrumentation (QEMU mode)
#[arg(short = 'Q')]
qemu_mode: bool,
#[cfg(target_os = "linux")]
/// Nyx mode (Note: unlike AFL++, you do not need to specify -Y for parallel nyx fuzzing)
#[cfg(feature = "nyx")]
#[arg(short = 'X')]
nyx_mode: bool,
/// use unicorn-based instrumentation (Unicorn mode)
Expand Down

0 comments on commit 6e707d1

Please sign in to comment.