diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index e79bf28a7d1..558eab4c536 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -124,6 +124,7 @@ jobs: with: profile: minimal toolchain: stable + components: llvm-tools - name: Remove existing clang and LLVM run: sudo apt purge llvm* clang* - name: Install and cache deps diff --git a/libafl_libfuzzer/Cargo.toml b/libafl_libfuzzer/Cargo.toml index 1182d4a3341..c358480692a 100644 --- a/libafl_libfuzzer/Cargo.toml +++ b/libafl_libfuzzer/Cargo.toml @@ -35,6 +35,9 @@ introspection = [] ## `-fsanitize=fuzzer-no-link -l:libafl_libfuzzer_runtime.a` embed-runtime = [] +## 🐇 +rabbit = [] + [dependencies] libfuzzer-sys = { version = "0.4.7", default-features = false } document-features = { version = "0.2" } diff --git a/libafl_libfuzzer/build.rs b/libafl_libfuzzer/build.rs index f4c701c4fa5..a5348203456 100644 --- a/libafl_libfuzzer/build.rs +++ b/libafl_libfuzzer/build.rs @@ -1,5 +1,17 @@ -use std::{path::PathBuf, process::Command}; - +use std::{ + fs::File, + io::{BufRead, BufReader, BufWriter, Write}, + path::PathBuf, + process::{Command, Stdio}, +}; + +#[cfg(feature = "rabbit")] +const NAMESPACE: &str = "🐇"; +#[cfg(not(feature = "rabbit"))] +const NAMESPACE: &str = "__libafl"; +const NAMESPACE_LEN: usize = NAMESPACE.as_bytes().len(); + +#[allow(clippy::too_many_lines)] fn main() { if cfg!(any(feature = "cargo-clippy", docsrs)) { return; // skip when clippy or docs is running @@ -69,20 +81,143 @@ fn main() { let mut lib_path = custom_lib_dir.join(std::env::var_os("TARGET").unwrap()); lib_path.push("release"); - #[cfg(all(feature = "embed-runtime", target_family = "unix"))] - { - // NOTE: lib, .a are added always on unix-like systems as described in: - // https://gist.github.com/novafacing/1389cbb2f0a362d7eb103e67b4468e2b + if cfg!(target_family = "unix") { + use std::path::Path; + + lib_path.push("libafl_libfuzzer_runtime.a"); + let target_libdir = Command::new("rustc") + .args(["--print", "target-libdir"]) + .output() + .expect("Couldn't find rustc's target-libdir"); + let target_libdir = String::from_utf8(target_libdir.stdout).unwrap(); + let target_libdir = Path::new(target_libdir.trim()); + + let rust_lld = target_libdir.join("../bin/rust-lld"); + let rust_ar = target_libdir.join("../bin/llvm-ar"); // NOTE: depends on llvm-tools + let rust_objcopy = target_libdir.join("../bin/llvm-objcopy"); // NOTE: depends on llvm-tools + let nm = "nm"; // NOTE: we use system nm here because llvm-nm doesn't respect the encoding? + + let redefined_symbols = custom_lib_dir.join("redefs.txt"); + + let objfile_orig = custom_lib_dir.join("libFuzzer.o"); + let objfile_dest = custom_lib_dir.join("libFuzzer-mimalloc.o"); + + let mut command = Command::new(rust_lld); + command + .args(["-flavor", "gnu"]) + .arg("-r") + .arg("--whole-archive") + .arg(lib_path) + .args(["-o", objfile_orig.to_str().expect("Invalid path characters present in your current directory prevent us from linking to the runtime")]); + + assert!( + !command.status().map(|s| !s.success()).unwrap_or(true), + "Couldn't link runtime crate! Do you have the llvm-tools component installed?" + ); + + let mut child = Command::new(nm) + .arg(&objfile_orig) + .stdout(Stdio::piped()) + .spawn() + .unwrap(); + + let mut redefinitions_file = BufWriter::new(File::create(&redefined_symbols).unwrap()); + + let replacement = format!("_ZN{NAMESPACE_LEN}{NAMESPACE}"); + + // redefine all the rust-mangled symbols we can + // TODO this will break when v0 mangling is stabilised + for line in BufReader::new(child.stdout.take().unwrap()).lines() { + let line = line.unwrap(); + let (_, symbol) = line.rsplit_once(' ').unwrap(); + if symbol.starts_with("_ZN") { + writeln!( + redefinitions_file, + "{} {}", + symbol, + symbol.replacen("_ZN", &replacement, 1) + ) + .unwrap(); + } + } + redefinitions_file.flush().unwrap(); + drop(redefinitions_file); + + assert!( + !child.wait().map(|s| !s.success()).unwrap_or(true), + "Couldn't link runtime crate! Do you have the llvm-tools component installed?" + ); + + let mut command = Command::new(rust_objcopy); + + for symbol in [ + "__rust_drop_panic", + "__rust_foreign_exception", + "rust_begin_unwind", + "rust_panic", + "rust_eh_personality", + "__rg_oom", + "__rdl_oom", + "__rdl_alloc", + "__rust_alloc", + "__rdl_dealloc", + "__rust_dealloc", + "__rdl_realloc", + "__rust_realloc", + "__rdl_alloc_zeroed", + "__rust_alloc_zeroed", + "__rust_alloc_error_handler", + "__rust_no_alloc_shim_is_unstable", + "__rust_alloc_error_handler_should_panic", + ] { + command + .arg("--redefine-sym") + .arg(format!("{symbol}={symbol}_libafl_libfuzzer_runtime")); + } + + command + .arg("--redefine-syms") + .arg(redefined_symbols) + .args([&objfile_orig, &objfile_dest]); + + assert!( + !command.status().map(|s| !s.success()).unwrap_or(true), + "Couldn't rename allocators in the runtime crate! Do you have the llvm-tools component installed?" + ); + + let mut command = Command::new(rust_ar); + command + .arg("cr") + .arg(custom_lib_dir.join("libFuzzer.a")) + .arg(objfile_dest); + + assert!( + !command.status().map(|s| !s.success()).unwrap_or(true), + "Couldn't create runtime archive!" + ); + + #[cfg(feature = "embed-runtime")] + { + // NOTE: lib, .a are added always on unix-like systems as described in: + // https://gist.github.com/novafacing/1389cbb2f0a362d7eb103e67b4468e2b + println!( + "cargo:rustc-env=LIBAFL_LIBFUZZER_RUNTIME_PATH={}", + custom_lib_dir.join("libFuzzer.a").display() + ); + } + println!( - "cargo:rustc-env=LIBAFL_LIBFUZZER_RUNTIME_PATH={}", - lib_path.join("libafl_libfuzzer_runtime.a").display() + "cargo:rustc-link-search=native={}", + custom_lib_dir.to_str().unwrap() ); + println!("cargo:rustc-link-lib=static=Fuzzer"); + } else { + println!( + "cargo:rustc-link-search=native={}", + lib_path.to_str().unwrap() + ); + println!("cargo:rustc-link-lib=static=afl_fuzzer_runtime"); } - println!( - "cargo:rustc-link-search=native={}", - lib_path.to_str().unwrap() - ); - println!("cargo:rustc-link-lib=static=afl_libfuzzer_runtime"); println!("cargo:rustc-link-lib=stdc++"); } diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml b/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml index e7824bf5a73..18b9eab450a 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml @@ -37,7 +37,7 @@ libafl_targets = { path = "../../libafl_targets", features = ["sancov_8bit", "sa ahash = { version = "0.8.3", default-features = false } libc = "0.2.139" log = "0.4.17" -mimalloc = { version = "0.1.34", default-features = false, optional = true } +mimalloc = { version = "0.1.34", default-features = false } num-traits = "0.2.15" rand = "0.8.5" serde = { version = "1.0", features = ["derive"] } # serialization lib diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/build.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/build.rs index 85b12f47bc8..c2566b847f9 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/build.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/build.rs @@ -1,5 +1,6 @@ use std::{env, path::Path}; +#[allow(clippy::too_many_lines)] fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs index 12734b60b22..3de1b59cc39 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs @@ -80,8 +80,11 @@ use libafl::{ }; use libafl_bolts::AsSlice; use libc::_exit; +use mimalloc::MiMalloc; use crate::options::{LibfuzzerMode, LibfuzzerOptions}; +#[global_allocator] +static GLOBAL: MiMalloc = MiMalloc; mod corpus; mod feedbacks;