Skip to content

libafl-fuzz: feature-flag nyx mode #2712

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 20, 2024
Merged
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
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
Loading