diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index ce796d60ba..0f04045694 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -114,7 +114,6 @@ jobs: with: profile: minimal toolchain: stable - components: llvm-tools - name: Install and cache deps uses: awalsh128/cache-apt-pkgs-action@v1.1.0 with: diff --git a/Cargo.toml b/Cargo.toml index 1f67e09493..bd3b13e518 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,4 +44,3 @@ lto = true codegen-units = 1 opt-level = 3 debug = true - diff --git a/fuzzers/fuzzbench/fuzz.c b/fuzzers/fuzzbench/fuzz.c index 9175059e59..83fbb181d9 100644 --- a/fuzzers/fuzzbench/fuzz.c +++ b/fuzzers/fuzzbench/fuzz.c @@ -1,8 +1,14 @@ #include #include +#include int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { if (Size >= 8 && *(uint32_t *)Data == 0xaabbccdd) { abort(); } + char buf[8] = {'a', 'b', 'c', 'd'}; + + if (memcmp(Data, buf, 4) == 0) { + abort(); + } return 0; } diff --git a/libafl_libfuzzer/Cargo.toml b/libafl_libfuzzer/Cargo.toml index f387bbe15d..a8a1d05f54 100644 --- a/libafl_libfuzzer/Cargo.toml +++ b/libafl_libfuzzer/Cargo.toml @@ -16,9 +16,22 @@ cc = "1.0" rustversion = "1.0" [features] +#! ## Feature Flags + +## enables the derive macros for the arbitrary dependency, transparently forwarded from libfuzzer-sys arbitrary-derive = ["libfuzzer-sys/arbitrary-derive"] +## enables fuzzer introspection with LibAFL's `introspection` feature introspection = [] -whole-archive = [] [dependencies] libfuzzer-sys = { version = "0.4.7", default-features = false } + +document-features = { version = "0.2" } + +[package.metadata.docs.rs] +features = ["document-features"] +all-features = true + +rustdoc-args = [ + "--cfg", "docsrs", +] diff --git a/libafl_libfuzzer/build.rs b/libafl_libfuzzer/build.rs index c670b73e6c..daf4615898 100644 --- a/libafl_libfuzzer/build.rs +++ b/libafl_libfuzzer/build.rs @@ -1,8 +1,8 @@ use std::{path::PathBuf, process::Command}; fn main() { - if cfg!(feature = "cargo-clippy") { - return; // skip when clippy is running + if cfg!(any(feature = "cargo-clippy", docsrs)) { + return; // skip when clippy or docs is running } if cfg!(not(target_os = "linux")) { println!( @@ -65,52 +65,11 @@ fn main() { let mut lib_path = custom_lib_dir.join(std::env::var_os("TARGET").unwrap()); lib_path.push("release"); - lib_path.push("libafl_libfuzzer_runtime.a"); - - // // TODO this is definitely not compat with macOS/Windows... - if cfg!(feature = "whole-archive") { - use std::path::Path; - 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 mut command = Command::new(rust_lld); - command - .args(["-flavor", "gnu"]) - .arg("-r") - .arg("--whole-archive") - .arg(lib_path) - .args(["-o", custom_lib_dir.join("libFuzzer.o").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 command = Command::new(rust_ar); - command - .arg("cr") - .arg(custom_lib_dir.join("libFuzzer.a")) - .arg(custom_lib_dir.join("libFuzzer.o")); - - assert!( - !command.status().map(|s| !s.success()).unwrap_or(true), - "Couldn't create runtime archive!" - ); - } else { - std::fs::copy(lib_path, custom_lib_dir.join("libFuzzer.a")).unwrap(); - } println!( "cargo:rustc-link-search=native={}", - custom_lib_dir.to_str().unwrap() + lib_path.to_str().unwrap() ); - println!("cargo:rustc-link-lib=static=Fuzzer"); + 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 474667974e..f1a2b3e1bf 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml @@ -18,6 +18,12 @@ codegen-units = 1 opt-level = 3 debug = true +# debug-free release profile for fuzzbench due to space restrictions +[profile.release-fuzzbench] +inherits = "release" +debug = false +strip = true + [lib] name = "afl_libfuzzer_runtime" # TODO fix name once cargo-fuzz stops stripping double-prefixes diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs index 4e22cacee3..0e687427d1 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs @@ -27,25 +27,25 @@ clippy::unsafe_derive_deserialize )] #![cfg_attr(not(test), warn( -missing_debug_implementations, -missing_docs, -//trivial_casts, -trivial_numeric_casts, -unused_extern_crates, -unused_import_braces, -unused_qualifications, -//unused_results + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results ))] #![cfg_attr(test, deny( -missing_debug_implementations, -missing_docs, -//trivial_casts, -trivial_numeric_casts, -unused_extern_crates, -unused_import_braces, -unused_qualifications, -unused_must_use, -//unused_results + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_must_use, + //unused_results ))] #![cfg_attr( test, diff --git a/libafl_libfuzzer/src/lib.rs b/libafl_libfuzzer/src/lib.rs index d12c8991dd..334652bdf1 100644 --- a/libafl_libfuzzer/src/lib.rs +++ b/libafl_libfuzzer/src/lib.rs @@ -1,5 +1,68 @@ //! `libafl_libfuzzer` offers a "permanent" replacement for the now-deprecated libfuzzer //! +//! ## Usage +//! +//! To use LibAFL in place of libfuzzer, change the following line in your fuzz/Cargo.toml: +//! +//! ```toml +//! libfuzzer-sys = { version = "*", features = [...] } +//! ``` +//! +//! With the following: +//! +//! ```toml +//! libfuzzer-sys = { version = "*", features = [...], package = "libafl_libfuzzer" } +//! ``` +//! +//! To use bleeding changes from upstream, use the following: +//! +//! ```toml +//! libfuzzer-sys = { version = "*", features = [...], package = "libafl_libfuzzer", git = "https://github.com/AFLplusplus/LibAFL" } +//! ``` +//! +//! ## Flags +//! +//! You can pass additional flags to the libfuzzer runtime in `cargo-fuzz` like so: +//! +//! ```bash +//! cargo fuzz run fuzz_target -- -extra_flag=1 +//! ``` +//! +//! You will commonly need this for flags such as `-ignore_crashes=1` and `-timeout=5`. In addition +//! to partial support of libfuzzer flags, `libafl_libfuzzer` offers: +//! +//! - `-dedup=n`, with `n` = 1 enabling deduplication of crashes by stacktrace. +//! - `-grimoire=n`, with `n` set to 0 or 1 disabling or enabling [grimoire] mutations, respectively. +//! - if not specified explicitly, `libafl_libfuzzer` will "guess" which setting is appropriate +//! - you should disable grimoire if your target is not string-like +//! - `-report=n`, with `n` = 1 causing `libafl_libfuzzer` to emit a report on the corpus content. +//! - `-skip_tracing=n`, with `n` = 1 causing `libafl_libfuzzer` to disable comparison log tracing. +//! - you should do this if your target performs many comparisons on memory sequences which are +//! not contained in the input +//! - `-tui=n`, with `n` = 1 enabling a graphical terminal interface. +//! - experimental; some users report inconsistent behaviour with tui enabled +//! +//! [grimoire]: https://www.usenix.org/conference/usenixsecurity19/presentation/blazytko +//! +//! ### Supported flags from libfuzzer +//! +//! - `-merge` +//! - `-minimize_crash` +//! - `-artifact_prefix` +//! - `-timeout` +//! - unlike libfuzzer, `libafl_libfuzzer` supports partial second timeouts (e.g. `-timeout=.5`) +//! - `-dict` +//! - `-fork` and `-jobs` +//! - in `libafl_libfuzzer`, these are synonymous +//! - `-ignore_crashes`, `-ignore_ooms`, and `-ignore_timeouts` +//! - `-rss_limit_mb` and `-malloc_limit_mb` +//! - `-ignore_remaining_args` +//! - `-shrink` +//! - `-runs` +//! - `-close_fd_mask` +//! +//! ## Important notes +//! //! This crate only offers sufficient functionality to replace libfuzzer for cargo-fuzz in its //! current state, but may be expanded to handle other flags in the future. //! @@ -7,12 +70,17 @@ //! The internal crate must be built separately to ensure flags from dependent crates are not leaked //! to the runtime (e.g., to prevent coverage being collected on the runtime). +#![doc = document_features::document_features!()] + use std::ffi::{c_char, c_int}; pub use libfuzzer_sys::*; extern "C" { - /// `LLVMFuzzerRunDriver` allows for harnesses which specify their own main. See: https://llvm.org/docs/LibFuzzer.html#using-libfuzzer-as-a-library + /// `LLVMFuzzerRunDriver` allows for harnesses which specify their own main. See: + /// + /// You can call this function inside of a main function in your harness, or specify `#![no_main]` + /// to accept the default runtime driver. pub fn LLVMFuzzerRunDriver( argc: *mut c_int, argv: *mut *mut *const c_char, diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs index 0cbce8e3bf..b561844235 100644 --- a/libafl_targets/build.rs +++ b/libafl_targets/build.rs @@ -67,6 +67,12 @@ fn main() { #[cfg(feature = "sancov_cmplog")] { sancov_cmp.define("SANCOV_CMPLOG", "1"); + + println!("cargo:rustc-link-arg=--undefined=__sanitizer_weak_hook_memcmp"); + println!("cargo:rustc-link-arg=--undefined=__sanitizer_weak_hook_strncmp"); + println!("cargo:rustc-link-arg=--undefined=__sanitizer_weak_hook_strncasecmp"); + println!("cargo:rustc-link-arg=--undefined=__sanitizer_weak_hook_strcmp"); + println!("cargo:rustc-link-arg=--undefined=__sanitizer_weak_hook_strcasecmp"); } sancov_cmp @@ -75,6 +81,18 @@ fn main() { .define("CMPLOG_MAP_H", Some(&*format!("{cmplog_map_h}"))) .file(src_dir.join("sancov_cmp.c")) .compile("sancov_cmp"); + + println!("cargo:rustc-link-arg=--undefined=__sanitizer_cov_trace_cmp1"); + println!("cargo:rustc-link-arg=--undefined=__sanitizer_cov_trace_cmp2"); + println!("cargo:rustc-link-arg=--undefined=__sanitizer_cov_trace_cmp4"); + println!("cargo:rustc-link-arg=--undefined=__sanitizer_cov_trace_cmp8"); + + println!("cargo:rustc-link-arg=--undefined=__sanitizer_cov_trace_const_cmp1"); + println!("cargo:rustc-link-arg=--undefined=__sanitizer_cov_trace_const_cmp2"); + println!("cargo:rustc-link-arg=--undefined=__sanitizer_cov_trace_const_cmp4"); + println!("cargo:rustc-link-arg=--undefined=__sanitizer_cov_trace_const_cmp8"); + + println!("cargo:rustc-link-arg=--undefined=__sanitizer_cov_trace_switch"); } #[cfg(feature = "libfuzzer")] diff --git a/libafl_targets/src/cmplog.c b/libafl_targets/src/cmplog.c index 893f7cc51a..d86689ae5b 100644 --- a/libafl_targets/src/cmplog.c +++ b/libafl_targets/src/cmplog.c @@ -141,7 +141,7 @@ void __libafl_targets_cmplog_routines_len(uintptr_t k, const uint8_t *ptr1, __libafl_targets_cmplog_routines_checked(k, ptr1, ptr2, len); } -static inline void __cmplog_rtn_hook(const uint8_t *ptr1, const uint8_t *ptr2) { +void __cmplog_rtn_hook(const uint8_t *ptr1, const uint8_t *ptr2) { uintptr_t k = RETADDR; k = (k >> 4) ^ (k << 8); k &= CMPLOG_MAP_W - 1; diff --git a/scripts/publish.sh b/scripts/publish.sh index 7767cea0d1..1daed65780 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -70,3 +70,7 @@ fi cd libafl_concolic/symcc_runtime cargo publish "$@" cd ../.. || exit 1 + +cd libafl_libfuzzer +cargo publish "$@" +cd ../.. || exit 1