libafl-fuzz: feature-flag nyx mode (#2712)

This commit is contained in:
Aarnav 2024-11-20 21:06:07 +01:00 committed by GitHub
parent e7f48889e7
commit 6e707d15bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 108 additions and 17 deletions

View File

@ -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"]

View File

@ -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",
]
@ -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"

View File

@ -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() {
@ -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",
@ -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) {
@ -252,12 +252,13 @@ 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,
@ -265,6 +266,7 @@ where
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,
@ -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>,
@ -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(),
}
}
@ -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,
@ -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(),
}
}
}

View File

@ -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};
@ -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
@ -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();
@ -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(
@ -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.

View File

@ -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)