Skip to content

Commit 78f0d3b

Browse files
committed
add "baby" redqueen fuzzer test harness/example
1 parent 19c2da9 commit 78f0d3b

9 files changed

Lines changed: 466 additions & 0 deletions

File tree

.github/workflows/build_and_test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ jobs:
294294
# - baby/backtrace_baby_fuzzers
295295
- baby/baby_fuzzer_unicode
296296
- baby/baby_fuzzer_minimizing
297+
- baby/baby_fuzzer_redqueen
297298
- baby/backtrace_baby_fuzzers/c_code_with_fork_executor
298299
- baby/backtrace_baby_fuzzers/c_code_with_inprocess_executor
299300
- baby/backtrace_baby_fuzzers/rust_code_with_fork_executor
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
out/
2+
in/
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[package]
2+
name = "baby_fuzzer_redqueen"
3+
version = "0.16.0"
4+
authors = ["Andrew Barbarello <andrew.barbarello@outlook.com>"]
5+
edition = "2021"
6+
7+
[features]
8+
default = ["std"]
9+
std = []
10+
11+
[profile.release]
12+
lto = true
13+
codegen-units = 1
14+
opt-level = 3
15+
debug = true
16+
17+
[dependencies]
18+
libafl = { path = "../../../crates/libafl/" }
19+
libafl_bolts = { path = "../../../crates/libafl_bolts/" }
20+
libafl_targets = { path = "../../../crates/libafl_targets", features = [
21+
"sancov_pcguard_edges",
22+
"libfuzzer",
23+
"cmplog_extended_instrumentation"
24+
] }
25+
libafl_cc = { path = "../../../crates/libafl_cc" }
26+
clap = { version = "4.5.18", features = ["default"] }
27+
28+
[lib]
29+
name = "baby_fuzzer_redqueen"
30+
crate-type = ["staticlib"]
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
FUZZER_NAME := 'baby_fuzzer_redqueen'
2+
PROJECT_DIR := absolute_path(".")
3+
PROFILE := 'release'
4+
PROFILE_DIR := 'release'
5+
CARGO_TARGET_DIR := env("CARGO_TARGET_DIR", "target")
6+
FUZZER := CARGO_TARGET_DIR / PROFILE_DIR / FUZZER_NAME
7+
8+
alias build := fuzzer
9+
alias cc := cxx
10+
11+
[linux]
12+
[macos]
13+
cxx:
14+
cargo build --profile={{ PROFILE }}
15+
16+
[windows]
17+
cxx:
18+
echo "Unsupported on this platform"
19+
20+
[linux]
21+
[macos]
22+
fuzz_o: cxx
23+
{{ CARGO_TARGET_DIR }}/{{ PROFILE_DIR }}/libafl_cc --libafl-no-link -O3 -g -c fuzz.c -o fuzz.o
24+
25+
[windows]
26+
fuzz_o:
27+
echo "Unsupported on this platform"
28+
29+
[linux]
30+
[macos]
31+
fuzzer: cxx fuzz_o
32+
{{ CARGO_TARGET_DIR }}/{{ PROFILE_DIR }}/libafl_cxx --libafl fuzz.o -o {{ FUZZER }}
33+
34+
[windows]
35+
fuzzer:
36+
echo "Unsupported on this platform"
37+
38+
run: fuzzer
39+
./{{ FUZZER }} -o out -i in
40+
41+
[windows]
42+
run:
43+
echo "Unsupported on this platform"
44+
45+
[linux]
46+
[macos]
47+
test: fuzzer
48+
#!/bin/bash
49+
mkdir -p in
50+
head -c 28 /dev/zero > in/zeros
51+
timeout 120s ./{{ FUZZER }} -o out -i in | tee fuzz_stdout.log || true
52+
if grep -qa "objectives: 1" fuzz_stdout.log; then
53+
echo "Fuzzer is working"
54+
else
55+
echo "Fuzzer does not generate any crashes"
56+
exit 1
57+
fi
58+
rm -rf out in
59+
60+
[windows]
61+
test: fuzzer
62+
echo "Unsupported on this platform"
63+
64+
clean:
65+
cargo clean
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Baby fuzzer with RedQueen-based CmpLog
2+
3+
This is a minimalistic example demonstrating the use of the `cmplog_extended_instrumentation` feature of LibAFL, all in-process. For a more production-quality reference, see the `fuzzbench_forkserver_cmplog` fuzzer.
4+
5+
The tested program is a simple function with comparisons to 16, 32,
6+
and 64 bit magic values, which are difficult/impossible for a simple
7+
bitflipping fuzzer to solve.
8+
9+
Build and run with `just run`, or `just test`, which is a CI target
10+
that checks that the run triggers a crash within a couple of minutes.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include <stdint.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
5+
#define MAGIC_U16 ((uint16_t)0xAABB)
6+
#define MAGIC_U32 ((uint32_t)0x11223344)
7+
#define MAGIC_U64 ((uint64_t)0x0102030405060708ULL)
8+
9+
static inline uint16_t load_u16(const uint8_t *p) {
10+
uint16_t v;
11+
memcpy(&v, p, sizeof(v));
12+
return v;
13+
}
14+
15+
static inline uint16_t bswap_u16(const uint16_t v) {
16+
return ((v & 0xff00) >> 8) | ((v & 0xff) << 8);
17+
}
18+
19+
static inline uint32_t load_u32(const uint8_t *p) {
20+
uint32_t v;
21+
memcpy(&v, p, sizeof(v));
22+
return v;
23+
}
24+
25+
static inline uint32_t bswap_u32(const uint32_t v) {
26+
return ((v & 0xff000000UL) >> 24) | ((v & 0x00ff0000UL) >> 8) |
27+
((v & 0x0000ff00UL) << 8) | ((v & 0x000000ffUL) << 24);
28+
}
29+
30+
static inline uint64_t load_u64(const uint8_t *p) {
31+
uint64_t v;
32+
memcpy(&v, p, sizeof(v));
33+
return v;
34+
}
35+
36+
static inline uint64_t bswap_u64(const uint64_t v) {
37+
return ((v & 0xff00000000000000ULL) >> 56) |
38+
((v & 0x00ff000000000000ULL) >> 40) |
39+
((v & 0x0000ff0000000000ULL) >> 24) |
40+
((v & 0x000000ff00000000ULL) >> 8) |
41+
((v & 0x00000000ff000000ULL) << 8) |
42+
((v & 0x0000000000ff0000ULL) << 24) |
43+
((v & 0x000000000000ff00ULL) << 40) |
44+
((v & 0x00000000000000ffULL) << 56);
45+
}
46+
47+
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
48+
if (size < 28) { return 0; }
49+
50+
if (load_u16(data + 0) == MAGIC_U16) {
51+
if (bswap_u16(load_u16(data + 2)) == MAGIC_U16) {
52+
if (load_u32(data + 4) == MAGIC_U32) {
53+
if (bswap_u32(load_u32(data + 8)) == MAGIC_U32) {
54+
if (load_u64(data + 12) == MAGIC_U64) {
55+
if (bswap_u64(load_u64(data + 20)) == MAGIC_U64) { abort(); }
56+
}
57+
}
58+
}
59+
}
60+
}
61+
62+
return 0;
63+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use std::env;
2+
3+
use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses, ToolWrapper};
4+
5+
pub fn main() {
6+
let args: Vec<String> = env::args().collect();
7+
if args.len() > 1 {
8+
let mut dir = env::current_exe().unwrap();
9+
let wrapper_name = dir.file_name().unwrap().to_str().unwrap();
10+
11+
let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() {
12+
"cc" => false,
13+
"++" | "pp" | "xx" => true,
14+
_ => panic!("Could not figure out if c or c++ wrapper was called. Expected {dir:?} to end with c or cxx"),
15+
};
16+
17+
dir.pop();
18+
19+
let mut cc = ClangWrapper::new();
20+
if let Some(code) = cc
21+
.cpp(is_cpp)
22+
// silence the compiler wrapper output, needed for some configure scripts.
23+
.silence(true)
24+
.need_libafl_arg(true)
25+
.parse_args(&args)
26+
.expect("Failed to parse the command line")
27+
.link_staticlib(&dir, "baby_fuzzer_redqueen")
28+
.add_arg("-fsanitize-coverage=trace-pc-guard")
29+
.add_pass(LLVMPasses::CmpLogInstructions)
30+
.add_passes_arg("-cmplog_instructions_extended=1")
31+
.run()
32+
.expect("Failed to run the wrapped compiler")
33+
{
34+
std::process::exit(code);
35+
}
36+
} else {
37+
panic!("LibAFL CC: No Arguments given");
38+
}
39+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pub mod libafl_cc;
2+
3+
fn main() {
4+
libafl_cc::main();
5+
}

0 commit comments

Comments
 (0)