Libafl-fuzz: introduce unicorn mode (#2499)
* libafl-fuzz: introduce unicorn mode * taplo format * libafl-fuzz: fix qemumode * taplo format
This commit is contained in:
parent
a388012429
commit
07db74b416
@ -12,7 +12,7 @@ FUZZER = '${CARGO_TARGET_DIR}/${PROFILE_DIR}/${FUZZER_NAME}'
|
|||||||
LLVM_CONFIG = { value = "llvm-config-18", condition = { env_not_set = [
|
LLVM_CONFIG = { value = "llvm-config-18", condition = { env_not_set = [
|
||||||
"LLVM_CONFIG",
|
"LLVM_CONFIG",
|
||||||
] } }
|
] } }
|
||||||
AFL_VERSION = "db23931e7c1727ddac8691a6241c97b2203ec6fc"
|
AFL_VERSION = "598a3c6b5e24bd33e84b914e145810d39f88adf6"
|
||||||
AFL_DIR = { value = "./AFLplusplus" }
|
AFL_DIR = { value = "./AFLplusplus" }
|
||||||
AFL_CC_PATH = { value = "${AFL_DIR}/afl-clang-fast" }
|
AFL_CC_PATH = { value = "${AFL_DIR}/afl-clang-fast" }
|
||||||
CC = { value = "clang" }
|
CC = { value = "clang" }
|
||||||
@ -39,6 +39,16 @@ cd ${AFL_DIR}/qemu_mode
|
|||||||
cd ../..
|
cd ../..
|
||||||
'''
|
'''
|
||||||
dependencies = ["build_afl"]
|
dependencies = ["build_afl"]
|
||||||
|
|
||||||
|
[tasks.build_unicorn_mode]
|
||||||
|
script_runner = "@shell"
|
||||||
|
script = '''
|
||||||
|
cd ${AFL_DIR}/unicorn_mode
|
||||||
|
./build_unicorn_support.sh
|
||||||
|
cd ../..
|
||||||
|
'''
|
||||||
|
dependencies = ["build_afl"]
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
[tasks.test]
|
[tasks.test]
|
||||||
linux_alias = "test_unix"
|
linux_alias = "test_unix"
|
||||||
@ -54,6 +64,7 @@ dependencies = [
|
|||||||
"test_cmplog",
|
"test_cmplog",
|
||||||
"test_frida",
|
"test_frida",
|
||||||
"test_qemu",
|
"test_qemu",
|
||||||
|
"test_unicorn_mode",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tasks.build_libafl_fuzz]
|
[tasks.build_libafl_fuzz]
|
||||||
@ -115,20 +126,20 @@ export AFL_PATH=${AFL_DIR}
|
|||||||
export AFL_CORES=1
|
export AFL_CORES=1
|
||||||
export AFL_STATS_INTERVAL=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
|
timeout 5 ${FUZZER} -m 0 -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_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 -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}'`
|
||||||
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 -O -i ./test/seeds_frida -o ./test/output-frida-persistent -- ./test/out-frida || true
|
||||||
|
|
||||||
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"
|
||||||
@ -163,14 +174,14 @@ export AFL_PATH=${AFL_DIR}
|
|||||||
export AFL_CORES=1
|
export AFL_CORES=1
|
||||||
export AFL_STATS_INTERVAL=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
|
timeout 5 ${FUZZER} -m 0 -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 )" || {
|
test -n "$( ls ./test/output-qemu/fuzzer_main/queue/id:000002* 2>/dev/null )" || {
|
||||||
echo "No new corpus entries found for QEMU mode"
|
echo "No new corpus entries found for QEMU mode"
|
||||||
exit 1
|
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}'`
|
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
|
timeout 5 ${FUZZER} -m 0 -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 )" || {
|
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"
|
echo "No new corpus entries found for QEMU mode with AFL_ENTRYPOINT"
|
||||||
exit 1
|
exit 1
|
||||||
@ -179,7 +190,7 @@ unset AFL_ENTRYPOINT
|
|||||||
|
|
||||||
export AFL_PRELOAD=${AFL_DIR}/libcompcov.so
|
export AFL_PRELOAD=${AFL_DIR}/libcompcov.so
|
||||||
export AFL_COMPCOV_LEVEL=2
|
export AFL_COMPCOV_LEVEL=2
|
||||||
timeout 5 ${FUZZER} -V07 -Q -i ./test/seeds_qemu -o ./test/output-qemu-cmpcov -- ./test/out-qemu-cmpcov || true
|
timeout 5 ${FUZZER} -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 )" || {
|
test -n "$( ls ./test/output-qemu-cmpcov/fuzzer_main/queue/id:000002* 2>/dev/null )" || {
|
||||||
echo "No new corpus entries found for QEMU mode"
|
echo "No new corpus entries found for QEMU mode"
|
||||||
exit 1
|
exit 1
|
||||||
@ -187,6 +198,27 @@ test -n "$( ls ./test/output-qemu-cmpcov/fuzzer_main/queue/id:000002* 2>/dev/nul
|
|||||||
'''
|
'''
|
||||||
dependencies = ["build_afl", "build_qemuafl", "build_libafl_fuzz"]
|
dependencies = ["build_afl", "build_qemuafl", "build_libafl_fuzz"]
|
||||||
|
|
||||||
|
[tasks.test_unicorn_mode]
|
||||||
|
script_runner = "@shell"
|
||||||
|
script = '''
|
||||||
|
export AFL_PATH=${AFL_DIR}
|
||||||
|
export AFL_CORES=1
|
||||||
|
export AFL_STATS_INTERVAL=1
|
||||||
|
# TODO: test unicorn persistent mode once it's fixed on AFL++
|
||||||
|
LIBAFL_DEBUG_OUTPUT=1 AFL_DEBUG=1 AFL_DEBUG_CHILD=1 timeout 15s ${FUZZER} -m 0 -U -i ./test/seeds_unicorn -o ./test/output-unicorn-python -- python3 ${AFL_DIR}/unicorn_mode/samples/python_simple/simple_test_harness.py @@ || true
|
||||||
|
test -n "$( ls ./test/output-unicorn-python/fuzzer_main/queue/id:000003* 2>/dev/null )" || {
|
||||||
|
echo "No new corpus entries found for Unicorn python3 mode"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
export AFL_COMPCOV_LEVEL=2
|
||||||
|
LIBAFL_DEBUG_OUTPUT=1 AFL_DEBUG=1 AFL_DEBUG_CHILD=1 timeout 15s ${FUZZER} -m 0 -U -i ./test/seeds_unicorn_cmpcov -o ./test/output-unicorn-cmpcov -- python3 ${AFL_DIR}/unicorn_mode/samples/compcov_x64/compcov_test_harness.py @@ || true
|
||||||
|
test -n "$( ls ./test/output-unicorn-cmpcov/fuzzer_main/queue/id:000002* 2>/dev/null )" || {
|
||||||
|
echo "No new corpus entries found for Unicorn cmpcov mode"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
dependencies = ["build_libafl_fuzz", "build_afl", "build_unicorn_mode"]
|
||||||
|
|
||||||
[tasks.clean]
|
[tasks.clean]
|
||||||
linux_alias = "clean_unix"
|
linux_alias = "clean_unix"
|
||||||
mac_alias = "clean_unix"
|
mac_alias = "clean_unix"
|
||||||
@ -196,11 +228,11 @@ windows_alias = "unsupported"
|
|||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
script = '''
|
script = '''
|
||||||
rm -rf AFLplusplus
|
rm -rf AFLplusplus
|
||||||
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-cmplog
|
rm -rf ./test/output-cmplog
|
||||||
rm -rf ./test/output-qemu*
|
rm -rf ./test/output-qemu*
|
||||||
|
rm -rf ./test/output-unicorn*
|
||||||
rm ./test/out-*
|
rm ./test/out-*
|
||||||
'''
|
'''
|
||||||
|
@ -53,7 +53,8 @@ use crate::{
|
|||||||
},
|
},
|
||||||
scheduler::SupportedSchedulers,
|
scheduler::SupportedSchedulers,
|
||||||
stages::{mutational_stage::SupportedMutationalStages, time_tracker::TimeTrackingStageWrapper},
|
stages::{mutational_stage::SupportedMutationalStages, time_tracker::TimeTrackingStageWrapper},
|
||||||
Opt, AFL_DEFAULT_INPUT_LEN_MAX, AFL_DEFAULT_INPUT_LEN_MIN, SHMEM_ENV_VAR,
|
Opt, AFL_DEFAULT_INPUT_LEN_MAX, AFL_DEFAULT_INPUT_LEN_MIN, AFL_HARNESS_FILE_INPUT,
|
||||||
|
SHMEM_ENV_VAR,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type LibaflFuzzState =
|
pub type LibaflFuzzState =
|
||||||
@ -234,7 +235,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the base Executor
|
// Create the base Executor
|
||||||
let mut executor = base_executor(opt, &mut shmem_provider);
|
let mut executor = base_executor(opt, &mut shmem_provider, fuzzer_dir)?;
|
||||||
// Set a custom exit code to be interpreted as a Crash if configured.
|
// Set a custom exit code to be interpreted as a Crash if configured.
|
||||||
if let Some(crash_exitcode) = opt.crash_exitcode {
|
if let Some(crash_exitcode) = opt.crash_exitcode {
|
||||||
executor = executor.crash_exitcode(crash_exitcode);
|
executor = executor.crash_exitcode(crash_exitcode);
|
||||||
@ -245,24 +246,6 @@ where
|
|||||||
executor = executor.autotokens(&mut tokens);
|
executor = executor.autotokens(&mut tokens);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set a custom directory for the current_input file if configured;
|
|
||||||
// Relevant only if harness input type is @@
|
|
||||||
if opt.harness_input_type.is_some() {
|
|
||||||
let mut file = get_unique_std_input_file();
|
|
||||||
if let Some(ext) = &opt.input_ext {
|
|
||||||
file = format!("{file}.{ext}");
|
|
||||||
}
|
|
||||||
if let Some(cur_input_dir) = &opt.cur_input_dir {
|
|
||||||
executor = executor.arg_input_file(cur_input_dir.join(file));
|
|
||||||
} else {
|
|
||||||
executor = executor.arg_input_file(fuzzer_dir.join(file));
|
|
||||||
}
|
|
||||||
} else if opt.cur_input_dir.is_some() {
|
|
||||||
return Err(Error::illegal_argument(
|
|
||||||
"cannot use AFL_TMPDIR with stdin input type.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finalize and build our Executor
|
// Finalize and build our Executor
|
||||||
let mut executor = executor
|
let mut executor = executor
|
||||||
.build(tuple_list!(time_observer, edges_observer))
|
.build(tuple_list!(time_observer, edges_observer))
|
||||||
@ -339,7 +322,7 @@ where
|
|||||||
|
|
||||||
// Create the CmpLog executor.
|
// Create the CmpLog executor.
|
||||||
// Cmplog has 25% execution overhead so we give it double the timeout
|
// Cmplog has 25% execution overhead so we give it double the timeout
|
||||||
let cmplog_executor = base_executor(opt, &mut shmem_provider)
|
let cmplog_executor = base_executor(opt, &mut shmem_provider, fuzzer_dir)?
|
||||||
.timeout(Duration::from_millis(opt.hang_timeout * 2))
|
.timeout(Duration::from_millis(opt.hang_timeout * 2))
|
||||||
.program(cmplog_executable_path)
|
.program(cmplog_executable_path)
|
||||||
.build(tuple_list!(cmplog_observer))
|
.build(tuple_list!(cmplog_observer))
|
||||||
@ -410,7 +393,8 @@ where
|
|||||||
fn base_executor<'a>(
|
fn base_executor<'a>(
|
||||||
opt: &'a Opt,
|
opt: &'a Opt,
|
||||||
shmem_provider: &'a mut StdShMemProvider,
|
shmem_provider: &'a mut StdShMemProvider,
|
||||||
) -> ForkserverExecutorBuilder<'a, StdShMemProvider> {
|
fuzzer_dir: &PathBuf,
|
||||||
|
) -> Result<ForkserverExecutorBuilder<'a, StdShMemProvider>, Error> {
|
||||||
let mut executor = ForkserverExecutor::builder()
|
let mut executor = ForkserverExecutor::builder()
|
||||||
.program(opt.executable.clone())
|
.program(opt.executable.clone())
|
||||||
.coverage_map_size(opt.map_size.unwrap_or(AFL_DEFAULT_MAP_SIZE))
|
.coverage_map_size(opt.map_size.unwrap_or(AFL_DEFAULT_MAP_SIZE))
|
||||||
@ -429,24 +413,40 @@ fn base_executor<'a>(
|
|||||||
if opt.is_persistent || opt.qemu_mode {
|
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 {
|
// Set arguments for the target if necessary
|
||||||
executor = executor.parse_afl_cmdline([harness_input_type]);
|
let exec = opt.executable.display().to_string();
|
||||||
|
// we skip all libafl-fuzz arguments.
|
||||||
|
let (mut skip, _) = env::args()
|
||||||
|
.enumerate()
|
||||||
|
.find(|i| i.1 == exec)
|
||||||
|
.expect("invariant; should never occur");
|
||||||
|
// we need the binary to remain as an argument if we are in qemu_mode
|
||||||
|
if !opt.qemu_mode {
|
||||||
|
skip += 1;
|
||||||
|
}
|
||||||
|
let args = env::args().skip(skip);
|
||||||
|
for arg in args {
|
||||||
|
if arg == AFL_HARNESS_FILE_INPUT {
|
||||||
|
let mut file = get_unique_std_input_file();
|
||||||
|
if let Some(ext) = &opt.input_ext {
|
||||||
|
file = format!("{file}.{ext}");
|
||||||
|
}
|
||||||
|
if let Some(cur_input_dir) = &opt.cur_input_dir {
|
||||||
|
executor = executor.arg_input_file(cur_input_dir.join(file));
|
||||||
|
} else {
|
||||||
|
executor = executor.arg_input_file(fuzzer_dir.join(file));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
executor = executor.arg(arg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if opt.qemu_mode {
|
if opt.qemu_mode {
|
||||||
let exec = opt.executable.display().to_string();
|
|
||||||
executor = executor.program(
|
executor = executor.program(
|
||||||
find_afl_binary("afl-qemu-trace", Some(opt.executable.clone()))
|
find_afl_binary("afl-qemu-trace", Some(opt.executable.clone()))
|
||||||
.expect("to find afl-qemu-trace"),
|
.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
|
Ok(executor)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fuzzer_target_mode(opt: &Opt) -> Cow<'static, str> {
|
pub fn fuzzer_target_mode(opt: &Opt) -> Cow<'static, str> {
|
||||||
|
@ -112,9 +112,6 @@ fn main() {
|
|||||||
struct Opt {
|
struct Opt {
|
||||||
executable: PathBuf,
|
executable: PathBuf,
|
||||||
|
|
||||||
#[arg(value_parser = validate_harness_input_stdin)]
|
|
||||||
harness_input_type: Option<&'static str>,
|
|
||||||
|
|
||||||
// NOTE: afl-fuzz does not accept multiple input directories
|
// NOTE: afl-fuzz does not accept multiple input directories
|
||||||
#[arg(short = 'i')]
|
#[arg(short = 'i')]
|
||||||
input_dir: PathBuf,
|
input_dir: PathBuf,
|
||||||
@ -258,13 +255,6 @@ struct Opt {
|
|||||||
non_instrumented_mode: bool,
|
non_instrumented_mode: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_harness_input_stdin(s: &str) -> Result<&'static str, String> {
|
|
||||||
if s != "@@" {
|
|
||||||
return Err("Unknown harness input type. Use \"@@\" for file, omit for stdin ".to_string());
|
|
||||||
}
|
|
||||||
Ok(AFL_HARNESS_FILE_INPUT)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CmplogOpts {
|
pub struct CmplogOpts {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user