From cf9c4188c081e1e4a4020d0e50e8dab1fe22fb5a Mon Sep 17 00:00:00 2001 From: Patrick Gersch Date: Tue, 25 Oct 2022 14:16:11 +0200 Subject: [PATCH] Disabling qemu dependecies for qemu fullsystem (#737) * Disabling qemu dependecies by default * Adding full emulation_mode support * Removing usermode from libafl_qemu default features * Fixing refactoring * Fixing typo in systemmode * Fixing clippy:needless-borrow * Mark libafl_load/save_qemu_snapshot as unused + cpu_reset * Fixing clippy::needless-borrow * Fixing needless-borrow yet again * reset_cpu -> cpu_reset * Fixing github workflow yet again * Fixing clippy::uninlined-format-args * Adding current libafl_qemu_bridge Co-authored-by: Andrea Fioraldi --- .github/workflows/build_and_test.yml | 2 +- fuzzers/fuzzbench_fork_qemu/Cargo.toml | 2 +- fuzzers/fuzzbench_qemu/Cargo.toml | 2 +- fuzzers/qemu_arm_launcher/Cargo.toml | 2 +- fuzzers/qemu_launcher/Cargo.toml | 2 +- libafl_qemu/Cargo.toml | 3 +- libafl_qemu/build_linux.rs | 378 ++++++++++++++++--------- libafl_qemu/src/calls.rs | 10 +- libafl_qemu/src/elf.rs | 14 +- libafl_qemu/src/emu.rs | 95 ++++--- libafl_qemu/src/hooks.rs | 28 +- libafl_qemu/src/lib.rs | 8 +- 12 files changed, 337 insertions(+), 209 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 2def8eb0c7..e2cc317035 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -96,7 +96,7 @@ jobs: # Skipping `python` as it has to be built with the `maturin` tool # `agpl`, `nautilus` require nightly # `sancov_pcguard_edges` is tested seperately - run: cargo hack check --each-feature --clean-per-run --exclude-features=agpl,nautilus,python,sancov_pcguard_edges,arm,aarch64,i386,be + run: cargo hack check --each-feature --clean-per-run --exclude-features=prelude,agpl,nautilus,python,sancov_pcguard_edges,arm,aarch64,i386,be,systemmode --no-dev-deps - name: Check nightly features run: cargo +nightly check --features=agpl && cargo +nightly check --features=nautilus - name: Build examples diff --git a/fuzzers/fuzzbench_fork_qemu/Cargo.toml b/fuzzers/fuzzbench_fork_qemu/Cargo.toml index 70b0617eb4..35402cc690 100644 --- a/fuzzers/fuzzbench_fork_qemu/Cargo.toml +++ b/fuzzers/fuzzbench_fork_qemu/Cargo.toml @@ -13,6 +13,6 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } -libafl_qemu = { path = "../../libafl_qemu/", features = ["x86_64"] } +libafl_qemu = { path = "../../libafl_qemu/", features = ["x86_64", "usermode"] } clap = { version = "3.2", features = ["default"] } nix = "0.25" diff --git a/fuzzers/fuzzbench_qemu/Cargo.toml b/fuzzers/fuzzbench_qemu/Cargo.toml index 958d41d120..431638f143 100644 --- a/fuzzers/fuzzbench_qemu/Cargo.toml +++ b/fuzzers/fuzzbench_qemu/Cargo.toml @@ -13,6 +13,6 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } -libafl_qemu = { path = "../../libafl_qemu/", features = ["x86_64"] } +libafl_qemu = { path = "../../libafl_qemu/", features = ["x86_64", "usermode"] } clap = { version = "3.2", features = ["default"] } nix = "0.25" diff --git a/fuzzers/qemu_arm_launcher/Cargo.toml b/fuzzers/qemu_arm_launcher/Cargo.toml index e0f38bdb16..c1fe49b378 100644 --- a/fuzzers/qemu_arm_launcher/Cargo.toml +++ b/fuzzers/qemu_arm_launcher/Cargo.toml @@ -16,4 +16,4 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } -libafl_qemu = { path = "../../libafl_qemu/", features = ["usermode", "arm"] } +libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "usermode"] } diff --git a/fuzzers/qemu_launcher/Cargo.toml b/fuzzers/qemu_launcher/Cargo.toml index bfb59ca349..f0aa74b61c 100644 --- a/fuzzers/qemu_launcher/Cargo.toml +++ b/fuzzers/qemu_launcher/Cargo.toml @@ -16,4 +16,4 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } -libafl_qemu = { path = "../../libafl_qemu/", features = ["x86_64"] } +libafl_qemu = { path = "../../libafl_qemu/", features = ["x86_64", "usermode"] } diff --git a/libafl_qemu/Cargo.toml b/libafl_qemu/Cargo.toml index c9547ae513..126700cae4 100644 --- a/libafl_qemu/Cargo.toml +++ b/libafl_qemu/Cargo.toml @@ -12,7 +12,7 @@ edition = "2021" categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"] [features] -default = ["usermode", "fork"] +default = ["fork"] python = ["pyo3", "pyo3-build-config"] fork = ["libafl/fork"] @@ -24,6 +24,7 @@ aarch64 = [] # build qemu for aarch64 be = [] usermode = [] +systemmode = [] clippy = [] # special feature for clippy, don't use in normal projects§ diff --git a/libafl_qemu/build_linux.rs b/libafl_qemu/build_linux.rs index d5efc2a78e..3602bc8635 100644 --- a/libafl_qemu/build_linux.rs +++ b/libafl_qemu/build_linux.rs @@ -3,7 +3,7 @@ use which::which; const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge"; const QEMU_DIRNAME: &str = "qemu-libafl-bridge"; -const QEMU_REVISION: &str = "35d36bf8fa2d483965a57ee0c7d7a997e8798273"; +const QEMU_REVISION: &str = "ddb71cf43844f8848ae655ca696bdfc3fb7839f1"; fn build_dep_check(tools: &[&str]) { for tool in tools { @@ -26,10 +26,25 @@ macro_rules! assert_unique_feature { #[allow(clippy::too_many_lines)] pub fn build() { + // Make sure that exactly one qemu mode is set + assert_unique_feature!("usermode", "systemmode"); + let emulation_mode = if cfg!(feature = "usermode") { + "usermode".to_string() + }else if cfg!(feature = "systemmode") { + "systemmode".to_string() + }else{ + env::var("EMULATION_MODE").unwrap_or_else(|_| { + println!( + "cargo:warning=No emulation mode feature enabled or EMULATION_MODE env specified for libafl_qemu, supported: usermode, systemmmode - defaulting to usermode" + ); + "usermode".to_string() + }) + }; + println!("cargo:rustc-cfg=emulation_mode=\"{emulation_mode}\""); + println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=src/asan-giovese.c"); println!("cargo:rerun-if-changed=src/asan-giovese.h"); - #[cfg(feature = "usermode")] println!("cargo:rerun-if-env-changed=CROSS_CC"); // Make sure we have at most one architecutre feature set @@ -60,7 +75,6 @@ pub fn build() { let jobs = env::var("NUM_JOBS"); - #[cfg(feature = "usermode")] let cross_cc = env::var("CROSS_CC").unwrap_or_else(|_| { println!("cargo:warning=CROSS_CC is not set, default to cc (things can go wrong if the selected cpu target ({cpu_target}) is not the host arch ({}))", env::consts::ARCH); "cc".to_owned() @@ -153,20 +167,20 @@ pub fn build() { qemu_path }; - #[cfg(feature = "usermode")] - let target_suffix = "linux-user"; - #[cfg(not(feature = "usermode"))] - let target_suffix = "softmmu"; + let build_dir = qemu_path.join("build"); - let build_dir = out_dir_path.join("build"); - if !build_dir.is_dir() { - fs::create_dir_all(&build_dir).unwrap(); - } - #[cfg(feature = "usermode")] - let output_lib = build_dir.join(&format!("libqemu-{cpu_target}.so")); - #[cfg(not(feature = "usermode"))] - let output_lib = build_dir.join(&format!("libqemu-system-{}.so", cpu_target)); + let target_suffix = if emulation_mode == "usermode" { + "linux-user".to_string() + }else{ + "softmmu".to_string() + }; + + let output_lib = if emulation_mode == "usermode" { + build_dir.join(&format!("libqemu-{cpu_target}.so")) + }else{ + build_dir.join(&format!("libqemu-system-{cpu_target}.so")) + }; println!("cargo:rerun-if-changed={}", output_lib.to_string_lossy()); @@ -177,25 +191,134 @@ pub fn build() { .arg("distclean") .status(), );*/ - let configure = qemu_path.join("configure"); - - #[cfg(feature = "usermode")] - Command::new(configure) - .current_dir(&build_dir) - //.arg("--as-static-lib") - .arg("--as-shared-lib") - .arg(&format!("--target-list={cpu_target}-{target_suffix}")) - .args(["--disable-blobs", "--disable-bsd-user", "--disable-fdt"]) - .status() - .expect("Configure failed"); - #[cfg(not(feature = "usermode"))] - Command::new(configure) - .current_dir(&build_dir) - //.arg("--as-static-lib") - .arg("--as-shared-lib") - .arg(&format!("--target-list={}-{}", cpu_target, target_suffix)) - .status() - .expect("Configure failed"); + if emulation_mode == "usermode" { + Command::new("./configure") + .current_dir(&qemu_path) + //.arg("--as-static-lib") + .arg("--as-shared-lib") + .arg(&format!("--target-list={cpu_target}-{target_suffix}")) + .args(["--disable-blobs", "--disable-bsd-user", "--disable-fdt", "--disable-system"]) + .status() + .expect("Configure failed"); + }else{ + Command::new("./configure") + .current_dir(&qemu_path) + //.arg("--as-static-lib") + .arg("--as-shared-lib") + .arg(&format!("--target-list={cpu_target}-{target_suffix}")) + .arg("--enable-slirp=internal") + .arg("--enable-fdt=internal") + .arg("--audio-drv-list=") + .arg("--disable-alsa") + .arg("--disable-attr") + .arg("--disable-auth-pam") + .arg("--disable-dbus-display") + .arg("--disable-blobs") + .arg("--disable-bochs") + .arg("--disable-bpf") + .arg("--disable-brlapi") + .arg("--disable-bsd-user") + .arg("--disable-bzip2") + .arg("--disable-cap-ng") + .arg("--disable-canokey") + .arg("--disable-cloop") + .arg("--disable-cocoa") + .arg("--disable-coreaudio") + .arg("--disable-curl") + .arg("--disable-curses") + .arg("--disable-dmg") + .arg("--disable-docs") + .arg("--disable-dsound") + .arg("--disable-fuse") + .arg("--disable-fuse-lseek") + .arg("--disable-gcrypt") + .arg("--disable-gettext") + .arg("--disable-gio") + .arg("--disable-glusterfs") + .arg("--disable-gnutls") + .arg("--disable-gtk") + .arg("--disable-guest-agent") + .arg("--disable-guest-agent-msi") + .arg("--disable-hax") + .arg("--disable-hvf") + .arg("--disable-iconv") + .arg("--disable-jack") + .arg("--disable-keyring") + .arg("--disable-kvm") + .arg("--disable-libdaxctl") + .arg("--disable-libiscsi") + .arg("--disable-libnfs") + .arg("--disable-libpmem") + .arg("--disable-libssh") + .arg("--disable-libudev") + .arg("--disable-libusb") + .arg("--disable-linux-aio") + .arg("--disable-linux-io-uring") + .arg("--disable-linux-user") + .arg("--disable-live-block-migration") + .arg("--disable-lzfse") + .arg("--disable-lzo") + .arg("--disable-l2tpv3") + .arg("--disable-malloc-trim") + .arg("--disable-mpath") + .arg("--disable-multiprocess") + .arg("--disable-netmap") + .arg("--disable-nettle") + .arg("--disable-numa") + .arg("--disable-nvmm") + .arg("--disable-opengl") + .arg("--disable-oss") + .arg("--disable-pa") + .arg("--disable-parallels") + .arg("--disable-plugins") + .arg("--disable-png") + .arg("--disable-pvrdma") + .arg("--disable-qcow1") + .arg("--disable-qed") + .arg("--disable-qga-vss") + .arg("--disable-rbd") + .arg("--disable-rdma") + .arg("--disable-replication") + .arg("--disable-sdl") + .arg("--disable-sdl-image") + .arg("--disable-seccomp") + .arg("--disable-selinux") + .arg("--disable-slirp-smbd") + .arg("--disable-smartcard") + .arg("--disable-snappy") + .arg("--disable-sparse") + .arg("--disable-spice") + .arg("--disable-spice-protocol") + .arg("--disable-tools") + .arg("--disable-tpm") + .arg("--disable-usb-redir") + .arg("--disable-user") + .arg("--disable-u2f") + .arg("--disable-vde") + .arg("--disable-vdi") + .arg("--disable-vduse-blk-export") + .arg("--disable-vhost-crypto") + .arg("--disable-vhost-kernel") + .arg("--disable-vhost-net") + .arg("--disable-vhost-user-blk-server") + .arg("--disable-vhost-vdpa") + .arg("--disable-virglrenderer") + .arg("--disable-virtfs") + .arg("--disable-virtiofsd") + .arg("--disable-vmnet") + .arg("--disable-vnc") + .arg("--disable-vnc-jpeg") + .arg("--disable-vnc-sasl") + .arg("--disable-vte") + .arg("--disable-vvfat") + .arg("--disable-whpx") + .arg("--disable-xen") + .arg("--disable-xen-pci-passthrough") + .arg("--disable-xkbcommon") + .arg("--disable-zstd") + .status() + .expect("Configure failed"); + } if let Ok(j) = jobs { Command::new("make") .current_dir(&build_dir) @@ -233,84 +356,86 @@ pub fn build() { } } - #[cfg(feature = "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("--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!( - "--dynamic-list={}/plugins/qemu-plugins.symbols", - qemu_path.display() - )) - .status() - .expect("Partial linked failure"); - - #[cfg(not(feature = "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!("{}/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/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!("{}/libfdt.a", build_dir.display())) - .arg(format!("{}/libslirp.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!("{}/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() - )) - .status() - .expect("Partial linked failure"); + if emulation_mode == "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("--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!( + "--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!("{}/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/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!("{}/libfdt.a", build_dir.display())) + .arg(format!("{}/libslirp.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!("{}/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") .current_dir(out_dir_path) - .arg("crus") + .arg("crs") .arg("libqemu-partially-linked.a") .arg("libqemu-partially-linked.o") .status() @@ -318,26 +443,13 @@ pub fn build() { println!("cargo:rustc-link-search=native={out_dir}"); println!("cargo:rustc-link-lib=static=qemu-partially-linked"); + 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"); - #[cfg(not(feature = "usermode"))] - { - println!("cargo:rustc-link-lib=png"); - println!("cargo:rustc-link-lib=z"); - println!("cargo:rustc-link-lib=gio-2.0"); - println!("cargo:rustc-link-lib=gobject-2.0"); - println!("cargo:rustc-link-lib=ncursesw"); - println!("cargo:rustc-link-lib=tinfo"); - println!("cargo:rustc-link-lib=gtk-3"); - println!("cargo:rustc-link-lib=gdk-3"); - println!("cargo:rustc-link-lib=pangocairo-1.0"); - println!("cargo:rustc-link-lib=pango-1.0"); - println!("cargo:rustc-link-lib=harfbuzz"); - println!("cargo:rustc-link-lib=atk-1.0"); - println!("cargo:rustc-link-lib=cairo-gobject"); - println!("cargo:rustc-link-lib=cairo"); - println!("cargo:rustc-link-lib=gdk_pixbuf-2.0"); - println!("cargo:rustc-link-lib=X11"); - println!("cargo:rustc-link-lib=epoxy"); + if emulation_mode == "systemmode" { println!("cargo:rustc-link-lib=pixman-1"); fs::create_dir_all(target_dir.join("pc-bios")).unwrap(); @@ -352,13 +464,6 @@ pub fn build() { } } - 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"); - /* #[cfg(not(feature = "python"))] { fs::copy( @@ -376,8 +481,7 @@ pub fn build() { println!("cargo:rustc-env=LD_LIBRARY_PATH={}", target_dir.display()); } */ - #[cfg(feature = "usermode")] - { + if emulation_mode == "usermode" { let qasan_dir = Path::new("libqasan"); let qasan_dir = fs::canonicalize(qasan_dir).unwrap(); diff --git a/libafl_qemu/src/calls.rs b/libafl_qemu/src/calls.rs index e187518206..b29db4a1a6 100644 --- a/libafl_qemu/src/calls.rs +++ b/libafl_qemu/src/calls.rs @@ -130,14 +130,14 @@ where #[allow(unused_mut)] let mut code = { - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] unsafe { std::slice::from_raw_parts(emu.g2h(pc), 512) } - #[cfg(not(feature = "usermode"))] + #[cfg(emulation_mode = "systemmode")] &mut [0; 512] }; - #[cfg(not(feature = "usermode"))] + #[cfg(emulation_mode = "systemmode")] unsafe { emu.read_mem(pc, code) }; // TODO handle faults @@ -188,11 +188,11 @@ where iaddr += insn.bytes().len() as GuestAddr; - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] unsafe { code = std::slice::from_raw_parts(emu.g2h(iaddr), 512); } - #[cfg(not(feature = "usermode"))] + #[cfg(emulation_mode = "systemmode")] unsafe { emu.read_mem(pc, code); } // TODO handle faults diff --git a/libafl_qemu/src/elf.rs b/libafl_qemu/src/elf.rs index 4ae1a1587a..bfc42e5328 100644 --- a/libafl_qemu/src/elf.rs +++ b/libafl_qemu/src/elf.rs @@ -47,9 +47,19 @@ impl<'a> EasyElf<'a> { return if sym.st_value == 0 { None } else if self.is_pic() { - Some(sym.st_value as GuestAddr + load_addr) + #[cfg(cpu_target = "arm")] + // Required because of arm interworking addresses aka bit(0) for thumb mode + let addr = (sym.st_value as GuestAddr + load_addr) & !(0x1 as GuestAddr); + #[cfg(not(cpu_target = "arm"))] + let addr = sym.st_value as GuestAddr + load_addr; + Some(addr) } else { - Some(sym.st_value as GuestAddr) + #[cfg(cpu_target = "arm")] + // Required because of arm interworking addresses aka bit(0) for thumb mode + let addr = (sym.st_value as GuestAddr) & !(0x1 as GuestAddr); + #[cfg(not(cpu_target = "arm"))] + let addr = sym.st_value as GuestAddr; + Some(addr) }; } } diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index fa6c236013..5d201f540c 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -5,11 +5,11 @@ use core::{ ffi::c_void, ptr::{addr_of, addr_of_mut, null}, }; -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] use core::{mem::MaybeUninit, ptr::copy_nonoverlapping}; use std::{slice::from_raw_parts, str::from_utf8_unchecked}; -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] use libc::c_int; use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_traits::Num; @@ -185,7 +185,7 @@ impl MapInfo { } } -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] extern "C" { fn qemu_user_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32; @@ -222,7 +222,7 @@ extern "C" { unsafe extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64; } -#[cfg(not(feature = "usermode"))] +#[cfg(emulation_mode = "systemmode")] extern "C" { fn qemu_init(argc: i32, argv: *const *const u8, envp: *const *const u8); @@ -246,11 +246,12 @@ extern "C" { /* fn libafl_save_qemu_snapshot(name: *const u8); + #[allow(unused)] fn libafl_load_qemu_snapshot(name: *const u8); */ } -#[cfg(not(feature = "usermode"))] +#[cfg(emulation_mode = "systemmode")] extern "C" fn qemu_cleanup_atexit() { unsafe { qemu_cleanup(); @@ -359,9 +360,11 @@ extern "C" { data: *const (), ); fn libafl_qemu_gdb_reply(buf: *const u8, len: usize); + + fn cpu_reset(cpu: CPUStatePtr); } -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] #[cfg_attr(feature = "python", pyclass(unsendable))] pub struct GuestMaps { orig_c_iter: *const c_void, @@ -369,7 +372,7 @@ pub struct GuestMaps { } // Consider a private new only for Emulator -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] impl GuestMaps { #[must_use] pub(crate) fn new() -> Self { @@ -383,7 +386,7 @@ impl GuestMaps { } } -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] impl Iterator for GuestMaps { type Item = MapInfo; @@ -404,7 +407,7 @@ impl Iterator for GuestMaps { } } -#[cfg(all(feature = "usermode", feature = "python"))] +#[cfg(all(emulation_mode = "usermode", feature = "python"))] #[pyproto] impl PyIterProtocol for GuestMaps { fn __iter__(slf: PyRef) -> PyRef { @@ -415,7 +418,7 @@ impl PyIterProtocol for GuestMaps { } } -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] impl Drop for GuestMaps { fn drop(&mut self) { unsafe { @@ -464,13 +467,13 @@ impl CPU { } } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] #[must_use] pub fn g2h(&self, addr: GuestAddr) -> *mut T { unsafe { (addr as usize + guest_base) as *mut T } } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] #[must_use] pub fn h2g(&self, addr: *const T) -> GuestAddr { unsafe { (addr as usize - guest_base) as GuestAddr } @@ -483,12 +486,12 @@ impl CPU { /// It just adds `guest_base` and writes to that location, without checking the bounds. /// This may only be safely used for valid guest addresses! pub unsafe fn write_mem(&self, addr: GuestAddr, buf: &[u8]) { - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] { let host_addr = Emulator::new_empty().g2h(addr); copy_nonoverlapping(buf.as_ptr(), host_addr, buf.len()); } - #[cfg(not(feature = "usermode"))] + #[cfg(emulation_mode = "systemmode")] cpu_memory_rw_debug(self.ptr, addr, buf.as_ptr() as *mut u8, buf.len() as i32, 1); } @@ -499,12 +502,12 @@ impl CPU { /// It just adds `guest_base` and writes to that location, without checking the bounds. /// This may only be safely used for valid guest addresses! pub unsafe fn read_mem(&self, addr: GuestAddr, buf: &mut [u8]) { - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] { let host_addr = Emulator::new_empty().g2h(addr); copy_nonoverlapping(host_addr, buf.as_mut_ptr(), buf.len()); } - #[cfg(not(feature = "usermode"))] + #[cfg(emulation_mode = "systemmode")] cpu_memory_rw_debug(self.ptr, addr, buf.as_mut_ptr(), buf.len() as i32, 0); } @@ -541,6 +544,10 @@ impl CPU { Ok(val) } } + + pub fn cpu_reset(&self) { + unsafe { cpu_reset(self.ptr) }; + } } static mut EMULATOR_IS_INITIALIZED: bool = false; @@ -573,13 +580,13 @@ impl Emulator { #[allow(clippy::cast_possible_wrap)] let argc = argv.len() as i32; unsafe { - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] qemu_user_init( argc, argv.as_ptr() as *const *const u8, envp.as_ptr() as *const *const u8, ); - #[cfg(not(feature = "usermode"))] + #[cfg(emulation_mode = "systemmode")] { qemu_init( argc, @@ -598,7 +605,7 @@ impl Emulator { Emulator { _private: () } } - #[cfg(not(feature = "usermode"))] + #[cfg(emulation_mode = "systemmode")] pub fn start(&self, cpu: &CPU) { unsafe { libafl_cpu_thread_fn(cpu.ptr); @@ -606,7 +613,7 @@ impl Emulator { } /// This function gets the memory mappings from the emulator. - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] #[must_use] pub fn mappings(&self) -> GuestMaps { GuestMaps::new() @@ -639,13 +646,13 @@ impl Emulator { } } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] #[must_use] pub fn g2h(&self, addr: GuestAddr) -> *mut T { unsafe { (addr as usize + guest_base) as *mut T } } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] #[must_use] pub fn h2g(&self, addr: *const T) -> GuestAddr { unsafe { (addr as usize - guest_base) as GuestAddr } @@ -713,47 +720,53 @@ impl Emulator { /// Should, in general, be safe to call. /// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system. pub unsafe fn run(&self) { - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] libafl_qemu_run(); - #[cfg(not(feature = "usermode"))] + #[cfg(emulation_mode = "systemmode")] qemu_main_loop(); } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] #[must_use] pub fn binary_path<'a>(&self) -> &'a str { unsafe { from_utf8_unchecked(from_raw_parts(exec_path, strlen(exec_path))) } } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] #[must_use] pub fn load_addr(&self) -> GuestAddr { unsafe { libafl_load_addr() as GuestAddr } } + #[cfg(emulation_mode = "systemmode")] + #[must_use] + pub fn load_addr(&self) -> GuestAddr { + // Only work if the binary is linked to the correct address and not pie + return 0x0 as GuestAddr; + } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] #[must_use] pub fn get_brk(&self) -> GuestAddr { unsafe { libafl_get_brk() as GuestAddr } } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] pub fn set_brk(&self, brk: GuestAddr) { unsafe { libafl_set_brk(brk.into()) }; } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] #[must_use] pub fn get_mmap_start(&self) -> GuestAddr { unsafe { mmap_next_start } } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] pub fn set_mmap_start(&self, start: GuestAddr) { unsafe { mmap_next_start = start }; } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] fn mmap( &self, addr: GuestAddr, @@ -769,7 +782,7 @@ impl Emulator { } } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] pub fn map_private( &self, addr: GuestAddr, @@ -781,7 +794,7 @@ impl Emulator { .map(|addr| addr as GuestAddr) } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] pub fn map_fixed( &self, addr: GuestAddr, @@ -798,7 +811,7 @@ impl Emulator { .map(|addr| addr as GuestAddr) } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] pub fn mprotect(&self, addr: GuestAddr, size: usize, perms: MmapPerms) -> Result<(), String> { let res = unsafe { target_mprotect(addr.into(), size as u64, perms.into()) }; if res == 0 { @@ -808,7 +821,7 @@ impl Emulator { } } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] pub fn unmap(&self, addr: GuestAddr, size: usize) -> Result<(), String> { if unsafe { target_munmap(addr.into(), size as u64) } == 0 { Ok(()) @@ -883,33 +896,33 @@ impl Emulator { unsafe { libafl_add_backdoor_hook(exec, data) }; } - #[cfg(not(feature = "usermode"))] + #[cfg(emulation_mode = "systemmode")] pub fn set_vcpu_start(&self, hook: extern "C" fn(cpu: CPU)) { unsafe { libafl_start_vcpu = core::mem::transmute(hook); } } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] pub fn set_on_thread_hook(&self, hook: extern "C" fn(tid: u32)) { unsafe { libafl_on_thread_hook = hook; } } - /*#[cfg(not(feature = "usermode"))] + /*#[cfg(emulation_mode = "systemmode")] pub fn save_snapshot(&self, name: &str) { let s = CString::new(name).expect("Invalid snapshot name"); unsafe { libafl_save_qemu_snapshot(s.as_ptr() as *const _) }; } - #[cfg(not(feature = "usermode"))] + #[cfg(emulation_mode = "systemmode")] pub fn load_snapshot(&self, name: &str) { let s = CString::new(name).expect("Invalid snapshot name"); unsafe { libafl_load_qemu_snapshot(s.as_ptr() as *const _) }; }*/ - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] pub fn set_pre_syscall_hook( &self, hook: extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult, @@ -919,7 +932,7 @@ impl Emulator { } } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] pub fn set_post_syscall_hook( &self, hook: extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64, diff --git a/libafl_qemu/src/hooks.rs b/libafl_qemu/src/hooks.rs index 643be274b5..21af12f175 100644 --- a/libafl_qemu/src/hooks.rs +++ b/libafl_qemu/src/hooks.rs @@ -23,7 +23,7 @@ use crate::{ enum Hook { Function(*const c_void), Closure(FatPtr), - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] Once(FatPtr), Empty, } @@ -419,9 +419,9 @@ define_cmp_exec_hook!(exec_cmp2_hook_wrapper, 2, u16); define_cmp_exec_hook!(exec_cmp4_hook_wrapper, 3, u32); define_cmp_exec_hook!(exec_cmp8_hook_wrapper, 4, u64); -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] static mut ON_THREAD_HOOKS: Vec = vec![]; -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] extern "C" fn on_thread_hooks_wrapper(tid: u32) where S: UsesInput, @@ -455,9 +455,9 @@ where } } -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] static mut SYSCALL_HOOKS: Vec = vec![]; -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] extern "C" fn syscall_hooks_wrapper( sys_num: i32, a0: u64, @@ -557,9 +557,9 @@ where } } -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] static mut SYSCALL_POST_HOOKS: Vec = vec![]; -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] extern "C" fn syscall_after_hooks_wrapper( result: u64, sys_num: i32, @@ -1417,7 +1417,7 @@ where } } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] pub fn thread_creation(&self, hook: fn(&mut Self, Option<&mut S>, tid: u32)) { unsafe { ON_THREAD_HOOKS.push(Hook::Function(hook as *const libc::c_void)); @@ -1426,7 +1426,7 @@ where .set_on_thread_hook(on_thread_hooks_wrapper::); } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] pub fn thread_creation_closure( &self, hook: Box, u32) + 'a>, @@ -1438,7 +1438,7 @@ where .set_on_thread_hook(on_thread_hooks_wrapper::); } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] pub fn thread_creation_once(&self, hook: Box, u32) + 'a>) { unsafe { ON_THREAD_HOOKS.push(Hook::Once(transmute(hook))); @@ -1447,7 +1447,7 @@ where .set_on_thread_hook(on_thread_hooks_wrapper::); } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] #[allow(clippy::type_complexity)] pub fn syscalls( &self, @@ -1472,7 +1472,7 @@ where .set_pre_syscall_hook(syscall_hooks_wrapper::); } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] #[allow(clippy::type_complexity)] pub fn syscalls_closure( &self, @@ -1499,7 +1499,7 @@ where .set_pre_syscall_hook(syscall_hooks_wrapper::); } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] #[allow(clippy::type_complexity)] pub fn after_syscalls( &self, @@ -1525,7 +1525,7 @@ where .set_post_syscall_hook(syscall_after_hooks_wrapper::); } - #[cfg(feature = "usermode")] + #[cfg(emulation_mode = "usermode")] #[allow(clippy::type_complexity)] pub fn after_syscalls_closure( &self, diff --git a/libafl_qemu/src/lib.rs b/libafl_qemu/src/lib.rs index 591a98a92a..f5c4fac701 100644 --- a/libafl_qemu/src/lib.rs +++ b/libafl_qemu/src/lib.rs @@ -50,13 +50,13 @@ pub mod edges; pub use edges::QemuEdgeCoverageHelper; pub mod cmplog; pub use cmplog::QemuCmpLogHelper; -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] pub mod snapshot; -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] pub use snapshot::QemuSnapshotHelper; -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] pub mod asan; -#[cfg(feature = "usermode")] +#[cfg(emulation_mode = "usermode")] pub use asan::{init_with_asan, QemuAsanHelper}; pub mod calls;