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:
parent
799c634fef
commit
2287afc59b
@ -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-*
|
||||||
'''
|
'''
|
||||||
|
@ -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",
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
fuzzers/others/libafl-fuzz/test/seeds_frida/init
Normal file
1
fuzzers/others/libafl-fuzz/test/seeds_frida/init
Normal file
@ -0,0 +1 @@
|
|||||||
|
00000
|
1
fuzzers/others/libafl-fuzz/test/seeds_qemu/init
Normal file
1
fuzzers/others/libafl-fuzz/test/seeds_qemu/init
Normal file
@ -0,0 +1 @@
|
|||||||
|
00000
|
@ -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?
|
||||||
|
Loading…
x
Reference in New Issue
Block a user