Nyx hypercall API support for LibAFL QEMU (#2801)
* 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.
This commit is contained in:
parent
7c8708d4b1
commit
17336dcf57
@ -30,8 +30,8 @@ use libafl_bolts::{
|
|||||||
};
|
};
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
modules::{drcov::DrCovModule, StdAddressFilter},
|
modules::{drcov::DrCovModule, utils::filters::StdAddressFilter},
|
||||||
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, QemuExecutor,
|
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExecutor,
|
||||||
QemuExitReason, QemuRWError, QemuShutdownCause, Regs,
|
QemuExitReason, QemuRWError, QemuShutdownCause, Regs,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,8 +39,10 @@ use libafl_bolts::{
|
|||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
modules::{
|
modules::{
|
||||||
cmplog::CmpLogObserver, edges::EdgeCoverageFullVariant, EdgeCoverageModule, EmulatorModule,
|
cmplog::CmpLogObserver,
|
||||||
EmulatorModuleTuple, NopPageFilter, StdAddressFilter, StdEdgeCoverageModule,
|
edges::EdgeCoverageFullVariant,
|
||||||
|
utils::filters::{NopPageFilter, StdAddressFilter},
|
||||||
|
EdgeCoverageModule, EmulatorModule, EmulatorModuleTuple, StdEdgeCoverageModule,
|
||||||
},
|
},
|
||||||
Emulator, GuestAddr, Qemu, QemuExecutor,
|
Emulator, GuestAddr, Qemu, QemuExecutor,
|
||||||
};
|
};
|
||||||
|
@ -17,19 +17,15 @@ lto = "fat"
|
|||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../../../../../libafl" }
|
libafl = { path = "../../../libafl" }
|
||||||
libafl_bolts = { path = "../../../../../libafl_bolts" }
|
libafl_bolts = { path = "../../../libafl_bolts" }
|
||||||
libafl_qemu = { path = "../../../../../libafl_qemu", features = [
|
libafl_qemu = { path = "../../../libafl_qemu", default-features = false, features = [
|
||||||
"x86_64",
|
|
||||||
"systemmode",
|
|
||||||
#"paranoid_debug"
|
|
||||||
] }
|
|
||||||
libafl_qemu_sys = { path = "../../../../../libafl_qemu/libafl_qemu_sys", features = [
|
|
||||||
"x86_64",
|
"x86_64",
|
||||||
"systemmode",
|
"systemmode",
|
||||||
#"paranoid_debug"
|
#"paranoid_debug"
|
||||||
] }
|
] }
|
||||||
|
libafl_targets = { path = "../../../libafl_targets" }
|
||||||
env_logger = "0.11.5"
|
env_logger = "0.11.5"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
libafl_qemu_build = { path = "../../../../../libafl_qemu/libafl_qemu_build" }
|
libafl_qemu_build = { path = "../../../libafl_qemu/libafl_qemu_build" }
|
||||||
|
@ -100,20 +100,23 @@ else
|
|||||||
LIBAFL_QEMU_BIOS_DIR=${LIBAFL_QEMU_CLONE_DIR}/build/qemu-bundle/usr/local/share/qemu
|
LIBAFL_QEMU_BIOS_DIR=${LIBAFL_QEMU_CLONE_DIR}/build/qemu-bundle/usr/local/share/qemu
|
||||||
fi
|
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 \
|
-accel tcg \
|
||||||
-m 4G \
|
-m 4G \
|
||||||
-drive if=pflash,format=raw,readonly=on,file="${LINUX_BUILDER_OUT}/OVMF_CODE.fd" \
|
-drive if=pflash,format=raw,file="${LINUX_BUILDER_OUT}/OVMF_CODE.4m.fd" `# OVMF code pflash` \
|
||||||
-drive if=pflash,format=raw,snapshot=off,file="${LINUX_BUILDER_OUT}/OVMF_VARS.fd" \
|
-drive if=pflash,format=raw,file="${LINUX_BUILDER_OUT}/OVMF_VARS.4m.fd" `# OVMF vars pflash` \
|
||||||
-blockdev filename="${LINUX_BUILDER_OUT}/linux.qcow2",node-name=storage,driver=file \
|
-device virtio-scsi-pci,id=scsi0 `# SCSI bus` \
|
||||||
-blockdev driver=qcow2,file=storage,node-name=disk \
|
|
||||||
-device virtio-scsi-pci,id=scsi0 \
|
|
||||||
-device scsi-hd,bus=scsi0.0,drive=disk,id=virtio-disk0,bootindex=1 \
|
-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}" \
|
-L "${LIBAFL_QEMU_BIOS_DIR}" \
|
||||||
-nographic \
|
-nographic \
|
||||||
-monitor null \
|
-monitor null \
|
||||||
-serial null \
|
-serial null
|
||||||
-snapshot
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
[tasks.debug]
|
[tasks.debug]
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
obj-m += harness.o
|
obj-m += harness.o
|
||||||
|
|
||||||
all:
|
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
|
gcc -Wall -Werror -o user user.c
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
make -C /lib/modules/$(LINUX_MODULES)/build M=$(PWD) clean
|
||||||
rm user
|
rm -f user
|
@ -91,6 +91,8 @@ static int harness_find_kallsyms_lookup(void) {
|
|||||||
|
|
||||||
lqprintf("kallsyms_lookup_name address = 0x%lx\n", kln_addr);
|
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;
|
kln_pointer = (unsigned long (*)(const char *name))kln_addr;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -108,6 +110,8 @@ static int __init harness_init(void) {
|
|||||||
|
|
||||||
err = alloc_chrdev_region(&dev, 0, 1, "harness");
|
err = alloc_chrdev_region(&dev, 0, 1, "harness");
|
||||||
|
|
||||||
|
if (err < 0) { return err; }
|
||||||
|
|
||||||
dev_major = MAJOR(dev);
|
dev_major = MAJOR(dev);
|
||||||
|
|
||||||
harness_class = class_create("harness");
|
harness_class = class_create("harness");
|
||||||
@ -120,7 +124,9 @@ static int __init harness_init(void) {
|
|||||||
|
|
||||||
device_create(harness_class, NULL, MKDEV(dev_major, 0), NULL, "harness");
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -135,7 +141,6 @@ static void __exit harness_exit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int harness_open(struct inode *inode, struct file *file) {
|
static int harness_open(struct inode *inode, struct file *file) {
|
||||||
int ret;
|
|
||||||
lqprintf("harness: Device open\n");
|
lqprintf("harness: Device open\n");
|
||||||
|
|
||||||
char *data = kzalloc(BUF_SIZE, GFP_KERNEL);
|
char *data = kzalloc(BUF_SIZE, GFP_KERNEL);
|
||||||
@ -144,6 +149,11 @@ static int harness_open(struct inode *inode, struct file *file) {
|
|||||||
unsigned long x509_fn_addr = kln_pointer("x509_cert_parse");
|
unsigned long x509_fn_addr = kln_pointer("x509_cert_parse");
|
||||||
lqprintf("harness: x509 fn addr: 0x%lx\n", x509_fn_addr);
|
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...
|
// TODO: better filtering...
|
||||||
libafl_qemu_trace_vaddr_size(x509_fn_addr, 0x1000);
|
libafl_qemu_trace_vaddr_size(x509_fn_addr, 0x1000);
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/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
|
cd /setup
|
||||||
make clean
|
make clean
|
||||||
make -j
|
make -j
|
||||||
ls /setup
|
|
@ -12,10 +12,7 @@ use libafl::{
|
|||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
inputs::BytesInput,
|
inputs::BytesInput,
|
||||||
monitors::MultiMonitor,
|
monitors::MultiMonitor,
|
||||||
mutators::{
|
mutators::{havoc_mutations, scheduled::StdScheduledMutator, I2SRandReplaceBinonly},
|
||||||
scheduled::{havoc_mutations, StdScheduledMutator},
|
|
||||||
I2SRandReplaceBinonly,
|
|
||||||
},
|
|
||||||
observers::{CanTrack, HitcountsMapObserver, TimeObserver, VariableMapObserver},
|
observers::{CanTrack, HitcountsMapObserver, TimeObserver, VariableMapObserver},
|
||||||
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
||||||
stages::{ShadowTracingStage, StdMutationalStage},
|
stages::{ShadowTracingStage, StdMutationalStage},
|
||||||
@ -33,16 +30,10 @@ use libafl_bolts::{
|
|||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
emu::Emulator,
|
emu::Emulator,
|
||||||
executor::QemuExecutor,
|
executor::QemuExecutor,
|
||||||
modules::{
|
modules::{cmplog::CmpLogObserver, edges::StdEdgeCoverageClassicModule, CmpLogModule},
|
||||||
cmplog::CmpLogObserver,
|
|
||||||
edges::{
|
|
||||||
edges_map_mut_ptr, StdEdgeCoverageClassicModule, EDGES_MAP_ALLOCATED_SIZE,
|
|
||||||
MAX_EDGES_FOUND,
|
|
||||||
},
|
|
||||||
CmpLogModule,
|
|
||||||
},
|
|
||||||
// StdEmulatorDriver
|
// StdEmulatorDriver
|
||||||
};
|
};
|
||||||
|
use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND};
|
||||||
|
|
||||||
pub fn fuzz() {
|
pub fn fuzz() {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
@ -61,9 +52,21 @@ pub fn fuzz() {
|
|||||||
// Initialize QEMU
|
// Initialize QEMU
|
||||||
let args: Vec<String> = env::args().collect();
|
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
|
// Choose modules to use
|
||||||
let modules = tuple_list!(
|
let modules = tuple_list!(
|
||||||
StdEdgeCoverageClassicModule::builder().build(),
|
StdEdgeCoverageClassicModule::builder()
|
||||||
|
.map_observer(edges_observer.as_mut())
|
||||||
|
.build()?,
|
||||||
CmpLogModule::default(),
|
CmpLogModule::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -81,16 +84,6 @@ pub fn fuzz() {
|
|||||||
emulator.run(state, input).unwrap().try_into().unwrap()
|
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
|
// Create an observation channel to keep track of the execution time
|
||||||
let time_observer = TimeObserver::new("time");
|
let time_observer = TimeObserver::new("time");
|
||||||
|
|
||||||
|
@ -5,6 +5,10 @@ authors = ["Romain Malmain <rmalmain@pm.me>"]
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
||||||
|
## Build and run the target with the Nyx API instead of the built-in LibAFL QEMU API.
|
||||||
|
nyx = []
|
||||||
|
|
||||||
shared = ["libafl_qemu/shared"]
|
shared = ["libafl_qemu/shared"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
@ -16,15 +20,10 @@ codegen-units = 1
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../../../libafl" }
|
libafl = { path = "../../../libafl" }
|
||||||
libafl_bolts = { path = "../../../libafl_bolts" }
|
libafl_bolts = { path = "../../../libafl_bolts" }
|
||||||
libafl_qemu = { path = "../../../libafl_qemu", features = [
|
libafl_qemu = { path = "../../../libafl_qemu", default-features = false, features = [
|
||||||
"x86_64",
|
"x86_64",
|
||||||
"systemmode",
|
"systemmode",
|
||||||
# "paranoid_debug"
|
# "paranoid_debug",
|
||||||
] }
|
|
||||||
libafl_qemu_sys = { path = "../../../libafl_qemu/libafl_qemu_sys", features = [
|
|
||||||
"x86_64",
|
|
||||||
"systemmode",
|
|
||||||
# "paranoid_debug"
|
|
||||||
] }
|
] }
|
||||||
env_logger = "0.11.5"
|
env_logger = "0.11.5"
|
||||||
libafl_targets = { path = "../../../libafl_targets" }
|
libafl_targets = { path = "../../../libafl_targets" }
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
env_scripts = ['''
|
env_scripts = ['''
|
||||||
#!@duckscript
|
#!@duckscript
|
||||||
profile = get_env PROFILE
|
profile = get_env PROFILE
|
||||||
|
harness_api = get_env HARNESS_API
|
||||||
|
|
||||||
if eq ${profile} "dev"
|
if eq ${profile} "dev"
|
||||||
set_env PROFILE_DIR debug
|
set_env PROFILE_DIR debug
|
||||||
else
|
else
|
||||||
set_env PROFILE_DIR ${profile}
|
set_env PROFILE_DIR ${profile}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if eq ${harness_api} "nyx"
|
||||||
|
set_env FEATURE nyx
|
||||||
|
else
|
||||||
|
set_env FEATURE ""
|
||||||
|
end
|
||||||
|
|
||||||
''', '''
|
''', '''
|
||||||
#!@duckscript
|
#!@duckscript
|
||||||
runs_on_ci = get_env RUN_ON_CI
|
runs_on_ci = get_env RUN_ON_CI
|
||||||
@ -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_CLONE_DIR = { value = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge", condition = { env_not_set = [
|
||||||
"LIBAFL_QEMU_DIR",
|
"LIBAFL_QEMU_DIR",
|
||||||
] } }
|
] } }
|
||||||
|
|
||||||
LINUX_BUILDER_URL = "git@github.com:AFLplusplus/linux-qemu-image-builder.git"
|
LINUX_BUILDER_URL = "git@github.com:AFLplusplus/linux-qemu-image-builder.git"
|
||||||
LINUX_BUILDER_DIR = { value = "${TARGET_DIR}/linux_builder", condition = { env_not_set = [
|
LINUX_BUILDER_DIR = { value = "${TARGET_DIR}/linux_builder", condition = { env_not_set = [
|
||||||
"LINUX_BUILDER_DIR",
|
"LINUX_BUILDER_DIR",
|
||||||
] } }
|
] } }
|
||||||
LINUX_BUILDER_OUT = "${LINUX_BUILDER_DIR}/output"
|
LINUX_BUILDER_OUT = "${LINUX_BUILDER_DIR}/output"
|
||||||
|
HARNESS_API = { value = "lqemu", condition = { env_not_set = ["HARNESS_API"] } }
|
||||||
|
|
||||||
[tasks.target_dir]
|
[tasks.target_dir]
|
||||||
condition = { files_not_exist = [
|
condition = { files_not_exist = [
|
||||||
@ -51,7 +59,22 @@ script = '''
|
|||||||
git clone ${LINUX_BUILDER_URL} ${LINUX_BUILDER_DIR}
|
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"]
|
dependencies = ["target_dir", "linux_builder_dir"]
|
||||||
command = "clang"
|
command = "clang"
|
||||||
args = [
|
args = [
|
||||||
@ -64,6 +87,9 @@ args = [
|
|||||||
"${TARGET_DIR}/${PROFILE_DIR}/include",
|
"${TARGET_DIR}/${PROFILE_DIR}/include",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[tasks.compile_target]
|
||||||
|
dependencies = ["compile_target_native", "compile_target_nyx"]
|
||||||
|
|
||||||
[tasks.target]
|
[tasks.target]
|
||||||
dependencies = ["build", "compile_target"]
|
dependencies = ["build", "compile_target"]
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
@ -96,7 +122,15 @@ ${LINUX_BUILDER_DIR}/update.sh
|
|||||||
[tasks.build]
|
[tasks.build]
|
||||||
dependencies = ["target_dir"]
|
dependencies = ["target_dir"]
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
args = ["build", "--profile", "${PROFILE}", "--target-dir", "${TARGET_DIR}"]
|
args = [
|
||||||
|
"build",
|
||||||
|
"--profile",
|
||||||
|
"${PROFILE}",
|
||||||
|
"--target-dir",
|
||||||
|
"${TARGET_DIR}",
|
||||||
|
"--features",
|
||||||
|
"${FEATURE}",
|
||||||
|
]
|
||||||
|
|
||||||
[tasks.run]
|
[tasks.run]
|
||||||
dependencies = ["build"]
|
dependencies = ["build"]
|
||||||
@ -111,15 +145,15 @@ else
|
|||||||
LIBAFL_QEMU_BIOS_DIR=${LIBAFL_QEMU_CLONE_DIR}/build/qemu-bundle/usr/local/share/qemu
|
LIBAFL_QEMU_BIOS_DIR=${LIBAFL_QEMU_CLONE_DIR}/build/qemu-bundle/usr/local/share/qemu
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cp ${LINUX_BUILDER_OUT}/OVMF_CODE.fd ${LINUX_BUILDER_OUT}/OVMF_CODE.fd.clone
|
cp ${LINUX_BUILDER_OUT}/OVMF_CODE.4m.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_VARS.4m.fd ${LINUX_BUILDER_OUT}/OVMF_VARS.fd.clone
|
||||||
cp ${LINUX_BUILDER_OUT}/linux.qcow2 ${LINUX_BUILDER_OUT}/linux.qcow2.clone
|
cp ${LINUX_BUILDER_OUT}/linux.qcow2 ${LINUX_BUILDER_OUT}/linux.qcow2.clone
|
||||||
|
|
||||||
${TARGET_DIR}/${PROFILE_DIR}/qemu_linux_process \
|
${TARGET_DIR}/${PROFILE_DIR}/qemu_linux_process \
|
||||||
-accel tcg \
|
-accel tcg \
|
||||||
-m 4G \
|
-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_CODE.4m.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_VARS.4m.fd" `# OVMF vars pflash` \
|
||||||
-device virtio-scsi-pci,id=scsi0 `# SCSI bus` \
|
-device virtio-scsi-pci,id=scsi0 `# SCSI bus` \
|
||||||
-device scsi-hd,bus=scsi0.0,drive=disk,id=virtio-disk0,bootindex=1 \
|
-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=file,filename="${LINUX_BUILDER_OUT}/linux.qcow2",node-name=storage `# Backend file of "disk"` \
|
||||||
|
137
fuzzers/full_system/qemu_linux_process/example/harness_nyx.c
Normal file
137
fuzzers/full_system/qemu_linux_process/example/harness_nyx.c
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
// Adapted from
|
||||||
|
// https://github.com/google/fuzzing/blob/master/tutorial/libFuzzer/fuzz_me.cc
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <nyx_api.h>
|
||||||
|
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
#define PAYLOAD_MAX_SIZE (1 * 1024 * 1024)
|
||||||
|
|
||||||
|
bool FuzzMe(const uint8_t *Data, size_t DataSize) {
|
||||||
|
if (DataSize > 3) {
|
||||||
|
if (Data[0] == 'F') {
|
||||||
|
if (Data[1] == 'U') {
|
||||||
|
if (Data[2] == 'Z') {
|
||||||
|
if (Data[3] == 'Z') { return true; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate page-aligned memory
|
||||||
|
*/
|
||||||
|
void *malloc_resident_pages(size_t num_pages) {
|
||||||
|
size_t data_size = PAGE_SIZE * num_pages;
|
||||||
|
void *ptr = NULL;
|
||||||
|
|
||||||
|
if ((ptr = aligned_alloc(PAGE_SIZE, data_size)) == NULL) {
|
||||||
|
fprintf(stderr, "Allocation failure: %s\n", strerror(errno));
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure pages are aligned and resident
|
||||||
|
memset(ptr, 0x42, data_size);
|
||||||
|
if (mlock(ptr, data_size) == -1) {
|
||||||
|
fprintf(stderr, "Error locking scratch buffer: %s\n", strerror(errno));
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(((uintptr_t)ptr % PAGE_SIZE) == 0);
|
||||||
|
return ptr;
|
||||||
|
err_out:
|
||||||
|
free(ptr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int agent_init(int verbose) {
|
||||||
|
host_config_t host_config;
|
||||||
|
|
||||||
|
// set ready state
|
||||||
|
kAFL_hypercall(HYPERCALL_KAFL_ACQUIRE, 0);
|
||||||
|
kAFL_hypercall(HYPERCALL_KAFL_RELEASE, 0);
|
||||||
|
|
||||||
|
kAFL_hypercall(HYPERCALL_KAFL_GET_HOST_CONFIG, (uintptr_t)&host_config);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
fprintf(stderr, "GET_HOST_CONFIG\n");
|
||||||
|
fprintf(stderr, "\thost magic: 0x%x, version: 0x%x\n",
|
||||||
|
host_config.host_magic, host_config.host_version);
|
||||||
|
fprintf(stderr, "\tbitmap size: 0x%x, ijon: 0x%x\n",
|
||||||
|
host_config.bitmap_size, host_config.ijon_bitmap_size);
|
||||||
|
fprintf(stderr, "\tpayload size: %u KB\n",
|
||||||
|
host_config.payload_buffer_size / 1024);
|
||||||
|
fprintf(stderr, "\tworker id: %d\n", host_config.worker_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host_config.host_magic != NYX_HOST_MAGIC) {
|
||||||
|
hprintf("HOST_MAGIC mismatch: %08x != %08x\n", host_config.host_magic,
|
||||||
|
NYX_HOST_MAGIC);
|
||||||
|
habort("HOST_MAGIC mismatch!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host_config.host_version != NYX_HOST_VERSION) {
|
||||||
|
hprintf("HOST_VERSION mismatch: %08x != %08x\n", host_config.host_version,
|
||||||
|
NYX_HOST_VERSION);
|
||||||
|
habort("HOST_VERSION mismatch!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host_config.payload_buffer_size > PAYLOAD_MAX_SIZE) {
|
||||||
|
hprintf("Fuzzer payload size too large: %lu > %lu\n",
|
||||||
|
host_config.payload_buffer_size, PAYLOAD_MAX_SIZE);
|
||||||
|
habort("Host payload size too large!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
agent_config_t agent_config = {0};
|
||||||
|
agent_config.agent_magic = NYX_AGENT_MAGIC;
|
||||||
|
agent_config.agent_version = NYX_AGENT_VERSION;
|
||||||
|
// agent_config.agent_timeout_detection = 0; // timeout by host
|
||||||
|
// agent_config.agent_tracing = 0; // trace by host
|
||||||
|
// agent_config.agent_ijon_tracing = 0; // no IJON
|
||||||
|
agent_config.agent_non_reload_mode = 0; // no persistent mode
|
||||||
|
// agent_config.trace_buffer_vaddr = 0xdeadbeef;
|
||||||
|
// agent_config.ijon_trace_buffer_vaddr = 0xdeadbeef;
|
||||||
|
agent_config.coverage_bitmap_size = host_config.bitmap_size;
|
||||||
|
// agent_config.input_buffer_size;
|
||||||
|
// agent_config.dump_payloads; // set by hypervisor (??)
|
||||||
|
|
||||||
|
kAFL_hypercall(HYPERCALL_KAFL_SET_AGENT_CONFIG, (uintptr_t)&agent_config);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
kAFL_payload *pbuf = malloc_resident_pages(PAYLOAD_MAX_SIZE / PAGE_SIZE);
|
||||||
|
assert(pbuf);
|
||||||
|
|
||||||
|
agent_init(1);
|
||||||
|
|
||||||
|
kAFL_hypercall(HYPERCALL_KAFL_GET_PAYLOAD, (uint64_t)pbuf);
|
||||||
|
|
||||||
|
hprintf("payload size addr: %p", &pbuf->size);
|
||||||
|
hprintf("payload addr: %p", &pbuf->data);
|
||||||
|
|
||||||
|
kAFL_hypercall(HYPERCALL_KAFL_NEXT_PAYLOAD, 0);
|
||||||
|
kAFL_hypercall(HYPERCALL_KAFL_ACQUIRE, 0);
|
||||||
|
|
||||||
|
// Call the target
|
||||||
|
bool ret = FuzzMe(pbuf->data, pbuf->size);
|
||||||
|
|
||||||
|
kAFL_hypercall(HYPERCALL_KAFL_RELEASE, 0);
|
||||||
|
|
||||||
|
habort("Error: post-release code has been triggered.");
|
||||||
|
}
|
@ -3,6 +3,8 @@
|
|||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
use std::{env, path::PathBuf, process};
|
use std::{env, path::PathBuf, process};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "nyx"))]
|
||||||
|
use libafl::state::{HasExecutions, State};
|
||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus},
|
corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus},
|
||||||
events::{launcher::Launcher, EventConfig},
|
events::{launcher::Launcher, EventConfig},
|
||||||
@ -10,7 +12,7 @@ use libafl::{
|
|||||||
feedback_or, feedback_or_fast,
|
feedback_or, feedback_or_fast,
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
inputs::BytesInput,
|
inputs::{BytesInput, HasTargetBytes, UsesInput},
|
||||||
monitors::MultiMonitor,
|
monitors::MultiMonitor,
|
||||||
mutators::{havoc_mutations, I2SRandReplaceBinonly, StdScheduledMutator},
|
mutators::{havoc_mutations, I2SRandReplaceBinonly, StdScheduledMutator},
|
||||||
observers::{CanTrack, HitcountsMapObserver, TimeObserver, VariableMapObserver},
|
observers::{CanTrack, HitcountsMapObserver, TimeObserver, VariableMapObserver},
|
||||||
@ -27,13 +29,64 @@ use libafl_bolts::{
|
|||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
tuples::tuple_list,
|
tuples::tuple_list,
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "nyx")]
|
||||||
|
use libafl_qemu::{command::nyx::NyxCommandManager, NyxEmulatorDriver};
|
||||||
|
#[cfg(not(feature = "nyx"))]
|
||||||
|
use libafl_qemu::{command::StdCommandManager, StdEmulatorDriver};
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
emu::Emulator,
|
emu::Emulator,
|
||||||
executor::QemuExecutor,
|
executor::QemuExecutor,
|
||||||
modules::{cmplog::CmpLogObserver, edges::StdEdgeCoverageClassicModule, CmpLogModule},
|
modules::{
|
||||||
|
cmplog::CmpLogObserver, edges::StdEdgeCoverageClassicModule, CmpLogModule,
|
||||||
|
EmulatorModuleTuple,
|
||||||
|
},
|
||||||
|
FastSnapshotManager, QemuInitError,
|
||||||
};
|
};
|
||||||
use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND};
|
use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND};
|
||||||
|
|
||||||
|
#[cfg(feature = "nyx")]
|
||||||
|
fn get_emulator<ET, S>(
|
||||||
|
args: Vec<String>,
|
||||||
|
modules: ET,
|
||||||
|
) -> Result<
|
||||||
|
Emulator<NyxCommandManager<S>, NyxEmulatorDriver, ET, S, FastSnapshotManager>,
|
||||||
|
QemuInitError,
|
||||||
|
>
|
||||||
|
where
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
S: UsesInput + Unpin,
|
||||||
|
<S as UsesInput>::Input: HasTargetBytes,
|
||||||
|
{
|
||||||
|
Emulator::empty()
|
||||||
|
.qemu_parameters(args)
|
||||||
|
.modules(modules)
|
||||||
|
.driver(
|
||||||
|
NyxEmulatorDriver::builder()
|
||||||
|
.allow_page_on_start(true)
|
||||||
|
.process_only(true)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.command_manager(NyxCommandManager::default())
|
||||||
|
.snapshot_manager(FastSnapshotManager::default())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "nyx"))]
|
||||||
|
fn get_emulator<ET, S>(
|
||||||
|
args: Vec<String>,
|
||||||
|
modules: ET,
|
||||||
|
) -> Result<
|
||||||
|
Emulator<StdCommandManager<S>, StdEmulatorDriver, ET, S, FastSnapshotManager>,
|
||||||
|
QemuInitError,
|
||||||
|
>
|
||||||
|
where
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
S: State + HasExecutions + Unpin,
|
||||||
|
<S as UsesInput>::Input: HasTargetBytes,
|
||||||
|
{
|
||||||
|
Emulator::builder().qemu_cli(args).modules(modules).build()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fuzz() {
|
pub fn fuzz() {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
@ -69,10 +122,7 @@ pub fn fuzz() {
|
|||||||
CmpLogModule::default(),
|
CmpLogModule::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let emu = Emulator::builder()
|
let emu = get_emulator(args, modules)?;
|
||||||
.qemu_parameters(args)
|
|
||||||
.modules(modules)
|
|
||||||
.build()?;
|
|
||||||
|
|
||||||
// The wrapped harness function, calling out to the LLVM-style harness
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness =
|
let mut harness =
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define posix_memalign(p, a, s) \
|
#define posix_memalign(p, a, s) \
|
||||||
(((*(p)) = _aligned_malloc((s), (a))), *(p) ? 0 : errno)
|
(((*(p)) = _aligned_malloc((s), (a))), *(p) ? 0 : errno)
|
||||||
#define RETADDR (uintptr_t)_ReturnAddress()
|
#define RETADDR (uintptr_t) _ReturnAddress()
|
||||||
#else
|
#else
|
||||||
#define RETADDR (uintptr_t)__builtin_return_address(0)
|
#define RETADDR (uintptr_t) __builtin_return_address(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
|
@ -265,6 +265,19 @@ pub fn hash_std(input: &[u8]) -> u64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fast hash function for 64 bits integers minimizing collisions.
|
||||||
|
/// Adapted from <https://xorshift.di.unimi.it/splitmix64.c>
|
||||||
|
#[must_use]
|
||||||
|
pub fn hash_64_fast(mut x: u64) -> u64 {
|
||||||
|
x = (x ^ (x.overflowing_shr(30).0))
|
||||||
|
.overflowing_mul(0xbf58476d1ce4e5b9)
|
||||||
|
.0;
|
||||||
|
x = (x ^ (x.overflowing_shr(27).0))
|
||||||
|
.overflowing_mul(0x94d049bb133111eb)
|
||||||
|
.0;
|
||||||
|
x ^ (x.overflowing_shr(31).0)
|
||||||
|
}
|
||||||
|
|
||||||
/// Hashes the input with a given hash
|
/// Hashes the input with a given hash
|
||||||
///
|
///
|
||||||
/// Hashes the input with a given hash, depending on features:
|
/// Hashes the input with a given hash, depending on features:
|
||||||
|
@ -41,7 +41,7 @@ use bitbybit::bitfield;
|
|||||||
use caps::{CapSet, Capability};
|
use caps::{CapSet, Capability};
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use libafl_bolts::ownedref::OwnedRefMut;
|
use libafl_bolts::ownedref::OwnedRefMut;
|
||||||
use libafl_bolts::Error;
|
use libafl_bolts::{hash_64_fast, Error};
|
||||||
use libipt::PtError;
|
use libipt::PtError;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use libipt::{
|
use libipt::{
|
||||||
@ -407,7 +407,7 @@ impl IntelPT {
|
|||||||
let offset = decoder.offset().map_err(error_from_pt_error)?;
|
let offset = decoder.offset().map_err(error_from_pt_error)?;
|
||||||
|
|
||||||
if b.ninsn() > 0 && skip < offset {
|
if b.ninsn() > 0 && skip < offset {
|
||||||
let id = hash_me(*previous_block_end_ip) ^ hash_me(b.ip());
|
let id = hash_64_fast(*previous_block_end_ip) ^ hash_64_fast(b.ip());
|
||||||
// SAFETY: the index is < map.len() since the modulo operation is applied
|
// SAFETY: the index is < map.len() since the modulo operation is applied
|
||||||
let map_loc = unsafe { map.get_unchecked_mut(id as usize % map.len()) };
|
let map_loc = unsafe { map.get_unchecked_mut(id as usize % map.len()) };
|
||||||
*map_loc = (*map_loc).saturating_add(&1u8.into());
|
*map_loc = (*map_loc).saturating_add(&1u8.into());
|
||||||
@ -961,21 +961,6 @@ const fn next_page_aligned_addr(address: u64) -> u64 {
|
|||||||
(address + PAGE_SIZE as u64 - 1) & !(PAGE_SIZE as u64 - 1)
|
(address + PAGE_SIZE as u64 - 1) & !(PAGE_SIZE as u64 - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy pasted from libafl_qemu/src/modules/edges.rs
|
|
||||||
// adapted from https://xorshift.di.unimi.it/splitmix64.c
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
const fn hash_me(mut x: u64) -> u64 {
|
|
||||||
x = (x ^ (x.overflowing_shr(30).0))
|
|
||||||
.overflowing_mul(0xbf58476d1ce4e5b9)
|
|
||||||
.0;
|
|
||||||
x = (x ^ (x.overflowing_shr(27).0))
|
|
||||||
.overflowing_mul(0x94d049bb133111eb)
|
|
||||||
.0;
|
|
||||||
x ^ (x.overflowing_shr(31).0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
#[inline]
|
#[inline]
|
||||||
fn smp_rmb() {
|
fn smp_rmb() {
|
||||||
|
@ -34,6 +34,9 @@ default = [
|
|||||||
"injections",
|
"injections",
|
||||||
]
|
]
|
||||||
document-features = ["dep:document-features"]
|
document-features = ["dep:document-features"]
|
||||||
|
|
||||||
|
qemu_sanitizers = ["libafl_qemu_sys/qemu_sanitizers"]
|
||||||
|
|
||||||
paranoid_debug = [
|
paranoid_debug = [
|
||||||
"libafl_qemu_sys/paranoid_debug",
|
"libafl_qemu_sys/paranoid_debug",
|
||||||
] # Will perform as many checks as possible. The target will be greatly slowed down.
|
] # Will perform as many checks as possible. The target will be greatly slowed down.
|
||||||
|
@ -51,6 +51,8 @@ pub fn build() {
|
|||||||
let libafl_qemu_defs_hdr_name = "libafl_qemu_defs.h";
|
let libafl_qemu_defs_hdr_name = "libafl_qemu_defs.h";
|
||||||
let libafl_qemu_impl_hdr_name = "libafl_qemu_impl.h";
|
let libafl_qemu_impl_hdr_name = "libafl_qemu_impl.h";
|
||||||
|
|
||||||
|
let nyx_hdr_name = "nyx_api.h";
|
||||||
|
|
||||||
let libafl_runtime_dir = src_dir.join("runtime");
|
let libafl_runtime_dir = src_dir.join("runtime");
|
||||||
|
|
||||||
let libafl_qemu_hdr = libafl_runtime_dir.join(libafl_qemu_hdr_name);
|
let libafl_qemu_hdr = libafl_runtime_dir.join(libafl_qemu_hdr_name);
|
||||||
@ -58,6 +60,8 @@ pub fn build() {
|
|||||||
let libafl_qemu_defs_hdr = libafl_runtime_dir.join(libafl_qemu_defs_hdr_name);
|
let libafl_qemu_defs_hdr = libafl_runtime_dir.join(libafl_qemu_defs_hdr_name);
|
||||||
let libafl_qemu_impl_hdr = libafl_runtime_dir.join(libafl_qemu_impl_hdr_name);
|
let libafl_qemu_impl_hdr = libafl_runtime_dir.join(libafl_qemu_impl_hdr_name);
|
||||||
|
|
||||||
|
let nyx_hdr = libafl_runtime_dir.join(nyx_hdr_name);
|
||||||
|
|
||||||
let libafl_runtime_testfile = out_dir.join("runtime_test.c");
|
let libafl_runtime_testfile = out_dir.join("runtime_test.c");
|
||||||
fs::write(&libafl_runtime_testfile, LIBAFL_QEMU_RUNTIME_TEST)
|
fs::write(&libafl_runtime_testfile, LIBAFL_QEMU_RUNTIME_TEST)
|
||||||
.expect("Could not write runtime test file");
|
.expect("Could not write runtime test file");
|
||||||
@ -76,6 +80,9 @@ pub fn build() {
|
|||||||
let runtime_bindings_file = out_dir.join("libafl_qemu_bindings.rs");
|
let runtime_bindings_file = out_dir.join("libafl_qemu_bindings.rs");
|
||||||
let stub_runtime_bindings_file = src_dir.join("runtime/libafl_qemu_stub_bindings.rs");
|
let stub_runtime_bindings_file = src_dir.join("runtime/libafl_qemu_stub_bindings.rs");
|
||||||
|
|
||||||
|
let nyx_bindings_file = out_dir.join("nyx_bindings.rs");
|
||||||
|
let stub_nyx_bindings_file = src_dir.join("runtime/nyx_stub_bindings.rs");
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
println!("cargo:rerun-if-changed=build_linux.rs");
|
println!("cargo:rerun-if-changed=build_linux.rs");
|
||||||
println!("cargo:rerun-if-changed={}", libafl_runtime_dir.display());
|
println!("cargo:rerun-if-changed={}", libafl_runtime_dir.display());
|
||||||
@ -121,6 +128,8 @@ pub fn build() {
|
|||||||
if env::var("DOCS_RS").is_ok() || cfg!(feature = "clippy") {
|
if env::var("DOCS_RS").is_ok() || cfg!(feature = "clippy") {
|
||||||
fs::copy(&stub_runtime_bindings_file, &runtime_bindings_file)
|
fs::copy(&stub_runtime_bindings_file, &runtime_bindings_file)
|
||||||
.expect("Could not copy stub bindings file");
|
.expect("Could not copy stub bindings file");
|
||||||
|
fs::copy(&stub_nyx_bindings_file, &nyx_bindings_file)
|
||||||
|
.expect("Could not copy stub bindings file");
|
||||||
return; // only build when we're not generating docs
|
return; // only build when we're not generating docs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,6 +159,12 @@ pub fn build() {
|
|||||||
)
|
)
|
||||||
.expect("Could not copy libafl_qemu_impl.h to out directory.");
|
.expect("Could not copy libafl_qemu_impl.h to out directory.");
|
||||||
|
|
||||||
|
fs::copy(
|
||||||
|
nyx_hdr.clone(),
|
||||||
|
include_dir.join(nyx_hdr_name),
|
||||||
|
)
|
||||||
|
.expect("Could not copy libafl_qemu_impl.h to out directory.");
|
||||||
|
|
||||||
bindgen::Builder::default()
|
bindgen::Builder::default()
|
||||||
.derive_debug(true)
|
.derive_debug(true)
|
||||||
.derive_default(true)
|
.derive_default(true)
|
||||||
@ -165,6 +180,21 @@ pub fn build() {
|
|||||||
.write_to_file(&runtime_bindings_file)
|
.write_to_file(&runtime_bindings_file)
|
||||||
.expect("Could not write bindings.");
|
.expect("Could not write bindings.");
|
||||||
|
|
||||||
|
bindgen::Builder::default()
|
||||||
|
.derive_debug(true)
|
||||||
|
.derive_default(true)
|
||||||
|
.impl_debug(true)
|
||||||
|
.generate_comments(true)
|
||||||
|
.default_enum_style(bindgen::EnumVariation::NewType {
|
||||||
|
is_global: true,
|
||||||
|
is_bitfield: true,
|
||||||
|
})
|
||||||
|
.header(nyx_hdr.display().to_string())
|
||||||
|
.generate()
|
||||||
|
.expect("Exit bindings generation failed.")
|
||||||
|
.write_to_file(&nyx_bindings_file)
|
||||||
|
.expect("Could not write bindings.");
|
||||||
|
|
||||||
maybe_generate_stub_bindings(
|
maybe_generate_stub_bindings(
|
||||||
&cpu_target,
|
&cpu_target,
|
||||||
emulation_mode,
|
emulation_mode,
|
||||||
@ -172,6 +202,13 @@ pub fn build() {
|
|||||||
runtime_bindings_file.as_path(),
|
runtime_bindings_file.as_path(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
maybe_generate_stub_bindings(
|
||||||
|
&cpu_target,
|
||||||
|
emulation_mode,
|
||||||
|
stub_nyx_bindings_file.as_path(),
|
||||||
|
nyx_bindings_file.as_path(),
|
||||||
|
);
|
||||||
|
|
||||||
if cfg!(feature = "usermode") && (qemu_asan || qemu_asan_guest) {
|
if cfg!(feature = "usermode") && (qemu_asan || qemu_asan_guest) {
|
||||||
let qasan_dir = Path::new("libqasan");
|
let qasan_dir = Path::new("libqasan");
|
||||||
let qasan_dir = fs::canonicalize(qasan_dir).unwrap();
|
let qasan_dir = fs::canonicalize(qasan_dir).unwrap();
|
||||||
|
@ -29,6 +29,8 @@ slirp = [] # build qemu with host libslirp (for user networking)
|
|||||||
|
|
||||||
clippy = [] # special feature for clippy, don't use in normal projects§
|
clippy = [] # special feature for clippy, don't use in normal projects§
|
||||||
|
|
||||||
|
qemu_sanitizers = [] # Enable QEMU sanitizers
|
||||||
|
|
||||||
paranoid_debug = [
|
paranoid_debug = [
|
||||||
] # Will perform as many checks as possible. The target will be greatly slowed down.
|
] # Will perform as many checks as possible. The target will be greatly slowed down.
|
||||||
|
|
||||||
|
@ -149,10 +149,8 @@ pub fn generate(
|
|||||||
.allowlist_type("MemOp")
|
.allowlist_type("MemOp")
|
||||||
.allowlist_type("DeviceSnapshotKind")
|
.allowlist_type("DeviceSnapshotKind")
|
||||||
.allowlist_type("ShutdownCause")
|
.allowlist_type("ShutdownCause")
|
||||||
.allowlist_type("libafl_exit_reason")
|
.allowlist_type("libafl.*")
|
||||||
.allowlist_type("libafl_exit_reason_kind")
|
.allowlist_type("LIBAFL.*")
|
||||||
.allowlist_type("libafl_exit_reason_sync_backdoor")
|
|
||||||
.allowlist_type("libafl_exit_reason_breakpoint")
|
|
||||||
.allowlist_type("Syx.*")
|
.allowlist_type("Syx.*")
|
||||||
.allowlist_type("libafl_mapinfo")
|
.allowlist_type("libafl_mapinfo")
|
||||||
.allowlist_type("IntervalTreeRoot")
|
.allowlist_type("IntervalTreeRoot")
|
||||||
|
@ -11,7 +11,7 @@ use crate::cargo_add_rpath;
|
|||||||
|
|
||||||
pub const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
pub const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
||||||
pub const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
pub const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
||||||
pub const QEMU_REVISION: &str = "06bf8facec33548840413fba1b20858f58e38e2d";
|
pub const QEMU_REVISION: &str = "ace364678aef879514e150e626695c4793db41e9";
|
||||||
|
|
||||||
pub struct BuildResult {
|
pub struct BuildResult {
|
||||||
pub qemu_path: PathBuf,
|
pub qemu_path: PathBuf,
|
||||||
@ -96,9 +96,11 @@ fn configure_qemu(
|
|||||||
.arg("--disable-tools");
|
.arg("--disable-tools");
|
||||||
|
|
||||||
if cfg!(feature = "paranoid_debug") {
|
if cfg!(feature = "paranoid_debug") {
|
||||||
cmd.arg("--enable-debug")
|
cmd.arg("--enable-debug").arg("--enable-debug-tcg");
|
||||||
.arg("--enable-debug-tcg")
|
}
|
||||||
.arg("--enable-sanitizers");
|
|
||||||
|
if cfg!(feature = "qemu_sanitizers") {
|
||||||
|
cmd.arg("--enable-sanitizers");
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_usermode {
|
if is_usermode {
|
||||||
@ -433,29 +435,6 @@ pub fn build(
|
|||||||
|
|
||||||
assert!(output_lib.is_file()); // Make sure this isn't very very wrong
|
assert!(output_lib.is_file()); // Make sure this isn't very very wrong
|
||||||
|
|
||||||
/*
|
|
||||||
let mut objects = vec![];
|
|
||||||
for dir in [
|
|
||||||
build_dir.join("libcommon.fa.p"),
|
|
||||||
build_dir.join(format!("libqemu-{cpu_target}-{target_suffix}.fa.p")),
|
|
||||||
] {
|
|
||||||
for path in fs::read_dir(dir).unwrap() {
|
|
||||||
let path = path.unwrap().path();
|
|
||||||
if path.is_file() {
|
|
||||||
if let Some(name) = path.file_name() {
|
|
||||||
if name.to_string_lossy().starts_with("stubs") {
|
|
||||||
continue;
|
|
||||||
} else if let Some(ext) = path.extension() {
|
|
||||||
if ext == "o" {
|
|
||||||
objects.push(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
let compile_commands_string = &fs::read_to_string(libafl_qemu_build_dir.join("linkinfo.json"))
|
let compile_commands_string = &fs::read_to_string(libafl_qemu_build_dir.join("linkinfo.json"))
|
||||||
.expect("Failed to read linkinfo.json");
|
.expect("Failed to read linkinfo.json");
|
||||||
|
|
||||||
@ -515,90 +494,6 @@ pub fn build(
|
|||||||
panic!("Linking failed.");
|
panic!("Linking failed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* // Old manual linking, kept here for reference and debugging
|
|
||||||
if is_usermode {
|
|
||||||
Command::new("ld")
|
|
||||||
.current_dir(out_dir_path)
|
|
||||||
.arg("-o")
|
|
||||||
.arg("libqemu-partially-linked.o")
|
|
||||||
.arg("-r")
|
|
||||||
.args(objects)
|
|
||||||
.arg("--start-group")
|
|
||||||
.arg("--whole-archive")
|
|
||||||
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libqom.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libevent-loop-base.a", build_dir.display()))
|
|
||||||
.arg(format!("{}/gdbstub/libgdb_user.fa", build_dir.display()))
|
|
||||||
.arg("--no-whole-archive")
|
|
||||||
.arg(format!("{}/libqemuutil.a", build_dir.display()))
|
|
||||||
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libqom.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/gdbstub/libgdb_user.fa", build_dir.display()))
|
|
||||||
.arg(format!(
|
|
||||||
"--dynamic-list={}/plugins/qemu-plugins.symbols",
|
|
||||||
qemu_path.display()
|
|
||||||
))
|
|
||||||
.arg("--end-group")
|
|
||||||
.status()
|
|
||||||
.expect("Partial linked failure");
|
|
||||||
} else {
|
|
||||||
Command::new("ld")
|
|
||||||
.current_dir(out_dir_path)
|
|
||||||
.arg("-o")
|
|
||||||
.arg("libqemu-partially-linked.o")
|
|
||||||
.arg("-r")
|
|
||||||
.args(objects)
|
|
||||||
.arg("--start-group")
|
|
||||||
.arg("--whole-archive")
|
|
||||||
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libqom.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libevent-loop-base.a", build_dir.display()))
|
|
||||||
.arg(format!("{}/gdbstub/libgdb_softmmu.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libio.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libcrypto.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libauthz.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libblockdev.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libblock.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libchardev.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libqmp.fa", build_dir.display()))
|
|
||||||
.arg("--no-whole-archive")
|
|
||||||
.arg(format!("{}/libqemuutil.a", build_dir.display()))
|
|
||||||
.arg(format!(
|
|
||||||
"{}/subprojects/dtc/libfdt/libfdt.a",
|
|
||||||
build_dir.display()
|
|
||||||
))
|
|
||||||
.arg(format!(
|
|
||||||
"{}/subprojects/libvhost-user/libvhost-user-glib.a",
|
|
||||||
build_dir.display()
|
|
||||||
))
|
|
||||||
.arg(format!(
|
|
||||||
"{}/subprojects/libvhost-user/libvhost-user.a",
|
|
||||||
build_dir.display()
|
|
||||||
))
|
|
||||||
.arg(format!(
|
|
||||||
"{}/subprojects/libvduse/libvduse.a",
|
|
||||||
build_dir.display()
|
|
||||||
))
|
|
||||||
.arg(format!("{}/libmigration.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libqom.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/gdbstub/libgdb_softmmu.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libio.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libcrypto.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libauthz.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libblockdev.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libblock.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libchardev.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libqmp.fa", build_dir.display()))
|
|
||||||
.arg(format!(
|
|
||||||
"--dynamic-list={}/plugins/qemu-plugins.symbols",
|
|
||||||
qemu_path.display()
|
|
||||||
))
|
|
||||||
.arg("--end-group")
|
|
||||||
.status()
|
|
||||||
.expect("Partial linked failure");
|
|
||||||
}*/
|
|
||||||
|
|
||||||
Command::new("ar")
|
Command::new("ar")
|
||||||
.current_dir(out_dir_path)
|
.current_dir(out_dir_path)
|
||||||
.arg("crs")
|
.arg("crs")
|
||||||
@ -640,29 +535,12 @@ pub fn build(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg!(feature = "paranoid_debug") {
|
if cfg!(feature = "qemu_sanitizers") {
|
||||||
println!("cargo:rustc-link-lib=ubsan");
|
println!("cargo:rustc-link-lib=ubsan");
|
||||||
println!("cargo:rustc-link-lib=asan");
|
println!("cargo:rustc-link-lib=asan");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
println!("cargo:rustc-link-lib=rt");
|
|
||||||
println!("cargo:rustc-link-lib=gmodule-2.0");
|
|
||||||
println!("cargo:rustc-link-lib=glib-2.0");
|
|
||||||
println!("cargo:rustc-link-lib=stdc++");
|
|
||||||
println!("cargo:rustc-link-lib=z");
|
|
||||||
// if keyutils is available, qemu meson script will compile code with keyutils.
|
|
||||||
// therefore, we need to link with keyutils if our system have libkeyutils.
|
|
||||||
let _: Result<pkg_config::Library, pkg_config::Error> =
|
|
||||||
pkg_config::Config::new().probe("libkeyutils");
|
|
||||||
*/
|
|
||||||
|
|
||||||
if !is_usermode {
|
if !is_usermode {
|
||||||
//println!("cargo:rustc-link-lib=pixman-1");
|
|
||||||
//if env::var("LINK_SLIRP").is_ok() || cfg!(feature = "slirp") {
|
|
||||||
// println!("cargo:rustc-link-lib=slirp");
|
|
||||||
//}
|
|
||||||
|
|
||||||
fs::create_dir_all(target_dir.join("pc-bios")).unwrap();
|
fs::create_dir_all(target_dir.join("pc-bios")).unwrap();
|
||||||
for path in fs::read_dir(libafl_qemu_build_dir.join("pc-bios")).unwrap() {
|
for path in fs::read_dir(libafl_qemu_build_dir.join("pc-bios")).unwrap() {
|
||||||
let path = path.unwrap().path();
|
let path = path.unwrap().path();
|
||||||
|
@ -56,6 +56,10 @@ clippy = [
|
|||||||
"libafl_qemu_build/clippy",
|
"libafl_qemu_build/clippy",
|
||||||
] # special feature for clippy, don't use in normal projects
|
] # special feature for clippy, don't use in normal projects
|
||||||
|
|
||||||
|
qemu_sanitizers = [
|
||||||
|
"libafl_qemu_build/qemu_sanitizers",
|
||||||
|
] # Compile QEMU with sanitizers enabled
|
||||||
|
|
||||||
paranoid_debug = [
|
paranoid_debug = [
|
||||||
"libafl_qemu_build/paranoid_debug",
|
"libafl_qemu_build/paranoid_debug",
|
||||||
] # Will perform as many checks as possible. The target will be greatly slowed down.
|
] # Will perform as many checks as possible. The target will be greatly slowed down.
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -78,23 +78,28 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define LIBAFL_DECLARE(name) \
|
||||||
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_##name##_call0(libafl_word action); \
|
||||||
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_##name##_call1(libafl_word action, \
|
||||||
|
libafl_word arg1); \
|
||||||
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_##name##_call2(libafl_word action, \
|
||||||
|
libafl_word arg1, \
|
||||||
|
libafl_word arg2);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define LIBAFL_DEFINE_FUNCTIONS(name, _opcode) \
|
#define LIBAFL_DEFINE_FUNCTIONS(name, _opcode) \
|
||||||
#ifdef __cplusplus \
|
#ifdef __cplusplus \
|
||||||
extern "C" { \
|
extern "C" { \
|
||||||
#endif \
|
#endif \
|
||||||
libafl_word LIBAFL_CALLING_CONVENTION _libafl_##name##_call0(libafl_word action); \
|
LIBAFL_DECLARE(name) \
|
||||||
libafl_word LIBAFL_CALLING_CONVENTION _libafl_##name##_call1(libafl_word action, \
|
|
||||||
##name## libafl_word arg1); \
|
|
||||||
libafl_word LIBAFL_CALLING_CONVENTION _libafl_##name##_call2(libafl_word action, \
|
|
||||||
libafl_word arg1, \
|
|
||||||
libafl_word arg2); \
|
|
||||||
#ifdef __cplusplus \
|
#ifdef __cplusplus \
|
||||||
} \
|
} \
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#if defined(__x86_64__)
|
#if defined(__x86_64__)
|
||||||
#define LIBAFL_DEFINE_FUNCTIONS(name, opcode) \
|
#define LIBAFL_DEFINE_FUNCTIONS(name, opcode) \
|
||||||
|
LIBAFL_DECLARE(name) \
|
||||||
|
\
|
||||||
libafl_word LIBAFL_CALLING_CONVENTION _libafl_##name##_call0( \
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_##name##_call0( \
|
||||||
libafl_word action) { \
|
libafl_word action) { \
|
||||||
libafl_word ret; \
|
libafl_word ret; \
|
||||||
@ -142,6 +147,8 @@
|
|||||||
|
|
||||||
#elif defined(__arm__)
|
#elif defined(__arm__)
|
||||||
#define LIBAFL_DEFINE_FUNCTIONS(name, opcode) \
|
#define LIBAFL_DEFINE_FUNCTIONS(name, opcode) \
|
||||||
|
LIBAFL_DECLARE(name) \
|
||||||
|
\
|
||||||
libafl_word LIBAFL_CALLING_CONVENTION _libafl_##name##_call0( \
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_##name##_call0( \
|
||||||
libafl_word action) { \
|
libafl_word action) { \
|
||||||
libafl_word ret; \
|
libafl_word ret; \
|
||||||
@ -189,6 +196,8 @@
|
|||||||
|
|
||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
#define LIBAFL_DEFINE_FUNCTIONS(name, opcode) \
|
#define LIBAFL_DEFINE_FUNCTIONS(name, opcode) \
|
||||||
|
LIBAFL_DECLARE(name) \
|
||||||
|
\
|
||||||
libafl_word LIBAFL_CALLING_CONVENTION _libafl_##name##_call0( \
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_##name##_call0( \
|
||||||
libafl_word action) { \
|
libafl_word action) { \
|
||||||
libafl_word ret; \
|
libafl_word ret; \
|
||||||
@ -235,6 +244,8 @@
|
|||||||
}
|
}
|
||||||
#elif defined(__riscv)
|
#elif defined(__riscv)
|
||||||
#define LIBAFL_DEFINE_FUNCTIONS(name, opcode) \
|
#define LIBAFL_DEFINE_FUNCTIONS(name, opcode) \
|
||||||
|
LIBAFL_DECLARE(name) \
|
||||||
|
\
|
||||||
libafl_word LIBAFL_CALLING_CONVENTION _libafl_##name##_call0( \
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_##name##_call0( \
|
||||||
libafl_word action) { \
|
libafl_word action) { \
|
||||||
libafl_word ret; \
|
libafl_word ret; \
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* 1.84.0-nightly */
|
/* 1.85.0-nightly */
|
||||||
/* qemu git hash: 805b14ffc44999952562e8f219d81c21a4fa50b9 */
|
/* qemu git hash: 81e52dc60f83c3ae191c1a07b39bb255e633c234 */
|
||||||
/* automatically generated by rust-bindgen 0.70.1 */
|
/* automatically generated by rust-bindgen 0.71.1 */
|
||||||
|
|
||||||
pub const LIBAFL_SYNC_EXIT_OPCODE: u32 = 1727150607;
|
pub const LIBAFL_SYNC_EXIT_OPCODE: u32 = 1727150607;
|
||||||
pub const LIBAFL_BACKDOOR_OPCODE: u32 = 1156725263;
|
pub const LIBAFL_BACKDOOR_OPCODE: u32 = 1156725263;
|
||||||
@ -562,25 +562,25 @@ pub type cookie_io_functions_t = _IO_cookie_io_functions_t;
|
|||||||
pub type va_list = __gnuc_va_list;
|
pub type va_list = __gnuc_va_list;
|
||||||
pub type off_t = __off_t;
|
pub type off_t = __off_t;
|
||||||
pub type fpos_t = __fpos_t;
|
pub type fpos_t = __fpos_t;
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub static mut stdin: *mut FILE;
|
pub static mut stdin: *mut FILE;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub static mut stdout: *mut FILE;
|
pub static mut stdout: *mut FILE;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub static mut stderr: *mut FILE;
|
pub static mut stderr: *mut FILE;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn remove(__filename: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int;
|
pub fn remove(__filename: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn rename(
|
pub fn rename(
|
||||||
__old: *const ::std::os::raw::c_char,
|
__old: *const ::std::os::raw::c_char,
|
||||||
__new: *const ::std::os::raw::c_char,
|
__new: *const ::std::os::raw::c_char,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn renameat(
|
pub fn renameat(
|
||||||
__oldfd: ::std::os::raw::c_int,
|
__oldfd: ::std::os::raw::c_int,
|
||||||
__old: *const ::std::os::raw::c_char,
|
__old: *const ::std::os::raw::c_char,
|
||||||
@ -588,71 +588,71 @@ extern "C" {
|
|||||||
__new: *const ::std::os::raw::c_char,
|
__new: *const ::std::os::raw::c_char,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fclose(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn fclose(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn tmpfile() -> *mut FILE;
|
pub fn tmpfile() -> *mut FILE;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn tmpnam(arg1: *mut ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char;
|
pub fn tmpnam(arg1: *mut ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn tmpnam_r(__s: *mut ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char;
|
pub fn tmpnam_r(__s: *mut ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn tempnam(
|
pub fn tempnam(
|
||||||
__dir: *const ::std::os::raw::c_char,
|
__dir: *const ::std::os::raw::c_char,
|
||||||
__pfx: *const ::std::os::raw::c_char,
|
__pfx: *const ::std::os::raw::c_char,
|
||||||
) -> *mut ::std::os::raw::c_char;
|
) -> *mut ::std::os::raw::c_char;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fflush(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn fflush(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fflush_unlocked(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn fflush_unlocked(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fopen(
|
pub fn fopen(
|
||||||
__filename: *const ::std::os::raw::c_char,
|
__filename: *const ::std::os::raw::c_char,
|
||||||
__modes: *const ::std::os::raw::c_char,
|
__modes: *const ::std::os::raw::c_char,
|
||||||
) -> *mut FILE;
|
) -> *mut FILE;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn freopen(
|
pub fn freopen(
|
||||||
__filename: *const ::std::os::raw::c_char,
|
__filename: *const ::std::os::raw::c_char,
|
||||||
__modes: *const ::std::os::raw::c_char,
|
__modes: *const ::std::os::raw::c_char,
|
||||||
__stream: *mut FILE,
|
__stream: *mut FILE,
|
||||||
) -> *mut FILE;
|
) -> *mut FILE;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fdopen(__fd: ::std::os::raw::c_int, __modes: *const ::std::os::raw::c_char)
|
pub fn fdopen(__fd: ::std::os::raw::c_int, __modes: *const ::std::os::raw::c_char)
|
||||||
-> *mut FILE;
|
-> *mut FILE;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fopencookie(
|
pub fn fopencookie(
|
||||||
__magic_cookie: *mut ::std::os::raw::c_void,
|
__magic_cookie: *mut ::std::os::raw::c_void,
|
||||||
__modes: *const ::std::os::raw::c_char,
|
__modes: *const ::std::os::raw::c_char,
|
||||||
__io_funcs: cookie_io_functions_t,
|
__io_funcs: cookie_io_functions_t,
|
||||||
) -> *mut FILE;
|
) -> *mut FILE;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fmemopen(
|
pub fn fmemopen(
|
||||||
__s: *mut ::std::os::raw::c_void,
|
__s: *mut ::std::os::raw::c_void,
|
||||||
__len: usize,
|
__len: usize,
|
||||||
__modes: *const ::std::os::raw::c_char,
|
__modes: *const ::std::os::raw::c_char,
|
||||||
) -> *mut FILE;
|
) -> *mut FILE;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn open_memstream(
|
pub fn open_memstream(
|
||||||
__bufloc: *mut *mut ::std::os::raw::c_char,
|
__bufloc: *mut *mut ::std::os::raw::c_char,
|
||||||
__sizeloc: *mut usize,
|
__sizeloc: *mut usize,
|
||||||
) -> *mut FILE;
|
) -> *mut FILE;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn setbuf(__stream: *mut FILE, __buf: *mut ::std::os::raw::c_char);
|
pub fn setbuf(__stream: *mut FILE, __buf: *mut ::std::os::raw::c_char);
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn setvbuf(
|
pub fn setvbuf(
|
||||||
__stream: *mut FILE,
|
__stream: *mut FILE,
|
||||||
__buf: *mut ::std::os::raw::c_char,
|
__buf: *mut ::std::os::raw::c_char,
|
||||||
@ -660,50 +660,50 @@ extern "C" {
|
|||||||
__n: usize,
|
__n: usize,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn setbuffer(__stream: *mut FILE, __buf: *mut ::std::os::raw::c_char, __size: usize);
|
pub fn setbuffer(__stream: *mut FILE, __buf: *mut ::std::os::raw::c_char, __size: usize);
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn setlinebuf(__stream: *mut FILE);
|
pub fn setlinebuf(__stream: *mut FILE);
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fprintf(
|
pub fn fprintf(
|
||||||
__stream: *mut FILE,
|
__stream: *mut FILE,
|
||||||
__format: *const ::std::os::raw::c_char,
|
__format: *const ::std::os::raw::c_char,
|
||||||
...
|
...
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn printf(__format: *const ::std::os::raw::c_char, ...) -> ::std::os::raw::c_int;
|
pub fn printf(__format: *const ::std::os::raw::c_char, ...) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn sprintf(
|
pub fn sprintf(
|
||||||
__s: *mut ::std::os::raw::c_char,
|
__s: *mut ::std::os::raw::c_char,
|
||||||
__format: *const ::std::os::raw::c_char,
|
__format: *const ::std::os::raw::c_char,
|
||||||
...
|
...
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn vfprintf(
|
pub fn vfprintf(
|
||||||
__s: *mut FILE,
|
__s: *mut FILE,
|
||||||
__format: *const ::std::os::raw::c_char,
|
__format: *const ::std::os::raw::c_char,
|
||||||
__arg: *mut __va_list_tag,
|
__arg: *mut __va_list_tag,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn vprintf(
|
pub fn vprintf(
|
||||||
__format: *const ::std::os::raw::c_char,
|
__format: *const ::std::os::raw::c_char,
|
||||||
__arg: *mut __va_list_tag,
|
__arg: *mut __va_list_tag,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn vsprintf(
|
pub fn vsprintf(
|
||||||
__s: *mut ::std::os::raw::c_char,
|
__s: *mut ::std::os::raw::c_char,
|
||||||
__format: *const ::std::os::raw::c_char,
|
__format: *const ::std::os::raw::c_char,
|
||||||
__arg: *mut __va_list_tag,
|
__arg: *mut __va_list_tag,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn snprintf(
|
pub fn snprintf(
|
||||||
__s: *mut ::std::os::raw::c_char,
|
__s: *mut ::std::os::raw::c_char,
|
||||||
__maxlen: ::std::os::raw::c_ulong,
|
__maxlen: ::std::os::raw::c_ulong,
|
||||||
@ -711,7 +711,7 @@ extern "C" {
|
|||||||
...
|
...
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn vsnprintf(
|
pub fn vsnprintf(
|
||||||
__s: *mut ::std::os::raw::c_char,
|
__s: *mut ::std::os::raw::c_char,
|
||||||
__maxlen: ::std::os::raw::c_ulong,
|
__maxlen: ::std::os::raw::c_ulong,
|
||||||
@ -719,52 +719,52 @@ extern "C" {
|
|||||||
__arg: *mut __va_list_tag,
|
__arg: *mut __va_list_tag,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn vasprintf(
|
pub fn vasprintf(
|
||||||
__ptr: *mut *mut ::std::os::raw::c_char,
|
__ptr: *mut *mut ::std::os::raw::c_char,
|
||||||
__f: *const ::std::os::raw::c_char,
|
__f: *const ::std::os::raw::c_char,
|
||||||
__arg: *mut __va_list_tag,
|
__arg: *mut __va_list_tag,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn __asprintf(
|
pub fn __asprintf(
|
||||||
__ptr: *mut *mut ::std::os::raw::c_char,
|
__ptr: *mut *mut ::std::os::raw::c_char,
|
||||||
__fmt: *const ::std::os::raw::c_char,
|
__fmt: *const ::std::os::raw::c_char,
|
||||||
...
|
...
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn asprintf(
|
pub fn asprintf(
|
||||||
__ptr: *mut *mut ::std::os::raw::c_char,
|
__ptr: *mut *mut ::std::os::raw::c_char,
|
||||||
__fmt: *const ::std::os::raw::c_char,
|
__fmt: *const ::std::os::raw::c_char,
|
||||||
...
|
...
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn vdprintf(
|
pub fn vdprintf(
|
||||||
__fd: ::std::os::raw::c_int,
|
__fd: ::std::os::raw::c_int,
|
||||||
__fmt: *const ::std::os::raw::c_char,
|
__fmt: *const ::std::os::raw::c_char,
|
||||||
__arg: *mut __va_list_tag,
|
__arg: *mut __va_list_tag,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn dprintf(
|
pub fn dprintf(
|
||||||
__fd: ::std::os::raw::c_int,
|
__fd: ::std::os::raw::c_int,
|
||||||
__fmt: *const ::std::os::raw::c_char,
|
__fmt: *const ::std::os::raw::c_char,
|
||||||
...
|
...
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fscanf(
|
pub fn fscanf(
|
||||||
__stream: *mut FILE,
|
__stream: *mut FILE,
|
||||||
__format: *const ::std::os::raw::c_char,
|
__format: *const ::std::os::raw::c_char,
|
||||||
...
|
...
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn scanf(__format: *const ::std::os::raw::c_char, ...) -> ::std::os::raw::c_int;
|
pub fn scanf(__format: *const ::std::os::raw::c_char, ...) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn sscanf(
|
pub fn sscanf(
|
||||||
__s: *const ::std::os::raw::c_char,
|
__s: *const ::std::os::raw::c_char,
|
||||||
__format: *const ::std::os::raw::c_char,
|
__format: *const ::std::os::raw::c_char,
|
||||||
@ -775,7 +775,7 @@ pub type _Float32 = f32;
|
|||||||
pub type _Float64 = f64;
|
pub type _Float64 = f64;
|
||||||
pub type _Float32x = f64;
|
pub type _Float32x = f64;
|
||||||
pub type _Float64x = u128;
|
pub type _Float64x = u128;
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
#[link_name = "\u{1}__isoc99_fscanf"]
|
#[link_name = "\u{1}__isoc99_fscanf"]
|
||||||
pub fn fscanf1(
|
pub fn fscanf1(
|
||||||
__stream: *mut FILE,
|
__stream: *mut FILE,
|
||||||
@ -783,11 +783,11 @@ extern "C" {
|
|||||||
...
|
...
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
#[link_name = "\u{1}__isoc99_scanf"]
|
#[link_name = "\u{1}__isoc99_scanf"]
|
||||||
pub fn scanf1(__format: *const ::std::os::raw::c_char, ...) -> ::std::os::raw::c_int;
|
pub fn scanf1(__format: *const ::std::os::raw::c_char, ...) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
#[link_name = "\u{1}__isoc99_sscanf"]
|
#[link_name = "\u{1}__isoc99_sscanf"]
|
||||||
pub fn sscanf1(
|
pub fn sscanf1(
|
||||||
__s: *const ::std::os::raw::c_char,
|
__s: *const ::std::os::raw::c_char,
|
||||||
@ -795,27 +795,27 @@ extern "C" {
|
|||||||
...
|
...
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn vfscanf(
|
pub fn vfscanf(
|
||||||
__s: *mut FILE,
|
__s: *mut FILE,
|
||||||
__format: *const ::std::os::raw::c_char,
|
__format: *const ::std::os::raw::c_char,
|
||||||
__arg: *mut __va_list_tag,
|
__arg: *mut __va_list_tag,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn vscanf(
|
pub fn vscanf(
|
||||||
__format: *const ::std::os::raw::c_char,
|
__format: *const ::std::os::raw::c_char,
|
||||||
__arg: *mut __va_list_tag,
|
__arg: *mut __va_list_tag,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn vsscanf(
|
pub fn vsscanf(
|
||||||
__s: *const ::std::os::raw::c_char,
|
__s: *const ::std::os::raw::c_char,
|
||||||
__format: *const ::std::os::raw::c_char,
|
__format: *const ::std::os::raw::c_char,
|
||||||
__arg: *mut __va_list_tag,
|
__arg: *mut __va_list_tag,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
#[link_name = "\u{1}__isoc99_vfscanf"]
|
#[link_name = "\u{1}__isoc99_vfscanf"]
|
||||||
pub fn vfscanf1(
|
pub fn vfscanf1(
|
||||||
__s: *mut FILE,
|
__s: *mut FILE,
|
||||||
@ -823,14 +823,14 @@ extern "C" {
|
|||||||
__arg: *mut __va_list_tag,
|
__arg: *mut __va_list_tag,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
#[link_name = "\u{1}__isoc99_vscanf"]
|
#[link_name = "\u{1}__isoc99_vscanf"]
|
||||||
pub fn vscanf1(
|
pub fn vscanf1(
|
||||||
__format: *const ::std::os::raw::c_char,
|
__format: *const ::std::os::raw::c_char,
|
||||||
__arg: *mut __va_list_tag,
|
__arg: *mut __va_list_tag,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
#[link_name = "\u{1}__isoc99_vsscanf"]
|
#[link_name = "\u{1}__isoc99_vsscanf"]
|
||||||
pub fn vsscanf1(
|
pub fn vsscanf1(
|
||||||
__s: *const ::std::os::raw::c_char,
|
__s: *const ::std::os::raw::c_char,
|
||||||
@ -838,57 +838,57 @@ extern "C" {
|
|||||||
__arg: *mut __va_list_tag,
|
__arg: *mut __va_list_tag,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fgetc(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn fgetc(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn getc(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn getc(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn getchar() -> ::std::os::raw::c_int;
|
pub fn getchar() -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn getc_unlocked(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn getc_unlocked(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn getchar_unlocked() -> ::std::os::raw::c_int;
|
pub fn getchar_unlocked() -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fgetc_unlocked(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn fgetc_unlocked(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fputc(__c: ::std::os::raw::c_int, __stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn fputc(__c: ::std::os::raw::c_int, __stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn putc(__c: ::std::os::raw::c_int, __stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn putc(__c: ::std::os::raw::c_int, __stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn putchar(__c: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
|
pub fn putchar(__c: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fputc_unlocked(__c: ::std::os::raw::c_int, __stream: *mut FILE)
|
pub fn fputc_unlocked(__c: ::std::os::raw::c_int, __stream: *mut FILE)
|
||||||
-> ::std::os::raw::c_int;
|
-> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn putc_unlocked(__c: ::std::os::raw::c_int, __stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn putc_unlocked(__c: ::std::os::raw::c_int, __stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn putchar_unlocked(__c: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
|
pub fn putchar_unlocked(__c: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn getw(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn getw(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn putw(__w: ::std::os::raw::c_int, __stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn putw(__w: ::std::os::raw::c_int, __stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fgets(
|
pub fn fgets(
|
||||||
__s: *mut ::std::os::raw::c_char,
|
__s: *mut ::std::os::raw::c_char,
|
||||||
__n: ::std::os::raw::c_int,
|
__n: ::std::os::raw::c_int,
|
||||||
__stream: *mut FILE,
|
__stream: *mut FILE,
|
||||||
) -> *mut ::std::os::raw::c_char;
|
) -> *mut ::std::os::raw::c_char;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn __getdelim(
|
pub fn __getdelim(
|
||||||
__lineptr: *mut *mut ::std::os::raw::c_char,
|
__lineptr: *mut *mut ::std::os::raw::c_char,
|
||||||
__n: *mut usize,
|
__n: *mut usize,
|
||||||
@ -896,7 +896,7 @@ extern "C" {
|
|||||||
__stream: *mut FILE,
|
__stream: *mut FILE,
|
||||||
) -> __ssize_t;
|
) -> __ssize_t;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn getdelim(
|
pub fn getdelim(
|
||||||
__lineptr: *mut *mut ::std::os::raw::c_char,
|
__lineptr: *mut *mut ::std::os::raw::c_char,
|
||||||
__n: *mut usize,
|
__n: *mut usize,
|
||||||
@ -904,23 +904,23 @@ extern "C" {
|
|||||||
__stream: *mut FILE,
|
__stream: *mut FILE,
|
||||||
) -> __ssize_t;
|
) -> __ssize_t;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn getline(
|
pub fn getline(
|
||||||
__lineptr: *mut *mut ::std::os::raw::c_char,
|
__lineptr: *mut *mut ::std::os::raw::c_char,
|
||||||
__n: *mut usize,
|
__n: *mut usize,
|
||||||
__stream: *mut FILE,
|
__stream: *mut FILE,
|
||||||
) -> __ssize_t;
|
) -> __ssize_t;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fputs(__s: *const ::std::os::raw::c_char, __stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn fputs(__s: *const ::std::os::raw::c_char, __stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn puts(__s: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int;
|
pub fn puts(__s: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn ungetc(__c: ::std::os::raw::c_int, __stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn ungetc(__c: ::std::os::raw::c_int, __stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fread(
|
pub fn fread(
|
||||||
__ptr: *mut ::std::os::raw::c_void,
|
__ptr: *mut ::std::os::raw::c_void,
|
||||||
__size: ::std::os::raw::c_ulong,
|
__size: ::std::os::raw::c_ulong,
|
||||||
@ -928,7 +928,7 @@ extern "C" {
|
|||||||
__stream: *mut FILE,
|
__stream: *mut FILE,
|
||||||
) -> ::std::os::raw::c_ulong;
|
) -> ::std::os::raw::c_ulong;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fwrite(
|
pub fn fwrite(
|
||||||
__ptr: *const ::std::os::raw::c_void,
|
__ptr: *const ::std::os::raw::c_void,
|
||||||
__size: ::std::os::raw::c_ulong,
|
__size: ::std::os::raw::c_ulong,
|
||||||
@ -936,7 +936,7 @@ extern "C" {
|
|||||||
__s: *mut FILE,
|
__s: *mut FILE,
|
||||||
) -> ::std::os::raw::c_ulong;
|
) -> ::std::os::raw::c_ulong;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fread_unlocked(
|
pub fn fread_unlocked(
|
||||||
__ptr: *mut ::std::os::raw::c_void,
|
__ptr: *mut ::std::os::raw::c_void,
|
||||||
__size: usize,
|
__size: usize,
|
||||||
@ -944,7 +944,7 @@ extern "C" {
|
|||||||
__stream: *mut FILE,
|
__stream: *mut FILE,
|
||||||
) -> usize;
|
) -> usize;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fwrite_unlocked(
|
pub fn fwrite_unlocked(
|
||||||
__ptr: *const ::std::os::raw::c_void,
|
__ptr: *const ::std::os::raw::c_void,
|
||||||
__size: usize,
|
__size: usize,
|
||||||
@ -952,87 +952,87 @@ extern "C" {
|
|||||||
__stream: *mut FILE,
|
__stream: *mut FILE,
|
||||||
) -> usize;
|
) -> usize;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fseek(
|
pub fn fseek(
|
||||||
__stream: *mut FILE,
|
__stream: *mut FILE,
|
||||||
__off: ::std::os::raw::c_long,
|
__off: ::std::os::raw::c_long,
|
||||||
__whence: ::std::os::raw::c_int,
|
__whence: ::std::os::raw::c_int,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn ftell(__stream: *mut FILE) -> ::std::os::raw::c_long;
|
pub fn ftell(__stream: *mut FILE) -> ::std::os::raw::c_long;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn rewind(__stream: *mut FILE);
|
pub fn rewind(__stream: *mut FILE);
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fseeko(
|
pub fn fseeko(
|
||||||
__stream: *mut FILE,
|
__stream: *mut FILE,
|
||||||
__off: __off_t,
|
__off: __off_t,
|
||||||
__whence: ::std::os::raw::c_int,
|
__whence: ::std::os::raw::c_int,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn ftello(__stream: *mut FILE) -> __off_t;
|
pub fn ftello(__stream: *mut FILE) -> __off_t;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fgetpos(__stream: *mut FILE, __pos: *mut fpos_t) -> ::std::os::raw::c_int;
|
pub fn fgetpos(__stream: *mut FILE, __pos: *mut fpos_t) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fsetpos(__stream: *mut FILE, __pos: *const fpos_t) -> ::std::os::raw::c_int;
|
pub fn fsetpos(__stream: *mut FILE, __pos: *const fpos_t) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn clearerr(__stream: *mut FILE);
|
pub fn clearerr(__stream: *mut FILE);
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn feof(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn feof(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn ferror(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn ferror(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn clearerr_unlocked(__stream: *mut FILE);
|
pub fn clearerr_unlocked(__stream: *mut FILE);
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn feof_unlocked(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn feof_unlocked(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn ferror_unlocked(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn ferror_unlocked(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn perror(__s: *const ::std::os::raw::c_char);
|
pub fn perror(__s: *const ::std::os::raw::c_char);
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fileno(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn fileno(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn fileno_unlocked(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn fileno_unlocked(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn pclose(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn pclose(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn popen(
|
pub fn popen(
|
||||||
__command: *const ::std::os::raw::c_char,
|
__command: *const ::std::os::raw::c_char,
|
||||||
__modes: *const ::std::os::raw::c_char,
|
__modes: *const ::std::os::raw::c_char,
|
||||||
) -> *mut FILE;
|
) -> *mut FILE;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn ctermid(__s: *mut ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char;
|
pub fn ctermid(__s: *mut ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn flockfile(__stream: *mut FILE);
|
pub fn flockfile(__stream: *mut FILE);
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn ftrylockfile(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn ftrylockfile(__stream: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn funlockfile(__stream: *mut FILE);
|
pub fn funlockfile(__stream: *mut FILE);
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn __uflow(arg1: *mut FILE) -> ::std::os::raw::c_int;
|
pub fn __uflow(arg1: *mut FILE) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn __overflow(arg1: *mut FILE, arg2: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
|
pub fn __overflow(arg1: *mut FILE, arg2: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
pub type int_least8_t = __int_least8_t;
|
pub type int_least8_t = __int_least8_t;
|
||||||
@ -1054,26 +1054,26 @@ pub type uint_fast64_t = ::std::os::raw::c_ulong;
|
|||||||
pub type intmax_t = __intmax_t;
|
pub type intmax_t = __intmax_t;
|
||||||
pub type uintmax_t = __uintmax_t;
|
pub type uintmax_t = __uintmax_t;
|
||||||
pub type libafl_word = u64;
|
pub type libafl_word = u64;
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn _libafl_sync_exit_call0(action: libafl_word) -> libafl_word;
|
pub fn _libafl_sync_exit_call0(action: libafl_word) -> libafl_word;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn _libafl_sync_exit_call1(action: libafl_word, arg1: libafl_word) -> libafl_word;
|
pub fn _libafl_sync_exit_call1(action: libafl_word, arg1: libafl_word) -> libafl_word;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn _libafl_sync_exit_call2(
|
pub fn _libafl_sync_exit_call2(
|
||||||
action: libafl_word,
|
action: libafl_word,
|
||||||
arg1: libafl_word,
|
arg1: libafl_word,
|
||||||
arg2: libafl_word,
|
arg2: libafl_word,
|
||||||
) -> libafl_word;
|
) -> libafl_word;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn _libafl_backdoor_call0(action: libafl_word) -> libafl_word;
|
pub fn _libafl_backdoor_call0(action: libafl_word) -> libafl_word;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn _libafl_backdoor_call1(action: libafl_word, arg1: libafl_word) -> libafl_word;
|
pub fn _libafl_backdoor_call1(action: libafl_word, arg1: libafl_word) -> libafl_word;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn _libafl_backdoor_call2(
|
pub fn _libafl_backdoor_call2(
|
||||||
action: libafl_word,
|
action: libafl_word,
|
||||||
arg1: libafl_word,
|
arg1: libafl_word,
|
||||||
@ -1113,61 +1113,61 @@ impl ::std::ops::BitAndAssign for LibaflQemuEndStatus {
|
|||||||
#[doc = " LibAFL QEMU header file.\n\n This file is a portable header file used to build target harnesses more\n conveniently. Its main purpose is to generate ready-to-use calls to\n communicate with the fuzzer. The list of commands is available at the bottom\n of this file. The rest mostly consists of macros generating the code used by\n the commands."]
|
#[doc = " LibAFL QEMU header file.\n\n This file is a portable header file used to build target harnesses more\n conveniently. Its main purpose is to generate ready-to-use calls to\n communicate with the fuzzer. The list of commands is available at the bottom\n of this file. The rest mostly consists of macros generating the code used by\n the commands."]
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct LibaflQemuEndStatus(pub ::std::os::raw::c_uint);
|
pub struct LibaflQemuEndStatus(pub ::std::os::raw::c_uint);
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn libafl_qemu_start_virt(
|
pub fn libafl_qemu_start_virt(
|
||||||
buf_vaddr: *mut ::std::os::raw::c_void,
|
buf_vaddr: *mut ::std::os::raw::c_void,
|
||||||
max_len: libafl_word,
|
max_len: libafl_word,
|
||||||
) -> libafl_word;
|
) -> libafl_word;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn libafl_qemu_start_phys(
|
pub fn libafl_qemu_start_phys(
|
||||||
buf_paddr: *mut ::std::os::raw::c_void,
|
buf_paddr: *mut ::std::os::raw::c_void,
|
||||||
max_len: libafl_word,
|
max_len: libafl_word,
|
||||||
) -> libafl_word;
|
) -> libafl_word;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn libafl_qemu_input_virt(
|
pub fn libafl_qemu_input_virt(
|
||||||
buf_vaddr: *mut ::std::os::raw::c_void,
|
buf_vaddr: *mut ::std::os::raw::c_void,
|
||||||
max_len: libafl_word,
|
max_len: libafl_word,
|
||||||
) -> libafl_word;
|
) -> libafl_word;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn libafl_qemu_input_phys(
|
pub fn libafl_qemu_input_phys(
|
||||||
buf_paddr: *mut ::std::os::raw::c_void,
|
buf_paddr: *mut ::std::os::raw::c_void,
|
||||||
max_len: libafl_word,
|
max_len: libafl_word,
|
||||||
) -> libafl_word;
|
) -> libafl_word;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn libafl_qemu_end(status: LibaflQemuEndStatus);
|
pub fn libafl_qemu_end(status: LibaflQemuEndStatus);
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn libafl_qemu_save();
|
pub fn libafl_qemu_save();
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn libafl_qemu_load();
|
pub fn libafl_qemu_load();
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn libafl_qemu_version() -> libafl_word;
|
pub fn libafl_qemu_version() -> libafl_word;
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn libafl_qemu_page_current_allow();
|
pub fn libafl_qemu_page_current_allow();
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn libafl_qemu_internal_error();
|
pub fn libafl_qemu_internal_error();
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn lqprintf(fmt: *const ::std::os::raw::c_char, ...);
|
pub fn lqprintf(fmt: *const ::std::os::raw::c_char, ...);
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn libafl_qemu_test();
|
pub fn libafl_qemu_test();
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn libafl_qemu_trace_vaddr_range(start: libafl_word, end: libafl_word);
|
pub fn libafl_qemu_trace_vaddr_range(start: libafl_word, end: libafl_word);
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn libafl_qemu_trace_vaddr_size(start: libafl_word, size: libafl_word);
|
pub fn libafl_qemu_trace_vaddr_size(start: libafl_word, size: libafl_word);
|
||||||
}
|
}
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
pub static mut _lqprintf_buffer: [::std::os::raw::c_char; 4096usize];
|
pub static mut _lqprintf_buffer: [::std::os::raw::c_char; 4096usize];
|
||||||
}
|
}
|
||||||
pub type __builtin_va_list = [__va_list_tag; 1usize];
|
pub type __builtin_va_list = [__va_list_tag; 1usize];
|
||||||
|
185
libafl_qemu/runtime/nyx_api.h
Normal file
185
libafl_qemu/runtime/nyx_api.h
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
* kAFl/Nyx low-level interface definitions
|
||||||
|
*
|
||||||
|
* Copyright 2022 Sergej Schumilo, Cornelius Aschermann
|
||||||
|
* Copyright 2022 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NYX_API_H
|
||||||
|
#define NYX_API_H
|
||||||
|
|
||||||
|
#ifndef __KERNEL__
|
||||||
|
// userspace
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifdef __MINGW64__
|
||||||
|
#ifndef uint64_t
|
||||||
|
#define uint64_t UINT64
|
||||||
|
#endif
|
||||||
|
#ifndef int32_t
|
||||||
|
#define int32_t INT32
|
||||||
|
#endif
|
||||||
|
#ifndef uint32_t
|
||||||
|
#define uint32_t UINT32
|
||||||
|
#endif
|
||||||
|
#ifndef u_long
|
||||||
|
#define u_long UINT64
|
||||||
|
#endif
|
||||||
|
#ifndef uint8_t
|
||||||
|
#define uint8_t UINT8
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
// Linux kernel
|
||||||
|
#include <linux/stdarg.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define HYPERCALL_KAFL_RAX_ID 0x01f
|
||||||
|
#define HYPERCALL_KAFL_ACQUIRE 0
|
||||||
|
#define HYPERCALL_KAFL_GET_PAYLOAD 1
|
||||||
|
#define HYPERCALL_KAFL_GET_PROGRAM 2 /* deprecated */
|
||||||
|
#define HYPERCALL_KAFL_GET_ARGV 3 /* deprecated */
|
||||||
|
#define HYPERCALL_KAFL_RELEASE 4
|
||||||
|
#define HYPERCALL_KAFL_SUBMIT_CR3 5
|
||||||
|
#define HYPERCALL_KAFL_SUBMIT_PANIC 6
|
||||||
|
#define HYPERCALL_KAFL_SUBMIT_KASAN 7
|
||||||
|
#define HYPERCALL_KAFL_PANIC 8
|
||||||
|
#define HYPERCALL_KAFL_KASAN 9
|
||||||
|
#define HYPERCALL_KAFL_LOCK 10
|
||||||
|
#define HYPERCALL_KAFL_INFO 11 /* deprecated */
|
||||||
|
#define HYPERCALL_KAFL_NEXT_PAYLOAD 12
|
||||||
|
#define HYPERCALL_KAFL_PRINTF 13
|
||||||
|
#define HYPERCALL_KAFL_PRINTK_ADDR 14 /* deprecated */
|
||||||
|
#define HYPERCALL_KAFL_PRINTK 15 /* deprecated */
|
||||||
|
|
||||||
|
/* user space only hypercalls */
|
||||||
|
#define HYPERCALL_KAFL_USER_RANGE_ADVISE 16
|
||||||
|
#define HYPERCALL_KAFL_USER_SUBMIT_MODE 17
|
||||||
|
#define HYPERCALL_KAFL_USER_FAST_ACQUIRE 18
|
||||||
|
/* 19 is already used for exit reason KVM_EXIT_KAFL_TOPA_MAIN_FULL */
|
||||||
|
#define HYPERCALL_KAFL_USER_ABORT 20
|
||||||
|
#define HYPERCALL_KAFL_TIMEOUT 21 /* deprecated */
|
||||||
|
#define HYPERCALL_KAFL_RANGE_SUBMIT 29
|
||||||
|
#define HYPERCALL_KAFL_REQ_STREAM_DATA 30
|
||||||
|
#define HYPERCALL_KAFL_PANIC_EXTENDED 32
|
||||||
|
|
||||||
|
#define HYPERCALL_KAFL_CREATE_TMP_SNAPSHOT 33
|
||||||
|
#define HYPERCALL_KAFL_DEBUG_TMP_SNAPSHOT \
|
||||||
|
34 /* hypercall for debugging / development purposes */
|
||||||
|
|
||||||
|
#define HYPERCALL_KAFL_GET_HOST_CONFIG 35
|
||||||
|
#define HYPERCALL_KAFL_SET_AGENT_CONFIG 36
|
||||||
|
|
||||||
|
#define HYPERCALL_KAFL_DUMP_FILE 37
|
||||||
|
|
||||||
|
#define HYPERCALL_KAFL_REQ_STREAM_DATA_BULK 38
|
||||||
|
#define HYPERCALL_KAFL_PERSIST_PAGE_PAST_SNAPSHOT 39
|
||||||
|
|
||||||
|
/* hypertrash only hypercalls */
|
||||||
|
#define HYPERTRASH_HYPERCALL_MASK 0xAA000000
|
||||||
|
|
||||||
|
#define HYPERCALL_KAFL_NESTED_PREPARE (0 | HYPERTRASH_HYPERCALL_MASK)
|
||||||
|
#define HYPERCALL_KAFL_NESTED_CONFIG (1 | HYPERTRASH_HYPERCALL_MASK)
|
||||||
|
#define HYPERCALL_KAFL_NESTED_ACQUIRE (2 | HYPERTRASH_HYPERCALL_MASK)
|
||||||
|
#define HYPERCALL_KAFL_NESTED_RELEASE (3 | HYPERTRASH_HYPERCALL_MASK)
|
||||||
|
#define HYPERCALL_KAFL_NESTED_HPRINTF (4 | HYPERTRASH_HYPERCALL_MASK)
|
||||||
|
|
||||||
|
#define HPRINTF_MAX_SIZE 0x1000 /* up to 4KB hprintf strings */
|
||||||
|
|
||||||
|
#define KAFL_MODE_64 0
|
||||||
|
#define KAFL_MODE_32 1
|
||||||
|
#define KAFL_MODE_16 2
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int32_t size;
|
||||||
|
uint8_t data[];
|
||||||
|
} kAFL_payload;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t ip[4];
|
||||||
|
uint64_t size[4];
|
||||||
|
uint8_t enabled[4];
|
||||||
|
} kAFL_ranges;
|
||||||
|
|
||||||
|
#if defined(__i386__)
|
||||||
|
static inline uint32_t kAFL_hypercall(uint32_t p1, uint32_t p2) {
|
||||||
|
uint32_t nr = HYPERCALL_KAFL_RAX_ID;
|
||||||
|
asm volatile("vmcall" : "=a"(nr) : "a"(nr), "b"(p1), "c"(p2));
|
||||||
|
return nr;
|
||||||
|
}
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
static inline uint64_t kAFL_hypercall(uint64_t p1, uint64_t p2) {
|
||||||
|
uint64_t nr = HYPERCALL_KAFL_RAX_ID;
|
||||||
|
asm volatile("vmcall" : "=a"(nr) : "a"(nr), "b"(p1), "c"(p2));
|
||||||
|
return nr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void habort(char *msg) __attribute__((unused));
|
||||||
|
static void habort(char *msg) {
|
||||||
|
kAFL_hypercall(HYPERCALL_KAFL_USER_ABORT, (uintptr_t)msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hprintf(const char *format, ...) __attribute__((unused));
|
||||||
|
static void hprintf(const char *format, ...) {
|
||||||
|
static char hprintf_buffer[HPRINTF_MAX_SIZE] __attribute__((aligned(4096)));
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
vsnprintf((char *)hprintf_buffer, HPRINTF_MAX_SIZE, format, args);
|
||||||
|
// printf("%s", hprintf_buffer);
|
||||||
|
kAFL_hypercall(HYPERCALL_KAFL_PRINTF, (uintptr_t)hprintf_buffer);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NYX_HOST_MAGIC 0x4878794e
|
||||||
|
#define NYX_AGENT_MAGIC 0x4178794e
|
||||||
|
|
||||||
|
#define NYX_HOST_VERSION 2
|
||||||
|
#define NYX_AGENT_VERSION 1
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t host_magic;
|
||||||
|
uint32_t host_version;
|
||||||
|
uint32_t bitmap_size;
|
||||||
|
uint32_t ijon_bitmap_size;
|
||||||
|
uint32_t payload_buffer_size;
|
||||||
|
uint32_t worker_id;
|
||||||
|
/* more to come */
|
||||||
|
} __attribute__((packed)) host_config_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t agent_magic;
|
||||||
|
uint32_t agent_version;
|
||||||
|
uint8_t agent_timeout_detection;
|
||||||
|
uint8_t agent_tracing;
|
||||||
|
uint8_t agent_ijon_tracing;
|
||||||
|
uint8_t agent_non_reload_mode;
|
||||||
|
uint64_t trace_buffer_vaddr;
|
||||||
|
uint64_t ijon_trace_buffer_vaddr;
|
||||||
|
uint32_t coverage_bitmap_size;
|
||||||
|
uint32_t input_buffer_size;
|
||||||
|
uint8_t dump_payloads; /* set by hypervisor */
|
||||||
|
/* more to come */
|
||||||
|
} __attribute__((packed)) agent_config_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t file_name_str_ptr;
|
||||||
|
uint64_t data_ptr;
|
||||||
|
uint64_t bytes;
|
||||||
|
uint8_t append;
|
||||||
|
} __attribute__((packed)) kafl_dump_file_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char file_name[256];
|
||||||
|
uint64_t num_addresses;
|
||||||
|
uint64_t addresses[479];
|
||||||
|
} __attribute__((packed)) req_data_bulk_t;
|
||||||
|
|
||||||
|
#endif /* NYX_API_H */
|
1255
libafl_qemu/runtime/nyx_stub_bindings.rs
Normal file
1255
libafl_qemu/runtime/nyx_stub_bindings.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -32,6 +32,8 @@ use crate::{
|
|||||||
IsSnapshotManager, Qemu, QemuMemoryChunk, QemuRWError, Regs, StdEmulatorDriver, CPU,
|
IsSnapshotManager, Qemu, QemuMemoryChunk, QemuRWError, Regs, StdEmulatorDriver, CPU,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(any(cpu_target = "i386", cpu_target = "x86_64"))]
|
||||||
|
pub mod nyx;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
|
||||||
mod bindings {
|
mod bindings {
|
||||||
|
532
libafl_qemu/src/command/nyx.rs
Normal file
532
libafl_qemu/src/command/nyx.rs
Normal file
@ -0,0 +1,532 @@
|
|||||||
|
//! # Nyx API Command handlers
|
||||||
|
//!
|
||||||
|
//! Nyx command handlers.
|
||||||
|
//! Makes it possible to run Nyx targets in `LibAFL` QEMU.
|
||||||
|
//! The [Nyx API](https://github.com/IntelLabs/kafl.targets/blob/master/nyx_api.h) refers to the hypercalls used in Nyx to communicate with the fuzzer, not to the fuzzer itself.
|
||||||
|
//! This is mostly a convenient way to run Nyx-compatible targets in `LibAFL` QEMU directly, without having to change a single bit of the target files.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
fmt,
|
||||||
|
fmt::{Debug, Formatter},
|
||||||
|
marker::PhantomData,
|
||||||
|
mem::offset_of,
|
||||||
|
ptr,
|
||||||
|
slice::from_raw_parts,
|
||||||
|
};
|
||||||
|
|
||||||
|
use enum_map::EnumMap;
|
||||||
|
use libafl::{
|
||||||
|
executors::ExitKind,
|
||||||
|
inputs::{HasTargetBytes, UsesInput},
|
||||||
|
};
|
||||||
|
use libafl_qemu_sys::GuestVirtAddr;
|
||||||
|
use libc::c_uint;
|
||||||
|
use paste::paste;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
command::{
|
||||||
|
parser::nyx::{
|
||||||
|
AcquireCommandParser, GetHostConfigCommandParser, GetPayloadCommandParser,
|
||||||
|
NextPayloadCommandParser, PrintfCommandParser, ReleaseCommandParser,
|
||||||
|
SetAgentConfigCommandParser,
|
||||||
|
},
|
||||||
|
CommandError, CommandManager, IsCommand, NativeCommandParser,
|
||||||
|
},
|
||||||
|
get_exit_arch_regs,
|
||||||
|
modules::EmulatorModuleTuple,
|
||||||
|
sync_exit::ExitArgs,
|
||||||
|
Emulator, EmulatorDriverError, EmulatorDriverResult, GuestReg, InputLocation,
|
||||||
|
IsSnapshotManager, NyxEmulatorDriver, Qemu, QemuMemoryChunk, Regs,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) mod bindings {
|
||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
#![allow(improper_ctypes)]
|
||||||
|
#![allow(unused_mut)]
|
||||||
|
#![allow(unused)]
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
#![allow(clippy::all)]
|
||||||
|
#![allow(clippy::pedantic)]
|
||||||
|
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/nyx_bindings.rs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! define_nyx_command_manager {
|
||||||
|
($name:ident, [$($command:ty),+], [$($native_command_parser:ty),+]) => {
|
||||||
|
paste! {
|
||||||
|
pub struct $name<S> {
|
||||||
|
has_started: bool,
|
||||||
|
phantom: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Clone for $name<S> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
has_started: self.has_started,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Debug for $name<S> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{} (has started? {:?})", stringify!($name), self.has_started)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Default for $name<S> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
has_started: false,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> $name<S> {
|
||||||
|
fn start(&mut self) -> bool {
|
||||||
|
let tmp = self.has_started;
|
||||||
|
self.has_started = true;
|
||||||
|
tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_started(&self) -> bool {
|
||||||
|
self.has_started
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ET, S, SM> CommandManager<NyxEmulatorDriver, ET, S, SM> for $name<S>
|
||||||
|
where
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
S: UsesInput + Unpin,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
{
|
||||||
|
type Commands = [<$name Commands>];
|
||||||
|
|
||||||
|
#[deny(unreachable_patterns)]
|
||||||
|
fn parse(&self, qemu: Qemu) -> Result<Self::Commands, CommandError> {
|
||||||
|
let arch_regs_map: &'static EnumMap<ExitArgs, Regs> = get_exit_arch_regs();
|
||||||
|
let nyx_backdoor = qemu.read_reg(arch_regs_map[ExitArgs::Cmd])? as c_uint;
|
||||||
|
|
||||||
|
// Check nyx backdoor correctness
|
||||||
|
debug_assert_eq!(nyx_backdoor, 0x1f);
|
||||||
|
|
||||||
|
let cmd_id = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])? as c_uint;
|
||||||
|
|
||||||
|
match cmd_id {
|
||||||
|
// <StartPhysCommandParser as NativeCommandParser<S>>::COMMAND_ID => Ok(StdCommandManagerCommands::StartPhysCommandParserCmd(<StartPhysCommandParser as NativeCommandParser<S>>::parse(qemu, arch_regs_map)?)),
|
||||||
|
$(<$native_command_parser as NativeCommandParser<Self, NyxEmulatorDriver, ET, S, SM>>::COMMAND_ID => Ok(<$native_command_parser as NativeCommandParser<Self, NyxEmulatorDriver, ET, S, SM>>::parse(qemu, arch_regs_map)?.into())),+,
|
||||||
|
_ => Err(CommandError::UnknownCommand(cmd_id.into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum [<$name Commands>]
|
||||||
|
{
|
||||||
|
// StartPhysCommand(StartPhysCommand)
|
||||||
|
$($command($command)),+,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ET, S, SM> IsCommand<$name<S>, NyxEmulatorDriver, ET, S, SM> for [<$name Commands>]
|
||||||
|
where
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
S: UsesInput + Unpin,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
$([<$name Commands>]::$command(cmd) => <$command as IsCommand<$name<S>, NyxEmulatorDriver, ET, S, SM>>::usable_at_runtime(cmd)),+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self,
|
||||||
|
emu: &mut Emulator<$name<S>, NyxEmulatorDriver, ET, S, SM>,
|
||||||
|
state: &mut S,
|
||||||
|
input: &S::Input,
|
||||||
|
ret_reg: Option<Regs>
|
||||||
|
) -> Result<Option<EmulatorDriverResult<$name<S>, NyxEmulatorDriver, ET, S, SM>>, EmulatorDriverError> {
|
||||||
|
match self {
|
||||||
|
$([<$name Commands>]::$command(cmd) => cmd.run(emu, state, input, ret_reg)),+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
impl From<$command> for [<$name Commands>] {
|
||||||
|
fn from(cmd: $command) -> [<$name Commands>] {
|
||||||
|
[<$name Commands>]::$command(cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
define_nyx_command_manager!(
|
||||||
|
NyxCommandManager,
|
||||||
|
[
|
||||||
|
AcquireCommand,
|
||||||
|
ReleaseCommand,
|
||||||
|
GetHostConfigCommand,
|
||||||
|
SetAgentConfigCommand,
|
||||||
|
PrintfCommand,
|
||||||
|
GetPayloadCommand,
|
||||||
|
NextPayloadCommand
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AcquireCommandParser,
|
||||||
|
ReleaseCommandParser,
|
||||||
|
GetHostConfigCommandParser,
|
||||||
|
SetAgentConfigCommandParser,
|
||||||
|
PrintfCommandParser,
|
||||||
|
GetPayloadCommandParser,
|
||||||
|
NextPayloadCommandParser
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AcquireCommand;
|
||||||
|
impl<CM, ED, ET, S, SM> IsCommand<CM, ED, ET, S, SM> for AcquireCommand
|
||||||
|
where
|
||||||
|
CM: CommandManager<ED, ET, S, SM>,
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_emu: &mut Emulator<CM, ED, ET, S, SM>,
|
||||||
|
_state: &mut S,
|
||||||
|
_input: &S::Input,
|
||||||
|
_ret_reg: Option<Regs>,
|
||||||
|
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct GetPayloadCommand {
|
||||||
|
input_struct_location: GuestVirtAddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetPayloadCommand {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(input_struct_location: GuestVirtAddr) -> Self {
|
||||||
|
Self {
|
||||||
|
input_struct_location,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ET, S, SM> IsCommand<NyxCommandManager<S>, NyxEmulatorDriver, ET, S, SM> for GetPayloadCommand
|
||||||
|
where
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
S: UsesInput + Unpin,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
emu: &mut Emulator<NyxCommandManager<S>, NyxEmulatorDriver, ET, S, SM>,
|
||||||
|
_state: &mut S,
|
||||||
|
_input: &S::Input,
|
||||||
|
_ret_reg: Option<Regs>,
|
||||||
|
) -> Result<
|
||||||
|
Option<EmulatorDriverResult<NyxCommandManager<S>, NyxEmulatorDriver, ET, S, SM>>,
|
||||||
|
EmulatorDriverError,
|
||||||
|
> {
|
||||||
|
let qemu = emu.qemu();
|
||||||
|
|
||||||
|
let struct_addr = self.input_struct_location;
|
||||||
|
let input_addr =
|
||||||
|
self.input_struct_location + offset_of!(bindings::kAFL_payload, data) as GuestVirtAddr;
|
||||||
|
|
||||||
|
let payload_struct_mem_chunk = QemuMemoryChunk::virt(
|
||||||
|
struct_addr,
|
||||||
|
size_of::<bindings::kAFL_payload>() as GuestReg,
|
||||||
|
qemu.current_cpu().unwrap(),
|
||||||
|
);
|
||||||
|
let payload_mem_chunk = QemuMemoryChunk::virt(
|
||||||
|
input_addr,
|
||||||
|
emu.driver().max_input_size() as GuestReg,
|
||||||
|
qemu.current_cpu().unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Save input struct location for next runs
|
||||||
|
emu.driver_mut()
|
||||||
|
.set_input_struct_location(InputLocation::new(
|
||||||
|
payload_struct_mem_chunk,
|
||||||
|
qemu.current_cpu().unwrap(),
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Save input location for next runs
|
||||||
|
emu.driver_mut()
|
||||||
|
.set_input_location(InputLocation::new(
|
||||||
|
payload_mem_chunk,
|
||||||
|
qemu.current_cpu().unwrap(),
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NextPayloadCommand;
|
||||||
|
|
||||||
|
impl<ET, S, SM> IsCommand<NyxCommandManager<S>, NyxEmulatorDriver, ET, S, SM> for NextPayloadCommand
|
||||||
|
where
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
S: UsesInput + Unpin,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
emu: &mut Emulator<NyxCommandManager<S>, NyxEmulatorDriver, ET, S, SM>,
|
||||||
|
state: &mut S,
|
||||||
|
input: &S::Input,
|
||||||
|
_ret_reg: Option<Regs>,
|
||||||
|
) -> Result<
|
||||||
|
Option<EmulatorDriverResult<NyxCommandManager<S>, NyxEmulatorDriver, ET, S, SM>>,
|
||||||
|
EmulatorDriverError,
|
||||||
|
> {
|
||||||
|
if emu.command_manager_mut().start() {
|
||||||
|
return Err(EmulatorDriverError::CommandError(
|
||||||
|
CommandError::StartedTwice,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let qemu = emu.qemu();
|
||||||
|
|
||||||
|
// Snapshot VM
|
||||||
|
let snapshot_id = emu.snapshot_manager_mut().save(qemu);
|
||||||
|
|
||||||
|
// Set snapshot ID to restore to after fuzzing ends
|
||||||
|
emu.driver_mut()
|
||||||
|
.set_snapshot_id(snapshot_id)
|
||||||
|
.map_err(|_| EmulatorDriverError::MultipleSnapshotDefinition)?;
|
||||||
|
|
||||||
|
// write nyx input to vm
|
||||||
|
emu.driver().write_input(qemu, input)?;
|
||||||
|
|
||||||
|
// Unleash hooks if locked
|
||||||
|
if emu.driver_mut().unlock_hooks() {
|
||||||
|
// Prepare hooks
|
||||||
|
emu.modules_mut().first_exec_all(qemu, state);
|
||||||
|
emu.modules_mut().pre_exec_all(qemu, state, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto page filtering if option is enabled
|
||||||
|
#[cfg(feature = "systemmode")]
|
||||||
|
if emu.driver_mut().allow_page_on_start() {
|
||||||
|
if let Some(page_id) = qemu.current_cpu().unwrap().current_paging_id() {
|
||||||
|
emu.modules_mut().modules_mut().allow_page_id_all(page_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "x86_64")]
|
||||||
|
if emu.driver_mut().is_process_only() {
|
||||||
|
emu.modules_mut()
|
||||||
|
.modules_mut()
|
||||||
|
.allow_address_range_all(crate::PROCESS_ADDRESS_RANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure JIT cache is empty just before starting
|
||||||
|
qemu.flush_jit();
|
||||||
|
|
||||||
|
log::info!("Fuzzing starts");
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ReleaseCommand;
|
||||||
|
impl<ET, S, SM> IsCommand<NyxCommandManager<S>, NyxEmulatorDriver, ET, S, SM> for ReleaseCommand
|
||||||
|
where
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
S: UsesInput + Unpin,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
emu: &mut Emulator<NyxCommandManager<S>, NyxEmulatorDriver, ET, S, SM>,
|
||||||
|
_state: &mut S,
|
||||||
|
_input: &S::Input,
|
||||||
|
_ret_reg: Option<Regs>,
|
||||||
|
) -> Result<
|
||||||
|
Option<EmulatorDriverResult<NyxCommandManager<S>, NyxEmulatorDriver, ET, S, SM>>,
|
||||||
|
EmulatorDriverError,
|
||||||
|
> {
|
||||||
|
let qemu = emu.qemu();
|
||||||
|
|
||||||
|
if emu.command_manager().has_started() {
|
||||||
|
let snapshot_id = emu
|
||||||
|
.driver_mut()
|
||||||
|
.snapshot_id()
|
||||||
|
.ok_or(EmulatorDriverError::SnapshotNotFound)?;
|
||||||
|
|
||||||
|
emu.snapshot_manager_mut().restore(qemu, &snapshot_id)?;
|
||||||
|
|
||||||
|
#[cfg(feature = "paranoid_debug")]
|
||||||
|
emu.snapshot_manager_mut().check(qemu, &snapshot_id)?;
|
||||||
|
|
||||||
|
Ok(Some(EmulatorDriverResult::EndOfRun(ExitKind::Ok)))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct GetHostConfigCommand {
|
||||||
|
host_config_location: QemuMemoryChunk,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetHostConfigCommand {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(host_config_location: QemuMemoryChunk) -> Self {
|
||||||
|
Self {
|
||||||
|
host_config_location,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CM, ED, ET, S, SM> IsCommand<CM, ED, ET, S, SM> for GetHostConfigCommand
|
||||||
|
where
|
||||||
|
CM: CommandManager<ED, ET, S, SM>,
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
emu: &mut Emulator<CM, ED, ET, S, SM>,
|
||||||
|
_state: &mut S,
|
||||||
|
_input: &S::Input,
|
||||||
|
_ret_reg: Option<Regs>,
|
||||||
|
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError> {
|
||||||
|
// TODO: check this against fuzzer code
|
||||||
|
let host_config = bindings::host_config_t {
|
||||||
|
bitmap_size: 0,
|
||||||
|
ijon_bitmap_size: 0,
|
||||||
|
payload_buffer_size: 0,
|
||||||
|
worker_id: 0,
|
||||||
|
host_magic: bindings::NYX_HOST_MAGIC,
|
||||||
|
host_version: bindings::NYX_HOST_VERSION,
|
||||||
|
};
|
||||||
|
|
||||||
|
let host_config_buf = unsafe {
|
||||||
|
from_raw_parts(
|
||||||
|
ptr::from_ref(&host_config) as *const u8,
|
||||||
|
size_of::<bindings::host_config_t>(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let qemu = emu.qemu();
|
||||||
|
|
||||||
|
self.host_config_location
|
||||||
|
.write(qemu, host_config_buf)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PrintfCommand {
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrintfCommand {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(content: String) -> Self {
|
||||||
|
Self { content }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CM, ED, ET, S, SM> IsCommand<CM, ED, ET, S, SM> for PrintfCommand
|
||||||
|
where
|
||||||
|
CM: CommandManager<ED, ET, S, SM>,
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_emu: &mut Emulator<CM, ED, ET, S, SM>,
|
||||||
|
_state: &mut S,
|
||||||
|
_input: &S::Input,
|
||||||
|
_ret_reg: Option<Regs>,
|
||||||
|
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError> {
|
||||||
|
println!("hprintf: {}", self.content);
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SetAgentConfigCommand {
|
||||||
|
agent_config: bindings::agent_config_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SetAgentConfigCommand {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(agent_config: bindings::agent_config_t) -> Self {
|
||||||
|
Self { agent_config }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CM, ED, ET, S, SM> IsCommand<CM, ED, ET, S, SM> for SetAgentConfigCommand
|
||||||
|
where
|
||||||
|
CM: CommandManager<ED, ET, S, SM>,
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_emu: &mut Emulator<CM, ED, ET, S, SM>,
|
||||||
|
_state: &mut S,
|
||||||
|
_input: &S::Input,
|
||||||
|
_ret_reg: Option<Regs>,
|
||||||
|
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError> {
|
||||||
|
let agent_magic = self.agent_config.agent_magic;
|
||||||
|
let agent_version = self.agent_config.agent_version;
|
||||||
|
|
||||||
|
assert_eq!(agent_magic, bindings::NYX_AGENT_MAGIC);
|
||||||
|
assert_eq!(agent_version, bindings::NYX_AGENT_VERSION);
|
||||||
|
|
||||||
|
// TODO: use agent config
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,9 @@ use crate::{
|
|||||||
GuestReg, IsSnapshotManager, Qemu, QemuMemoryChunk, Regs, StdEmulatorDriver,
|
GuestReg, IsSnapshotManager, Qemu, QemuMemoryChunk, Regs, StdEmulatorDriver,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(any(cpu_target = "i386", cpu_target = "x86_64"))]
|
||||||
|
pub mod nyx;
|
||||||
|
|
||||||
pub static EMU_EXIT_KIND_MAP: OnceLock<EnumMap<NativeExitKind, Option<ExitKind>>> = OnceLock::new();
|
pub static EMU_EXIT_KIND_MAP: OnceLock<EnumMap<NativeExitKind, Option<ExitKind>>> = OnceLock::new();
|
||||||
|
|
||||||
pub trait NativeCommandParser<CM, ED, ET, S, SM>
|
pub trait NativeCommandParser<CM, ED, ET, S, SM>
|
189
libafl_qemu/src/command/parser/nyx.rs
Normal file
189
libafl_qemu/src/command/parser/nyx.rs
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
use std::{ffi::CStr, mem::transmute, sync::OnceLock};
|
||||||
|
|
||||||
|
use enum_map::EnumMap;
|
||||||
|
use libafl::{
|
||||||
|
executors::ExitKind,
|
||||||
|
inputs::{HasTargetBytes, UsesInput},
|
||||||
|
};
|
||||||
|
use libafl_qemu_sys::GuestVirtAddr;
|
||||||
|
use libc::c_uint;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
command::{
|
||||||
|
nyx::{
|
||||||
|
bindings, AcquireCommand, GetHostConfigCommand, GetPayloadCommand, NextPayloadCommand,
|
||||||
|
NyxCommandManager, PrintfCommand, ReleaseCommand, SetAgentConfigCommand,
|
||||||
|
},
|
||||||
|
parser::NativeCommandParser,
|
||||||
|
CommandError, CommandManager, NativeExitKind,
|
||||||
|
},
|
||||||
|
modules::EmulatorModuleTuple,
|
||||||
|
sync_exit::ExitArgs,
|
||||||
|
IsSnapshotManager, NyxEmulatorDriver, Qemu, QemuMemoryChunk, Regs,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static EMU_EXIT_KIND_MAP: OnceLock<EnumMap<NativeExitKind, Option<ExitKind>>> = OnceLock::new();
|
||||||
|
|
||||||
|
pub struct AcquireCommandParser;
|
||||||
|
impl<CM, ED, ET, S, SM> NativeCommandParser<CM, ED, ET, S, SM> for AcquireCommandParser
|
||||||
|
where
|
||||||
|
CM: CommandManager<ED, ET, S, SM>,
|
||||||
|
S: UsesInput,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
{
|
||||||
|
type OutputCommand = AcquireCommand;
|
||||||
|
|
||||||
|
const COMMAND_ID: c_uint = bindings::HYPERCALL_KAFL_ACQUIRE;
|
||||||
|
|
||||||
|
fn parse(
|
||||||
|
_qemu: Qemu,
|
||||||
|
_arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||||
|
) -> Result<Self::OutputCommand, CommandError> {
|
||||||
|
Ok(AcquireCommand)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GetPayloadCommandParser;
|
||||||
|
impl<ET, S, SM> NativeCommandParser<NyxCommandManager<S>, NyxEmulatorDriver, ET, S, SM>
|
||||||
|
for GetPayloadCommandParser
|
||||||
|
where
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
S: UsesInput + Unpin,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
{
|
||||||
|
type OutputCommand = GetPayloadCommand;
|
||||||
|
|
||||||
|
const COMMAND_ID: c_uint = bindings::HYPERCALL_KAFL_GET_PAYLOAD;
|
||||||
|
|
||||||
|
fn parse(
|
||||||
|
qemu: Qemu,
|
||||||
|
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||||
|
) -> Result<Self::OutputCommand, CommandError> {
|
||||||
|
let payload_addr = qemu.read_reg(arch_regs_map[ExitArgs::Arg2]).unwrap() as GuestVirtAddr;
|
||||||
|
|
||||||
|
Ok(GetPayloadCommand::new(payload_addr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NextPayloadCommandParser;
|
||||||
|
impl<ET, S, SM> NativeCommandParser<NyxCommandManager<S>, NyxEmulatorDriver, ET, S, SM>
|
||||||
|
for NextPayloadCommandParser
|
||||||
|
where
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
S: UsesInput + Unpin,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
{
|
||||||
|
type OutputCommand = NextPayloadCommand;
|
||||||
|
|
||||||
|
const COMMAND_ID: c_uint = bindings::HYPERCALL_KAFL_NEXT_PAYLOAD;
|
||||||
|
|
||||||
|
fn parse(
|
||||||
|
_qemu: Qemu,
|
||||||
|
_arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||||
|
) -> Result<Self::OutputCommand, CommandError> {
|
||||||
|
Ok(NextPayloadCommand)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ReleaseCommandParser;
|
||||||
|
impl<ET, S, SM> NativeCommandParser<NyxCommandManager<S>, NyxEmulatorDriver, ET, S, SM>
|
||||||
|
for ReleaseCommandParser
|
||||||
|
where
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
S: UsesInput + Unpin,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
{
|
||||||
|
type OutputCommand = ReleaseCommand;
|
||||||
|
|
||||||
|
const COMMAND_ID: c_uint = bindings::HYPERCALL_KAFL_RELEASE;
|
||||||
|
|
||||||
|
fn parse(
|
||||||
|
_qemu: Qemu,
|
||||||
|
_arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||||
|
) -> Result<Self::OutputCommand, CommandError> {
|
||||||
|
Ok(ReleaseCommand)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GetHostConfigCommandParser;
|
||||||
|
impl<CM, ED, ET, S, SM> NativeCommandParser<CM, ED, ET, S, SM> for GetHostConfigCommandParser
|
||||||
|
where
|
||||||
|
CM: CommandManager<ED, ET, S, SM>,
|
||||||
|
S: UsesInput,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
{
|
||||||
|
type OutputCommand = GetHostConfigCommand;
|
||||||
|
|
||||||
|
const COMMAND_ID: c_uint = bindings::HYPERCALL_KAFL_GET_HOST_CONFIG;
|
||||||
|
|
||||||
|
fn parse(
|
||||||
|
qemu: Qemu,
|
||||||
|
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||||
|
) -> Result<Self::OutputCommand, CommandError> {
|
||||||
|
let host_config_addr = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])? as GuestVirtAddr;
|
||||||
|
|
||||||
|
Ok(GetHostConfigCommand::new(QemuMemoryChunk::virt(
|
||||||
|
host_config_addr,
|
||||||
|
GuestVirtAddr::try_from(size_of::<bindings::host_config_t>()).unwrap(),
|
||||||
|
qemu.current_cpu().unwrap(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SetAgentConfigCommandParser;
|
||||||
|
impl<CM, ED, ET, S, SM> NativeCommandParser<CM, ED, ET, S, SM> for SetAgentConfigCommandParser
|
||||||
|
where
|
||||||
|
CM: CommandManager<ED, ET, S, SM>,
|
||||||
|
S: UsesInput,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
{
|
||||||
|
type OutputCommand = SetAgentConfigCommand;
|
||||||
|
|
||||||
|
const COMMAND_ID: c_uint = bindings::HYPERCALL_KAFL_SET_AGENT_CONFIG;
|
||||||
|
|
||||||
|
fn parse(
|
||||||
|
qemu: Qemu,
|
||||||
|
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||||
|
) -> Result<Self::OutputCommand, CommandError> {
|
||||||
|
let agent_config_addr = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])? as GuestVirtAddr;
|
||||||
|
|
||||||
|
let mut agent_config_buf: [u8; size_of::<bindings::agent_config_t>()] =
|
||||||
|
[0; size_of::<bindings::agent_config_t>()];
|
||||||
|
|
||||||
|
qemu.read_mem(agent_config_addr, &mut agent_config_buf)?;
|
||||||
|
|
||||||
|
let agent_config: bindings::agent_config_t = unsafe { transmute(agent_config_buf) };
|
||||||
|
|
||||||
|
Ok(SetAgentConfigCommand::new(agent_config))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PrintfCommandParser;
|
||||||
|
impl<CM, ED, ET, S, SM> NativeCommandParser<CM, ED, ET, S, SM> for PrintfCommandParser
|
||||||
|
where
|
||||||
|
CM: CommandManager<ED, ET, S, SM>,
|
||||||
|
S: UsesInput,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
{
|
||||||
|
type OutputCommand = PrintfCommand;
|
||||||
|
|
||||||
|
const COMMAND_ID: c_uint = bindings::HYPERCALL_KAFL_PRINTF;
|
||||||
|
|
||||||
|
fn parse(
|
||||||
|
qemu: Qemu,
|
||||||
|
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||||
|
) -> Result<Self::OutputCommand, CommandError> {
|
||||||
|
let str_addr = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])? as GuestVirtAddr;
|
||||||
|
|
||||||
|
let mut msg_chunk: [u8; bindings::HPRINTF_MAX_SIZE as usize] =
|
||||||
|
[0; bindings::HPRINTF_MAX_SIZE as usize];
|
||||||
|
qemu.read_mem(str_addr, &mut msg_chunk)?;
|
||||||
|
|
||||||
|
let cstr = CStr::from_bytes_until_nul(&msg_chunk).unwrap();
|
||||||
|
|
||||||
|
Ok(PrintfCommand::new(cstr.to_str().unwrap().to_string()))
|
||||||
|
}
|
||||||
|
}
|
@ -14,10 +14,15 @@ use typed_builder::TypedBuilder;
|
|||||||
use crate::{
|
use crate::{
|
||||||
command::{CommandError, CommandManager, InputCommand, IsCommand},
|
command::{CommandError, CommandManager, InputCommand, IsCommand},
|
||||||
modules::EmulatorModuleTuple,
|
modules::EmulatorModuleTuple,
|
||||||
Emulator, EmulatorExitError, EmulatorExitResult, InputLocation, IsSnapshotManager,
|
Emulator, EmulatorExitError, EmulatorExitResult, InputLocation, IsSnapshotManager, QemuError,
|
||||||
QemuShutdownCause, Regs, SnapshotId, SnapshotManagerCheckError, SnapshotManagerError,
|
QemuShutdownCause, Regs, SnapshotId, SnapshotManagerCheckError, SnapshotManagerError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(any(cpu_target = "i386", cpu_target = "x86_64"))]
|
||||||
|
pub mod nyx;
|
||||||
|
#[cfg(any(cpu_target = "i386", cpu_target = "x86_64"))]
|
||||||
|
pub use nyx::{NyxEmulatorDriver, NyxEmulatorDriverBuilder};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum EmulatorDriverResult<CM, ED, ET, S, SM>
|
pub enum EmulatorDriverResult<CM, ED, ET, S, SM>
|
||||||
where
|
where
|
||||||
@ -33,6 +38,7 @@ where
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum EmulatorDriverError {
|
pub enum EmulatorDriverError {
|
||||||
|
QemuError(QemuError),
|
||||||
QemuExitReasonError(EmulatorExitError),
|
QemuExitReasonError(EmulatorExitError),
|
||||||
SMError(SnapshotManagerError),
|
SMError(SnapshotManagerError),
|
||||||
SMCheckError(SnapshotManagerCheckError),
|
SMCheckError(SnapshotManagerCheckError),
|
||||||
@ -43,6 +49,12 @@ pub enum EmulatorDriverError {
|
|||||||
SnapshotNotFound,
|
SnapshotNotFound,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<QemuError> for EmulatorDriverError {
|
||||||
|
fn from(error: QemuError) -> Self {
|
||||||
|
EmulatorDriverError::QemuError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An Emulator Driver.
|
/// An Emulator Driver.
|
||||||
// TODO remove 'static when specialization will be stable
|
// TODO remove 'static when specialization will be stable
|
||||||
pub trait EmulatorDriver<CM, ET, S, SM>: 'static + Sized
|
pub trait EmulatorDriver<CM, ET, S, SM>: 'static + Sized
|
||||||
@ -252,9 +264,9 @@ where
|
|||||||
return Ok(Some(EmulatorDriverResult::EndOfRun(ExitKind::Timeout)))
|
return Ok(Some(EmulatorDriverResult::EndOfRun(ExitKind::Timeout)))
|
||||||
}
|
}
|
||||||
EmulatorExitResult::Breakpoint(bp) => (bp.trigger(qemu), None),
|
EmulatorExitResult::Breakpoint(bp) => (bp.trigger(qemu), None),
|
||||||
EmulatorExitResult::SyncExit(sync_backdoor) => {
|
EmulatorExitResult::CustomInsn(custom_insn) => {
|
||||||
let command = sync_backdoor.command().clone();
|
let command = custom_insn.command().clone();
|
||||||
(Some(command), Some(sync_backdoor.ret_reg()))
|
(Some(command), Some(custom_insn.ret_reg()))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
223
libafl_qemu/src/emu/drivers/nyx.rs
Normal file
223
libafl_qemu/src/emu/drivers/nyx.rs
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
use std::{cell::OnceCell, cmp::min, ptr, slice::from_raw_parts};
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
executors::ExitKind,
|
||||||
|
inputs::{HasTargetBytes, UsesInput},
|
||||||
|
observers::ObserversTuple,
|
||||||
|
};
|
||||||
|
use libafl_bolts::os::CTRL_C_EXIT;
|
||||||
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
command::{nyx::bindings, CommandManager, IsCommand},
|
||||||
|
modules::EmulatorModuleTuple,
|
||||||
|
Emulator, EmulatorDriver, EmulatorDriverError, EmulatorDriverResult, EmulatorExitError,
|
||||||
|
EmulatorExitResult, InputLocation, IsSnapshotManager, Qemu, QemuError, QemuShutdownCause, Regs,
|
||||||
|
SnapshotId,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, TypedBuilder)]
|
||||||
|
#[allow(clippy::struct_excessive_bools)] // cfg dependent
|
||||||
|
pub struct NyxEmulatorDriver {
|
||||||
|
#[builder(default = OnceCell::new())]
|
||||||
|
snapshot_id: OnceCell<SnapshotId>,
|
||||||
|
#[builder(default = OnceCell::new())]
|
||||||
|
input_struct_location: OnceCell<InputLocation>,
|
||||||
|
#[builder(default = OnceCell::new())]
|
||||||
|
input_location: OnceCell<InputLocation>,
|
||||||
|
#[builder(default = true)]
|
||||||
|
hooks_locked: bool,
|
||||||
|
#[cfg(feature = "systemmode")]
|
||||||
|
#[builder(default = false)]
|
||||||
|
allow_page_on_start: bool, // when fuzzing starts, module filters will only allow the current page table.
|
||||||
|
#[cfg(feature = "x86_64")]
|
||||||
|
#[builder(default = false)]
|
||||||
|
process_only: bool, // adds x86_64 process address space in the address filters of every module.
|
||||||
|
#[builder(default = false)]
|
||||||
|
print_commands: bool,
|
||||||
|
#[builder(default = (1024 * 1024))]
|
||||||
|
max_input_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NyxEmulatorDriver {
|
||||||
|
pub fn max_input_size(&self) -> usize {
|
||||||
|
self.max_input_size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_input<I>(&self, qemu: Qemu, input: &I) -> Result<(), QemuError>
|
||||||
|
where
|
||||||
|
I: HasTargetBytes,
|
||||||
|
{
|
||||||
|
let input_len =
|
||||||
|
i32::try_from(min(self.max_input_size, input.target_bytes().len())).unwrap();
|
||||||
|
|
||||||
|
let kafl_payload = bindings::kAFL_payload {
|
||||||
|
size: input_len,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let kafl_payload_buf = unsafe {
|
||||||
|
from_raw_parts(
|
||||||
|
ptr::from_ref(&kafl_payload) as *const u8,
|
||||||
|
size_of::<bindings::kAFL_payload>(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let input_struct_mem_chunk = &self.input_struct_location.get().unwrap().mem_chunk;
|
||||||
|
|
||||||
|
// TODO: manage endianness correctly.
|
||||||
|
input_struct_mem_chunk.write(qemu, kafl_payload_buf)?;
|
||||||
|
|
||||||
|
// write struct first
|
||||||
|
self.input_location
|
||||||
|
.get()
|
||||||
|
.unwrap()
|
||||||
|
.mem_chunk
|
||||||
|
.write(qemu, input.target_bytes().as_ref())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_input_location(&self, input_location: InputLocation) -> Result<(), InputLocation> {
|
||||||
|
self.input_location.set(input_location)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_input_struct_location(
|
||||||
|
&self,
|
||||||
|
input_struct_location: InputLocation,
|
||||||
|
) -> Result<(), InputLocation> {
|
||||||
|
self.input_struct_location.set(input_struct_location)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_snapshot_id(&self, snapshot_id: SnapshotId) -> Result<(), SnapshotId> {
|
||||||
|
self.snapshot_id.set(snapshot_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn snapshot_id(&self) -> Option<SnapshotId> {
|
||||||
|
Some(*self.snapshot_id.get()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
// return if was locked or not
|
||||||
|
pub fn unlock_hooks(&mut self) -> bool {
|
||||||
|
let was_locked = self.hooks_locked;
|
||||||
|
self.hooks_locked = false;
|
||||||
|
was_locked
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "systemmode")]
|
||||||
|
pub fn allow_page_on_start(&self) -> bool {
|
||||||
|
self.allow_page_on_start
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "x86_64")]
|
||||||
|
pub fn is_process_only(&self) -> bool {
|
||||||
|
self.process_only
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CM, ET, S, SM> EmulatorDriver<CM, ET, S, SM> for NyxEmulatorDriver
|
||||||
|
where
|
||||||
|
CM: CommandManager<NyxEmulatorDriver, ET, S, SM>,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
S: UsesInput + Unpin,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
{
|
||||||
|
fn first_harness_exec(emulator: &mut Emulator<CM, Self, ET, S, SM>, state: &mut S) {
|
||||||
|
if !emulator.driver.hooks_locked {
|
||||||
|
emulator.modules.first_exec_all(emulator.qemu, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pre_harness_exec(
|
||||||
|
emulator: &mut Emulator<CM, Self, ET, S, SM>,
|
||||||
|
state: &mut S,
|
||||||
|
input: &S::Input,
|
||||||
|
) {
|
||||||
|
if !emulator.driver.hooks_locked {
|
||||||
|
emulator.modules.pre_exec_all(emulator.qemu, state, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
if emulator.driver.input_location.get().is_some() {
|
||||||
|
let qemu = emulator.qemu();
|
||||||
|
|
||||||
|
emulator.driver.write_input(qemu, input).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_harness_exec<OT>(
|
||||||
|
emulator: &mut Emulator<CM, Self, ET, S, SM>,
|
||||||
|
input: &S::Input,
|
||||||
|
observers: &mut OT,
|
||||||
|
state: &mut S,
|
||||||
|
exit_kind: &mut ExitKind,
|
||||||
|
) where
|
||||||
|
OT: ObserversTuple<S::Input, S>,
|
||||||
|
{
|
||||||
|
if !emulator.driver.hooks_locked {
|
||||||
|
emulator
|
||||||
|
.modules
|
||||||
|
.post_exec_all(emulator.qemu, state, input, observers, exit_kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pre_qemu_exec(_emulator: &mut Emulator<CM, Self, ET, S, SM>, _input: &S::Input) {}
|
||||||
|
|
||||||
|
fn post_qemu_exec(
|
||||||
|
emulator: &mut Emulator<CM, Self, ET, S, SM>,
|
||||||
|
state: &mut S,
|
||||||
|
exit_reason: &mut Result<EmulatorExitResult<CM, Self, ET, S, SM>, EmulatorExitError>,
|
||||||
|
input: &S::Input,
|
||||||
|
) -> Result<Option<EmulatorDriverResult<CM, Self, ET, S, SM>>, EmulatorDriverError> {
|
||||||
|
let qemu = emulator.qemu();
|
||||||
|
|
||||||
|
let mut exit_reason = match exit_reason {
|
||||||
|
Ok(exit_reason) => exit_reason,
|
||||||
|
Err(exit_error) => match exit_error {
|
||||||
|
EmulatorExitError::UnexpectedExit => {
|
||||||
|
if let Some(snapshot_id) = emulator.driver.snapshot_id.get() {
|
||||||
|
emulator.snapshot_manager.restore(qemu, snapshot_id)?;
|
||||||
|
}
|
||||||
|
return Ok(Some(EmulatorDriverResult::EndOfRun(ExitKind::Crash)));
|
||||||
|
}
|
||||||
|
_ => Err(exit_error.clone())?,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let (command, ret_reg): (Option<CM::Commands>, Option<Regs>) = match &mut exit_reason {
|
||||||
|
EmulatorExitResult::QemuExit(shutdown_cause) => match shutdown_cause {
|
||||||
|
QemuShutdownCause::HostSignal(signal) => {
|
||||||
|
signal.handle();
|
||||||
|
return Err(EmulatorDriverError::UnhandledSignal(*signal));
|
||||||
|
}
|
||||||
|
QemuShutdownCause::GuestPanic => {
|
||||||
|
return Ok(Some(EmulatorDriverResult::EndOfRun(ExitKind::Crash)))
|
||||||
|
}
|
||||||
|
QemuShutdownCause::GuestShutdown | QemuShutdownCause::HostQmpQuit => {
|
||||||
|
log::warn!("Guest shutdown. Stopping fuzzing...");
|
||||||
|
std::process::exit(CTRL_C_EXIT);
|
||||||
|
}
|
||||||
|
_ => panic!("Unhandled QEMU shutdown cause: {shutdown_cause:?}."),
|
||||||
|
},
|
||||||
|
EmulatorExitResult::Timeout => {
|
||||||
|
return Ok(Some(EmulatorDriverResult::EndOfRun(ExitKind::Timeout)))
|
||||||
|
}
|
||||||
|
EmulatorExitResult::Breakpoint(bp) => (bp.trigger(qemu), None),
|
||||||
|
EmulatorExitResult::CustomInsn(sync_backdoor) => {
|
||||||
|
let command = sync_backdoor.command().clone();
|
||||||
|
(Some(command), Some(sync_backdoor.ret_reg()))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(cmd) = command {
|
||||||
|
if emulator.driver.print_commands {
|
||||||
|
println!("Received command: {cmd:?}");
|
||||||
|
}
|
||||||
|
cmd.run(emulator, state, input, ret_reg)
|
||||||
|
} else {
|
||||||
|
Ok(Some(EmulatorDriverResult::ReturnToHarness(
|
||||||
|
exit_reason.clone(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,7 @@ use crate::{
|
|||||||
breakpoint::{Breakpoint, BreakpointId},
|
breakpoint::{Breakpoint, BreakpointId},
|
||||||
command::{CommandError, CommandManager, NopCommandManager, StdCommandManager},
|
command::{CommandError, CommandManager, NopCommandManager, StdCommandManager},
|
||||||
modules::EmulatorModuleTuple,
|
modules::EmulatorModuleTuple,
|
||||||
sync_exit::SyncExit,
|
sync_exit::CustomInsn,
|
||||||
Qemu, QemuExitError, QemuExitReason, QemuHooks, QemuInitError, QemuMemoryChunk, QemuParams,
|
Qemu, QemuExitError, QemuExitReason, QemuHooks, QemuInitError, QemuMemoryChunk, QemuParams,
|
||||||
QemuShutdownCause, Regs, CPU,
|
QemuShutdownCause, Regs, CPU,
|
||||||
};
|
};
|
||||||
@ -62,8 +62,8 @@ where
|
|||||||
{
|
{
|
||||||
QemuExit(QemuShutdownCause), // QEMU ended for some reason.
|
QemuExit(QemuShutdownCause), // QEMU ended for some reason.
|
||||||
Breakpoint(Breakpoint<CM, ED, ET, S, SM>), // Breakpoint triggered. Contains the address of the trigger.
|
Breakpoint(Breakpoint<CM, ED, ET, S, SM>), // Breakpoint triggered. Contains the address of the trigger.
|
||||||
SyncExit(SyncExit<CM, ED, ET, S, SM>), // Synchronous backdoor: The guest triggered a backdoor and should return to LibAFL.
|
CustomInsn(CustomInsn<CM, ED, ET, S, SM>), // Synchronous backdoor: The guest triggered a backdoor and should return to LibAFL.
|
||||||
Timeout, // Timeout
|
Timeout, // Timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CM, ED, ET, S, SM> Clone for EmulatorExitResult<CM, ED, ET, S, SM>
|
impl<CM, ED, ET, S, SM> Clone for EmulatorExitResult<CM, ED, ET, S, SM>
|
||||||
@ -77,8 +77,8 @@ where
|
|||||||
EmulatorExitResult::QemuExit(qemu_exit.clone())
|
EmulatorExitResult::QemuExit(qemu_exit.clone())
|
||||||
}
|
}
|
||||||
EmulatorExitResult::Breakpoint(bp) => EmulatorExitResult::Breakpoint(bp.clone()),
|
EmulatorExitResult::Breakpoint(bp) => EmulatorExitResult::Breakpoint(bp.clone()),
|
||||||
EmulatorExitResult::SyncExit(sync_exit) => {
|
EmulatorExitResult::CustomInsn(sync_exit) => {
|
||||||
EmulatorExitResult::SyncExit(sync_exit.clone())
|
EmulatorExitResult::CustomInsn(sync_exit.clone())
|
||||||
}
|
}
|
||||||
EmulatorExitResult::Timeout => EmulatorExitResult::Timeout,
|
EmulatorExitResult::Timeout => EmulatorExitResult::Timeout,
|
||||||
}
|
}
|
||||||
@ -98,7 +98,7 @@ where
|
|||||||
EmulatorExitResult::Breakpoint(bp) => {
|
EmulatorExitResult::Breakpoint(bp) => {
|
||||||
write!(f, "{bp:?}")
|
write!(f, "{bp:?}")
|
||||||
}
|
}
|
||||||
EmulatorExitResult::SyncExit(sync_exit) => {
|
EmulatorExitResult::CustomInsn(sync_exit) => {
|
||||||
write!(f, "{sync_exit:?}")
|
write!(f, "{sync_exit:?}")
|
||||||
}
|
}
|
||||||
EmulatorExitResult::Timeout => {
|
EmulatorExitResult::Timeout => {
|
||||||
@ -220,6 +220,16 @@ impl InputLocation {
|
|||||||
ret_register,
|
ret_register,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn mem_chunk(&self) -> &QemuMemoryChunk {
|
||||||
|
&self.mem_chunk
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn ret_register(&self) -> &Option<Regs> {
|
||||||
|
&self.ret_register
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<EmulatorExitError> for EmulatorDriverError {
|
impl From<EmulatorExitError> for EmulatorDriverError {
|
||||||
@ -243,7 +253,7 @@ where
|
|||||||
match self {
|
match self {
|
||||||
EmulatorExitResult::QemuExit(shutdown_cause) => write!(f, "End: {shutdown_cause:?}"),
|
EmulatorExitResult::QemuExit(shutdown_cause) => write!(f, "End: {shutdown_cause:?}"),
|
||||||
EmulatorExitResult::Breakpoint(bp) => write!(f, "{bp}"),
|
EmulatorExitResult::Breakpoint(bp) => write!(f, "{bp}"),
|
||||||
EmulatorExitResult::SyncExit(sync_exit) => {
|
EmulatorExitResult::CustomInsn(sync_exit) => {
|
||||||
write!(f, "Sync exit: {sync_exit:?}")
|
write!(f, "Sync exit: {sync_exit:?}")
|
||||||
}
|
}
|
||||||
EmulatorExitResult::Timeout => {
|
EmulatorExitResult::Timeout => {
|
||||||
@ -445,7 +455,9 @@ where
|
|||||||
ED::pre_qemu_exec(self, input);
|
ED::pre_qemu_exec(self, input);
|
||||||
|
|
||||||
// Run QEMU
|
// Run QEMU
|
||||||
|
log::debug!("Running QEMU...");
|
||||||
let mut exit_reason = self.run_qemu();
|
let mut exit_reason = self.run_qemu();
|
||||||
|
log::debug!("QEMU stopped.");
|
||||||
|
|
||||||
// Handle QEMU exit
|
// Handle QEMU exit
|
||||||
if let Some(exit_handler_result) =
|
if let Some(exit_handler_result) =
|
||||||
@ -479,7 +491,7 @@ where
|
|||||||
.clone();
|
.clone();
|
||||||
EmulatorExitResult::Breakpoint(bp.clone())
|
EmulatorExitResult::Breakpoint(bp.clone())
|
||||||
}
|
}
|
||||||
QemuExitReason::SyncExit => EmulatorExitResult::SyncExit(SyncExit::new(
|
QemuExitReason::SyncExit => EmulatorExitResult::CustomInsn(CustomInsn::new(
|
||||||
self.command_manager.parse(self.qemu)?,
|
self.command_manager.parse(self.qemu)?,
|
||||||
)),
|
)),
|
||||||
}),
|
}),
|
||||||
|
@ -62,6 +62,7 @@ impl Default for NopSnapshotManager {
|
|||||||
|
|
||||||
impl IsSnapshotManager for NopSnapshotManager {
|
impl IsSnapshotManager for NopSnapshotManager {
|
||||||
fn save(&mut self, _qemu: Qemu) -> SnapshotId {
|
fn save(&mut self, _qemu: Qemu) -> SnapshotId {
|
||||||
|
log::warn!("Saving snapshot with the NopSnapshotManager");
|
||||||
SnapshotId { id: 0 }
|
SnapshotId { id: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +71,7 @@ impl IsSnapshotManager for NopSnapshotManager {
|
|||||||
_qemu: Qemu,
|
_qemu: Qemu,
|
||||||
_snapshot_id: &SnapshotId,
|
_snapshot_id: &SnapshotId,
|
||||||
) -> Result<(), SnapshotManagerError> {
|
) -> Result<(), SnapshotManagerError> {
|
||||||
|
log::warn!("Restoring snapshot with the NopSnapshotManager");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,12 @@ use libafl_qemu_sys::GuestAddr;
|
|||||||
use thread_local::ThreadLocal;
|
use thread_local::ThreadLocal;
|
||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
use crate::modules::{NopPageFilter, NOP_PAGE_FILTER};
|
use crate::modules::utils::filters::{NopPageFilter, NOP_PAGE_FILTER};
|
||||||
use crate::{
|
use crate::{
|
||||||
capstone,
|
capstone,
|
||||||
modules::{
|
modules::{
|
||||||
AddressFilter, EmulatorModule, EmulatorModuleTuple, EmulatorModules, StdAddressFilter,
|
utils::filters::StdAddressFilter, AddressFilter, EmulatorModule, EmulatorModuleTuple,
|
||||||
|
EmulatorModules,
|
||||||
},
|
},
|
||||||
qemu::{ArchExtras, Hook},
|
qemu::{ArchExtras, Hook},
|
||||||
Qemu,
|
Qemu,
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
use capstone::{arch::BuildsCapstone, Capstone, InsnDetail};
|
use capstone::{arch::BuildsCapstone, Capstone, InsnDetail};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use libafl::{inputs::UsesInput, HasMetadata};
|
use libafl::{inputs::UsesInput, HasMetadata};
|
||||||
|
use libafl_bolts::hash_64_fast;
|
||||||
use libafl_qemu_sys::GuestAddr;
|
use libafl_qemu_sys::GuestAddr;
|
||||||
pub use libafl_targets::{
|
pub use libafl_targets::{
|
||||||
cmps::{
|
cmps::{
|
||||||
@ -12,12 +13,14 @@ pub use libafl_targets::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
use crate::modules::{NopPageFilter, NOP_PAGE_FILTER};
|
use crate::modules::utils::filters::{NopPageFilter, NOP_PAGE_FILTER};
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
use crate::{capstone, qemu::ArchExtras, CallingConvention};
|
use crate::{capstone, qemu::ArchExtras, CallingConvention};
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::EmulatorModules,
|
emu::EmulatorModules,
|
||||||
modules::{hash_me, AddressFilter, EmulatorModule, EmulatorModuleTuple, StdAddressFilter},
|
modules::{
|
||||||
|
utils::filters::StdAddressFilter, AddressFilter, EmulatorModule, EmulatorModuleTuple,
|
||||||
|
},
|
||||||
qemu::Hook,
|
qemu::Hook,
|
||||||
Qemu,
|
Qemu,
|
||||||
};
|
};
|
||||||
@ -229,7 +232,7 @@ where
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(hash_me(pc.into()) & (CMPLOG_MAP_W as u64 - 1))
|
Some(hash_64_fast(pc.into()) & (CMPLOG_MAP_W as u64 - 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern "C" fn trace_cmp1_cmplog(_: *const (), id: u64, v0: u8, v1: u8) {
|
pub extern "C" fn trace_cmp1_cmplog(_: *const (), id: u64, v0: u8, v1: u8) {
|
||||||
@ -358,7 +361,7 @@ impl CmpLogRoutinesModule {
|
|||||||
for detail in insn_detail.groups() {
|
for detail in insn_detail.groups() {
|
||||||
match u32::from(detail.0) {
|
match u32::from(detail.0) {
|
||||||
capstone::InsnGroupType::CS_GRP_CALL => {
|
capstone::InsnGroupType::CS_GRP_CALL => {
|
||||||
let k = (hash_me(pc.into())) & (CMPLOG_MAP_W as u64 - 1);
|
let k = (hash_64_fast(pc.into())) & (CMPLOG_MAP_W as u64 - 1);
|
||||||
qemu.hooks().add_instruction_hooks(
|
qemu.hooks().add_instruction_hooks(
|
||||||
k,
|
k,
|
||||||
insn.address() as GuestAddr,
|
insn.address() as GuestAddr,
|
||||||
|
@ -8,10 +8,12 @@ use rangemap::RangeMap;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
use crate::modules::{NopPageFilter, NOP_PAGE_FILTER};
|
use crate::modules::utils::filters::{NopPageFilter, NOP_PAGE_FILTER};
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::EmulatorModules,
|
emu::EmulatorModules,
|
||||||
modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, NopAddressFilter},
|
modules::{
|
||||||
|
utils::filters::NopAddressFilter, AddressFilter, EmulatorModule, EmulatorModuleTuple,
|
||||||
|
},
|
||||||
qemu::Hook,
|
qemu::Hook,
|
||||||
Qemu,
|
Qemu,
|
||||||
};
|
};
|
||||||
|
@ -6,8 +6,9 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
modules::{
|
modules::{
|
||||||
|
utils::filters::{StdAddressFilter, StdPageFilter},
|
||||||
AddressFilter, EdgeCoverageModule, EdgeCoverageModuleBuilder, EmulatorModuleTuple,
|
AddressFilter, EdgeCoverageModule, EdgeCoverageModuleBuilder, EmulatorModuleTuple,
|
||||||
PageFilter, StdAddressFilter, StdPageFilter,
|
PageFilter,
|
||||||
},
|
},
|
||||||
EmulatorModules, Hook,
|
EmulatorModules, Hook,
|
||||||
};
|
};
|
||||||
|
@ -8,8 +8,9 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
modules::{
|
modules::{
|
||||||
|
utils::filters::{StdAddressFilter, StdPageFilter},
|
||||||
AddressFilter, EdgeCoverageModule, EdgeCoverageModuleBuilder, EmulatorModuleTuple,
|
AddressFilter, EdgeCoverageModule, EdgeCoverageModuleBuilder, EmulatorModuleTuple,
|
||||||
PageFilter, StdAddressFilter, StdPageFilter,
|
PageFilter,
|
||||||
},
|
},
|
||||||
EmulatorModules, Hook,
|
EmulatorModules, Hook,
|
||||||
};
|
};
|
||||||
|
@ -6,8 +6,9 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
modules::{
|
modules::{
|
||||||
|
utils::filters::{StdAddressFilter, StdPageFilter},
|
||||||
AddressFilter, EdgeCoverageModule, EdgeCoverageModuleBuilder, EmulatorModuleTuple,
|
AddressFilter, EdgeCoverageModule, EdgeCoverageModuleBuilder, EmulatorModuleTuple,
|
||||||
PageFilter, StdAddressFilter, StdPageFilter,
|
PageFilter,
|
||||||
},
|
},
|
||||||
EmulatorModules, Hook,
|
EmulatorModules, Hook,
|
||||||
};
|
};
|
||||||
|
@ -52,6 +52,7 @@ mod generators {
|
|||||||
|
|
||||||
use hashbrown::hash_map::Entry;
|
use hashbrown::hash_map::Entry;
|
||||||
use libafl::{inputs::UsesInput, HasMetadata};
|
use libafl::{inputs::UsesInput, HasMetadata};
|
||||||
|
use libafl_bolts::hash_64_fast;
|
||||||
use libafl_qemu_sys::GuestAddr;
|
use libafl_qemu_sys::GuestAddr;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -59,7 +60,7 @@ mod generators {
|
|||||||
LIBAFL_QEMU_EDGES_MAP_SIZE_PTR,
|
LIBAFL_QEMU_EDGES_MAP_SIZE_PTR,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
modules::{hash_me, AddressFilter, EdgeCoverageModule, EmulatorModuleTuple, PageFilter},
|
modules::{AddressFilter, EdgeCoverageModule, EmulatorModuleTuple, PageFilter},
|
||||||
EmulatorModules, Qemu,
|
EmulatorModules, Qemu,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -192,7 +193,7 @@ mod generators {
|
|||||||
let mask = get_mask::<IS_CONST_MAP, MAP_SIZE>() as u64;
|
let mask = get_mask::<IS_CONST_MAP, MAP_SIZE>() as u64;
|
||||||
|
|
||||||
#[expect(clippy::unnecessary_cast)]
|
#[expect(clippy::unnecessary_cast)]
|
||||||
let id = (hash_me(src as u64) ^ hash_me(dest as u64)) & mask;
|
let id = (hash_64_fast(src as u64) ^ hash_64_fast(dest as u64)) & mask;
|
||||||
|
|
||||||
if !IS_CONST_MAP {
|
if !IS_CONST_MAP {
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -245,7 +246,7 @@ mod generators {
|
|||||||
|
|
||||||
let mask = get_mask::<IS_CONST_MAP, MAP_SIZE>() as u64;
|
let mask = get_mask::<IS_CONST_MAP, MAP_SIZE>() as u64;
|
||||||
|
|
||||||
let id = hash_me(pc as u64) & mask;
|
let id = hash_64_fast(pc as u64) & mask;
|
||||||
|
|
||||||
if !IS_CONST_MAP {
|
if !IS_CONST_MAP {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
use core::{fmt::Debug, ops::Range};
|
use core::{fmt::Debug, ops::Range};
|
||||||
use std::cell::UnsafeCell;
|
|
||||||
|
|
||||||
use hashbrown::HashSet;
|
|
||||||
use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple};
|
use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple};
|
||||||
use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType};
|
use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType};
|
||||||
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr};
|
use libafl_qemu_sys::GuestAddr;
|
||||||
|
#[cfg(feature = "systemmode")]
|
||||||
|
use libafl_qemu_sys::GuestPhysAddr;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
emu::EmulatorModules,
|
||||||
|
modules::utils::filters::{AddressFilter, PageFilter},
|
||||||
|
Qemu, QemuParams,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
pub mod usermode;
|
pub mod usermode;
|
||||||
@ -40,7 +46,7 @@ pub mod drcov;
|
|||||||
#[cfg(not(cpu_target = "hexagon"))]
|
#[cfg(not(cpu_target = "hexagon"))]
|
||||||
pub use drcov::{DrCovMetadata, DrCovModule, DrCovModuleBuilder};
|
pub use drcov::{DrCovMetadata, DrCovModule, DrCovModuleBuilder};
|
||||||
|
|
||||||
use crate::{emu::EmulatorModules, Qemu, QemuParams};
|
pub mod utils;
|
||||||
|
|
||||||
/// A module for `libafl_qemu`.
|
/// A module for `libafl_qemu`.
|
||||||
// TODO remove 'static when specialization will be stable
|
// TODO remove 'static when specialization will be stable
|
||||||
@ -374,224 +380,3 @@ where
|
|||||||
self.1.allow_page_id_all(page_id);
|
self.1.allow_page_id_all(page_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum FilterList<T> {
|
|
||||||
AllowList(T),
|
|
||||||
DenyList(T),
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> AddressFilter for FilterList<T>
|
|
||||||
where
|
|
||||||
T: AddressFilter,
|
|
||||||
{
|
|
||||||
fn register(&mut self, address_range: Range<GuestAddr>) {
|
|
||||||
match self {
|
|
||||||
FilterList::AllowList(allow_list) => allow_list.register(address_range),
|
|
||||||
FilterList::DenyList(deny_list) => deny_list.register(address_range),
|
|
||||||
FilterList::None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allowed(&self, address: &GuestAddr) -> bool {
|
|
||||||
match self {
|
|
||||||
FilterList::AllowList(allow_list) => allow_list.allowed(address),
|
|
||||||
FilterList::DenyList(deny_list) => !deny_list.allowed(address),
|
|
||||||
FilterList::None => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> PageFilter for FilterList<T>
|
|
||||||
where
|
|
||||||
T: PageFilter,
|
|
||||||
{
|
|
||||||
fn register(&mut self, page_id: GuestPhysAddr) {
|
|
||||||
match self {
|
|
||||||
FilterList::AllowList(allow_list) => allow_list.register(page_id),
|
|
||||||
FilterList::DenyList(deny_list) => deny_list.register(page_id),
|
|
||||||
FilterList::None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allowed(&self, page: &GuestPhysAddr) -> bool {
|
|
||||||
match self {
|
|
||||||
FilterList::AllowList(allow_list) => allow_list.allowed(page),
|
|
||||||
FilterList::DenyList(deny_list) => !deny_list.allowed(page),
|
|
||||||
FilterList::None => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
|
||||||
pub struct AddressFilterVec {
|
|
||||||
// ideally, we should use a tree
|
|
||||||
registered_addresses: Vec<Range<GuestAddr>>,
|
|
||||||
}
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct StdAddressFilter(FilterList<AddressFilterVec>);
|
|
||||||
|
|
||||||
impl Default for StdAddressFilter {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(FilterList::None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdAddressFilter {
|
|
||||||
#[must_use]
|
|
||||||
pub fn allow_list(registered_addresses: Vec<Range<GuestAddr>>) -> Self {
|
|
||||||
StdAddressFilter(FilterList::AllowList(AddressFilterVec::new(
|
|
||||||
registered_addresses,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn deny_list(registered_addresses: Vec<Range<GuestAddr>>) -> Self {
|
|
||||||
StdAddressFilter(FilterList::DenyList(AddressFilterVec::new(
|
|
||||||
registered_addresses,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddressFilterVec {
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(registered_addresses: Vec<Range<GuestAddr>>) -> Self {
|
|
||||||
Self {
|
|
||||||
registered_addresses,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddressFilter for AddressFilterVec {
|
|
||||||
fn register(&mut self, address_range: Range<GuestAddr>) {
|
|
||||||
self.registered_addresses.push(address_range);
|
|
||||||
Qemu::get().unwrap().flush_jit();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allowed(&self, addr: &GuestAddr) -> bool {
|
|
||||||
if self.registered_addresses.is_empty() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for addr_range in &self.registered_addresses {
|
|
||||||
if addr_range.contains(addr) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddressFilter for StdAddressFilter {
|
|
||||||
fn register(&mut self, address_range: Range<GuestAddr>) {
|
|
||||||
self.0.register(address_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allowed(&self, address: &GuestAddr) -> bool {
|
|
||||||
self.0.allowed(address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PageFilterVec {
|
|
||||||
registered_pages: HashSet<GuestPhysAddr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct StdPageFilter(FilterList<PageFilterVec>);
|
|
||||||
|
|
||||||
#[cfg(feature = "usermode")]
|
|
||||||
pub type StdPageFilter = NopPageFilter;
|
|
||||||
|
|
||||||
impl Default for PageFilterVec {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
registered_pages: HashSet::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
|
||||||
impl Default for StdPageFilter {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(FilterList::None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PageFilter for PageFilterVec {
|
|
||||||
fn register(&mut self, page_id: GuestPhysAddr) {
|
|
||||||
self.registered_pages.insert(page_id);
|
|
||||||
Qemu::get().unwrap().flush_jit();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allowed(&self, paging_id: &GuestPhysAddr) -> bool {
|
|
||||||
// if self.allowed_pages.is_empty() {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
self.registered_pages.contains(paging_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
|
||||||
impl PageFilter for StdPageFilter {
|
|
||||||
fn register(&mut self, page_id: GuestPhysAddr) {
|
|
||||||
self.0.register(page_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allowed(&self, page_id: &GuestPhysAddr) -> bool {
|
|
||||||
self.0.allowed(page_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// adapted from https://xorshift.di.unimi.it/splitmix64.c
|
|
||||||
#[must_use]
|
|
||||||
pub fn hash_me(mut x: u64) -> u64 {
|
|
||||||
x = (x ^ (x.overflowing_shr(30).0))
|
|
||||||
.overflowing_mul(0xbf58476d1ce4e5b9)
|
|
||||||
.0;
|
|
||||||
x = (x ^ (x.overflowing_shr(27).0))
|
|
||||||
.overflowing_mul(0x94d049bb133111eb)
|
|
||||||
.0;
|
|
||||||
x ^ (x.overflowing_shr(31).0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AddressFilter: 'static + Debug {
|
|
||||||
fn register(&mut self, address_range: Range<GuestAddr>);
|
|
||||||
|
|
||||||
fn allowed(&self, address: &GuestAddr) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct NopAddressFilter;
|
|
||||||
impl AddressFilter for NopAddressFilter {
|
|
||||||
fn register(&mut self, _address: Range<GuestAddr>) {}
|
|
||||||
|
|
||||||
fn allowed(&self, _address: &GuestAddr) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait PageFilter: 'static + Debug {
|
|
||||||
fn register(&mut self, page_id: GuestPhysAddr);
|
|
||||||
|
|
||||||
fn allowed(&self, page_id: &GuestPhysAddr) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
|
||||||
pub struct NopPageFilter;
|
|
||||||
impl PageFilter for NopPageFilter {
|
|
||||||
fn register(&mut self, _page_id: GuestPhysAddr) {}
|
|
||||||
|
|
||||||
fn allowed(&self, _page_id: &GuestPhysAddr) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "usermode")]
|
|
||||||
static mut NOP_ADDRESS_FILTER: UnsafeCell<NopAddressFilter> = UnsafeCell::new(NopAddressFilter);
|
|
||||||
#[cfg(feature = "systemmode")]
|
|
||||||
static mut NOP_PAGE_FILTER: UnsafeCell<NopPageFilter> = UnsafeCell::new(NopPageFilter);
|
|
||||||
|
@ -153,7 +153,7 @@ use object::{Object, ObjectSection};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::EmulatorModules,
|
emu::EmulatorModules,
|
||||||
modules::{AddressFilter, StdAddressFilter},
|
modules::{utils::filters::StdAddressFilter, AddressFilter},
|
||||||
qemu::{Hook, QemuHooks, SyscallHookResult},
|
qemu::{Hook, QemuHooks, SyscallHookResult},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,7 +14,9 @@ use libafl_qemu_sys::{GuestAddr, MapInfo};
|
|||||||
use crate::sys::libafl_tcg_gen_asan;
|
use crate::sys::libafl_tcg_gen_asan;
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::EmulatorModules,
|
emu::EmulatorModules,
|
||||||
modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, StdAddressFilter},
|
modules::{
|
||||||
|
utils::filters::StdAddressFilter, AddressFilter, EmulatorModule, EmulatorModuleTuple,
|
||||||
|
},
|
||||||
qemu::{Hook, MemAccessInfo, Qemu},
|
qemu::{Hook, MemAccessInfo, Qemu},
|
||||||
sys::TCGTemp,
|
sys::TCGTemp,
|
||||||
QemuParams,
|
QemuParams,
|
||||||
|
@ -23,7 +23,10 @@ use crate::SYS_execve;
|
|||||||
use crate::{
|
use crate::{
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
emu::EmulatorModules,
|
emu::EmulatorModules,
|
||||||
modules::{EmulatorModule, EmulatorModuleTuple, NopAddressFilter, NOP_ADDRESS_FILTER},
|
modules::{
|
||||||
|
utils::filters::{NopAddressFilter, NOP_ADDRESS_FILTER},
|
||||||
|
EmulatorModule, EmulatorModuleTuple,
|
||||||
|
},
|
||||||
qemu::{ArchExtras, Hook, SyscallHookResult},
|
qemu::{ArchExtras, Hook, SyscallHookResult},
|
||||||
CallingConvention, Qemu,
|
CallingConvention, Qemu,
|
||||||
};
|
};
|
||||||
|
@ -24,8 +24,9 @@ use crate::SYS_newfstatat;
|
|||||||
use crate::{
|
use crate::{
|
||||||
emu::EmulatorModules,
|
emu::EmulatorModules,
|
||||||
modules::{
|
modules::{
|
||||||
asan::AsanModule, EmulatorModule, EmulatorModuleTuple, NopAddressFilter, Range,
|
asan::AsanModule,
|
||||||
NOP_ADDRESS_FILTER,
|
utils::filters::{NopAddressFilter, NOP_ADDRESS_FILTER},
|
||||||
|
EmulatorModule, EmulatorModuleTuple, Range,
|
||||||
},
|
},
|
||||||
qemu::{Hook, SyscallHookResult},
|
qemu::{Hook, SyscallHookResult},
|
||||||
Qemu, SYS_brk, SYS_mprotect, SYS_mremap, SYS_munmap, SYS_pread64, SYS_read, SYS_readlinkat,
|
Qemu, SYS_brk, SYS_mprotect, SYS_mremap, SYS_munmap, SYS_pread64, SYS_read, SYS_readlinkat,
|
||||||
|
221
libafl_qemu/src/modules/utils/filters.rs
Normal file
221
libafl_qemu/src/modules/utils/filters.rs
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
//! Helpers to get filtering for modules
|
||||||
|
//!
|
||||||
|
//! It is not a module by itself, but instead used as helper to have filters
|
||||||
|
//! in other modules.
|
||||||
|
|
||||||
|
use std::{cell::UnsafeCell, fmt::Debug, ops::Range};
|
||||||
|
|
||||||
|
use hashbrown::HashSet;
|
||||||
|
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr};
|
||||||
|
|
||||||
|
use crate::Qemu;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum FilterList<T> {
|
||||||
|
AllowList(T),
|
||||||
|
DenyList(T),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AddressFilter for FilterList<T>
|
||||||
|
where
|
||||||
|
T: AddressFilter,
|
||||||
|
{
|
||||||
|
fn register(&mut self, address_range: Range<GuestAddr>) {
|
||||||
|
match self {
|
||||||
|
FilterList::AllowList(allow_list) => allow_list.register(address_range),
|
||||||
|
FilterList::DenyList(deny_list) => deny_list.register(address_range),
|
||||||
|
FilterList::None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allowed(&self, address: &GuestAddr) -> bool {
|
||||||
|
match self {
|
||||||
|
FilterList::AllowList(allow_list) => allow_list.allowed(address),
|
||||||
|
FilterList::DenyList(deny_list) => !deny_list.allowed(address),
|
||||||
|
FilterList::None => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PageFilter for FilterList<T>
|
||||||
|
where
|
||||||
|
T: PageFilter,
|
||||||
|
{
|
||||||
|
fn register(&mut self, page_id: GuestPhysAddr) {
|
||||||
|
match self {
|
||||||
|
FilterList::AllowList(allow_list) => allow_list.register(page_id),
|
||||||
|
FilterList::DenyList(deny_list) => deny_list.register(page_id),
|
||||||
|
FilterList::None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allowed(&self, page: &GuestPhysAddr) -> bool {
|
||||||
|
match self {
|
||||||
|
FilterList::AllowList(allow_list) => allow_list.allowed(page),
|
||||||
|
FilterList::DenyList(deny_list) => !deny_list.allowed(page),
|
||||||
|
FilterList::None => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct AddressFilterVec {
|
||||||
|
// ideally, we should use a tree
|
||||||
|
registered_addresses: Vec<Range<GuestAddr>>,
|
||||||
|
}
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct StdAddressFilter(FilterList<AddressFilterVec>);
|
||||||
|
|
||||||
|
impl Default for StdAddressFilter {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(FilterList::None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StdAddressFilter {
|
||||||
|
#[must_use]
|
||||||
|
pub fn allow_list(registered_addresses: Vec<Range<GuestAddr>>) -> Self {
|
||||||
|
StdAddressFilter(FilterList::AllowList(AddressFilterVec::new(
|
||||||
|
registered_addresses,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn deny_list(registered_addresses: Vec<Range<GuestAddr>>) -> Self {
|
||||||
|
StdAddressFilter(FilterList::DenyList(AddressFilterVec::new(
|
||||||
|
registered_addresses,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddressFilterVec {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(registered_addresses: Vec<Range<GuestAddr>>) -> Self {
|
||||||
|
Self {
|
||||||
|
registered_addresses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddressFilter for AddressFilterVec {
|
||||||
|
fn register(&mut self, address_range: Range<GuestAddr>) {
|
||||||
|
self.registered_addresses.push(address_range);
|
||||||
|
Qemu::get().unwrap().flush_jit();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allowed(&self, addr: &GuestAddr) -> bool {
|
||||||
|
if self.registered_addresses.is_empty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for addr_range in &self.registered_addresses {
|
||||||
|
if addr_range.contains(addr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddressFilter for StdAddressFilter {
|
||||||
|
fn register(&mut self, address_range: Range<GuestAddr>) {
|
||||||
|
self.0.register(address_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allowed(&self, address: &GuestAddr) -> bool {
|
||||||
|
self.0.allowed(address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PageFilterVec {
|
||||||
|
registered_pages: HashSet<GuestPhysAddr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "systemmode")]
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct StdPageFilter(FilterList<PageFilterVec>);
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
pub type StdPageFilter = NopPageFilter;
|
||||||
|
|
||||||
|
impl Default for PageFilterVec {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
registered_pages: HashSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "systemmode")]
|
||||||
|
impl Default for StdPageFilter {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(FilterList::None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageFilter for PageFilterVec {
|
||||||
|
fn register(&mut self, page_id: GuestPhysAddr) {
|
||||||
|
self.registered_pages.insert(page_id);
|
||||||
|
Qemu::get().unwrap().flush_jit();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allowed(&self, paging_id: &GuestPhysAddr) -> bool {
|
||||||
|
// if self.allowed_pages.is_empty() {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
self.registered_pages.contains(paging_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "systemmode")]
|
||||||
|
impl PageFilter for StdPageFilter {
|
||||||
|
fn register(&mut self, page_id: GuestPhysAddr) {
|
||||||
|
self.0.register(page_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allowed(&self, page_id: &GuestPhysAddr) -> bool {
|
||||||
|
self.0.allowed(page_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AddressFilter: 'static + Debug {
|
||||||
|
fn register(&mut self, address_range: Range<GuestAddr>);
|
||||||
|
|
||||||
|
fn allowed(&self, address: &GuestAddr) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NopAddressFilter;
|
||||||
|
impl AddressFilter for NopAddressFilter {
|
||||||
|
fn register(&mut self, _address: Range<GuestAddr>) {}
|
||||||
|
|
||||||
|
fn allowed(&self, _address: &GuestAddr) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PageFilter: 'static + Debug {
|
||||||
|
fn register(&mut self, page_id: GuestPhysAddr);
|
||||||
|
|
||||||
|
fn allowed(&self, page_id: &GuestPhysAddr) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct NopPageFilter;
|
||||||
|
impl PageFilter for NopPageFilter {
|
||||||
|
fn register(&mut self, _page_id: GuestPhysAddr) {}
|
||||||
|
|
||||||
|
fn allowed(&self, _page_id: &GuestPhysAddr) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
pub(crate) static mut NOP_ADDRESS_FILTER: UnsafeCell<NopAddressFilter> =
|
||||||
|
UnsafeCell::new(NopAddressFilter);
|
||||||
|
#[cfg(feature = "systemmode")]
|
||||||
|
pub(crate) static mut NOP_PAGE_FILTER: UnsafeCell<NopPageFilter> = UnsafeCell::new(NopPageFilter);
|
1
libafl_qemu/src/modules/utils/mod.rs
Normal file
1
libafl_qemu/src/modules/utils/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod filters;
|
@ -5,14 +5,14 @@ use libafl_qemu_sys::{CPUStatePtr, GuestAddr};
|
|||||||
|
|
||||||
use crate::CallingConvention;
|
use crate::CallingConvention;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum QemuError {
|
pub enum QemuError {
|
||||||
Init(QemuInitError),
|
Init(QemuInitError),
|
||||||
Exit(QemuExitError),
|
Exit(QemuExitError),
|
||||||
RW(QemuRWError),
|
RW(QemuRWError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum QemuInitError {
|
pub enum QemuInitError {
|
||||||
MultipleInstances,
|
MultipleInstances,
|
||||||
NoParametersProvided,
|
NoParametersProvided,
|
||||||
@ -21,19 +21,19 @@ pub enum QemuInitError {
|
|||||||
TooManyArgs(usize),
|
TooManyArgs(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum QemuExitError {
|
pub enum QemuExitError {
|
||||||
UnknownKind, // Exit reason was not NULL, but exit kind is unknown. Should never happen.
|
UnknownKind, // Exit reason was not NULL, but exit kind is unknown. Should never happen.
|
||||||
UnexpectedExit, // Qemu exited without going through an expected exit point. Can be caused by a crash for example.
|
UnexpectedExit, // Qemu exited without going through an expected exit point. Can be caused by a crash for example.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum QemuRWErrorKind {
|
pub enum QemuRWErrorKind {
|
||||||
Read,
|
Read,
|
||||||
Write,
|
Write,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum QemuRWErrorCause {
|
pub enum QemuRWErrorCause {
|
||||||
WrongCallingConvention(CallingConvention, CallingConvention), // expected, given
|
WrongCallingConvention(CallingConvention, CallingConvention), // expected, given
|
||||||
WrongArgument(i32),
|
WrongArgument(i32),
|
||||||
@ -42,7 +42,7 @@ pub enum QemuRWErrorCause {
|
|||||||
WrongMemoryLocation(GuestAddr, usize), // addr, size
|
WrongMemoryLocation(GuestAddr, usize), // addr, size
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone, Debug)]
|
||||||
#[expect(dead_code)]
|
#[expect(dead_code)]
|
||||||
pub struct QemuRWError {
|
pub struct QemuRWError {
|
||||||
kind: QemuRWErrorKind,
|
kind: QemuRWErrorKind,
|
||||||
@ -142,6 +142,12 @@ impl QemuRWError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<QemuRWError> for QemuError {
|
||||||
|
fn from(qemu_rw_error: QemuRWError) -> Self {
|
||||||
|
QemuError::RW(qemu_rw_error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<QemuError> for libafl::Error {
|
impl From<QemuError> for libafl::Error {
|
||||||
fn from(qemu_error: QemuError) -> Self {
|
fn from(qemu_error: QemuError) -> Self {
|
||||||
libafl::Error::runtime(qemu_error)
|
libafl::Error::runtime(qemu_error)
|
||||||
|
@ -675,7 +675,7 @@ impl Qemu {
|
|||||||
let bp_addr = exit_reason.data.breakpoint.addr;
|
let bp_addr = exit_reason.data.breakpoint.addr;
|
||||||
QemuExitReason::Breakpoint(bp_addr)
|
QemuExitReason::Breakpoint(bp_addr)
|
||||||
},
|
},
|
||||||
libafl_qemu_sys::libafl_exit_reason_kind_SYNC_EXIT => QemuExitReason::SyncExit,
|
libafl_qemu_sys::libafl_exit_reason_kind_CUSTOM_INSN => QemuExitReason::SyncExit,
|
||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
libafl_qemu_sys::libafl_exit_reason_kind_TIMEOUT => QemuExitReason::Timeout,
|
libafl_qemu_sys::libafl_exit_reason_kind_TIMEOUT => QemuExitReason::Timeout,
|
||||||
|
@ -286,9 +286,7 @@ pub mod pybind {
|
|||||||
a6: u64,
|
a6: u64,
|
||||||
a7: u64,
|
a7: u64,
|
||||||
) -> SyscallHookResult {
|
) -> SyscallHookResult {
|
||||||
// If we don't deref_addrof we run into the "static-mut-references" lint which is worse.
|
unsafe { (&raw const PY_SYSCALL_HOOK).read() }.map_or_else(
|
||||||
#[expect(clippy::deref_addrof)]
|
|
||||||
unsafe { (*(&raw const PY_SYSCALL_HOOK)).as_ref() }.map_or_else(
|
|
||||||
|| SyscallHookResult::new(None),
|
|| SyscallHookResult::new(None),
|
||||||
|obj| {
|
|obj| {
|
||||||
let args = (sys_num, a0, a1, a2, a3, a4, a5, a6, a7);
|
let args = (sys_num, a0, a1, a2, a3, a4, a5, a6, a7);
|
||||||
|
@ -17,7 +17,7 @@ pub enum ExitArgs {
|
|||||||
Arg6,
|
Arg6,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SyncExit<CM, ED, ET, S, SM>
|
pub struct CustomInsn<CM, ED, ET, S, SM>
|
||||||
where
|
where
|
||||||
CM: CommandManager<ED, ET, S, SM>,
|
CM: CommandManager<ED, ET, S, SM>,
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
@ -25,7 +25,7 @@ where
|
|||||||
command: CM::Commands,
|
command: CM::Commands,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CM, ED, ET, S, SM> Clone for SyncExit<CM, ED, ET, S, SM>
|
impl<CM, ED, ET, S, SM> Clone for CustomInsn<CM, ED, ET, S, SM>
|
||||||
where
|
where
|
||||||
CM: CommandManager<ED, ET, S, SM>,
|
CM: CommandManager<ED, ET, S, SM>,
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
@ -37,7 +37,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CM, ED, ET, S, SM> Debug for SyncExit<CM, ED, ET, S, SM>
|
impl<CM, ED, ET, S, SM> Debug for CustomInsn<CM, ED, ET, S, SM>
|
||||||
where
|
where
|
||||||
CM: CommandManager<ED, ET, S, SM>,
|
CM: CommandManager<ED, ET, S, SM>,
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
@ -47,7 +47,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CM, ED, ET, S, SM> SyncExit<CM, ED, ET, S, SM>
|
impl<CM, ED, ET, S, SM> CustomInsn<CM, ED, ET, S, SM>
|
||||||
where
|
where
|
||||||
CM: CommandManager<ED, ET, S, SM>,
|
CM: CommandManager<ED, ET, S, SM>,
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
|
@ -223,6 +223,7 @@ async fn main() -> io::Result<()> {
|
|||||||
r".*Little-CMS.*",
|
r".*Little-CMS.*",
|
||||||
r".*cms_transform_fuzzer.cc.*",
|
r".*cms_transform_fuzzer.cc.*",
|
||||||
r".*sqlite3.*",
|
r".*sqlite3.*",
|
||||||
|
r".*libfuzzer_libmozjpeg.*",
|
||||||
])
|
])
|
||||||
.expect("Could not create the regex set from the given regex");
|
.expect("Could not create the regex set from the given regex");
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user