Skip to content

Commit

Permalink
Nyx hypercall API support for LibAFL QEMU (#2801)
Browse files Browse the repository at this point in the history
* Nyx hypercall API support

* fix linux kernel fuzzer

* hash_me -> hash_64_fast

* fix multiple bug in kernel harness

* do not check libmozjpeg's C files format.
  • Loading branch information
rmalmain authored Jan 6, 2025
1 parent 7c8708d commit 17336dc
Show file tree
Hide file tree
Showing 53 changed files with 5,352 additions and 1,252 deletions.
4 changes: 2 additions & 2 deletions fuzzers/binary_only/qemu_coverage/src/fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ use libafl_bolts::{
};
use libafl_qemu::{
elf::EasyElf,
modules::{drcov::DrCovModule, StdAddressFilter},
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, QemuExecutor,
modules::{drcov::DrCovModule, utils::filters::StdAddressFilter},
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExecutor,
QemuExitReason, QemuRWError, QemuShutdownCause, Regs,
};

Expand Down
6 changes: 4 additions & 2 deletions fuzzers/binary_only/qemu_launcher/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ use libafl_bolts::{
use libafl_qemu::{
elf::EasyElf,
modules::{
cmplog::CmpLogObserver, edges::EdgeCoverageFullVariant, EdgeCoverageModule, EmulatorModule,
EmulatorModuleTuple, NopPageFilter, StdAddressFilter, StdEdgeCoverageModule,
cmplog::CmpLogObserver,
edges::EdgeCoverageFullVariant,
utils::filters::{NopPageFilter, StdAddressFilter},
EdgeCoverageModule, EmulatorModule, EmulatorModuleTuple, StdEdgeCoverageModule,
},
Emulator, GuestAddr, Qemu, QemuExecutor,
};
Expand Down
14 changes: 5 additions & 9 deletions fuzzers/full_system/qemu_linux_kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,15 @@ lto = "fat"
codegen-units = 1

[dependencies]
libafl = { path = "../../../../../libafl" }
libafl_bolts = { path = "../../../../../libafl_bolts" }
libafl_qemu = { path = "../../../../../libafl_qemu", features = [
"x86_64",
"systemmode",
#"paranoid_debug"
] }
libafl_qemu_sys = { path = "../../../../../libafl_qemu/libafl_qemu_sys", features = [
libafl = { path = "../../../libafl" }
libafl_bolts = { path = "../../../libafl_bolts" }
libafl_qemu = { path = "../../../libafl_qemu", default-features = false, features = [
"x86_64",
"systemmode",
#"paranoid_debug"
] }
libafl_targets = { path = "../../../libafl_targets" }
env_logger = "0.11.5"

[build-dependencies]
libafl_qemu_build = { path = "../../../../../libafl_qemu/libafl_qemu_build" }
libafl_qemu_build = { path = "../../../libafl_qemu/libafl_qemu_build" }
19 changes: 11 additions & 8 deletions fuzzers/full_system/qemu_linux_kernel/Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -100,20 +100,23 @@ else
LIBAFL_QEMU_BIOS_DIR=${LIBAFL_QEMU_CLONE_DIR}/build/qemu-bundle/usr/local/share/qemu
fi
${TARGET_DIR}/${PROFILE_DIR}/qemu_systemmode_linux_kernel \
cp ${LINUX_BUILDER_OUT}/OVMF_CODE.4m.fd ${LINUX_BUILDER_OUT}/OVMF_CODE.fd.clone
cp ${LINUX_BUILDER_OUT}/OVMF_VARS.4m.fd ${LINUX_BUILDER_OUT}/OVMF_VARS.fd.clone
cp ${LINUX_BUILDER_OUT}/linux.qcow2 ${LINUX_BUILDER_OUT}/linux.qcow2.clone
${TARGET_DIR}/${PROFILE_DIR}/qemu_linux_kernel \
-accel tcg \
-m 4G \
-drive if=pflash,format=raw,readonly=on,file="${LINUX_BUILDER_OUT}/OVMF_CODE.fd" \
-drive if=pflash,format=raw,snapshot=off,file="${LINUX_BUILDER_OUT}/OVMF_VARS.fd" \
-blockdev filename="${LINUX_BUILDER_OUT}/linux.qcow2",node-name=storage,driver=file \
-blockdev driver=qcow2,file=storage,node-name=disk \
-device virtio-scsi-pci,id=scsi0 \
-drive if=pflash,format=raw,file="${LINUX_BUILDER_OUT}/OVMF_CODE.4m.fd" `# OVMF code pflash` \
-drive if=pflash,format=raw,file="${LINUX_BUILDER_OUT}/OVMF_VARS.4m.fd" `# OVMF vars pflash` \
-device virtio-scsi-pci,id=scsi0 `# SCSI bus` \
-device scsi-hd,bus=scsi0.0,drive=disk,id=virtio-disk0,bootindex=1 \
-blockdev driver=file,filename="${LINUX_BUILDER_OUT}/linux.qcow2",node-name=storage `# Backend file of "disk"` \
-blockdev driver=qcow2,file=storage,node-name=disk `# QCOW2 "disk"` \
-L "${LIBAFL_QEMU_BIOS_DIR}" \
-nographic \
-monitor null \
-serial null \
-snapshot
-serial null
'''

[tasks.debug]
Expand Down
6 changes: 3 additions & 3 deletions fuzzers/full_system/qemu_linux_kernel/setup/Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
obj-m += harness.o

all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
make -C /lib/modules/$(LINUX_MODULES)/build M=$(PWD) modules
gcc -Wall -Werror -o user user.c

clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
rm user
make -C /lib/modules/$(LINUX_MODULES)/build M=$(PWD) clean
rm -f user
14 changes: 12 additions & 2 deletions fuzzers/full_system/qemu_linux_kernel/setup/harness.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ static int harness_find_kallsyms_lookup(void) {

lqprintf("kallsyms_lookup_name address = 0x%lx\n", kln_addr);

if (kln_addr == 0) { return -1; }

kln_pointer = (unsigned long (*)(const char *name))kln_addr;

return ret;
Expand All @@ -108,6 +110,8 @@ static int __init harness_init(void) {

err = alloc_chrdev_region(&dev, 0, 1, "harness");

if (err < 0) { return err; }

dev_major = MAJOR(dev);

harness_class = class_create("harness");
Expand All @@ -120,7 +124,9 @@ static int __init harness_init(void) {

device_create(harness_class, NULL, MKDEV(dev_major, 0), NULL, "harness");

harness_find_kallsyms_lookup();
err = harness_find_kallsyms_lookup();

if (err < 0) { return err; }

return 0;
}
Expand All @@ -135,7 +141,6 @@ static void __exit harness_exit(void) {
}

static int harness_open(struct inode *inode, struct file *file) {
int ret;
lqprintf("harness: Device open\n");

char *data = kzalloc(BUF_SIZE, GFP_KERNEL);
Expand All @@ -144,6 +149,11 @@ static int harness_open(struct inode *inode, struct file *file) {
unsigned long x509_fn_addr = kln_pointer("x509_cert_parse");
lqprintf("harness: x509 fn addr: 0x%lx\n", x509_fn_addr);

if (x509_fn_addr == 0) {
lqprintf("harness: Error: x509 function not found.\n");
return -1;
}

// TODO: better filtering...
libafl_qemu_trace_vaddr_size(x509_fn_addr, 0x1000);

Expand Down
6 changes: 4 additions & 2 deletions fuzzers/full_system/qemu_linux_kernel/setup/setup.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/bin/bash

LINUX_MODULES=$(pacman -Ql linux-headers | grep -m 1 -E '/usr/lib/modules/[^/]*/' | sed 's|.*/usr/lib/modules/\([^/]*\)/.*|\1|')
export LINUX_MODULES

cd /setup
make clean
make -j
ls /setup
make -j
39 changes: 16 additions & 23 deletions fuzzers/full_system/qemu_linux_kernel/src/fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ use libafl::{
fuzzer::{Fuzzer, StdFuzzer},
inputs::BytesInput,
monitors::MultiMonitor,
mutators::{
scheduled::{havoc_mutations, StdScheduledMutator},
I2SRandReplaceBinonly,
},
mutators::{havoc_mutations, scheduled::StdScheduledMutator, I2SRandReplaceBinonly},
observers::{CanTrack, HitcountsMapObserver, TimeObserver, VariableMapObserver},
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
stages::{ShadowTracingStage, StdMutationalStage},
Expand All @@ -33,16 +30,10 @@ use libafl_bolts::{
use libafl_qemu::{
emu::Emulator,
executor::QemuExecutor,
modules::{
cmplog::CmpLogObserver,
edges::{
edges_map_mut_ptr, StdEdgeCoverageClassicModule, EDGES_MAP_ALLOCATED_SIZE,
MAX_EDGES_FOUND,
},
CmpLogModule,
},
modules::{cmplog::CmpLogObserver, edges::StdEdgeCoverageClassicModule, CmpLogModule},
// StdEmulatorDriver
};
use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND};

pub fn fuzz() {
env_logger::init();
Expand All @@ -61,9 +52,21 @@ pub fn fuzz() {
// Initialize QEMU
let args: Vec<String> = env::args().collect();

// Create an observation channel using the coverage map
let mut edges_observer = unsafe {
HitcountsMapObserver::new(VariableMapObserver::from_mut_slice(
"edges",
OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_DEFAULT_SIZE),
&raw mut MAX_EDGES_FOUND,
))
.track_indices()
};

// Choose modules to use
let modules = tuple_list!(
StdEdgeCoverageClassicModule::builder().build(),
StdEdgeCoverageClassicModule::builder()
.map_observer(edges_observer.as_mut())
.build()?,
CmpLogModule::default(),
);

Expand All @@ -81,16 +84,6 @@ pub fn fuzz() {
emulator.run(state, input).unwrap().try_into().unwrap()
};

// Create an observation channel using the coverage map
let edges_observer = unsafe {
HitcountsMapObserver::new(VariableMapObserver::from_mut_slice(
"edges",
OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_ALLOCATED_SIZE),
&raw mut MAX_EDGES_FOUND,
))
.track_indices()
};

// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");

Expand Down
13 changes: 6 additions & 7 deletions fuzzers/full_system/qemu_linux_process/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ authors = ["Romain Malmain <[email protected]>"]
edition = "2021"

[features]

## Build and run the target with the Nyx API instead of the built-in LibAFL QEMU API.
nyx = []

shared = ["libafl_qemu/shared"]

[profile.release]
Expand All @@ -16,15 +20,10 @@ codegen-units = 1
[dependencies]
libafl = { path = "../../../libafl" }
libafl_bolts = { path = "../../../libafl_bolts" }
libafl_qemu = { path = "../../../libafl_qemu", features = [
"x86_64",
"systemmode",
# "paranoid_debug"
] }
libafl_qemu_sys = { path = "../../../libafl_qemu/libafl_qemu_sys", features = [
libafl_qemu = { path = "../../../libafl_qemu", default-features = false, features = [
"x86_64",
"systemmode",
# "paranoid_debug"
# "paranoid_debug",
] }
env_logger = "0.11.5"
libafl_targets = { path = "../../../libafl_targets" }
Expand Down
48 changes: 41 additions & 7 deletions fuzzers/full_system/qemu_linux_process/Makefile.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
env_scripts = ['''
#!@duckscript
profile = get_env PROFILE
harness_api = get_env HARNESS_API
if eq ${profile} "dev"
set_env PROFILE_DIR debug
else
set_env PROFILE_DIR ${profile}
end
if eq ${harness_api} "nyx"
set_env FEATURE nyx
else
set_env FEATURE ""
end
''', '''
#!@duckscript
runs_on_ci = get_env RUN_ON_CI
Expand All @@ -25,12 +33,12 @@ TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}"
LIBAFL_QEMU_CLONE_DIR = { value = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge", condition = { env_not_set = [
"LIBAFL_QEMU_DIR",
] } }

LINUX_BUILDER_URL = "[email protected]:AFLplusplus/linux-qemu-image-builder.git"
LINUX_BUILDER_DIR = { value = "${TARGET_DIR}/linux_builder", condition = { env_not_set = [
"LINUX_BUILDER_DIR",
] } }
LINUX_BUILDER_OUT = "${LINUX_BUILDER_DIR}/output"
HARNESS_API = { value = "lqemu", condition = { env_not_set = ["HARNESS_API"] } }

[tasks.target_dir]
condition = { files_not_exist = [
Expand All @@ -51,7 +59,22 @@ script = '''
git clone ${LINUX_BUILDER_URL} ${LINUX_BUILDER_DIR}
'''

[tasks.compile_target]
[tasks.compile_target_nyx]
condition = { env = { "HARNESS_API" = "nyx" } }
dependencies = ["target_dir", "linux_builder_dir"]
command = "clang"
args = [
"-O0",
"-static",
"${WORKING_DIR}/example/harness_nyx.c",
"-o",
"${TARGET_DIR}/runtime/harness",
"-I",
"${TARGET_DIR}/${PROFILE_DIR}/include",
]

[tasks.compile_target_native]
condition = { env = { "HARNESS_API" = "lqemu" } }
dependencies = ["target_dir", "linux_builder_dir"]
command = "clang"
args = [
Expand All @@ -64,6 +87,9 @@ args = [
"${TARGET_DIR}/${PROFILE_DIR}/include",
]

[tasks.compile_target]
dependencies = ["compile_target_native", "compile_target_nyx"]

[tasks.target]
dependencies = ["build", "compile_target"]
script_runner = "@shell"
Expand Down Expand Up @@ -96,7 +122,15 @@ ${LINUX_BUILDER_DIR}/update.sh
[tasks.build]
dependencies = ["target_dir"]
command = "cargo"
args = ["build", "--profile", "${PROFILE}", "--target-dir", "${TARGET_DIR}"]
args = [
"build",
"--profile",
"${PROFILE}",
"--target-dir",
"${TARGET_DIR}",
"--features",
"${FEATURE}",
]

[tasks.run]
dependencies = ["build"]
Expand All @@ -111,15 +145,15 @@ else
LIBAFL_QEMU_BIOS_DIR=${LIBAFL_QEMU_CLONE_DIR}/build/qemu-bundle/usr/local/share/qemu
fi
cp ${LINUX_BUILDER_OUT}/OVMF_CODE.fd ${LINUX_BUILDER_OUT}/OVMF_CODE.fd.clone
cp ${LINUX_BUILDER_OUT}/OVMF_VARS.fd ${LINUX_BUILDER_OUT}/OVMF_VARS.fd.clone
cp ${LINUX_BUILDER_OUT}/OVMF_CODE.4m.fd ${LINUX_BUILDER_OUT}/OVMF_CODE.fd.clone
cp ${LINUX_BUILDER_OUT}/OVMF_VARS.4m.fd ${LINUX_BUILDER_OUT}/OVMF_VARS.fd.clone
cp ${LINUX_BUILDER_OUT}/linux.qcow2 ${LINUX_BUILDER_OUT}/linux.qcow2.clone
${TARGET_DIR}/${PROFILE_DIR}/qemu_linux_process \
-accel tcg \
-m 4G \
-drive if=pflash,format=raw,file="${LINUX_BUILDER_OUT}/OVMF_CODE.fd" `# OVMF code pflash` \
-drive if=pflash,format=raw,file="${LINUX_BUILDER_OUT}/OVMF_VARS.fd" `# OVMF vars pflash` \
-drive if=pflash,format=raw,file="${LINUX_BUILDER_OUT}/OVMF_CODE.4m.fd" `# OVMF code pflash` \
-drive if=pflash,format=raw,file="${LINUX_BUILDER_OUT}/OVMF_VARS.4m.fd" `# OVMF vars pflash` \
-device virtio-scsi-pci,id=scsi0 `# SCSI bus` \
-device scsi-hd,bus=scsi0.0,drive=disk,id=virtio-disk0,bootindex=1 \
-blockdev driver=file,filename="${LINUX_BUILDER_OUT}/linux.qcow2",node-name=storage `# Backend file of "disk"` \
Expand Down
Loading

0 comments on commit 17336dc

Please sign in to comment.