Skip to content

Commit

Permalink
Document libafl_libfuzzer (#1457)
Browse files Browse the repository at this point in the history
* prep for publishing libafl_libfuzzer

* learn to use linkers

* document-features

* special handling for fuzzbench builds

* Update cmplog.c

* drop dep for llvm-tools; add testcase for memcmp sanity

---------

Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
  • Loading branch information
addisoncrump and tokatoka authored Aug 24, 2023
1 parent f7c94f9 commit 9aa40c0
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 67 deletions.
1 change: 0 additions & 1 deletion .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,3 @@ lto = true
codegen-units = 1
opt-level = 3
debug = true

6 changes: 6 additions & 0 deletions fuzzers/fuzzbench/fuzz.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

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;
}

Expand Down
15 changes: 14 additions & 1 deletion libafl_libfuzzer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]
49 changes: 4 additions & 45 deletions libafl_libfuzzer/build.rs
Original file line number Diff line number Diff line change
@@ -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!(
Expand Down Expand Up @@ -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++");
}
6 changes: 6 additions & 0 deletions libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 17 additions & 17 deletions libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
70 changes: 69 additions & 1 deletion libafl_libfuzzer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,86 @@
//! `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.
//!
//! This crate links to a (separately built) internal crate which affords the actual functionality.
//! 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: <https://llvm.org/docs/LibFuzzer.html#using-libfuzzer-as-a-library>
///
/// 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,
Expand Down
18 changes: 18 additions & 0 deletions libafl_targets/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")]
Expand Down
2 changes: 1 addition & 1 deletion libafl_targets/src/cmplog.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions scripts/publish.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,7 @@ fi
cd libafl_concolic/symcc_runtime
cargo publish "$@"
cd ../.. || exit 1

cd libafl_libfuzzer
cargo publish "$@"
cd ../.. || exit 1

0 comments on commit 9aa40c0

Please sign in to comment.