Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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