From 93048f627070f9b43b8098b943a1a07d62a53ddf Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Tue, 14 Jun 2022 11:45:14 +0200 Subject: [PATCH] Add custom GDB commands to libafl_qemu (#671) * Add custom GDB commands * clippy * statically linked QEMU * fix Calibrate * clippy --- libafl/src/stages/calibrate.rs | 12 +- libafl_qemu/build_linux.rs | 244 +++++++++------------------------ libafl_qemu/src/emu.rs | 46 ++++++- libafl_qemu/src/hooks.rs | 6 +- libafl_qemu/src/lib.rs | 2 + 5 files changed, 117 insertions(+), 193 deletions(-) diff --git a/libafl/src/stages/calibrate.rs b/libafl/src/stages/calibrate.rs index 145a875af3..9975a5e059 100644 --- a/libafl/src/stages/calibrate.rs +++ b/libafl/src/stages/calibrate.rs @@ -155,9 +155,15 @@ where .unwrap() .history_map; - for j in 0..map_len { - if map_first[j] != map[j] && history_map[j] != O::Entry::max_value() { - history_map[j] = O::Entry::max_value(); + if history_map.len() < map_len { + history_map.resize(map_len, O::Entry::default()); + } + + for (first, (cur, history)) in + map_first.iter().zip(map.iter().zip(history_map.iter_mut())) + { + if *first != *cur && *history != O::Entry::max_value() { + *history = O::Entry::max_value(); unstable_entries += 1; }; } diff --git a/libafl_qemu/build_linux.rs b/libafl_qemu/build_linux.rs index daa58bc044..1747c78773 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 = "d840462c2e4cdda4428d99cd4003ddda95c5a2dc"; +const QEMU_REVISION: &str = "6a9a929222cbc8b10adfb048aa24f73486e0a886"; fn build_dep_check(tools: &[&str]) { for tool in tools { @@ -141,81 +141,18 @@ pub fn build() { let build_dir = qemu_path.join("build"); let output_lib = build_dir.join(&format!("libqemu-{}.so", cpu_target)); if !output_lib.is_file() { - drop( + /*drop( Command::new("make") .current_dir(&qemu_path) .arg("distclean") .status(), - ); + );*/ Command::new("./configure") .current_dir(&qemu_path) //.arg("--as-static-lib") .arg("--as-shared-lib") .arg(&format!("--target-list={}-linux-user", cpu_target)) - .args(&[ - "--audio-drv-list=", - "--disable-blobs", - "--disable-bochs", - "--disable-brlapi", - "--disable-bsd-user", - "--disable-bzip2", - "--disable-cap-ng", - "--disable-cloop", - "--disable-curl", - "--disable-curses", - "--disable-dmg", - "--disable-fdt", - "--disable-gcrypt", - "--disable-glusterfs", - "--disable-gnutls", - "--disable-gtk", - "--disable-guest-agent", - "--disable-iconv", - "--disable-libiscsi", - "--disable-libnfs", - "--disable-libssh", - "--disable-libusb", - "--disable-linux-aio", - "--disable-live-block-migration", - "--disable-lzo", - "--disable-nettle", - "--disable-numa", - "--disable-opengl", - "--disable-parallels", - "--disable-plugins", - "--disable-qcow1", - "--disable-qed", - "--disable-rbd", - "--disable-rdma", - "--disable-replication", - "--disable-sdl", - "--disable-seccomp", - "--disable-smartcard", - "--disable-snappy", - "--disable-spice", - "--disable-system", - "--disable-tools", - "--disable-tpm", - "--disable-usb-redir", - "--disable-vde", - "--disable-vdi", - "--disable-vhost-crypto", - "--disable-vhost-kernel", - "--disable-vhost-net", - "--disable-vhost-scsi", - "--disable-vhost-user", - "--disable-vhost-vdpa", - "--disable-vhost-vsock", - "--disable-virglrenderer", - "--disable-virtfs", - "--disable-vnc", - "--disable-vnc-jpeg", - "--disable-vnc-sasl", - "--disable-vte", - "--disable-vvfat", - "--disable-xen", - "--disable-xen-pci-passthrough", - ]) + .args(&["--disable-blobs", "--disable-bsd-user", "--disable-fdt"]) .status() .expect("Configure failed"); if let Ok(j) = jobs { @@ -235,77 +172,71 @@ pub fn build() { //let _ = remove_file(build_dir.join(&format!("libqemu-{}.so", cpu_target))); } - #[cfg(feature = "python")] - { - let mut objects = vec![]; - for dir in &[ - build_dir.join("libcommon.fa.p"), - build_dir.join(&format!("libqemu-{}-linux-user.fa.p", cpu_target)), - //build_dir.join("libcommon-user.fa.p"), - //build_dir.join("libqemuutil.a.p"), - //build_dir.join("libqom.fa.p"), - //build_dir.join("libhwcore.fa.p"), - //build_dir.join("libcapstone.a.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 mut objects = vec![]; + for dir in &[ + build_dir.join("libcommon.fa.p"), + build_dir.join(&format!("libqemu-{}-linux-user.fa.p", cpu_target)), + //build_dir.join("libcommon-user.fa.p"), + //build_dir.join("libqemuutil.a.p"), + //build_dir.join("libqom.fa.p"), + //build_dir.join("libhwcore.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); } } } } } - - for obj in &objects { - println!("cargo:rustc-cdylib-link-arg={}", obj.display()); - } - - println!("cargo:rustc-cdylib-link-arg=-Wl,--start-group"); - - println!("cargo:rustc-cdylib-link-arg=-Wl,--whole-archive"); - println!( - "cargo:rustc-cdylib-link-arg={}/libhwcore.fa", - build_dir.display() - ); - println!( - "cargo:rustc-cdylib-link-arg={}/libqom.fa", - build_dir.display() - ); - println!("cargo:rustc-cdylib-link-arg=-Wl,--no-whole-archive"); - println!( - "cargo:rustc-cdylib-link-arg={}/libcapstone.a", - build_dir.display() - ); - println!( - "cargo:rustc-cdylib-link-arg={}/libqemuutil.a", - build_dir.display() - ); - println!( - "cargo:rustc-cdylib-link-arg={}/libhwcore.fa", - build_dir.display() - ); - println!( - "cargo:rustc-cdylib-link-arg={}/libqom.fa", - build_dir.display() - ); - - println!("cargo:rustc-cdylib-link-arg=-lrt"); - println!("cargo:rustc-cdylib-link-arg=-lutil"); - println!("cargo:rustc-cdylib-link-arg=-lgthread-2.0"); - println!("cargo:rustc-cdylib-link-arg=-lglib-2.0"); - println!("cargo:rustc-cdylib-link-arg=-lstdc++"); - - println!("cargo:rustc-cdylib-link-arg=-Wl,--end-group"); } - #[cfg(not(feature = "python"))] + 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"); + + drop( + Command::new("ar") + .current_dir(&out_dir_path) + .arg("crus") + .arg("libqemu-partially-linked.a") + .arg("libqemu-partially-linked.o") + .status(), + ); + + 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++"); + + /* #[cfg(not(feature = "python"))] { fs::copy( build_dir.join(&format!("libqemu-{}.so", cpu_target)), @@ -320,7 +251,7 @@ pub fn build() { println!("cargo:rustc-link-lib=qemu-{}", cpu_target); println!("cargo:rustc-env=LD_LIBRARY_PATH={}", target_dir.display()); - } + } */ drop( Command::new("make") @@ -347,52 +278,3 @@ pub fn build() { .file(src_dir.join("asan-giovese.c")) .compile("asan_giovese"); } - -/* - // Build a static library - let mut objects = vec![]; - for dir in &[ - build_dir.join("libcommon.fa.p"), - build_dir.join(&format!("libqemu-{}-linux-user.fa.p", cpu_target)), - build_dir.join("libqemuutil.a.p"), - build_dir.join("libqom.fa.p"), - build_dir.join("libhwcore.fa.p"), - build_dir.join("libcapstone.a.p"), - ] { - for path in 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); - } - } - } - } - } - } - - - Command::new("ar") - .current_dir(&out_dir_path) - .arg("crus") - .arg("libqemu-bridge.a") - .args(&objects) - .status() - .expect("Ar failed"); - - println!("cargo:rustc-link-search=native={}", &out_dir); - println!("cargo:rustc-link-lib=static=qemu-bridge"); - - println!("cargo:rustc-link-lib=rt"); - println!("cargo:rustc-link-lib=util"); - println!("cargo:rustc-link-lib=gthread-2.0"); - println!("cargo:rustc-link-lib=glib-2.0"); - println!("cargo:rustc-link-lib=stdc++"); - -} -*/ diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 8e0a2390ab..2756acd812 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -3,7 +3,7 @@ use core::{ convert::Into, ffi::c_void, - mem::{transmute, MaybeUninit}, + mem::MaybeUninit, ptr::{addr_of, addr_of_mut, copy_nonoverlapping, null}, }; use libc::c_int; @@ -248,6 +248,12 @@ extern "C" { unsafe extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult; static mut libafl_post_syscall_hook: unsafe extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64; + + fn libafl_qemu_add_gdb_cmd( + callback: extern "C" fn(*const u8, usize, *const ()) -> i32, + data: *const (), + ); + fn libafl_qemu_gdb_reply(buf: *const u8, len: usize); } #[cfg_attr(feature = "python", pyclass(unsendable))] @@ -309,6 +315,25 @@ impl Drop for GuestMaps { } } +#[repr(C)] +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) struct FatPtr(*const c_void, *const c_void); + +static mut GDB_COMMANDS: Vec = vec![]; + +extern "C" fn gdb_cmd(buf: *const u8, len: usize, data: *const ()) -> i32 { + unsafe { + let closure = + &mut *(data as *mut std::boxed::Box std::ops::FnMut(&'r str) -> bool>); + let cmd = std::str::from_utf8_unchecked(std::slice::from_raw_parts(buf, len)); + if closure(cmd) { + 1 + } else { + 0 + } + } +} + static mut EMULATOR_IS_INITIALIZED: bool = false; #[derive(Debug)] @@ -451,7 +476,7 @@ impl Emulator { #[must_use] pub fn g2h(&self, addr: GuestAddr) -> *mut T { - unsafe { transmute(addr as usize + guest_base) } + unsafe { (addr as usize + guest_base) as *mut T } } #[must_use] @@ -704,12 +729,25 @@ impl Emulator { libafl_post_syscall_hook = hook; } } + + pub fn add_gdb_cmd(&self, callback: Box bool>) { + unsafe { + GDB_COMMANDS.push(core::mem::transmute(callback)); + libafl_qemu_add_gdb_cmd( + gdb_cmd, + GDB_COMMANDS.last().unwrap() as *const _ as *const (), + ); + } + } + + pub fn gdb_reply(&self, output: &str) { + unsafe { libafl_qemu_gdb_reply(output.as_bytes().as_ptr(), output.len()) }; + } } #[cfg(feature = "python")] pub mod pybind { use super::{GuestAddr, GuestUsize, MmapPerms, SyscallHookResult}; - use core::mem::transmute; use pyo3::exceptions::PyValueError; use pyo3::{prelude::*, types::PyInt}; use std::convert::TryFrom; @@ -820,7 +858,7 @@ pub mod pybind { } fn h2g(&self, addr: u64) -> GuestAddr { - self.emu.h2g(unsafe { transmute::<_, *const u8>(addr) }) + self.emu.h2g(addr as *const u8) } fn binary_path(&self) -> String { diff --git a/libafl_qemu/src/hooks.rs b/libafl_qemu/src/hooks.rs index 6e9d742654..b9d91ee344 100644 --- a/libafl_qemu/src/hooks.rs +++ b/libafl_qemu/src/hooks.rs @@ -14,15 +14,11 @@ use libafl::{executors::inprocess::inprocess_get_state, inputs::Input}; pub use crate::emu::SyscallHookResult; use crate::{ - emu::{Emulator, SKIP_EXEC_HOOK}, + emu::{Emulator, FatPtr, SKIP_EXEC_HOOK}, helper::{QemuHelper, QemuHelperTuple}, GuestAddr, }; -#[repr(C)] -#[derive(Clone, Copy, PartialEq, Eq)] -struct FatPtr(*const c_void, *const c_void); - // all kinds of hooks #[derive(Clone, Copy, PartialEq, Eq)] enum Hook { diff --git a/libafl_qemu/src/lib.rs b/libafl_qemu/src/lib.rs index 9873eaa7a7..a28a0071d8 100644 --- a/libafl_qemu/src/lib.rs +++ b/libafl_qemu/src/lib.rs @@ -7,6 +7,8 @@ allow(clippy::useless_conversion) )] #![allow(clippy::needless_pass_by_value)] +// Till they fix this buggy lint in clippy +#![allow(clippy::borrow_deref_ref)] use std::env;