Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ jobs:
# - baby/backtrace_baby_fuzzers
- baby/baby_fuzzer_unicode
- baby/baby_fuzzer_minimizing
- baby/baby_fuzzer_redqueen
- baby/backtrace_baby_fuzzers/c_code_with_fork_executor
- baby/backtrace_baby_fuzzers/c_code_with_inprocess_executor
- baby/backtrace_baby_fuzzers/rust_code_with_fork_executor
Expand Down
22 changes: 5 additions & 17 deletions crates/libafl/src/mutators/token_mutations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1076,8 +1076,8 @@ impl AflppRedQueen {

if buf_16 == pattern as u16 && another_buf_16 == another_pattern as u16 {
let mut cloned = buf.to_vec();
cloned[buf_idx + 1] = (repl & 0xff) as u8;
cloned[buf_idx] = ((repl >> 8) & 0xff) as u8;
cloned[buf_idx..(buf_idx + 2)]
.copy_from_slice(&(repl as u16).to_be_bytes());
vec.push(cloned);
return Ok(true);
}
Expand All @@ -1091,12 +1091,9 @@ impl AflppRedQueen {
// println!("buf: {buf_32} {another_buf_32} {pattern} {another_pattern}");
if buf_32 == pattern as u32 && another_buf_32 == another_pattern as u32 {
let mut cloned = buf.to_vec();
cloned[buf_idx + 3] = (repl & 0xff) as u8;
cloned[buf_idx + 2] = ((repl >> 8) & 0xff) as u8;
cloned[buf_idx + 1] = ((repl >> 16) & 0xff) as u8;
cloned[buf_idx] = ((repl >> 24) & 0xff) as u8;
cloned[buf_idx..(buf_idx + 4)]
.copy_from_slice(&(repl as u32).to_be_bytes());
vec.push(cloned);

return Ok(true);
}
}
Expand All @@ -1109,16 +1106,7 @@ impl AflppRedQueen {

if buf_64 == pattern && another_buf_64 == another_pattern {
let mut cloned = buf.to_vec();

cloned[buf_idx + 7] = (repl & 0xff) as u8;
cloned[buf_idx + 6] = ((repl >> 8) & 0xff) as u8;
cloned[buf_idx + 5] = ((repl >> 16) & 0xff) as u8;
cloned[buf_idx + 4] = ((repl >> 24) & 0xff) as u8;
cloned[buf_idx + 3] = ((repl >> 32) & 0xff) as u8;
cloned[buf_idx + 2] = ((repl >> 32) & 0xff) as u8;
cloned[buf_idx + 1] = ((repl >> 40) & 0xff) as u8;
cloned[buf_idx] = ((repl >> 48) & 0xff) as u8;

cloned[buf_idx..(buf_idx + 8)].copy_from_slice(&repl.to_be_bytes());
vec.push(cloned);
return Ok(true);
}
Expand Down
30 changes: 16 additions & 14 deletions crates/libafl_targets/src/cmps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,8 @@ impl AflppCmpLogOperands {
#[repr(C, packed)]
/// Comparison function operands, like for strcmp/memcmp, represented as two byte arrays.
pub struct AflppCmpLogFnOperands {
v0: [u8; 32],
v1: [u8; 32],
v0: [u8; CMPLOG_RTN_LEN],
v1: [u8; CMPLOG_RTN_LEN],
v0_len: u8,
v1_len: u8,
unused: [u8; 6],
Expand All @@ -235,14 +235,16 @@ impl AflppCmpLogFnOperands {
#[must_use]
/// Create a new AFL++ function operands comparison values from two byte slices
pub fn new(v0: &[u8], v1: &[u8]) -> Self {
let v0_len = v0.len() as u8;
let v1_len = v1.len() as u8;
let v0_len = v0.len().min(CMPLOG_RTN_LEN) as u8;
let v0_truncated = &v0[..v0_len as usize];
let v1_len = v1.len().min(CMPLOG_RTN_LEN) as u8;
let v1_truncated = &v1[..v1_len as usize];

let mut v0_arr = [0; 32];
let mut v1_arr = [0; 32];
let mut v0_arr = [0; CMPLOG_RTN_LEN];
let mut v1_arr = [0; CMPLOG_RTN_LEN];

v0_arr.copy_from_slice(v0);
v1_arr.copy_from_slice(v1);
v0_arr[..v0_len as usize].copy_from_slice(v0_truncated);
v1_arr[..v1_len as usize].copy_from_slice(v1_truncated);

Self {
v0: v0_arr,
Expand All @@ -255,7 +257,7 @@ impl AflppCmpLogFnOperands {

#[must_use]
/// first rtn operand
pub fn v0(&self) -> &[u8; 32] {
pub fn v0(&self) -> &[u8; CMPLOG_RTN_LEN] {
&self.v0
}

Expand All @@ -267,7 +269,7 @@ impl AflppCmpLogFnOperands {

#[must_use]
/// first rtn operand len
pub fn v1(&self) -> &[u8; 32] {
pub fn v1(&self) -> &[u8; CMPLOG_RTN_LEN] {
&self.v1
}

Expand All @@ -279,14 +281,14 @@ impl AflppCmpLogFnOperands {

/// Set the v0 (left) side of the comparison
pub fn set_v0(&mut self, v0: &[u8]) {
self.v0_len = v0.len() as u8;
self.v0.copy_from_slice(v0);
self.v0_len = v0.len().min(CMPLOG_RTN_LEN) as u8;
self.v0[..self.v0_len as usize].copy_from_slice(&v0[..self.v0_len as usize]);
}

/// Set the v1 (right) side of the comparison
pub fn set_v1(&mut self, v1: &[u8]) {
self.v1_len = v1.len() as u8;
self.v1.copy_from_slice(v1);
self.v1_len = v1.len().min(CMPLOG_RTN_LEN) as u8;
self.v1[..self.v1_len as usize].copy_from_slice(&v1[..self.v1_len as usize]);
}
}

Expand Down
2 changes: 2 additions & 0 deletions fuzzers/baby/baby_fuzzer_redqueen/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
out/
in/
30 changes: 30 additions & 0 deletions fuzzers/baby/baby_fuzzer_redqueen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "baby_fuzzer_redqueen"
version = "0.16.0"
authors = ["Andrew Barbarello <andrew.barbarello@outlook.com>"]
edition = "2021"

[features]
default = ["std"]
std = []

[profile.release]
lto = true
codegen-units = 1
opt-level = 3
debug = true

[dependencies]
libafl = { path = "../../../crates/libafl/" }
libafl_bolts = { path = "../../../crates/libafl_bolts/" }
libafl_targets = { path = "../../../crates/libafl_targets", features = [
"sancov_pcguard_edges",
"libfuzzer",
"cmplog_extended_instrumentation",
] }
libafl_cc = { path = "../../../crates/libafl_cc" }
clap = { version = "4.5.18", features = ["default"] }

[lib]
name = "baby_fuzzer_redqueen"
crate-type = ["staticlib"]
65 changes: 65 additions & 0 deletions fuzzers/baby/baby_fuzzer_redqueen/Justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
FUZZER_NAME := 'baby_fuzzer_redqueen'
PROJECT_DIR := absolute_path(".")
PROFILE := 'release'
PROFILE_DIR := 'release'
CARGO_TARGET_DIR := env("CARGO_TARGET_DIR", "target")
FUZZER := absolute_path(CARGO_TARGET_DIR / PROFILE_DIR / FUZZER_NAME)

alias build := fuzzer
alias cc := cxx

[linux]
[macos]
cxx:
cargo build --profile={{ PROFILE }}

[windows]
cxx:
echo "Unsupported on this platform"

[linux]
[macos]
fuzz_o: cxx
{{ CARGO_TARGET_DIR }}/{{ PROFILE_DIR }}/libafl_cc --libafl-no-link -O3 -g -c fuzz.c -o fuzz.o

[windows]
fuzz_o:
echo "Unsupported on this platform"

[linux]
[macos]
fuzzer: cxx fuzz_o
{{ CARGO_TARGET_DIR }}/{{ PROFILE_DIR }}/libafl_cxx --libafl fuzz.o -o {{ FUZZER }}

[windows]
fuzzer:
echo "Unsupported on this platform"

run: fuzzer
{{ FUZZER }} -o out -i in

[windows]
run:
echo "Unsupported on this platform"

[linux]
[macos]
test: fuzzer
#!/bin/bash
mkdir -p in
head -c 28 /dev/zero > in/zeros
timeout 120s {{ FUZZER }} -o out -i in | tee fuzz_stdout.log || true
if grep -qa "objectives: 1" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any crashes"
exit 1
fi
rm -rf out in

[windows]
test: fuzzer
echo "Unsupported on this platform"

clean:
cargo clean
10 changes: 10 additions & 0 deletions fuzzers/baby/baby_fuzzer_redqueen/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Baby fuzzer with RedQueen-based CmpLog

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.

The tested program is a simple function with comparisons to 16, 32,
and 64 bit magic values, which are difficult/impossible for a simple
bitflipping fuzzer to solve.

Build and run with `just run`, or `just test`, which is a CI target
that checks that the run triggers a crash within a couple of minutes.
63 changes: 63 additions & 0 deletions fuzzers/baby/baby_fuzzer_redqueen/fuzz.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#define MAGIC_U16 ((uint16_t)0xAABB)
#define MAGIC_U32 ((uint32_t)0x11223344)
#define MAGIC_U64 ((uint64_t)0x0102030405060708ULL)

static inline uint16_t load_u16(const uint8_t *p) {
uint16_t v;
memcpy(&v, p, sizeof(v));
return v;
}

static inline uint16_t bswap_u16(const uint16_t v) {
return ((v & 0xff00) >> 8) | ((v & 0xff) << 8);
}

static inline uint32_t load_u32(const uint8_t *p) {
uint32_t v;
memcpy(&v, p, sizeof(v));
return v;
}

static inline uint32_t bswap_u32(const uint32_t v) {
return ((v & 0xff000000UL) >> 24) | ((v & 0x00ff0000UL) >> 8) |
((v & 0x0000ff00UL) << 8) | ((v & 0x000000ffUL) << 24);
}

static inline uint64_t load_u64(const uint8_t *p) {
uint64_t v;
memcpy(&v, p, sizeof(v));
return v;
}

static inline uint64_t bswap_u64(const uint64_t v) {
return ((v & 0xff00000000000000ULL) >> 56) |
((v & 0x00ff000000000000ULL) >> 40) |
((v & 0x0000ff0000000000ULL) >> 24) |
((v & 0x000000ff00000000ULL) >> 8) |
((v & 0x00000000ff000000ULL) << 8) |
((v & 0x0000000000ff0000ULL) << 24) |
((v & 0x000000000000ff00ULL) << 40) |
((v & 0x00000000000000ffULL) << 56);
}

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size < 28) { return 0; }

if (load_u16(data + 0) == MAGIC_U16) {
if (bswap_u16(load_u16(data + 2)) == MAGIC_U16) {
if (load_u32(data + 4) == MAGIC_U32) {
if (bswap_u32(load_u32(data + 8)) == MAGIC_U32) {
if (load_u64(data + 12) == MAGIC_U64) {
if (bswap_u64(load_u64(data + 20)) == MAGIC_U64) { abort(); }
}
}
}
}
}

return 0;
}
39 changes: 39 additions & 0 deletions fuzzers/baby/baby_fuzzer_redqueen/src/bin/libafl_cc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use std::env;

use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses, ToolWrapper};

pub fn main() {
let args: Vec<String> = env::args().collect();
if args.len() > 1 {
let mut dir = env::current_exe().unwrap();
let wrapper_name = dir.file_name().unwrap().to_str().unwrap();

let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() {
"cc" => false,
"++" | "pp" | "xx" => true,
_ => panic!("Could not figure out if c or c++ wrapper was called. Expected {dir:?} to end with c or cxx"),
};

dir.pop();

let mut cc = ClangWrapper::new();
if let Some(code) = cc
.cpp(is_cpp)
// silence the compiler wrapper output, needed for some configure scripts.
.silence(true)
.need_libafl_arg(true)
.parse_args(&args)
.expect("Failed to parse the command line")
.link_staticlib(&dir, "baby_fuzzer_redqueen")
.add_arg("-fsanitize-coverage=trace-pc-guard")
.add_pass(LLVMPasses::CmpLogInstructions)
.add_passes_arg("-cmplog_instructions_extended=1")
.run()
.expect("Failed to run the wrapped compiler")
{
std::process::exit(code);
}
} else {
panic!("LibAFL CC: No Arguments given");
}
}
5 changes: 5 additions & 0 deletions fuzzers/baby/baby_fuzzer_redqueen/src/bin/libafl_cxx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub mod libafl_cc;

fn main() {
libafl_cc::main();
}
Loading
Loading