Linux kernel fuzzing example (#2496)

* linux kernel (x509_cert) and process fuzzing example

* rework filters

* update to latest qemu

* working for process and kernel fuzzing

* new i2s mutator for binary only fuzzers

* refactoring modules with new filtering interface

* add state as parameter of harness

* hide unused global in usermode

* Script for stub bindings generation

* do not try to check whether it is worth generating the bindings, always
  generate when the env variable is on.

* add taplo to fmt_all.sh

* Moved fuzzers (again) in a target-centric way.

* fix rust 2024 warnings.

* new libafl_qemu harness structure.

* rename qemu_systemmode into qemu_baremetal

* fix qemu baremetal makefile

* fix formatter

---------

Co-authored-by: Toka <tokazerkje@outlook.com>
This commit is contained in:
Romain Malmain 2024-09-26 14:29:33 +02:00 committed by GitHub
parent 5ab7a07f14
commit c944a70056
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
482 changed files with 5430 additions and 1697 deletions

View File

@ -206,7 +206,7 @@ jobs:
- name: Run a maturin build
run: export LLVM_CONFIG=llvm-config-${{env.MAIN_LLVM_VERSION}} && cd ./bindings/pylibafl && python3 -m venv .env && . .env/bin/activate && pip install --upgrade --force-reinstall . && ./test.sh
- name: Run python test
run: . ./bindings/pylibafl/.env/bin/activate # && cd ./fuzzers/qemu/python_qemu/ && python3 fuzzer.py 2>&1 | grep "Bye"
run: . ./bindings/pylibafl/.env/bin/activate # && cd ./fuzzers/binary-only/python_qemu/ && python3 fuzzer.py 2>&1 | grep "Bye"
cargo-fmt:
runs-on: ubuntu-24.04
@ -269,55 +269,53 @@ jobs:
- ./fuzzers/baby/backtrace_baby_fuzzers/command_executor
- ./fuzzers/baby/backtrace_baby_fuzzers/forkserver_executor
# Binary-only
- ./fuzzers/binary-only/fuzzbench_fork_qemu
- ./fuzzers/binary-only/frida_executable_libpng
- ./fuzzers/binary-only/frida_gdiplus
- ./fuzzers/binary-only/frida_libpng
- ./fuzzers/binary-only/fuzzbench_qemu
- ./fuzzers/binary-only/tinyinst_simple
# Forkserver
- ./fuzzers/forkserver/forkserver_simple
- ./fuzzers/forkserver/forkserver_libafl_cc
- ./fuzzers/forkserver/fuzzbench_forkserver
- ./fuzzers/forkserver/fuzzbench_forkserver_cmplog
- ./fuzzers/forkserver/libafl-fuzz
# Frida
- ./fuzzers/frida/frida_executable_libpng
- ./fuzzers/frida/frida_gdiplus
- ./fuzzers/frida/frida_libpng
# Full-system
- ./fuzzers/full-system/nyx_libxml2_standalone
- ./fuzzers/full-system/nyx_libxml2_parallel
# Fuzzbench
- ./fuzzers/fuzzbench/fuzzbench
- ./fuzzers/fuzzbench/fuzzbench_qemu
- ./fuzzers/fuzzbench/fuzzbench_fork_qemu
- ./fuzzers/fuzzbench/fuzzbench_text
- ./fuzzers/fuzzbench/fuzzbench_ctx
- ./fuzzers/fuzzbench/fuzzbench_forkserver_cmplog
- ./fuzzers/fuzzbench/fuzzbench_forkserver
# Grammar-aware
- ./fuzzers/grammar-aware/nautilus_sync
# LibPNG
- ./fuzzers/libpng/libfuzzer_libpng
- ./fuzzers/libpng/libfuzzer_libpng_launcher
- ./fuzzers/libpng/libfuzzer_libpng_accounting
- ./fuzzers/libpng/libfuzzer_libpng_centralized
- ./fuzzers/libpng/libfuzzer_libpng_cmin
- ./fuzzers/libpng/libfuzzer_libpng_norestart
# - ./fuzzers/libpng/libfuzzer_libpng_tcp_manager
# Nyx
- ./fuzzers/nyx/nyx_libxml2_standalone
- ./fuzzers/nyx/nyx_libxml2_parallel
# Stb
- ./fuzzers/stb/libfuzzer_stb_image_sugar
- ./fuzzers/stb/libfuzzer_stb_image
# - ./fuzzers/stb/libfuzzer_stb_image_concolic
# In-process
- ./fuzzers/inprocess/cargo_fuzz
# - ./fuzzers/inprocess/dynamic_analysis
- ./fuzzers/inprocess/fuzzbench
- ./fuzzers/inprocess/fuzzbench_text
- ./fuzzers/inprocess/fuzzbench_ctx
- ./fuzzers/inprocess/libfuzzer_libmozjpeg
- ./fuzzers/inprocess/libfuzzer_libpng
- ./fuzzers/inprocess/libfuzzer_libpng_launcher
- ./fuzzers/inprocess/libfuzzer_libpng_accounting
- ./fuzzers/inprocess/libfuzzer_libpng_centralized
- ./fuzzers/inprocess/libfuzzer_libpng_cmin
- ./fuzzers/inprocess/libfuzzer_libpng_norestart
# - ./fuzzers/inprocess/libfuzzer_libpng_tcp_manager
- ./fuzzers/inprocess/libfuzzer_stb_image_sugar
- ./fuzzers/inprocess/libfuzzer_stb_image
# - ./fuzzers/inprocess/libfuzzer_stb_image_concolic
# - ./fuzzers/inprocess/libfuzzer_windows_asan
- ./fuzzers/inprocess/push_harness
- ./fuzzers/inprocess/push_stage_harness
# - ./fuzzers/inprocess/sqlite_centralized_multi_machine
- ./fuzzers/inprocess/tutorial
# Others
- ./fuzzers/others/cargo_fuzz
# - ./fuzzers/others/dynamic_analysis
- ./fuzzers/others/libafl_atheris
- ./fuzzers/others/libafl-fuzz
- ./fuzzers/others/libfuzzer_libmozjpeg
# - ./fuzzers/others/libfuzzer_windows_asan
- ./fuzzers/others/nautilus_sync
- ./fuzzers/others/push_harness
- ./fuzzers/others/push_stage_harness
# - ./fuzzers/others/sqlite_centralized_multi_machine
- ./fuzzers/others/tinyinst_simple
- ./fuzzers/others/tutorial
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
@ -356,10 +354,15 @@ jobs:
matrix:
os: [ubuntu-24.04]
fuzzer:
- ./fuzzers/qemu/qemu_cmin
- ./fuzzers/qemu/qemu_systemmode
- ./fuzzers/qemu/qemu_coverage
- ./fuzzers/qemu/qemu_launcher
# Binary only
- ./fuzzers/binary-only/qemu_cmin
- ./fuzzers/binary-only/qemu_coverage
- ./fuzzers/binary-only/qemu_launcher
# Full-system
- ./fuzzers/full-system/qemu_baremetal
# - ./fuzzers/full-system/qemu_linux_kernel
#- ./fuzzers/full-system/qemu_linux_process
runs-on: [ self-hosted, qemu ]
container: registry.gitlab.com/qemu-project/qemu/qemu/ubuntu2204:latest
@ -426,8 +429,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/workflows/windows-tester-prepare
- name: Build fuzzers/frida_libpng
run: cd fuzzers/frida/frida_libpng/ && cargo make test
- name: Build fuzzers/binary-only/frida_libpng
run: cd fuzzers/binary-only/frida_libpng/ && cargo make test
windows-frida-libfuzzer-stb-image:
runs-on: windows-latest
@ -436,8 +439,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/workflows/windows-tester-prepare
- name: Build fuzzers/stb/libfuzzer_stb_image
run: cd fuzzers/stb/libfuzzer_stb_image && cargo build --release
- name: Build fuzzers/inprocess/libfuzzer_stb_image
run: cd fuzzers/inprocess/libfuzzer_stb_image && cargo build --release
windows-frida-gdiplus:
runs-on: windows-latest
@ -446,8 +449,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/workflows/windows-tester-prepare
- name: Build fuzzers/frida/frida_gdiplus
run: cd fuzzers/frida/frida_gdiplus/ && cargo make test && cargo make test_cmplog
- name: Build fuzzers/binary-only/frida_gdiplus
run: cd fuzzers/binary-only/frida_gdiplus/ && cargo make test && cargo make test_cmplog
windows-tinyinst-simple:
runs-on: windows-latest
@ -458,8 +461,8 @@ jobs:
run: cargo install cxxbridge-cmd
- uses: actions/checkout@v4
- uses: ./.github/workflows/windows-tester-prepare
- name: Build fuzzers/others/tinyinst_simple
run: cd fuzzers/others/tinyinst_simple/ && cargo make test
- name: Build fuzzers/binary-only/tinyinst_simple
run: cd fuzzers/binary-only/tinyinst_simple/ && cargo make test
windows-clippy:
runs-on: windows-latest

View File

@ -2,6 +2,9 @@
"ignorePatterns": [
{
"pattern": "^https://crates.io"
},
{
"pattern": "^https://github.com/AFLplusplus/linux-qemu-image-builder"
}
],
"aliveStatusCodes": [0, 200, 403]

View File

@ -93,7 +93,7 @@ cargo make run
as long as the fuzzer directory has `Makefile.toml` file.
The best-tested fuzzer is [`./fuzzers/libpng/libfuzzer_libpng`](./fuzzers/libpng/libfuzzer_libpng), a multicore libfuzzer-like fuzzer using LibAFL for a libpng harness.
The best-tested fuzzer is [`./fuzzers/inprocess/libfuzzer_libpng`](./fuzzers/inprocess/libfuzzer_libpng), a multicore libfuzzer-like fuzzer using LibAFL for a libpng harness.
## Resources

View File

@ -6,18 +6,17 @@ You can find here all the example fuzzers built on top of LibAFL.
They are sorted by fuzzer types:
- `baby`: Minimal fuzzers demonstrating a specific feature.
- `binary-only`: Fuzzers for binary-only targets.
- `forkserver`: Fuzzers using a forkserver-style executor.
- `frida`: Fuzzers using [Frida](../libafl_frida).
- `full-system`: Fuzzers for full-system targets (kernels, firmwares, etc...).
- `fuzzbench`: Fuzzbench fuzzers.
- `libpng`: Fuzzers targeting libpng.
- `nyx`: Fuzzers based on [Nyx](../libafl_nyx).
- `others`: Various fuzzers, with no specific categories.
- `qemu`: Fuzzers using [Qemu](../libafl_qemu).
- `stb`: Fuzzers targeting stb.
- `grammar-aware`: Grammar-aware fuzzers.
- `inprocess`: In-process fuzzers, whn they don't fit another more specific type.
- `others`: Fuzzers for specific / specialized things, that do not go in a specific category.
## Paper Artifacts
Multiple papers based on LibAFL have been published alongside artifacts.
Multiple papers based on LibAFL have been published and include artifacts.
Here is a list of LibAFL artifacts:
- Fuzzbench implementation: https://github.com/AFLplusplus/libafl_fuzzbench

View File

Before

Width:  |  Height:  |  Size: 218 B

After

Width:  |  Height:  |  Size: 218 B

View File

Before

Width:  |  Height:  |  Size: 376 B

After

Width:  |  Height:  |  Size: 376 B

View File

Before

Width:  |  Height:  |  Size: 228 B

After

Width:  |  Height:  |  Size: 228 B

View File

Before

Width:  |  Height:  |  Size: 427 B

After

Width:  |  Height:  |  Size: 427 B

View File

Before

Width:  |  Height:  |  Size: 218 B

After

Width:  |  Height:  |  Size: 218 B

View File

Before

Width:  |  Height:  |  Size: 376 B

After

Width:  |  Height:  |  Size: 376 B

View File

Before

Width:  |  Height:  |  Size: 228 B

After

Width:  |  Height:  |  Size: 228 B

View File

Before

Width:  |  Height:  |  Size: 427 B

After

Width:  |  Height:  |  Size: 427 B

View File

Before

Width:  |  Height:  |  Size: 218 B

After

Width:  |  Height:  |  Size: 218 B

View File

Before

Width:  |  Height:  |  Size: 376 B

After

Width:  |  Height:  |  Size: 376 B

View File

Before

Width:  |  Height:  |  Size: 228 B

After

Width:  |  Height:  |  Size: 228 B

View File

Before

Width:  |  Height:  |  Size: 427 B

After

Width:  |  Height:  |  Size: 427 B

View File

@ -52,7 +52,7 @@ make -C libpng-1.6.37
cc -c "${PROJECT_DIR}/libfuzzer_main.c"
# Build the libpng harness
c++ \
../../libpng/libfuzzer_libpng/harness.cc \
../../inprocess/libfuzzer_libpng/harness.cc \
./libpng-1.6.37/.libs/libpng16.a \
./libfuzzer_main.o \
-I./libpng-1.6.37/ \

View File

@ -50,7 +50,7 @@ use libafl_qemu::{
filter_qemu_args,
modules::{
cmplog::{CmpLogChildModule, CmpLogMap, CmpLogObserver},
edges::{EdgeCoverageChildModule, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE},
edges::{StdEdgeCoverageChildModule, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE},
},
Emulator, GuestReg, MmapPerms, QemuExitError, QemuExitReason, QemuForkExecutor,
QemuShutdownCause, Regs,
@ -151,7 +151,7 @@ fn fuzz(
let env: Vec<(String, String)> = env::vars().collect();
let emulator_modules = tuple_list!(
EdgeCoverageChildModule::default(),
StdEdgeCoverageChildModule::builder().build(),
CmpLogChildModule::default(),
);

View File

@ -52,7 +52,7 @@ make -C libpng-1.6.37
cc -c "${PROJECT_DIR}/libfuzzer_main.c"
# Build the libpng harness
c++ \
../../libpng/libfuzzer_libpng/harness.cc \
../../inprocess/libfuzzer_libpng/harness.cc \
./libpng-1.6.37/.libs/libpng16.a \
./libfuzzer_main.o \
-I./libpng-1.6.37/ \
@ -76,7 +76,7 @@ args = [
"./${FUZZER_NAME}",
"--",
"--libafl-in",
"../../libpng/libfuzzer_libpng/corpus",
"../../inprocess/libfuzzer_libpng/corpus",
"--libafl-out",
"./out",
"./${FUZZER_NAME}",

View File

@ -51,7 +51,7 @@ use libafl_qemu::{
// asan::{init_with_asan, QemuAsanHelper},
modules::cmplog::{CmpLogModule, CmpLogObserver},
modules::edges::{
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
edges_map_mut_ptr, StdEdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
},
Emulator,
GuestReg,
@ -326,38 +326,39 @@ fn fuzz(
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| {
let target = input.target_bytes();
let mut buf = target.as_slice();
let mut len = buf.len();
if len > MAX_INPUT_SIZE {
buf = &buf[0..MAX_INPUT_SIZE];
len = MAX_INPUT_SIZE;
}
unsafe {
qemu.write_mem(input_addr, buf);
qemu.write_reg(Regs::Rdi, input_addr).unwrap();
qemu.write_reg(Regs::Rsi, len as GuestReg).unwrap();
qemu.write_reg(Regs::Rip, test_one_input_ptr).unwrap();
qemu.write_reg(Regs::Rsp, stack_ptr).unwrap();
match qemu.run() {
Ok(QemuExitReason::Breakpoint(_)) => {}
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => {
process::exit(0)
}
Err(QemuExitError::UnexpectedExit) => return ExitKind::Crash,
_ => panic!("Unexpected QEMU exit."),
let mut harness =
|_emulator: &mut Emulator<_, _, _, _, _>, _state: &mut _, input: &BytesInput| {
let target = input.target_bytes();
let mut buf = target.as_slice();
let mut len = buf.len();
if len > MAX_INPUT_SIZE {
buf = &buf[0..MAX_INPUT_SIZE];
len = MAX_INPUT_SIZE;
}
}
ExitKind::Ok
};
unsafe {
qemu.write_mem(input_addr, buf);
qemu.write_reg(Regs::Rdi, input_addr).unwrap();
qemu.write_reg(Regs::Rsi, len as GuestReg).unwrap();
qemu.write_reg(Regs::Rip, test_one_input_ptr).unwrap();
qemu.write_reg(Regs::Rsp, stack_ptr).unwrap();
match qemu.run() {
Ok(QemuExitReason::Breakpoint(_)) => {}
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(
Signal::SigInterrupt,
))) => process::exit(0),
Err(QemuExitError::UnexpectedExit) => return ExitKind::Crash,
_ => panic!("Unexpected QEMU exit."),
}
}
ExitKind::Ok
};
let modules = tuple_list!(
EdgeCoverageModule::default(),
StdEdgeCoverageModule::builder().build(),
CmpLogModule::default(),
// QemuAsanHelper::default(asan),
//QemuSnapshotHelper::new()

View File

Before

Width:  |  Height:  |  Size: 218 B

After

Width:  |  Height:  |  Size: 218 B

View File

Before

Width:  |  Height:  |  Size: 376 B

After

Width:  |  Height:  |  Size: 376 B

View File

Before

Width:  |  Height:  |  Size: 228 B

After

Width:  |  Height:  |  Size: 228 B

View File

Before

Width:  |  Height:  |  Size: 427 B

After

Width:  |  Height:  |  Size: 427 B

View File

@ -28,7 +28,7 @@ use libafl_bolts::{
};
use libafl_qemu::{
elf::EasyElf,
modules::edges::{EdgeCoverageChildModule, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE},
modules::edges::{StdEdgeCoverageChildModule, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE},
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExitError,
QemuExitReason, QemuForkExecutor, QemuShutdownCause, Regs,
};
@ -218,7 +218,7 @@ pub fn fuzz() -> Result<(), Error> {
ExitKind::Ok
};
let modules = tuple_list!(EdgeCoverageChildModule::default(),);
let modules = tuple_list!(StdEdgeCoverageChildModule::builder().build());
let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;

View File

Before

Width:  |  Height:  |  Size: 218 B

After

Width:  |  Height:  |  Size: 218 B

View File

Before

Width:  |  Height:  |  Size: 376 B

After

Width:  |  Height:  |  Size: 376 B

View File

Before

Width:  |  Height:  |  Size: 228 B

After

Width:  |  Height:  |  Size: 228 B

View File

Before

Width:  |  Height:  |  Size: 427 B

After

Width:  |  Height:  |  Size: 427 B

View File

@ -27,7 +27,7 @@ use libafl_bolts::{
};
use libafl_qemu::{
elf::EasyElf,
modules::{drcov::DrCovModule, QemuInstrumentationAddressRangeFilter},
modules::{drcov::DrCovModule, StdAddressFilter},
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExecutor,
QemuExitReason, QemuRWError, QemuShutdownCause, Regs,
};
@ -175,18 +175,19 @@ pub fn fuzz() {
}
};
let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| {
let target = input.target_bytes();
let mut buf = target.as_slice();
let mut len = buf.len();
if len > MAX_INPUT_SIZE {
buf = &buf[0..MAX_INPUT_SIZE];
len = MAX_INPUT_SIZE;
}
let len = len as GuestReg;
reset(buf, len).unwrap();
ExitKind::Ok
};
let mut harness =
|_emulator: &mut Emulator<_, _, _, _, _>, _state: &mut _, input: &BytesInput| {
let target = input.target_bytes();
let mut buf = target.as_slice();
let mut len = buf.len();
if len > MAX_INPUT_SIZE {
buf = &buf[0..MAX_INPUT_SIZE];
len = MAX_INPUT_SIZE;
}
let len = len as GuestReg;
reset(buf, len).unwrap();
ExitKind::Ok
};
let mut run_client =
|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _, _>, core_id| {
@ -233,7 +234,7 @@ pub fn fuzz() {
cov_path.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}"));
let emulator_modules = tuple_list!(DrCovModule::new(
QemuInstrumentationAddressRangeFilter::None,
StdAddressFilter::default(),
cov_path,
false,
));

View File

Before

Width:  |  Height:  |  Size: 218 B

After

Width:  |  Height:  |  Size: 218 B

View File

Before

Width:  |  Height:  |  Size: 376 B

After

Width:  |  Height:  |  Size: 376 B

View File

Before

Width:  |  Height:  |  Size: 228 B

After

Width:  |  Height:  |  Size: 228 B

View File

Before

Width:  |  Height:  |  Size: 427 B

After

Width:  |  Height:  |  Size: 427 B

View File

@ -16,8 +16,8 @@ use libafl_qemu::{
asan::{init_qemu_with_asan, AsanModule},
asan_guest::{init_qemu_with_asan_guest, AsanGuestModule},
cmplog::CmpLogModule,
edges::EdgeCoverageModule,
QemuInstrumentationAddressRangeFilter,
edges::StdEdgeCoverageModule,
StdAddressFilter,
},
ArchExtras, GuestAddr, Qemu,
};
@ -68,7 +68,7 @@ impl<'a> Client<'a> {
}
#[allow(clippy::similar_names)] // elf != self
fn coverage_filter(&self, qemu: &Qemu) -> Result<QemuInstrumentationAddressRangeFilter, Error> {
fn coverage_filter(&self, qemu: &Qemu) -> Result<StdAddressFilter, Error> {
/* Conversion is required on 32-bit targets, but not on 64-bit ones */
if let Some(includes) = &self.options.include {
#[cfg_attr(target_pointer_width = "64", allow(clippy::useless_conversion))]
@ -79,7 +79,7 @@ impl<'a> Client<'a> {
end: x.end.into(),
})
.collect::<Vec<Range<GuestAddr>>>();
Ok(QemuInstrumentationAddressRangeFilter::AllowList(rules))
Ok(StdAddressFilter::allow_list(rules))
} else if let Some(excludes) = &self.options.exclude {
#[cfg_attr(target_pointer_width = "64", allow(clippy::useless_conversion))]
let rules = excludes
@ -89,16 +89,14 @@ impl<'a> Client<'a> {
end: x.end.into(),
})
.collect::<Vec<Range<GuestAddr>>>();
Ok(QemuInstrumentationAddressRangeFilter::DenyList(rules))
Ok(StdAddressFilter::deny_list(rules))
} else {
let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer)?;
let range = elf
.get_section(".text", qemu.load_addr())
.ok_or_else(|| Error::key_not_found("Failed to find .text section"))?;
Ok(QemuInstrumentationAddressRangeFilter::AllowList(vec![
range,
]))
Ok(StdAddressFilter::allow_list(vec![range]))
}
}
@ -167,7 +165,9 @@ impl<'a> Client<'a> {
let is_cmplog = self.options.is_cmplog_core(core_id);
let edge_coverage_module = EdgeCoverageModule::new(self.coverage_filter(&qemu)?);
let edge_coverage_module = StdEdgeCoverageModule::builder()
.address_filter(self.coverage_filter(&qemu)?)
.build();
let instance = Instance::builder()
.options(self.options)

Some files were not shown because too many files have changed in this diff Show More