diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index b3d913e324..f7b7a9c638 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -40,7 +40,7 @@ jobs: toolchain: stable - uses: Swatinem/rust-cache@v1 - name: Install deps - run: sudo apt-get install -y llvm llvm-dev clang + run: sudo apt-get install -y llvm llvm-dev clang ninja-build - name: get clang version run: command -v llvm-config && clang -v - name: Install cargo-hack @@ -90,7 +90,7 @@ jobs: - name: Add nightly rustfmt and clippy run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade - name: Install deps - run: sudo apt-get install -y llvm llvm-dev clang nasm + run: sudo apt-get install -y llvm llvm-dev clang nasm ninja-build - name: Build and run example fuzzers run: ./scripts/test_all_fuzzers.sh nostd-build: diff --git a/fuzzers/fuzzbench_qemu/Cargo.toml b/fuzzers/fuzzbench_qemu/Cargo.toml index 0cf8570eaf..a277ea409e 100644 --- a/fuzzers/fuzzbench_qemu/Cargo.toml +++ b/fuzzers/fuzzbench_qemu/Cargo.toml @@ -9,9 +9,6 @@ default = ["std"] std = [] [profile.release] -lto = true -codegen-units = 1 -opt-level = 3 debug = true [dependencies] @@ -19,7 +16,3 @@ libafl = { path = "../../libafl/" } libafl_qemu = { path = "../../libafl_qemu/" } clap = { version = "3.0.0-beta.2", features = ["default"] } nix = "0.20.0" - -[lib] -name = "fuzzbench_qemu" -crate-type = ["staticlib"] diff --git a/fuzzers/fuzzbench_qemu/src/fuzzer.rs b/fuzzers/fuzzbench_qemu/src/fuzzer.rs index 72c7e1ceb3..7b5e3e874a 100644 --- a/fuzzers/fuzzbench_qemu/src/fuzzer.rs +++ b/fuzzers/fuzzbench_qemu/src/fuzzer.rs @@ -44,13 +44,16 @@ use libafl_qemu::{ QemuExecutor, }; -/// The fuzzer main (as `no_mangle` C function) -#[no_mangle] -pub fn libafl_qemu_main() { +/// The fuzzer main +pub fn main() { // Registry the metadata types used in this fuzzer // Needed only on no_std //RegistryBuilder::register::(); + let args: Vec = env::args().collect(); + let env: Vec<(String, String)> = env::vars().collect(); + emu::init(&args, &env); + let res = match App::new("libafl_qemu_fuzzbench") .version("0.4.0") .author("AFLplusplus team") diff --git a/fuzzers/fuzzbench_qemu/src/lib.rs b/fuzzers/fuzzbench_qemu/src/lib.rs deleted file mode 100644 index 101c91748e..0000000000 --- a/fuzzers/fuzzbench_qemu/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[cfg(target_os = "linux")] -pub mod fuzzer; diff --git a/fuzzers/fuzzbench_qemu/src/main.rs b/fuzzers/fuzzbench_qemu/src/main.rs new file mode 100644 index 0000000000..6f78885bcc --- /dev/null +++ b/fuzzers/fuzzbench_qemu/src/main.rs @@ -0,0 +1,7 @@ +#[cfg(target_os = "linux")] +pub mod fuzzer; + +fn main() { + #[cfg(target_os = "linux")] + fuzzer::main() +} diff --git a/libafl_qemu/Cargo.toml b/libafl_qemu/Cargo.toml index 7f820da14a..3784f4f747 100644 --- a/libafl_qemu/Cargo.toml +++ b/libafl_qemu/Cargo.toml @@ -25,3 +25,7 @@ libc = "0.2.97" [build-dependencies] cc = { version = "1.0" } +which = "4.1" + +[lib] +crate-type = ["rlib", "cdylib"] diff --git a/libafl_qemu/build.rs b/libafl_qemu/build.rs index 7f455f8072..ad9f55a94e 100644 --- a/libafl_qemu/build.rs +++ b/libafl_qemu/build.rs @@ -1,21 +1,215 @@ -use std::{env, path::Path}; +use std::{env, fs::copy, path::Path, process::Command}; +//use std::fs::read_dir; +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 = "22daaa7d0c76b32f8391bad40c0b220f3e659f66"; + +fn build_dep_check(tools: &[&str]) { + for tool in tools { + which(tool).unwrap_or_else(|_| panic!("Build tool {} not found", tool)); + } +} + +#[allow(clippy::too_many_lines)] fn main() { - let out_dir = env::var_os("OUT_DIR").unwrap(); - let out_dir = out_dir.to_string_lossy().to_string(); - let src_dir = Path::new("src"); + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-env-changed=CPU_TARGET"); let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); - - if target_os == "linux" { - println!("cargo:rerun-if-changed=src/weaks.c"); - - cc::Build::new() - .file(src_dir.join("weaks.c")) - .compile("weaks"); - - println!("cargo:rustc-link-search=native={}", &out_dir); + if target_os != "linux" { + return; } - println!("cargo:rerun-if-changed=build.rs"); + let jobs = env::var("CARGO_BUILD_JOBS").unwrap_or_else(|_| "1".to_owned()); + let cpu_target = env::var("CPU_TARGET").unwrap_or_else(|_| { + println!("cargo:warning=CPU_TARGET is not set, default to x86_64"); + "x86_64".to_owned() + }); + + let out_dir = env::var_os("OUT_DIR").unwrap(); + let out_dir = out_dir.to_string_lossy().to_string(); + let out_dir_path = Path::new(&out_dir); + let mut target_dir = out_dir_path.to_path_buf(); + target_dir.pop(); + target_dir.pop(); + target_dir.pop(); + //let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); + + build_dep_check(&["git", "make"]); + + let qemu_path = out_dir_path.join(QEMU_DIRNAME); + if !qemu_path.is_dir() { + println!( + "cargo:warning=Qemu not found, cloning with git ({})...", + QEMU_REVISION + ); + Command::new("git") + .current_dir(&out_dir_path) + .arg("clone") + .arg(QEMU_URL) + .status() + .unwrap(); + Command::new("git") + .current_dir(&qemu_path) + .arg("checkout") + .arg(QEMU_REVISION) + .status() + .unwrap(); + } + + let build_dir = qemu_path.join("build"); + let output_lib = build_dir.join(&format!("libqemu-{}.so", cpu_target)); + if !output_lib.is_file() { + 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-png", + "--disable-vnc-sasl", + "--disable-vte", + "--disable-vvfat", + "--disable-xen", + "--disable-xen-pci-passthrough", + "--disable-xfsctl", + ]) + .status() + .expect("Configure failed"); + Command::new("make") + .current_dir(&qemu_path) + .arg("-j") + .arg(&jobs) + .status() + .expect("Make failed"); + //let _ = remove_file(build_dir.join(&format!("libqemu-{}.so", cpu_target))); + } + + copy( + build_dir.join(&format!("libqemu-{}.so", cpu_target)), + target_dir.join(&format!("libqemu-{}.so", cpu_target)), + ) + .expect("Failed to copy the QEMU shared object"); + + println!( + "cargo:rustc-link-search=native={}", + &target_dir.to_string_lossy().to_string() + ); + println!("cargo:rustc-link-lib=qemu-{}", cpu_target); + + println!("cargo:rustc-env=LD_LIBRARY_PATH={}", target_dir.display()); } + +/* + // 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 65fb2b8d74..4d9a3f2fd9 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -5,7 +5,7 @@ use core::{ convert::TryFrom, ffi::c_void, mem::{size_of, transmute, MaybeUninit}, - ptr::copy_nonoverlapping, + ptr::{copy_nonoverlapping, null}, }; use num::Num; use num_enum::{IntoPrimitive, TryFromPrimitive}; @@ -83,6 +83,8 @@ impl MapInfo { } extern "C" { + fn qemu_user_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32; + fn libafl_qemu_write_reg(reg: i32, val: *const u8) -> i32; fn libafl_qemu_read_reg(reg: i32, val: *mut u8) -> i32; fn libafl_qemu_num_regs() -> i32; @@ -137,6 +139,24 @@ extern "C" { unsafe extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult; } +#[allow(clippy::must_use_candidate, clippy::similar_names)] +pub fn init(args: &[String], env: &[(String, String)]) -> i32 { + let argv: Vec<*const u8> = args.iter().map(|x| x.as_bytes().as_ptr()).collect(); + assert!(argv.len() < i32::MAX as usize); + let env_strs: Vec = env.iter().map(|(k, v)| format!("{}={}", &k, &v)).collect(); + let mut envp: Vec<*const u8> = env_strs.iter().map(|x| x.as_bytes().as_ptr()).collect(); + envp.push(null()); + #[allow(clippy::cast_possible_wrap)] + let argc = argv.len() as i32; + unsafe { + qemu_user_init( + argc, + argv.as_ptr() as *const *const u8, + envp.as_ptr() as *const *const u8, + ) + } +} + pub struct GuestMaps { orig_c_iter: *const c_void, c_iter: *const c_void,