libafl-fuzz: Introduce Support for QEMU mode (#2481)

* libafl-fuzz: simplify Makefile.toml

* Re-introduce support for old AFL++ forkserver

* clippy

* libafl-fuzz: add support for QEMU mode

* libafl-fuzz: simplify Makefile
This commit is contained in:
Aarnav 2024-08-13 14:13:59 +02:00 committed by GitHub
parent 799c634fef
commit 2287afc59b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 260 additions and 73 deletions

View File

@ -13,27 +13,32 @@ LLVM_CONFIG = { value = "llvm-config-18", condition = { env_not_set = [
"LLVM_CONFIG", "LLVM_CONFIG",
] } } ] } }
AFL_VERSION = "db23931e7c1727ddac8691a6241c97b2203ec6fc" AFL_VERSION = "db23931e7c1727ddac8691a6241c97b2203ec6fc"
AFL_DIR_NAME = { value = "./AFLplusplus-${AFL_VERSION}" } AFL_DIR = { value = "./AFLplusplus" }
AFL_CC_PATH = { value = "${AFL_DIR_NAME}/afl-clang-fast" } AFL_CC_PATH = { value = "${AFL_DIR}/afl-clang-fast" }
CC = { value = "clang" } CC = { value = "clang" }
[tasks.build_afl] [tasks.build_afl]
script_runner = "@shell" script_runner = "@shell"
script = ''' script = '''
if [ ! -d "$AFL_DIR_NAME" ]; then if [ ! -d "$AFL_DIR" ]; then
if [ -f "v${AFL_VERSION}.zip" ]; then git clone https://github.com/AFLplusplus/AFLplusplus.git
rm v${AFL_VERSION}.zip cd ${AFL_DIR}
fi git checkout ${AFL_VERSION}
wget https://github.com/AFLplusplus/AFLplusplus/archive/${AFL_VERSION}.zip
unzip ${AFL_VERSION}.zip
cd ${AFL_DIR_NAME}
LLVM_CONFIG=${LLVM_CONFIG} make LLVM_CONFIG=${LLVM_CONFIG} make
cd frida_mode cd frida_mode
LLVM_CONFIG=${LLVM_CONFIG} make LLVM_CONFIG=${LLVM_CONFIG} make
cd ../.. cd ../..
fi fi
''' '''
[tasks.build_qemuafl]
script_runner = "@shell"
script = '''
cd ${AFL_DIR}/qemu_mode
./build_qemu_support.sh
cd ../..
'''
dependencies = ["build_afl"]
# Test # Test
[tasks.test] [tasks.test]
linux_alias = "test_unix" linux_alias = "test_unix"
@ -43,14 +48,22 @@ windows_alias = "unsupported"
[tasks.test_unix] [tasks.test_unix]
script_runner = "@shell" script_runner = "@shell"
script = "echo done" script = "echo done"
dependencies = ["build_afl", "test_instr", "test_cmplog", "test_frida"] dependencies = ["build_afl", "test_instr", "test_cmplog", "test_frida", "test_qemu"]
[tasks.build_libafl_fuzz]
script_runner = "@shell"
script = "cargo build --profile ${PROFILE}"
[tasks.test_instr] [tasks.test_instr]
script_runner = "@shell" script_runner = "@shell"
script = ''' script = '''
cargo build --profile ${PROFILE} AFL_PATH=${AFL_DIR} ${AFL_CC_PATH} ./test/test-instr.c -o ./test/out-instr
AFL_PATH=${AFL_DIR_NAME} ${AFL_CC_PATH} ./test/test-instr.c -o ./test/out-instr
LIBAFL_DEBUG_OUTPUT=1 AFL_CORES=1 AFL_STATS_INTERVAL=1 timeout 5 ${FUZZER} -i ./test/seeds -o ./test/output ./test/out-instr || true export LIBAFL_DEBUG_OUTPUT=1
export AFL_CORES=1
export AFL_STATS_INTERVAL=1
timeout 5 ${FUZZER} -i ./test/seeds -o ./test/output ./test/out-instr || true
test -n "$( ls ./test/output/fuzzer_main/queue/id:000002* 2>/dev/null )" || { test -n "$( ls ./test/output/fuzzer_main/queue/id:000002* 2>/dev/null )" || {
echo "No new corpus entries found" echo "No new corpus entries found"
exit 1 exit 1
@ -72,46 +85,50 @@ test -d "./test/output/fuzzer_main/crashes" || {
exit 1 exit 1
} }
''' '''
dependencies = ["build_afl"] dependencies = ["build_afl", "build_libafl_fuzz"]
[tasks.test_cmplog] [tasks.test_cmplog]
script_runner = "@shell" script_runner = "@shell"
script = ''' script = '''
cargo build --profile ${PROFILE}
# cmplog TODO: AFL_BENCH_UNTIL_CRASH=1 instead of timeout 15s # cmplog TODO: AFL_BENCH_UNTIL_CRASH=1 instead of timeout 15s
AFL_LLVM_CMPLOG=1 AFL_PATH=${AFL_DIR_NAME} ${AFL_CC_PATH} ./test/test-cmplog.c -o ./test/out-cmplog AFL_LLVM_CMPLOG=1 AFL_PATH=${AFL_DIR} ${AFL_CC_PATH} ./test/test-cmplog.c -o ./test/out-cmplog
AFL_CORES=1 timeout 5 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true AFL_CORES=1 timeout 5 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true
test -n "$( ls -A ./test/output-cmplog/fuzzer_main/crashes/)" || { test -n "$( ls -A ./test/output-cmplog/fuzzer_main/crashes/)" || {
echo "No crashes found" echo "No crashes found"
exit 1 exit 1
} }
''' '''
dependencies = ["build_afl"] dependencies = ["build_afl", "build_libafl_fuzz"]
[tasks.test_frida] [tasks.test_frida]
script_runner = "@shell" script_runner = "@shell"
script = ''' script = '''
cargo build --profile ${PROFILE}
${CC} -no-pie ./test/test-instr.c -o ./test/out-frida ${CC} -no-pie ./test/test-instr.c -o ./test/out-frida
AFL_PATH=${AFL_DIR_NAME} AFL_CORES=1 AFL_STATS_INTERVAL=1 timeout 5 ${FUZZER} -m 0 -V07 -O -i ./test/seeds-frida -o ./test/output-frida -- ./test/out-frida || true
export AFL_PATH=${AFL_DIR}
export AFL_CORES=1
export AFL_STATS_INTERVAL=1
timeout 5 ${FUZZER} -m 0 -V07 -O -i ./test/seeds_frida -o ./test/output-frida -- ./test/out-frida || true
test -n "$( ls ./test/output-frida/fuzzer_main/queue/id:000002* 2>/dev/null )" || { test -n "$( ls ./test/output-frida/fuzzer_main/queue/id:000002* 2>/dev/null )" || {
echo "No new corpus entries found for FRIDA mode" echo "No new corpus entries found for FRIDA mode"
exit 1 exit 1
} }
${CC} ./test/test-cmpcov.c -o ./test/out-frida-cmpcov ${CC} ./test/test-cmpcov.c -o ./test/out-frida-cmpcov
AFL_PATH=${AFL_DIR_NAME} LIBAFL_DEBUG_OUTPUT=1 AFL_DEBUG=1 AFL_CORES=1 AFL_FRIDA_VERBOSE=1 timeout 10 ${FUZZER} -m 0 -V07 -O -c 0 -l 3 -i ./test/seeds-frida -o ./test/output-frida-cmpcov -- ./test/out-frida-cmpcov || true AFL_FRIDA_VERBOSE=1 timeout 10 ${FUZZER} -m 0 -V07 -O -c 0 -l 3 -i ./test/seeds_frida -o ./test/output-frida-cmpcov -- ./test/out-frida-cmpcov || true
test -n "$( ls ./test/output-frida-cmpcov/fuzzer_main/queue/id:000003* 2>/dev/null )" || { test -n "$( ls ./test/output-frida-cmpcov/fuzzer_main/queue/id:000003* 2>/dev/null )" || {
echo "No new corpus entries found for FRIDA cmplog mode" echo "No new corpus entries found for FRIDA cmplog mode"
exit 1 exit 1
} }
export AFL_FRIDA_PERSISTENT_ADDR=0x`nm ./test/out-frida | grep -Ei "T _main|T main" | awk '{print $1}'` export AFL_FRIDA_PERSISTENT_ADDR=0x`nm ./test/out-frida | grep -Ei "T _main|T main" | awk '{print $1}'`
AFL_PATH=${AFL_DIR_NAME} AFL_STATS_INTERVAL=1 AFL_CORES=1 timeout 5 ${FUZZER} -m 0 -V07 -O -i ./test/seeds-frida -o ./test/output-frida-persistent -- ./test/out-frida || true timeout 5 ${FUZZER} -m 0 -V07 -O -i ./test/seeds_frida -o ./test/output-frida-persistent -- ./test/out-frida || true
# TODO: change it to id:000003* once persistent mode is fixed
test -n "$( ls ./test/output-frida-persistent/fuzzer_main/queue/id:000002* 2>/dev/null )" || { test -n "$( ls ./test/output-frida-persistent/fuzzer_main/queue/id:000002* 2>/dev/null )" || {
echo "No new corpus entries found for FRIDA persistent mode" echo "No new corpus entries found for FRIDA persistent mode"
exit 1 exit 1
} }
RUNTIME_PERSISTENT=`grep execs_done ./test/output-frida-persistent/fuzzer_main/fuzzer_stats | awk '{print$3}'` RUNTIME_PERSISTENT=`grep execs_done ./test/output-frida-persistent/fuzzer_main/fuzzer_stats | awk '{print$3}'`
RUNTIME=`grep execs_done ./test/output-frida/fuzzer_main/fuzzer_stats | awk '{print$3}'` RUNTIME=`grep execs_done ./test/output-frida/fuzzer_main/fuzzer_stats | awk '{print$3}'`
test -n "$RUNTIME" -a -n "$RUNTIME_PERSISTENT" && { test -n "$RUNTIME" -a -n "$RUNTIME_PERSISTENT" && {
@ -125,8 +142,44 @@ test -n "$RUNTIME" -a -n "$RUNTIME_PERSISTENT" && {
} || { } || {
echo "we got no data on executions performed? weird!" echo "we got no data on executions performed? weird!"
} }
unset AFL_FRIDA_PERSISTENT_ADDR
''' '''
dependencies = ["build_afl"] dependencies = ["build_afl", "build_libafl_fuzz"]
[tasks.test_qemu]
script_runner = "@shell"
script = '''
${CC} -pie -fPIE ./test/test-instr.c -o ./test/out-qemu
${CC} -o ./test/out-qemu-cmpcov ./test/test-cmpcov.c
export AFL_PATH=${AFL_DIR}
export AFL_CORES=1
export AFL_STATS_INTERVAL=1
timeout 5 ${FUZZER} -m 0 -V07 -Q -i ./test/seeds_qemu -o ./test/output-qemu -- ./test/out-qemu || true
test -n "$( ls ./test/output-qemu/fuzzer_main/queue/id:000002* 2>/dev/null )" || {
echo "No new corpus entries found for QEMU mode"
exit 1
}
export AFL_ENTRYPOINT=`printf 1 | AFL_DEBUG=1 ${AFL_DIR}/afl-qemu-trace ./test/out-qemu 2>&1 >/dev/null | awk '/forkserver/{print $4; exit}'`
timeout 5 ${FUZZER} -m 0 -V2 -Q -i ./test/seeds_qemu -o ./test/output-qemu-entrypoint -- ./test/out-qemu || true
test -n "$( ls ./test/output-qemu-entrypoint/fuzzer_main/queue/id:000002* 2>/dev/null )" || {
echo "No new corpus entries found for QEMU mode with AFL_ENTRYPOINT"
exit 1
}
unset AFL_ENTRYPOINT
export AFL_PRELOAD=${AFL_DIR}/libcompcov.so
export AFL_COMPCOV_LEVEL=2
timeout 5 ${FUZZER} -V07 -Q -i ./test/seeds_qemu -o ./test/output-qemu-cmpcov -- ./test/out-qemu-cmpcov || true
test -n "$( ls ./test/output-qemu-cmpcov/fuzzer_main/queue/id:000002* 2>/dev/null )" || {
echo "No new corpus entries found for QEMU mode"
exit 1
}
'''
dependencies = ["build_afl", "build_qemuafl","build_libafl_fuzz"]
[tasks.clean] [tasks.clean]
linux_alias = "clean_unix" linux_alias = "clean_unix"
@ -136,14 +189,12 @@ windows_alias = "unsupported"
[tasks.clean_unix] [tasks.clean_unix]
script_runner = "@shell" script_runner = "@shell"
script = ''' script = '''
rm -rf AFLplusplus-${AFL_VERSION} rm -rf AFLplusplus
rm ${AFL_VERSION}.zip
rm -rf ./test/out-instr rm -rf ./test/out-instr
rm -rf ./test/output rm -rf ./test/output
rm -rf ./test/cmplog-output rm -rf ./test/cmplog-output
rm -rf ./test/output-frida rm -rf ./test/output-frida*
rm -rf ./test/output-frida-cmpcov
rm -rf ./test/output-frida-persistent
rm -rf ./test/output-cmplog rm -rf ./test/output-cmplog
rm -rf ./test/output-qemu*
rm ./test/out-* rm ./test/out-*
''' '''

View File

@ -117,7 +117,8 @@ pub fn check_binary(opt: &mut Opt, shmem_env_var: &str) -> Result<(), Error> {
)); ));
} }
if opt.forkserver_cs || opt.qemu_mode || opt.frida_mode && is_instrumented(&mmap, shmem_env_var) if (opt.forkserver_cs || opt.qemu_mode || opt.frida_mode)
&& is_instrumented(&mmap, shmem_env_var)
{ {
return Err(Error::illegal_argument( return Err(Error::illegal_argument(
"Instrumentation found in -Q/-O mode", "Instrumentation found in -Q/-O mode",

View File

@ -1,4 +1,4 @@
use std::{borrow::Cow, marker::PhantomData, path::PathBuf, time::Duration}; use std::{borrow::Cow, env, marker::PhantomData, path::PathBuf, time::Duration};
use libafl::{ use libafl::{
corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus}, corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus},
@ -426,12 +426,26 @@ fn base_executor<'a>(
if let Some(kill_signal) = opt.kill_signal { if let Some(kill_signal) = opt.kill_signal {
executor = executor.kill_signal(kill_signal); executor = executor.kill_signal(kill_signal);
} }
if opt.is_persistent { if opt.is_persistent || opt.qemu_mode {
executor = executor.shmem_provider(shmem_provider); executor = executor.shmem_provider(shmem_provider);
} }
if let Some(harness_input_type) = &opt.harness_input_type { if let Some(harness_input_type) = &opt.harness_input_type {
executor = executor.parse_afl_cmdline([harness_input_type]); executor = executor.parse_afl_cmdline([harness_input_type]);
} }
if opt.qemu_mode {
let exec = opt.executable.display().to_string();
executor = executor.program(
find_afl_binary("afl-qemu-trace", Some(opt.executable.clone()))
.expect("to find afl-qemu-trace"),
);
// we skip all libafl-fuzz arguments.
let (skip, _) = env::args()
.enumerate()
.find(|i| i.1 == exec)
.expect("invariant; should never occur");
let args = env::args().skip(skip);
executor = executor.args(args);
}
executor executor
} }

View File

@ -0,0 +1 @@
00000

View File

@ -0,0 +1 @@
00000

View File

@ -53,12 +53,24 @@ const FS_NEW_ERROR: i32 = 0xeffe0000_u32 as i32;
const FS_NEW_VERSION_MIN: u32 = 1; const FS_NEW_VERSION_MIN: u32 = 1;
const FS_NEW_VERSION_MAX: u32 = 1; const FS_NEW_VERSION_MAX: u32 = 1;
#[allow(clippy::cast_possible_wrap)]
const FS_OPT_ENABLED: i32 = 0x80000001_u32 as i32;
#[allow(clippy::cast_possible_wrap)] #[allow(clippy::cast_possible_wrap)]
const FS_NEW_OPT_MAPSIZE: i32 = 1_u32 as i32; const FS_NEW_OPT_MAPSIZE: i32 = 1_u32 as i32;
#[allow(clippy::cast_possible_wrap)] #[allow(clippy::cast_possible_wrap)]
const FS_OPT_MAPSIZE: i32 = 0x40000000_u32 as i32;
#[allow(clippy::cast_possible_wrap)]
const FS_OPT_SHDMEM_FUZZ: i32 = 0x01000000_u32 as i32;
#[allow(clippy::cast_possible_wrap)]
const FS_NEW_OPT_SHDMEM_FUZZ: i32 = 2_u32 as i32; const FS_NEW_OPT_SHDMEM_FUZZ: i32 = 2_u32 as i32;
#[allow(clippy::cast_possible_wrap)] #[allow(clippy::cast_possible_wrap)]
const FS_NEW_OPT_AUTODICT: i32 = 0x00000800_u32 as i32; const FS_NEW_OPT_AUTODICT: i32 = 0x00000800_u32 as i32;
#[allow(clippy::cast_possible_wrap)]
const FS_OPT_AUTODICT: i32 = 0x10000000_u32 as i32;
#[allow(clippy::cast_possible_wrap)] #[allow(clippy::cast_possible_wrap)]
const FS_ERROR_MAP_SIZE: i32 = 1_u32 as i32; const FS_ERROR_MAP_SIZE: i32 = 1_u32 as i32;
@ -280,6 +292,10 @@ impl Drop for Forkserver {
} }
} }
const fn fs_opt_get_mapsize(x: i32) -> i32 {
((x & 0x00fffffe) >> 1) + 1
}
#[allow(clippy::fn_params_excessive_bools)] #[allow(clippy::fn_params_excessive_bools)]
impl Forkserver { impl Forkserver {
/// Create a new [`Forkserver`] /// Create a new [`Forkserver`]
@ -343,7 +359,6 @@ impl Forkserver {
}; };
let mut command = Command::new(target); let mut command = Command::new(target);
// Setup args, stdio // Setup args, stdio
command command
.args(args) .args(args)
@ -627,7 +642,10 @@ pub struct ForkserverExecutorBuilder<'a, SP> {
crash_exitcode: Option<i8>, crash_exitcode: Option<i8>,
} }
impl<'a, SP> ForkserverExecutorBuilder<'a, SP> { impl<'a, SP> ForkserverExecutorBuilder<'a, SP>
where
SP: ShMemProvider,
{
/// Builds `ForkserverExecutor`. /// Builds `ForkserverExecutor`.
/// This Forkserver will attempt to provide inputs over shared mem when `shmem_provider` is given. /// This Forkserver will attempt to provide inputs over shared mem when `shmem_provider` is given.
/// Else this forkserver will pass the input to the target via `stdin` /// Else this forkserver will pass the input to the target via `stdin`
@ -814,27 +832,47 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
report_error_and_exit(version_status & 0x0000ffff)?; report_error_and_exit(version_status & 0x0000ffff)?;
} }
let keep = version_status; if Self::is_old_forkserver(version_status) {
let version: u32 = version_status as u32 - 0x41464c00_u32; log::info!("Old fork server model is used by the target, this still works though.");
if (0x41464c00..=0x41464cff).contains(&version_status) { self.initialize_old_forkserver(version_status, &map, &mut forkserver)?;
match version { } else {
0 => { self.initialize_forkserver(version_status, &map, &mut forkserver)?;
return Err(Error::unknown("Fork server version is not assigned, this should not happen. Recompile target.")); }
} Ok((forkserver, input_file, map))
FS_NEW_VERSION_MIN..=FS_NEW_VERSION_MAX => { }
// good, do nothing
} fn is_old_forkserver(version_status: i32) -> bool {
_ => { !(0x41464c00..0x41464cff).contains(&version_status)
return Err(Error::unknown( }
"Fork server version is not supported. Recompile the target.",
)); /// Intialize forkserver > v4.20c
} #[allow(clippy::cast_possible_wrap)]
#[allow(clippy::cast_sign_loss)]
fn initialize_forkserver(
&mut self,
status: i32,
map: &Option<SP::ShMem>,
forkserver: &mut Forkserver,
) -> Result<(), Error> {
let keep = status;
let version: u32 = status as u32 - 0x41464c00_u32;
match version {
0 => {
return Err(Error::unknown("Fork server version is not assigned, this should not happen. Recompile target."));
}
FS_NEW_VERSION_MIN..=FS_NEW_VERSION_MAX => {
// good, do nothing
}
_ => {
return Err(Error::unknown(
"Fork server version is not supported. Recompile the target.",
));
} }
} }
let xored_version_status = (version_status as u32 ^ 0xffffffff) as i32; let xored_status = (status as u32 ^ 0xffffffff) as i32;
let send_len = forkserver.write_ctl(xored_version_status)?; let send_len = forkserver.write_ctl(xored_status)?;
if send_len != 4 { if send_len != 4 {
return Err(Error::unknown("Writing to forkserver failed.".to_string())); return Err(Error::unknown("Writing to forkserver failed.".to_string()));
} }
@ -852,29 +890,13 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
} }
if status & FS_NEW_OPT_MAPSIZE == FS_NEW_OPT_MAPSIZE { if status & FS_NEW_OPT_MAPSIZE == FS_NEW_OPT_MAPSIZE {
// When 0, we assume that map_size was filled by the user or const let (read_len, fsrv_map_size) = forkserver.read_st()?;
/* TODO autofill map size from the observer
if map_size > 0 {
self.map_size = Some(map_size as usize);
}
*/
let (read_len, mut map_size) = forkserver.read_st()?;
if read_len != 4 { if read_len != 4 {
return Err(Error::unknown( return Err(Error::unknown(
"Failed to read map size from forkserver".to_string(), "Failed to read map size from forkserver".to_string(),
)); ));
} }
self.set_map_size(fsrv_map_size);
if map_size % 64 != 0 {
map_size = ((map_size + 63) >> 6) << 6;
}
// TODO set AFL_MAP_SIZE
assert!(self.map_size.is_none() || map_size as usize <= self.map_size.unwrap());
// we'll use this later when we truncate the observer
self.map_size = Some(map_size as usize);
} }
if status & FS_NEW_OPT_SHDMEM_FUZZ != 0 { if status & FS_NEW_OPT_SHDMEM_FUZZ != 0 {
@ -921,14 +943,111 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
return Err(Error::unknown("Reading from forkserver failed".to_string())); return Err(Error::unknown("Reading from forkserver failed".to_string()));
} }
if aflx != version_status { if aflx != keep {
return Err(Error::unknown(format!( return Err(Error::unknown(format!(
"Error in forkserver communication ({:x}=>{:x})", "Error in forkserver communication ({aflx:?}=>{keep:?})",
keep, aflx
))); )));
} }
Ok(())
}
Ok((forkserver, input_file, map)) /// Intialize old forkserver. < v4.20c
#[allow(clippy::cast_possible_wrap)]
#[allow(clippy::cast_sign_loss)]
fn initialize_old_forkserver(
&mut self,
status: i32,
map: &Option<SP::ShMem>,
forkserver: &mut Forkserver,
) -> Result<(), Error> {
if status & FS_OPT_ENABLED == FS_OPT_ENABLED && status & FS_OPT_MAPSIZE == FS_OPT_MAPSIZE {
let fsrv_map_size = fs_opt_get_mapsize(status);
self.set_map_size(fsrv_map_size);
}
// Only with SHMEM or AUTODICT we can send send_status back or it breaks!
// If forkserver is responding, we then check if there's any option enabled.
// We'll send 4-bytes message back to the forkserver to tell which features to use
// The forkserver is listening to our response if either shmem fuzzing is enabled or auto dict is enabled
// <https://github.com/AFLplusplus/AFLplusplus/blob/147654f8715d237fe45c1657c87b2fe36c4db22a/instrumentation/afl-compiler-rt.o.c#L1026>
if status & FS_OPT_ENABLED == FS_OPT_ENABLED
&& (status & FS_OPT_SHDMEM_FUZZ == FS_OPT_SHDMEM_FUZZ
|| status & FS_OPT_AUTODICT == FS_OPT_AUTODICT)
{
let mut send_status = FS_OPT_ENABLED;
if (status & FS_OPT_SHDMEM_FUZZ == FS_OPT_SHDMEM_FUZZ) && map.is_some() {
log::info!("Using SHARED MEMORY FUZZING feature.");
send_status |= FS_OPT_SHDMEM_FUZZ;
self.uses_shmem_testcase = true;
}
if (status & FS_OPT_AUTODICT == FS_OPT_AUTODICT) && self.autotokens.is_some() {
log::info!("Using AUTODICT feature");
send_status |= FS_OPT_AUTODICT;
}
if send_status != FS_OPT_ENABLED {
// if send_status is not changed (Options are available but we didn't use any), then don't send the next write_ctl message.
// This is important
let send_len = forkserver.write_ctl(send_status)?;
if send_len != 4 {
return Err(Error::unknown("Writing to forkserver failed.".to_string()));
}
if (send_status & FS_OPT_AUTODICT) == FS_OPT_AUTODICT {
let (read_len, dict_size) = forkserver.read_st()?;
if read_len != 4 {
return Err(Error::unknown(
"Reading from forkserver failed.".to_string(),
));
}
if !(2..=0xffffff).contains(&dict_size) {
return Err(Error::illegal_state(
"Dictionary has an illegal size".to_string(),
));
}
log::info!("Autodict size {dict_size:x}");
let (rlen, buf) = forkserver.read_st_size(dict_size as usize)?;
if rlen != dict_size as usize {
return Err(Error::unknown("Failed to load autodictionary".to_string()));
}
if let Some(t) = &mut self.autotokens {
t.parse_autodict(&buf, dict_size as usize);
}
}
}
} else {
log::warn!("Forkserver Options are not available.");
}
Ok(())
}
#[allow(clippy::cast_sign_loss)]
fn set_map_size(&mut self, fsrv_map_size: i32) {
// When 0, we assume that map_size was filled by the user or const
/* TODO autofill map size from the observer
if fsrv_map_size > 0 {
self.map_size = Some(fsrv_map_size as usize);
}
*/
let mut map_size = fsrv_map_size;
if map_size % 64 != 0 {
map_size = ((map_size + 63) >> 6) << 6;
}
// TODO set AFL_MAP_SIZE
assert!(self.map_size.is_none() || map_size as usize <= self.map_size.unwrap());
// we'll use this later when we truncate the observer
self.map_size = Some(map_size as usize);
} }
/// Use autodict? /// Use autodict?