Sancov based ngram & ctx implementation (#1864)

* ngram

* ctx

* push stuff

* passing cargo check

* save stuf

* add

* no default

* fuzzbench ready

* formatt

* aaaaaaaa

* adaptive map

* add all

* chg

* fix

* deleting stuff

* fmt

* clang18

* fuck

* save space

* fix?

* feature name

* fucking nightly only

* harness

* fix libafl_cc

---------

Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
Dongjia "toka" Zhang 2024-03-01 13:59:13 +01:00 committed by GitHub
parent 8c773a6b85
commit 1a41e65a5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 998 additions and 1521 deletions

View File

@ -168,13 +168,26 @@ jobs:
ubuntu-check: ubuntu-check:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Remove Dotnet & Haskell
run: rm -rf /usr/share/dotnet && rm -rf /opt/ghc
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
profile: minimal profile: minimal
toolchain: stable toolchain: stable
components: llvm-tools components: llvm-tools
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: false
# all of these default to true, but feel free to set to
# "false" if necessary for your workflow
android: true
dotnet: true
haskell: true
large-packages: false
docker-images: true
swap-storage: true
- name: Remove existing clang and LLVM - name: Remove existing clang and LLVM
run: sudo apt purge llvm* clang* run: sudo apt purge llvm* clang*
- name: Install and cache deps - name: Install and cache deps

View File

@ -21,7 +21,7 @@ debug = true
[build-dependencies] [build-dependencies]
anyhow = "1" anyhow = "1"
bindgen = "0.63" bindgen = "0.69.4"
cc = "1.0" cc = "1.0"
[dependencies] [dependencies]

View File

@ -26,7 +26,7 @@ nix = "0.27"
libafl = { path = "../../libafl/" } libafl = { path = "../../libafl/" }
libafl_bolts = { path = "../../libafl_bolts/" } libafl_bolts = { path = "../../libafl_bolts/" }
libafl_cc = { path = "../../libafl_cc/" } libafl_cc = { path = "../../libafl_cc/" }
libafl_targets = { path = "../../libafl_targets/" } libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] }
[lib] [lib]
name = "libforkserver_libafl_cc" name = "libforkserver_libafl_cc"

View File

@ -24,9 +24,6 @@ pub fn main() {
.parse_args(&args) .parse_args(&args)
.expect("Failed to parse the command line") .expect("Failed to parse the command line")
// Enable libafl's coverage instrumentation // Enable libafl's coverage instrumentation
.add_pass(LLVMPasses::AFLCoverage)
.add_arg("-mllvm")
.add_arg("-ctx") // Context sensitive coverage
// Imitate afl-cc's compile definitions // Imitate afl-cc's compile definitions
.add_arg("-D__AFL_FUZZ_INIT()=int __afl_sharedmem_fuzzing = 1;extern unsigned int *__afl_fuzz_len;extern unsigned char *__afl_fuzz_ptr;unsigned char __afl_fuzz_alt[1048576];unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;void libafl_start_forkserver(void)") .add_arg("-D__AFL_FUZZ_INIT()=int __afl_sharedmem_fuzzing = 1;extern unsigned int *__afl_fuzz_len;extern unsigned char *__afl_fuzz_ptr;unsigned char __afl_fuzz_alt[1048576];unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;void libafl_start_forkserver(void)")
.add_arg("-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : __afl_fuzz_alt_ptr)") .add_arg("-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : __afl_fuzz_alt_ptr)")

View File

@ -1,5 +1,5 @@
[package] [package]
name = "libfuzzer_libpng_ctx" name = "fuzzbench"
version = "0.11.2" version = "0.11.2"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"] authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2021" edition = "2021"
@ -7,6 +7,7 @@ edition = "2021"
[features] [features]
default = ["std"] default = ["std"]
std = [] std = []
no_link_main = ["libafl_targets/libfuzzer_no_link_main"]
[profile.release] [profile.release]
lto = true lto = true
@ -14,19 +15,25 @@ codegen-units = 1
opt-level = 3 opt-level = 3
debug = true debug = true
[profile.release-fuzzbench]
inherits = "release"
debug = false
strip = true
[build-dependencies] [build-dependencies]
cc = { version = "1.0", features = ["parallel"] } cc = { version = "1.0", features = ["parallel"] }
which = "4.4" which = "4.4"
[dependencies] [dependencies]
libafl = { path = "../../libafl/", features = ["std", "derive", "llmp_compression", "introspection"] } libafl = { path = "../../libafl/" }
libafl_bolts = { path = "../../libafl_bolts/" } libafl_bolts = { path = "../../libafl_bolts/" }
libafl_targets = { path = "../../libafl_targets/", features = ["libfuzzer"] } libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "sancov_cmplog", "libfuzzer", "sancov_ctx"] }
# TODO Include it only when building cc # TODO Include it only when building cc
libafl_cc = { path = "../../libafl_cc/" } libafl_cc = { path = "../../libafl_cc/" }
clap = { version = "4.0", features = ["derive"] } clap = { version = "4.0", features = ["default"] }
nix = { version = "0.27", features = ["fs"] }
mimalloc = { version = "*", default-features = false } mimalloc = { version = "*", default-features = false }
[lib] [lib]
name = "libfuzzer_libpng" name = "fuzzbench"
crate-type = ["staticlib"] crate-type = ["staticlib"]

View File

@ -0,0 +1,108 @@
[env]
PROJECT_DIR = { script = ["pwd"] }
CARGO_TARGET_DIR = { value = "${PROJECT_DIR}/target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } }
FUZZER_NAME="fuzzer"
PROFILE = { value = "release", condition = {env_not_set = ["PROFILE"]} }
PROFILE_DIR = {value = "release", condition = {env_not_set = ["PROFILE_DIR"] }}
[tasks.unsupported]
script_runner="@shell"
script='''
echo "Cargo-make not integrated yet on this"
'''
# Compilers
[tasks.cxx]
linux_alias = "cxx_unix"
mac_alias = "cxx_unix"
windows_alias = "unsupported"
[tasks.cxx_unix]
command = "cargo"
args = ["build", "--profile", "${PROFILE}"]
[tasks.cc]
linux_alias = "cc_unix"
mac_alias = "cc_unix"
windows_alias = "unsupported"
[tasks.cc_unix]
command = "cargo"
args = ["build", "--profile", "${PROFILE}"]
# fuzz.o File
[tasks.fuzz_o]
linux_alias = "fuzz_o_unix"
mac_alias = "fuzz_o_unix"
windows_alias = "unsupported"
[tasks.fuzz_o_unix]
command = "${CARGO_TARGET_DIR}/${PROFILE_DIR}/libafl_cc"
args = ["--libafl-no-link", "-O3", "-c", "fuzz.c", "-o", "fuzz.o"]
dependencies = ["cc", "cxx"]
# Fuzzer
[tasks.fuzzer]
linux_alias = "fuzzer_unix"
mac_alias = "fuzzer_unix"
windows_alias = "unsupported"
[tasks.fuzzer_unix]
command = "${CARGO_TARGET_DIR}/${PROFILE_DIR}/libafl_cxx"
args = ["--libafl", "fuzz.o", "-o", "${FUZZER_NAME}", "-lm", "-lz"]
dependencies = ["cc", "cxx", "fuzz_o"]
# Run
[tasks.run]
linux_alias = "run_unix"
mac_alias = "run_unix"
windows_alias = "unsupported"
[tasks.run_unix]
script_runner="@shell"
script='''
rm -rf libafl_unix_shmem_server || true
mkdir in || true
echo a > in/a
./${FUZZER_NAME} -o out -i in
'''
dependencies = ["fuzzer"]
# Test
[tasks.test]
linux_alias = "test_unix"
mac_alias = "test_unix"
windows_alias = "unsupported"
[tasks.test_unix]
script_runner="@shell"
script='''
rm -rf libafl_unix_shmem_server || true
mkdir in || true
echo a > in/a
# Allow sigterm as exit code
timeout 31s ./${FUZZER_NAME} -o out -i in >fuzz_stdout.log || true
if grep -qa "objectives: 1" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
fi
rm -rf out || true
rm -rf in || true
'''
dependencies = ["fuzzer"]
# Clean
[tasks.clean]
linux_alias = "clean_unix"
mac_alias = "clean_unix"
windows_alias = "unsupported"
[tasks.clean_unix]
script_runner="@shell"
script='''
rm ./${FUZZER_NAME} || true
rm fuzz.o || true
'''

View File

@ -0,0 +1,19 @@
#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;
}
/*
int main() {
char buf [10] = {0};
LLVMFuzzerTestOneInput(buf, 10);
}*/

View File

@ -3,7 +3,7 @@ use std::env;
use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses, ToolWrapper}; use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses, ToolWrapper};
pub fn main() { pub fn main() {
let args: Vec<String> = env::args().collect(); let mut args: Vec<String> = env::args().collect();
if args.len() > 1 { if args.len() > 1 {
let mut dir = env::current_exe().unwrap(); let mut dir = env::current_exe().unwrap();
let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); let wrapper_name = dir.file_name().unwrap().to_str().unwrap();
@ -16,16 +16,25 @@ pub fn main() {
dir.pop(); dir.pop();
// Must be always present, even without --libafl
args.push("-fsanitize-coverage=trace-pc-guard,trace-cmp".into());
let mut cc = ClangWrapper::new(); let mut cc = ClangWrapper::new();
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
cc.add_pass(LLVMPasses::AutoTokens);
if let Some(code) = cc if let Some(code) = cc
.cpp(is_cpp) .cpp(is_cpp)
// silence the compiler wrapper output, needed for some configure scripts. // silence the compiler wrapper output, needed for some configure scripts.
.silence(true) .silence(true)
// add arguments only if --libafl or --libafl-no-link are present
.need_libafl_arg(true)
.parse_args(&args) .parse_args(&args)
.expect("Failed to parse the command line") .expect("Failed to parse the command line")
.add_pass(LLVMPasses::AFLCoverage) .link_staticlib(&dir, "fuzzbench")
.add_passes_arg("-ctx") // Context sensitive coverage .add_pass(LLVMPasses::CmpLogRtn)
.link_staticlib(&dir, "libfuzzer_libpng") .add_pass(LLVMPasses::Ctx)
.run() .run()
.expect("Failed to run the wrapped compiler") .expect("Failed to run the wrapped compiler")
{ {

View File

@ -0,0 +1,407 @@
//! A singlethreaded libfuzzer-like fuzzer that can auto-restart.
use mimalloc::MiMalloc;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
use core::{cell::RefCell, time::Duration};
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::{
env,
fs::{self, File, OpenOptions},
io::{self, Read, Write},
path::PathBuf,
process,
};
use clap::{Arg, Command};
use libafl::{
corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus},
events::SimpleRestartingEventManager,
executors::{inprocess::HookableInProcessExecutor, ExitKind},
feedback_or,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes},
monitors::SimpleMonitor,
mutators::{
scheduled::havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations,
StdMOptMutator, StdScheduledMutator, Tokens,
},
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
schedulers::{
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
},
stages::{
calibrate::CalibrationStage, power::StdPowerMutationalStage, StdMutationalStage,
TracingStage,
},
state::{HasCorpus, HasMetadata, StdState},
Error,
};
use libafl_bolts::{
current_nanos, current_time,
os::dup2,
ownedref::OwnedMutSlice,
rands::StdRand,
shmem::{ShMemProvider, StdShMemProvider},
tuples::{tuple_list, Merge},
AsSlice,
};
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
use libafl_targets::autotokens;
use libafl_targets::{
edges_map_mut_ptr, edges_max_num, libfuzzer_initialize, libfuzzer_test_one_input,
std_edges_map_observer, CmpLogObserver, CtxHook, EDGES_MAP_SIZE,
};
#[cfg(unix)]
use nix::{self, unistd::dup};
/// The fuzzer main (as `no_mangle` C function)
#[no_mangle]
pub extern "C" fn libafl_main() {
// Registry the metadata types used in this fuzzer
// Needed only on no_std
// unsafe { RegistryBuilder::register::<Tokens>(); }
let res = match Command::new(env!("CARGO_PKG_NAME"))
.version(env!("CARGO_PKG_VERSION"))
.author("AFLplusplus team")
.about("LibAFL-based fuzzer for Fuzzbench")
.arg(
Arg::new("out")
.short('o')
.long("output")
.help("The directory to place finds in ('corpus')"),
)
.arg(
Arg::new("in")
.short('i')
.long("input")
.help("The directory to read initial inputs from ('seeds')"),
)
.arg(
Arg::new("tokens")
.short('x')
.long("tokens")
.help("A file to read tokens from, to be used during fuzzing"),
)
.arg(
Arg::new("logfile")
.short('l')
.long("logfile")
.help("Duplicates all output to this file")
.default_value("libafl.log"),
)
.arg(
Arg::new("timeout")
.short('t')
.long("timeout")
.help("Timeout for each individual execution, in milliseconds")
.default_value("1200"),
)
.arg(Arg::new("remaining"))
.try_get_matches()
{
Ok(res) => res,
Err(err) => {
println!(
"Syntax: {}, [-x dictionary] -o corpus_dir -i seed_dir\n{:?}",
env::current_exe()
.unwrap_or_else(|_| "fuzzer".into())
.to_string_lossy(),
err,
);
return;
}
};
println!(
"Workdir: {:?}",
env::current_dir().unwrap().to_string_lossy().to_string()
);
if let Some(filenames) = res.get_many::<String>("remaining") {
let filenames: Vec<&str> = filenames.map(String::as_str).collect();
if !filenames.is_empty() {
run_testcases(&filenames);
return;
}
}
// For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir.
let mut out_dir = PathBuf::from(
res.get_one::<String>("out")
.expect("The --output parameter is missing")
.to_string(),
);
if fs::create_dir(&out_dir).is_err() {
println!("Out dir at {:?} already exists.", &out_dir);
if !out_dir.is_dir() {
println!("Out dir at {:?} is not a valid directory!", &out_dir);
return;
}
}
let mut crashes = out_dir.clone();
crashes.push("crashes");
out_dir.push("queue");
let in_dir = PathBuf::from(
res.get_one::<String>("in")
.expect("The --input parameter is missing")
.to_string(),
);
if !in_dir.is_dir() {
println!("In dir at {:?} is not a valid directory!", &in_dir);
return;
}
let tokens = res.get_one::<String>("tokens").map(PathBuf::from);
let logfile = PathBuf::from(res.get_one::<String>("logfile").unwrap().to_string());
let timeout = Duration::from_millis(
res.get_one::<String>("timeout")
.unwrap()
.to_string()
.parse()
.expect("Could not parse timeout in milliseconds"),
);
fuzz(out_dir, crashes, &in_dir, tokens, &logfile, timeout)
.expect("An error occurred while fuzzing");
}
fn run_testcases(filenames: &[&str]) {
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.
let args: Vec<String> = env::args().collect();
if libfuzzer_initialize(&args) == -1 {
println!("Warning: LLVMFuzzerInitialize failed with -1");
}
println!(
"You are not fuzzing, just executing {} testcases",
filenames.len()
);
for fname in filenames {
println!("Executing {fname}");
let mut file = File::open(fname).expect("No file found");
let mut buffer = vec![];
file.read_to_end(&mut buffer).expect("Buffer overflow");
libfuzzer_test_one_input(&buffer);
}
}
/// The actual fuzzer
#[allow(clippy::too_many_lines)]
fn fuzz(
corpus_dir: PathBuf,
objective_dir: PathBuf,
seed_dir: &PathBuf,
tokenfile: Option<PathBuf>,
logfile: &PathBuf,
timeout: Duration,
) -> Result<(), Error> {
let log = RefCell::new(OpenOptions::new().append(true).create(true).open(logfile)?);
#[cfg(unix)]
let mut stdout_cpy = unsafe {
let new_fd = dup(io::stdout().as_raw_fd())?;
File::from_raw_fd(new_fd)
};
#[cfg(unix)]
let file_null = File::open("/dev/null")?;
// 'While the monitor are state, they are usually used in the broker - which is likely never restarted
let monitor = SimpleMonitor::new(|s| {
#[cfg(unix)]
writeln!(&mut stdout_cpy, "{s}").unwrap();
#[cfg(windows)]
println!("{s}");
writeln!(log.borrow_mut(), "{:?} {s}", current_time()).unwrap();
});
// We need a shared map to store our state before a crash.
// This way, we are able to continue fuzzing afterwards.
let mut shmem_provider = StdShMemProvider::new()?;
let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider)
{
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
Ok(res) => res,
Err(err) => match err {
Error::ShuttingDown => {
return Ok(());
}
_ => {
panic!("Failed to setup the restarter: {err}");
}
},
};
// Create an observation channel using the coverage map
// We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges)
let edges_observer = HitcountsMapObserver::new(unsafe {
StdMapObserver::from_mut_slice(
"edges",
OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_SIZE),
)
});
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
let cmplog_observer = CmpLogObserver::new("cmplog", true);
let map_feedback = MaxMapFeedback::tracking(&edges_observer, true, false);
let calibration = CalibrationStage::new(&map_feedback);
// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let mut feedback = feedback_or!(
// New maximization map feedback linked to the edges observer and the feedback state
map_feedback,
// Time feedback, this one does not need a feedback state
TimeFeedback::with_observer(&time_observer)
);
// A feedback to choose if an input is a solution or not
let mut objective = CrashFeedback::new();
// If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| {
StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryOnDiskCorpus::new(corpus_dir).unwrap(),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(objective_dir).unwrap(),
// States of the feedbacks.
// The feedbacks can report the data that should persist in the State.
&mut feedback,
// Same for objective feedbacks
&mut objective,
)
.unwrap()
});
println!("Let's fuzz :)");
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.
let args: Vec<String> = env::args().collect();
if libfuzzer_initialize(&args) == -1 {
println!("Warning: LLVMFuzzerInitialize failed with -1");
}
// Setup a randomic Input2State stage
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new())));
// Setup a MOPT mutator
let mutator = StdMOptMutator::new(
&mut state,
havoc_mutations().merge(tokens_mutations()),
7,
5,
)?;
let power = StdPowerMutationalStage::new(mutator);
// A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerScheduler::new(StdWeightedScheduler::with_schedule(
&mut state,
&edges_observer,
Some(PowerSchedule::FAST),
));
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
libfuzzer_test_one_input(buf);
ExitKind::Ok
};
let mut tracing_harness = harness;
let ctx_hook = CtxHook::default();
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time
let mut executor = HookableInProcessExecutor::with_timeout_generic(
tuple_list!(ctx_hook),
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut mgr,
timeout,
)?;
// Setup a tracing stage in which we log comparisons
let tracing = TracingStage::new(
HookableInProcessExecutor::with_timeout_generic(
tuple_list!(ctx_hook),
&mut tracing_harness,
tuple_list!(cmplog_observer),
&mut fuzzer,
&mut state,
&mut mgr,
timeout * 10,
)?,
// Give it more time!
);
// The order of the stages matter!
let mut stages = tuple_list!(calibration, tracing, i2s, power);
// Read tokens
if state.metadata_map().get::<Tokens>().is_none() {
let mut toks = Tokens::default();
if let Some(tokenfile) = tokenfile {
toks.add_from_file(tokenfile)?;
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
{
toks += autotokens()?;
}
if !toks.is_empty() {
state.add_metadata(toks);
}
}
// In case the corpus is empty (on first run), reset
if state.must_load_initial_inputs() {
state
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()])
.unwrap_or_else(|_| {
println!("Failed to load initial corpus at {:?}", &seed_dir);
process::exit(0);
});
println!("We imported {} inputs from disk.", state.corpus().count());
}
// Remove target output (logs still survive)
#[cfg(unix)]
{
let null_fd = file_null.as_raw_fd();
// dup2(null_fd, io::stdout().as_raw_fd())?;
if std::env::var("LIBAFL_FUZZBENCH_DEBUG").is_err() {
dup2(null_fd, io::stderr().as_raw_fd())?;
}
}
// reopen file to make sure we're at the end
log.replace(OpenOptions::new().append(true).create(true).open(logfile)?);
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
// Never reached
Ok(())
}

View File

@ -0,0 +1,34 @@
#include <stdint.h>
__attribute__((weak)) void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,
uint32_t *stop) {
}
__attribute__((weak)) void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
}
__attribute__((weak)) void __cmplog_rtn_hook(uint8_t *ptr1, uint8_t *ptr2) {
}
__attribute__((weak)) void __cmplog_rtn_gcc_stdstring_cstring(
uint8_t *stdstring, uint8_t *cstring) {
}
__attribute__((weak)) void __cmplog_rtn_gcc_stdstring_stdstring(
uint8_t *stdstring1, uint8_t *stdstring2) {
}
__attribute__((weak)) void __cmplog_rtn_llvm_stdstring_cstring(
uint8_t *stdstring, uint8_t *cstring) {
}
__attribute__((weak)) void __cmplog_rtn_llvm_stdstring_stdstring(
uint8_t *stdstring1, uint8_t *stdstring2) {
}
extern void libafl_main(void);
int main(int argc, char **argv) {
libafl_main();
return 0;
}

View File

@ -1 +0,0 @@
libpng-*

View File

@ -1,134 +0,0 @@
# Variables
[env]
FUZZER_NAME='fuzzer_libpng_ctx'
PROJECT_DIR = { script = ["pwd"] }
CARGO_TARGET_DIR = { value = "${PROJECT_DIR}/target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } }
PROFILE = { value = "release", condition = {env_not_set = ["PROFILE"]} }
PROFILE_DIR = {value = "release", condition = {env_not_set = ["PROFILE_DIR"] }}
LIBAFL_CC = '${CARGO_TARGET_DIR}/${PROFILE_DIR}/libafl_cc'
LIBAFL_CXX = '${CARGO_TARGET_DIR}/${PROFILE}/libafl_cxx'
FUZZER = '${CARGO_TARGET_DIR}/${PROFILE_DIR}/${FUZZER_NAME}'
[tasks.unsupported]
script_runner="@shell"
script='''
echo "Cargo-make not integrated yet on this platform"
'''
# libpng
[tasks.libpng]
linux_alias = "libpng_unix"
mac_alias = "libpng_unix"
windows_alias = "unsupported"
[tasks.libpng_unix]
condition = { files_not_exist = ["./libpng-1.6.37"]}
script_runner="@shell"
script='''
wget https://github.com/glennrp/libpng/archive/refs/tags/v1.6.37.tar.gz
tar -xvf v1.6.37.tar.gz
'''
# Compilers
[tasks.cxx]
linux_alias = "cxx_unix"
mac_alias = "cxx_unix"
windows_alias = "unsupported"
[tasks.cxx_unix]
command = "cargo"
args = ["build" , "--profile", "${PROFILE}"]
[tasks.cc]
linux_alias = "cc_unix"
mac_alias = "cc_unix"
windows_alias = "unsupported"
[tasks.cc_unix]
command = "cargo"
args = ["build" , "--profile", "${PROFILE}"]
# Library
[tasks.lib]
linux_alias = "lib_unix"
mac_alias = "lib_unix"
windows_alias = "unsupported"
[tasks.lib_unix]
script_runner="@shell"
script='''
cd libpng-1.6.37 && ./configure --enable-shared=no --with-pic=yes --enable-hardware-optimizations=yes
cd "${PROJECT_DIR}"
make -C libpng-1.6.37 CC="${CARGO_TARGET_DIR}/${PROFILE_DIR}/libafl_cc" CXX="${CARGO_TARGET_DIR}/${PROFILE_DIR}/libafl_cxx"
'''
dependencies = [ "libpng", "cxx", "cc" ]
# Harness
[tasks.fuzzer]
linux_alias = "fuzzer_unix"
mac_alias = "fuzzer_unix"
windows_alias = "unsupported"
[tasks.fuzzer_unix]
command = "${CARGO_TARGET_DIR}/${PROFILE_DIR}/libafl_cxx"
args = ["${PROJECT_DIR}/harness.cc", "${PROJECT_DIR}/libpng-1.6.37/.libs/libpng16.a", "-I", "${PROJECT_DIR}/libpng-1.6.37/", "-o", "${FUZZER_NAME}", "-lm", "-lz"]
dependencies = [ "lib", "cxx", "cc" ]
# Run the fuzzer
[tasks.run]
linux_alias = "run_unix"
mac_alias = "run_unix"
windows_alias = "unsupported"
[tasks.run_unix]
script_runner = "@shell"
script='''
./${FUZZER_NAME} --cores 0 --input ./corpus
'''
dependencies = [ "fuzzer" ]
# Test
[tasks.test]
linux_alias = "test_unix"
mac_alias = "test_mac"
windows_alias = "unsupported"
[tasks.test_unix]
script_runner = "@shell"
script='''
rm -rf libafl_unix_shmem_server || true
timeout 31s ./${FUZZER_NAME} --cores 0 --input ./corpus >fuzz_stdout.log 2>/dev/null || true
if grep -qa "corpus: 30" fuzz_stdout.log; then
echo "Fuzzer is working"
elif grep -qa "objectives: 1" fuzz_stdout.log; then
echo "Fuzzer finds timeout or crash"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
fi
'''
dependencies = [ "fuzzer" ]
[tasks.test_mac]
script_runner = "@shell"
script='''
rm -rf libafl_unix_shmem_server || true
timeout 31s ./${FUZZER_NAME} --cores 0 --input ./corpus >fuzz_stdout.log 2>/dev/null || true
'''
# Clean up
[tasks.clean]
linux_alias = "clean_unix"
mac_alias = "clean_unix"
windows_alias = "unsupported"
[tasks.clean_unix]
# Disable default `clean` definition
clear = true
script_runner="@shell"
script='''
rm -f ./${FUZZER_NAME}
make -C libpng-1.6.37 clean
cargo clean
'''

View File

@ -1,47 +0,0 @@
# Libfuzzer for libpng, with launcher
This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection.
To show off crash detection, we added a `ud2` instruction to the harness, edit harness.cc if you want a non-crashing example.
It has been tested on Linux.
In contrast to the normal libfuzzer libpng example, this uses the `launcher` feature, that automatically spawns `n` child processes, and binds them to a free core.
## Build
To build this example, run
```bash
cargo build --release
```
This will build the library with the fuzzer (src/lib.rs) with the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback.
In addition, it will also build two C and C++ compiler wrappers (bin/libafl_c(libafl_c/xx).rs) that you must use to compile the target.
Then download libpng, and unpack the archive:
```bash
wget https://github.com/glennrp/libpng/archive/refs/tags/v1.6.37.tar.gz
tar -xvf v1.6.37.tar.gz
```
Now compile libpng, using the libafl_cc compiler wrapper:
```bash
cd libpng-1.6.37
./configure
make CC=../target/release/libafl_cc CXX=../target/release/libafl_cxx -j `nproc`
```
You can find the static lib at `libpng-1.6.37/.libs/libpng16.a`.
Now, we have to build the libfuzzer harness and link all together to create our fuzzer binary.
```
cd ..
./target/release/libafl_cxx ./harness.cc libpng-1.6.37/.libs/libpng16.a -I libpng-1.6.37/ -o fuzzer_libpng -lz -lm
```
Afterwards, the fuzzer will be ready to run.
## Run
Just run once, the launcher feature should do the rest.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 427 B

View File

@ -1,188 +0,0 @@
// libpng_read_fuzzer.cc
// Copyright 2017-2018 Glenn Randers-Pehrson
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that may
// be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE
// Last changed in libpng 1.6.35 [July 15, 2018]
// The modifications in 2017 by Glenn Randers-Pehrson include
// 1. addition of a PNG_CLEANUP macro,
// 2. setting the option to ignore ADLER32 checksums,
// 3. adding "#include <string.h>" which is needed on some platforms
// to provide memcpy().
// 4. adding read_end_info() and creating an end_info structure.
// 5. adding calls to png_set_*() transforms commonly used by browsers.
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <vector>
#define PNG_INTERNAL
#include "png.h"
#define PNG_CLEANUP \
if (png_handler.png_ptr) { \
if (png_handler.row_ptr) \
png_free(png_handler.png_ptr, png_handler.row_ptr); \
if (png_handler.end_info_ptr) \
png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr, \
&png_handler.end_info_ptr); \
else if (png_handler.info_ptr) \
png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr, \
nullptr); \
else \
png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \
png_handler.png_ptr = nullptr; \
png_handler.row_ptr = nullptr; \
png_handler.info_ptr = nullptr; \
png_handler.end_info_ptr = nullptr; \
}
struct BufState {
const uint8_t *data;
size_t bytes_left;
};
struct PngObjectHandler {
png_infop info_ptr = nullptr;
png_structp png_ptr = nullptr;
png_infop end_info_ptr = nullptr;
png_voidp row_ptr = nullptr;
BufState *buf_state = nullptr;
~PngObjectHandler() {
if (row_ptr) { png_free(png_ptr, row_ptr); }
if (end_info_ptr)
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr);
else if (info_ptr)
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
else
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
delete buf_state;
}
};
void user_read_data(png_structp png_ptr, png_bytep data, size_t length) {
BufState *buf_state = static_cast<BufState *>(png_get_io_ptr(png_ptr));
if (length > buf_state->bytes_left) { png_error(png_ptr, "read error"); }
memcpy(data, buf_state->data, length);
buf_state->bytes_left -= length;
buf_state->data += length;
}
static const int kPngHeaderSize = 8;
// Entry point for LibFuzzer.
// Roughly follows the libpng book example:
// http://www.libpng.org/pub/png/book/chapter13.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size < kPngHeaderSize) { return 0; }
std::vector<unsigned char> v(data, data + size);
if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) {
// not a PNG.
return 0;
}
PngObjectHandler png_handler;
png_handler.png_ptr = nullptr;
png_handler.row_ptr = nullptr;
png_handler.info_ptr = nullptr;
png_handler.end_info_ptr = nullptr;
png_handler.png_ptr =
png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!png_handler.png_ptr) { return 0; }
png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr);
if (!png_handler.info_ptr) {
PNG_CLEANUP
return 0;
}
png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr);
if (!png_handler.end_info_ptr) {
PNG_CLEANUP
return 0;
}
png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
#ifdef PNG_IGNORE_ADLER32
png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
#endif
// Setting up reading from buffer.
png_handler.buf_state = new BufState();
png_handler.buf_state->data = data + kPngHeaderSize;
png_handler.buf_state->bytes_left = size - kPngHeaderSize;
png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data);
png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize);
if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
PNG_CLEANUP
return 0;
}
// Reading.
png_read_info(png_handler.png_ptr, png_handler.info_ptr);
// reset error handler to put png_deleter into scope.
if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
PNG_CLEANUP
return 0;
}
png_uint_32 width, height;
int bit_depth, color_type, interlace_type, compression_type;
int filter_type;
if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width, &height,
&bit_depth, &color_type, &interlace_type, &compression_type,
&filter_type)) {
PNG_CLEANUP
return 0;
}
// This is going to be too slow.
if (width && height > 100000000 / width) {
PNG_CLEANUP
#ifdef HAS_DUMMY_CRASH
#ifdef __aarch64__
asm volatile(".word 0xf7f0a000\n");
#else
asm("ud2");
#endif
#endif
return 0;
}
// Set several transforms that browsers typically use:
png_set_gray_to_rgb(png_handler.png_ptr);
png_set_expand(png_handler.png_ptr);
png_set_packing(png_handler.png_ptr);
png_set_scale_16(png_handler.png_ptr);
png_set_tRNS_to_alpha(png_handler.png_ptr);
int passes = png_set_interlace_handling(png_handler.png_ptr);
png_read_update_info(png_handler.png_ptr, png_handler.info_ptr);
png_handler.row_ptr =
png_malloc(png_handler.png_ptr,
png_get_rowbytes(png_handler.png_ptr, png_handler.info_ptr));
for (int pass = 0; pass < passes; ++pass) {
for (png_uint_32 y = 0; y < height; ++y) {
png_read_row(png_handler.png_ptr,
static_cast<png_bytep>(png_handler.row_ptr), nullptr);
}
}
png_read_end(png_handler.png_ptr, png_handler.end_info_ptr);
PNG_CLEANUP
return 0;
}

View File

@ -1,243 +0,0 @@
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
//! The example harness is built for libpng.
//! In this example, you will see the use of the `launcher` feature.
//! The `launcher` will spawn new processes for each cpu core.
use mimalloc::MiMalloc;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
use core::time::Duration;
use std::{env, net::SocketAddr, path::PathBuf};
use clap::{self, Parser};
use libafl::{
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
events::{launcher::Launcher, EventConfig},
executors::{inprocess::InProcessExecutor, ExitKind},
feedback_or, feedback_or_fast,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes},
monitors::MultiMonitor,
mutators::{
scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator},
token_mutations::Tokens,
},
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, StdState},
Error,
};
use libafl_bolts::{
core_affinity::Cores,
current_nanos,
rands::StdRand,
shmem::{ShMemProvider, StdShMemProvider},
tuples::{tuple_list, Merge},
AsSlice,
};
use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, std_edges_map_observer};
fn timeout_from_millis_str(time: &str) -> Result<Duration, Error> {
Ok(Duration::from_millis(time.parse()?))
}
#[derive(Debug, Parser)]
#[command(
name = "libfuzzer_libpng_ctx",
about = "A clone of libfuzzer using LibAFL for a libpng harness",
author = "Andrea Fioraldi <andreafioraldi@gmail.com>, Dominik Maier <domenukk@gmail.com>"
)]
struct Opt {
#[arg(
short,
long,
value_parser = Cores::from_cmdline,
help = "Spawn a client in each of the provided cores. Broker runs in the 0th core. 'all' to select all available cores. 'none' to run a client without binding to any core. eg: '1,2-4,6' selects the cores 1,2,3,4,6.",
name = "CORES"
)]
cores: Cores,
#[arg(
short = 'p',
long,
help = "Choose the broker TCP port, default is 1337",
name = "PORT",
default_value = "1337"
)]
broker_port: u16,
#[arg(short = 'a', long, help = "Specify a remote broker", name = "REMOTE")]
remote_broker_addr: Option<SocketAddr>,
#[arg(short, long, help = "Set an initial corpus directory", name = "INPUT")]
input: Vec<PathBuf>,
#[arg(
short,
long,
help = "Set the output directory, default is ./out",
name = "OUTPUT",
default_value = "./out"
)]
output: PathBuf,
#[arg(
short,
long,
value_parser = timeout_from_millis_str,
help = "Set the exeucution timeout in milliseconds, default is 10000",
name = "TIMEOUT",
default_value = "10000",
)]
timeout: Duration,
/*
// The tokens are hardcoded in this example.
#[arg(
short = "x",
long,
help = "Feed the fuzzer with an user-specified list of tokens (often called \"dictionary\"",
name = "TOKENS",
multiple = true
)]
tokens: Vec<PathBuf>,*/
}
/// The main fn, `no_mangle` as it is a C symbol
#[no_mangle]
pub extern "C" fn libafl_main() {
// Registry the metadata types used in this fuzzer
// Needed only on no_std
// unsafe { RegistryBuilder::register::<Tokens>(); }
let opt = Opt::parse();
let broker_port = opt.broker_port;
let cores = opt.cores;
println!(
"Workdir: {:?}",
env::current_dir().unwrap().to_string_lossy().to_string()
);
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
let monitor = MultiMonitor::new(|s| println!("{s}"));
let mut run_client = |state: Option<_>, mut restarting_mgr, _core_id| {
// Create an observation channel using the coverage map
let edges_observer = unsafe { HitcountsMapObserver::new(std_edges_map_observer("edges")) };
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let mut feedback = feedback_or!(
// New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::tracking(&edges_observer, true, false),
// Time feedback, this one does not need a feedback state
TimeFeedback::with_observer(&time_observer)
);
// A feedback to choose if an input is a solution or not
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
// If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| {
StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(opt.output.clone()).unwrap(),
// States of the feedbacks.
// The feedbacks can report the data that should persist in the State.
&mut feedback,
// Same for objective feedbacks
&mut objective,
)
.unwrap()
});
println!("We're a client, let's fuzz :)");
// Create a PNG dictionary if not existing
if state.metadata_map().get::<Tokens>().is_none() {
state.add_metadata(Tokens::from([
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
"IHDR".as_bytes().to_vec(),
"IDAT".as_bytes().to_vec(),
"PLTE".as_bytes().to_vec(),
"IEND".as_bytes().to_vec(),
]));
}
// Setup a basic mutator with a mutational stage
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
// A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new());
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
libfuzzer_test_one_input(buf);
ExitKind::Ok
};
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time
let mut executor = InProcessExecutor::with_timeout(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut restarting_mgr,
opt.timeout,
)?;
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.
let args: Vec<String> = env::args().collect();
if libfuzzer_initialize(&args) == -1 {
println!("Warning: LLVMFuzzerInitialize failed with -1");
}
// In case the corpus is empty (on first run), reset
if state.must_load_initial_inputs() {
state
.load_initial_inputs(&mut fuzzer, &mut executor, &mut restarting_mgr, &opt.input)
.unwrap_or_else(|_| panic!("Failed to load initial corpus at {:?}", &opt.input));
println!("We imported {} inputs from disk.", state.corpus().count());
}
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?;
Ok(())
};
match Launcher::builder()
.shmem_provider(shmem_provider)
.configuration(EventConfig::from_name("default"))
.monitor(monitor)
.run_client(&mut run_client)
.cores(&cores)
.broker_port(broker_port)
.remote_broker_addr(opt.remote_broker_addr)
.stdout_file(Some("/dev/null"))
.build()
.launch()
{
Ok(()) => (),
Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."),
Err(err) => panic!("Failed to run launcher: {err:?}"),
}
}

View File

@ -44,6 +44,9 @@ use crate::{
/// The process executor simply calls a target function, as mutable reference to a closure /// The process executor simply calls a target function, as mutable reference to a closure
pub type InProcessExecutor<'a, H, OT, S> = GenericInProcessExecutor<H, &'a mut H, (), OT, S>; pub type InProcessExecutor<'a, H, OT, S> = GenericInProcessExecutor<H, &'a mut H, (), OT, S>;
/// The inprocess executor that allows hooks
pub type HookableInProcessExecutor<'a, H, HT, OT, S> =
GenericInProcessExecutor<H, &'a mut H, HT, OT, S>;
/// The process executor simply calls a target function, as boxed `FnMut` trait object /// The process executor simply calls a target function, as boxed `FnMut` trait object
pub type OwnedInProcessExecutor<OT, S> = GenericInProcessExecutor< pub type OwnedInProcessExecutor<OT, S> = GenericInProcessExecutor<
dyn FnMut(&<S as UsesInput>::Input) -> ExitKind, dyn FnMut(&<S as UsesInput>::Input) -> ExitKind,

View File

@ -254,6 +254,7 @@ where
.ok_or_else(|| Error::key_not_found("MapObserver not found".to_string()))?; .ok_or_else(|| Error::key_not_found("MapObserver not found".to_string()))?;
let mut bitmap_size = map.count_bytes(); let mut bitmap_size = map.count_bytes();
assert!(bitmap_size != 0);
bitmap_size = bitmap_size.max(1); // just don't make it 0 because we take log2 of it later. bitmap_size = bitmap_size.max(1); // just don't make it 0 because we take log2 of it later.
let psmeta = state let psmeta = state
.metadata_map_mut() .metadata_map_mut()

View File

@ -404,10 +404,10 @@ pub const LIBAFL_CC_LLVM_VERSION: Option<usize> = None;
for pass in &[ for pass in &[
"cmplog-routines-pass.cc", "cmplog-routines-pass.cc",
"afl-coverage-pass.cc",
"autotokens-pass.cc", "autotokens-pass.cc",
"coverage-accounting-pass.cc", "coverage-accounting-pass.cc",
"cmplog-instructions-pass.cc", "cmplog-instructions-pass.cc",
"ctx-pass.cc",
] { ] {
build_pass( build_pass(
bindir_path, bindir_path,

View File

@ -1,872 +0,0 @@
/*
american fuzzy lop++ - LLVM-mode instrumentation pass
---------------------------------------------------
Written by Laszlo Szekeres <lszekeres@google.com>,
Adrian Herrera <adrian.herrera@anu.edu.au>,
Michal Zalewski
LLVM integration design comes from Laszlo Szekeres. C bits copied-and-pasted
from afl-as.c are Michal's fault.
NGRAM previous location coverage comes from Adrian Herrera.
Copyright 2015, 2016 Google Inc. All rights reserved.
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
This library is plugged into LLVM when invoking clang through afl-clang-fast.
It tells the compiler to add code roughly equivalent to the bits discussed
in ../afl-as.h.
*/
#include "common-llvm.h"
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef _WIN32
#include <unistd.h>
#include <sys/time.h>
#else
#include <io.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <list>
#include <string>
#include <fstream>
#include "llvm/Support/CommandLine.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/FormatVariadic.h"
// Without this, Can't build with llvm-14 & old PM
#if LLVM_VERSION_MAJOR >= 14 && !defined(USE_NEW_PM)
#include "llvm/Pass.h"
#endif
#if LLVM_VERSION_MAJOR > 3 || \
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/CFG.h"
#else
#include "llvm/DebugInfo.h"
#include "llvm/Support/CFG.h"
#endif
typedef uint32_t prev_loc_t;
/* Maximum ngram size */
#define NGRAM_SIZE_MAX 16U
/* Maximum K for top-K context sensitivity */
#define CTX_MAX_K 32U
#define MAP_SIZE LIBAFL_EDGES_MAP_SIZE
using namespace llvm;
static cl::opt<bool> Debug("debug_afl_coverage", cl::desc("Debug prints"),
cl::init(false), cl::NotHidden);
static cl::opt<uint32_t> InstRatio(
"inst_ratio_afl_coverage", cl::desc("Instrumentation ratio in percentage"),
cl::init(100), cl::NotHidden);
static cl::opt<bool> NotZero("not_zero",
cl::desc("Never hit 0 again in the hitcount"),
cl::init(true), cl::NotHidden);
static cl::opt<uint32_t> Ngram(
"ngram", cl::desc("Size of the Ngram instrumentation (0 to disable)"),
cl::init(0), cl::NotHidden);
static cl::opt<uint32_t> CtxK(
"ctx_k",
cl::desc(
"Size of the context for K-Ctx context sensitivity (0 to disable)"),
cl::init(0), cl::NotHidden);
static cl::opt<bool> Ctx("ctx",
cl::desc("Enable full context sensitive coverage"),
cl::init(false), cl::NotHidden);
static cl::opt<bool> ThreadSafe("thread_safe_afl_coverage",
cl::desc("Use the thread safe instrumentation"),
cl::init(false), cl::NotHidden);
static cl::opt<bool> DumpCFG(
"dump_afl_cfg", cl::desc("Dump CFG containing AFL-style edge index"),
cl::init(false), cl::NotHidden);
static cl::opt<std::string> DumpCFGPath(
"dump_afl_cfg_path",
cl::desc("Path to dump CFG containing AFL-style edge index"),
cl::init(".cfg"), cl::NotHidden);
namespace {
#ifdef USE_NEW_PM
class AFLCoverage : public PassInfoMixin<AFLCoverage> {
public:
AFLCoverage() {
#else
class AFLCoverage : public ModulePass {
public:
static char ID;
AFLCoverage() : ModulePass(ID) {
#endif
// initInstrumentList();
}
#ifdef USE_NEW_PM
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
#else
bool runOnModule(Module &M) override;
#endif
protected:
uint32_t map_size = MAP_SIZE;
uint32_t function_minimum_size = 1;
DenseMap<BasicBlock *, int32_t> bb_to_cur_loc;
DenseMap<StringRef, BasicBlock *> entry_bb;
};
} // namespace
#ifdef USE_NEW_PM
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
return {LLVM_PLUGIN_API_VERSION, "AFLCoverage", "v0.1",
/* lambda to insert our pass into the pass pipeline. */
[](PassBuilder &PB) {
#if 1
#if LLVM_VERSION_MAJOR <= 13
using OptimizationLevel = typename PassBuilder::OptimizationLevel;
#endif
PB.registerOptimizerLastEPCallback(
[](ModulePassManager &MPM, OptimizationLevel OL) {
MPM.addPass(AFLCoverage());
});
/* TODO LTO registration */
#else
using PipelineElement = typename PassBuilder::PipelineElement;
PB.registerPipelineParsingCallback([](StringRef Name,
ModulePassManager &MPM,
ArrayRef<PipelineElement>) {
if (Name == "AFLCoverage") {
MPM.addPass(AFLCoverage());
return true;
} else {
return false;
}
});
#endif
}};
}
#else
char AFLCoverage::ID = 0;
#endif
#ifdef USE_NEW_PM
PreservedAnalyses AFLCoverage::run(Module &M, ModuleAnalysisManager &MAM) {
#else
bool AFLCoverage::runOnModule(Module &M) {
#endif
if (Ctx && DumpCFG) {
FATAL(
"Does not support dumping CFG with full context sensitive coverage "
"enabled.");
}
LLVMContext &C = M.getContext();
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
#ifdef HAVE_VECTOR_INTRINSICS
IntegerType *IntLocTy =
IntegerType::getIntNTy(C, sizeof(prev_loc_t) * CHAR_BIT);
#endif
uint32_t rand_seed;
unsigned int cur_loc = 0;
#ifdef USE_NEW_PM
auto PA = PreservedAnalyses::all();
#endif
/* Setup random() so we get Actually Random(TM) */
rand_seed = time(NULL);
srand(rand_seed);
/*
char *ptr;
if ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE"))) {
map_size = atoi(ptr);
if (map_size < 8 || map_size > (1 << 29)) {
FATAL("illegal AFL_MAP_SIZE %u, must be between 2^3 and 2^30",
}
map_size); if (map_size % 8) {map_size = (((map_size >> 3) + 1) << 3);}
}
*/
/* Decide instrumentation ratio */
if (!InstRatio || InstRatio > 100) {
FATAL("Bad value of the instrumentation ratio (must be between 1 and 100)");
}
unsigned PrevLocSize = 0;
unsigned PrevCallerSize = 0;
bool instrument_ctx = Ctx || CtxK > 0;
bool instrument_caller = false;
#ifdef HAVE_VECTOR_INTRINSICS
/* Decide previous location vector size (must be a power of two) */
VectorType *PrevLocTy = NULL;
if (Ngram && (Ngram < 2 || Ngram > NGRAM_SIZE_MAX)) {
FATAL(
"Bad value of the Ngram size (must be between 2 and NGRAM_SIZE_MAX "
"(%u))",
NGRAM_SIZE_MAX);
}
if (Ngram) {
PrevLocSize = Ngram - 1;
} else {
PrevLocSize = 1;
}
/* Decide K-Ctx vector size (must be a power of two) */
VectorType *PrevCallerTy = NULL;
if (CtxK > CTX_MAX_K) {
FATAL(
"Bad value of K for K-context sensitivity (must be between 1 and "
"CTX_MAX_K (%u))",
CTX_MAX_K);
}
if (CtxK == 1) {
CtxK = 0;
instrument_ctx = true;
instrument_caller = true; // Enable CALLER instead
}
if (CtxK) {
PrevCallerSize = CtxK;
instrument_ctx = true;
}
#else
if (Ngram)
#ifndef LLVM_VERSION_PATCH
FATAL(
"Sorry, NGRAM branch coverage is not supported with llvm version "
"%d.%d.%d!",
LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, 0);
#else
FATAL(
"Sorry, NGRAM branch coverage is not supported with llvm version "
"%d.%d.%d!",
LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH);
#endif
if (CtxK)
#ifndef LLVM_VERSION_PATCH
FATAL(
"Sorry, K-CTX branch coverage is not supported with llvm version "
"%d.%d.%d!",
LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, 0);
#else
FATAL(
"Sorry, K-CTX branch coverage is not supported with llvm version "
"%d.%d.%d!",
LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH);
#endif
PrevLocSize = 1;
#endif
#ifdef HAVE_VECTOR_INTRINSICS
int PrevLocVecSize = PowerOf2Ceil(PrevLocSize);
if (Ngram)
PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize
#if LLVM_VERSION_MAJOR >= 12
,
false
#endif
);
#endif
#ifdef HAVE_VECTOR_INTRINSICS
int PrevCallerVecSize = PowerOf2Ceil(PrevCallerSize);
if (CtxK)
PrevCallerTy = VectorType::get(IntLocTy, PrevCallerVecSize
#if LLVM_VERSION_MAJOR >= 12
,
false
#endif
);
#endif
/* Get globals for the SHM region and the previous location. Note that
__afl_prev_loc is thread-local. */
GlobalVariable *AFLMapPtr =
new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
GlobalVariable *AFLPrevLoc;
GlobalVariable *AFLPrevCaller;
GlobalVariable *AFLContext = NULL;
if (Ctx || instrument_caller)
#if defined(__ANDROID__) || defined(__HAIKU__)
AFLContext = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx");
#else
AFLContext = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx", 0,
GlobalVariable::GeneralDynamicTLSModel, 0, false);
#endif
#ifdef HAVE_VECTOR_INTRINSICS
if (Ngram)
#if defined(__ANDROID__) || defined(__HAIKU__)
AFLPrevLoc = new GlobalVariable(
M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
/* Initializer */ nullptr, "__afl_prev_loc");
#else
AFLPrevLoc = new GlobalVariable(
M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
/* Initializer */ nullptr, "__afl_prev_loc",
/* InsertBefore */ nullptr, GlobalVariable::GeneralDynamicTLSModel,
/* AddressSpace */ 0, /* IsExternallyInitialized */ false);
#endif
else
#endif
#if defined(__ANDROID__) || defined(__HAIKU__)
AFLPrevLoc = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc");
#else
AFLPrevLoc = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", 0,
GlobalVariable::GeneralDynamicTLSModel, 0, false);
#endif
#ifdef HAVE_VECTOR_INTRINSICS
if (CtxK)
#if defined(__ANDROID__) || defined(__HAIKU__)
AFLPrevCaller = new GlobalVariable(
M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
/* Initializer */ nullptr, "__afl_prev_caller");
#else
AFLPrevCaller = new GlobalVariable(
M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
/* Initializer */ nullptr, "__afl_prev_caller",
/* InsertBefore */ nullptr, GlobalVariable::GeneralDynamicTLSModel,
/* AddressSpace */ 0, /* IsExternallyInitialized */ false);
#endif
else
#endif
#if defined(__ANDROID__) || defined(__HAIKU__)
AFLPrevCaller =
new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, 0,
"__afl_prev_caller");
#else
AFLPrevCaller = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_caller",
0, GlobalVariable::GeneralDynamicTLSModel, 0, false);
#endif
#ifdef HAVE_VECTOR_INTRINSICS
/* Create the vector shuffle mask for updating the previous block history.
Note that the first element of the vector will store cur_loc, so just set
it to undef to allow the optimizer to do its thing. */
SmallVector<Constant *, 32> PrevLocShuffle = {UndefValue::get(Int32Ty)};
for (unsigned I = 0; I < PrevLocSize - 1; ++I) {
PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, I));
}
for (int I = PrevLocSize; I < PrevLocVecSize; ++I) {
PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, PrevLocSize));
}
Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle);
Constant *PrevCallerShuffleMask = NULL;
SmallVector<Constant *, 32> PrevCallerShuffle = {UndefValue::get(Int32Ty)};
if (CtxK) {
for (unsigned I = 0; I < PrevCallerSize - 1; ++I) {
PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, I));
}
for (int I = PrevCallerSize; I < PrevCallerVecSize; ++I) {
PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, PrevCallerSize));
}
PrevCallerShuffleMask = ConstantVector::get(PrevCallerShuffle);
}
#endif
// other constants we need
ConstantInt *One = ConstantInt::get(Int8Ty, 1);
Value *PrevCtx = NULL; // CTX sensitive coverage
LoadInst *PrevCaller = NULL; // K-CTX coverage
/* Instrument all the things! */
int inst_blocks = 0;
// scanForDangerousFunctions(&M);
for (auto &F : M) {
int has_calls = 0;
if (Debug)
fprintf(stderr, "FUNCTION: %s (%zu)\n", F.getName().str().c_str(),
F.size());
if (isIgnoreFunction(&F)) { continue; }
if (F.size() < function_minimum_size) { continue; }
if (DumpCFG) { entry_bb[F.getName()] = &F.getEntryBlock(); }
std::list<Value *> todo;
for (auto &BB : F) {
BasicBlock::iterator IP = BB.getFirstInsertionPt();
IRBuilder<> IRB(&(*IP));
// Context sensitive coverage
if (instrument_ctx && &BB == &F.getEntryBlock()) {
#ifdef HAVE_VECTOR_INTRINSICS
if (CtxK) {
PrevCaller = IRB.CreateLoad(
#if LLVM_VERSION_MAJOR >= 14
PrevCallerTy,
#endif
AFLPrevCaller);
PrevCaller->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
PrevCtx =
IRB.CreateZExt(IRB.CreateXorReduce(PrevCaller), IRB.getInt32Ty());
} else
#endif
{
// load the context ID of the previous function and write to to a
// local variable on the stack
LoadInst *PrevCtxLoad = IRB.CreateLoad(
#if LLVM_VERSION_MAJOR >= 14
IRB.getInt32Ty(),
#endif
AFLContext);
PrevCtxLoad->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
PrevCtx = PrevCtxLoad;
}
// does the function have calls? and is any of the calls larger than one
// basic block?
for (auto &BB_2 : F) {
if (has_calls) { break; }
for (auto &IN : BB_2) {
CallInst *callInst = nullptr;
if ((callInst = dyn_cast<CallInst>(&IN))) {
Function *Callee = callInst->getCalledFunction();
if (!Callee || Callee->size() < function_minimum_size)
continue;
else {
has_calls = 1;
break;
}
}
}
}
// if yes we store a context ID for this function in the global var
if (has_calls) {
Value *NewCtx = ConstantInt::get(Int32Ty, RandBelow(map_size));
#ifdef HAVE_VECTOR_INTRINSICS
if (CtxK) {
Value *ShuffledPrevCaller = IRB.CreateShuffleVector(
PrevCaller, UndefValue::get(PrevCallerTy),
PrevCallerShuffleMask);
Value *UpdatedPrevCaller = IRB.CreateInsertElement(
ShuffledPrevCaller, NewCtx, (uint64_t)0);
StoreInst *Store =
IRB.CreateStore(UpdatedPrevCaller, AFLPrevCaller);
Store->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
} else
#endif
{
if (Ctx) { NewCtx = IRB.CreateXor(PrevCtx, NewCtx); }
StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext);
StoreCtx->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
}
}
}
if (RandBelow(100) >= InstRatio) { continue; }
/* Make up cur_loc */
// cur_loc++;
cur_loc = RandBelow(map_size);
if (DumpCFG) { bb_to_cur_loc[&BB] = cur_loc; }
/* There is a problem with Ubuntu 18.04 and llvm 6.0 (see issue #63).
The inline function successors() is not inlined and also not found at runtime
:-( As I am unable to detect Ubuntu18.04 heree, the next best thing is to
disable this optional optimization for LLVM 6.0.0 and Linux */
#if !(LLVM_VERSION_MAJOR == 6 && LLVM_VERSION_MINOR == 0) || !defined __linux__
// only instrument if this basic block is the destination of a previous
// basic block that has multiple successors
// this gets rid of ~5-10% of instrumentations that are unnecessary
// result: a little more speed and less map pollution
int more_than_one = -1;
// fprintf(stderr, "BB %u: ", cur_loc);
for (pred_iterator PI = pred_begin(&BB), E = pred_end(&BB); PI != E;
++PI) {
BasicBlock *Pred = *PI;
int count = 0;
if (more_than_one == -1) { more_than_one = 0; }
// fprintf(stderr, " %p=>", Pred);
for (succ_iterator SI = succ_begin(Pred), E = succ_end(Pred); SI != E;
++SI) {
BasicBlock *Succ = *SI;
// if (count > 0)
// fprintf(stderr, "|");
if (Succ != NULL) { count++; }
// fprintf(stderr, "%p", Succ);
}
if (count > 1) { more_than_one = 1; }
}
// fprintf(stderr, " == %d\n", more_than_one);
if (F.size() > 1 && more_than_one != 1) {
// in CTX mode we have to restore the original context for the caller -
// she might be calling other functions which need the correct CTX
if (instrument_ctx && has_calls) {
Instruction *Inst = BB.getTerminator();
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
IRBuilder<> Post_IRB(Inst);
StoreInst *RestoreCtx;
#ifdef HAVE_VECTOR_INTRINSICS
if (CtxK)
RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller);
else
#endif
RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext);
RestoreCtx->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
}
}
continue;
}
#endif
ConstantInt *CurLoc;
#ifdef HAVE_VECTOR_INTRINSICS
if (Ngram)
CurLoc = ConstantInt::get(IntLocTy, cur_loc);
else
#endif
CurLoc = ConstantInt::get(Int32Ty, cur_loc);
/* Load prev_loc */
LoadInst *PrevLoc;
if (Ngram) {
PrevLoc = IRB.CreateLoad(
#if LLVM_VERSION_MAJOR >= 14
PrevLocTy,
#endif
AFLPrevLoc);
} else {
PrevLoc = IRB.CreateLoad(
#if LLVM_VERSION_MAJOR >= 14
IRB.getInt32Ty(),
#endif
AFLPrevLoc);
}
PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
Value *PrevLocTrans;
#ifdef HAVE_VECTOR_INTRINSICS
/* "For efficiency, we propose to hash the tuple as a key into the
hit_count map as (prev_block_trans << 1) ^ curr_block_trans, where
prev_block_trans = (block_trans_1 ^ ... ^ block_trans_(n-1)" */
if (Ngram)
PrevLocTrans =
IRB.CreateZExt(IRB.CreateXorReduce(PrevLoc), IRB.getInt32Ty());
else
#endif
PrevLocTrans = PrevLoc;
if (instrument_ctx)
PrevLocTrans =
IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty);
else
PrevLocTrans = IRB.CreateZExt(PrevLocTrans, IRB.getInt32Ty());
/* Load SHM pointer */
LoadInst *MapPtr = IRB.CreateLoad(
#if LLVM_VERSION_MAJOR >= 14
PointerType::get(Int8Ty, 0),
#endif
AFLMapPtr);
MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
Value *MapPtrIdx;
#ifdef HAVE_VECTOR_INTRINSICS
if (Ngram)
MapPtrIdx = IRB.CreateGEP(
#if LLVM_VERSION_MAJOR >= 14
Int8Ty,
#endif
MapPtr,
IRB.CreateZExt(
IRB.CreateXor(PrevLocTrans, IRB.CreateZExt(CurLoc, Int32Ty)),
Int32Ty));
else
#endif
MapPtrIdx = IRB.CreateGEP(
#if LLVM_VERSION_MAJOR >= 14
Int8Ty,
#endif
MapPtr, IRB.CreateXor(PrevLocTrans, CurLoc));
/* Update bitmap */
if (ThreadSafe) { /* Atomic */
/*
#if LLVM_VERSION_MAJOR < 9
if (neverZero_counters_str !=
NULL) { // with llvm 9 we make this the default as the bug
in llvm
// is then fixed
#else
if (NotZero) {
#endif
// register MapPtrIdx in a todo list
todo.push_back(MapPtrIdx);
} else {
*/
IRB.CreateAtomicRMW(llvm::AtomicRMWInst::BinOp::Add, MapPtrIdx, One,
#if LLVM_VERSION_MAJOR >= 13
llvm::MaybeAlign(1),
#endif
llvm::AtomicOrdering::Monotonic);
/*
}
*/
} else {
LoadInst *Counter = IRB.CreateLoad(
#if LLVM_VERSION_MAJOR >= 14
IRB.getInt8Ty(),
#endif
MapPtrIdx);
Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
Value *Incr = IRB.CreateAdd(Counter, One);
#if LLVM_VERSION_MAJOR < 9
if (neverZero_counters_str !=
NULL) { // with llvm 9 we make this the default as the bug in llvm
// is then fixed
#else
if (NotZero) {
#endif
/* hexcoder: Realize a counter that skips zero during overflow.
* Once this counter reaches its maximum value, it next increments to
* 1
*
* Instead of
* Counter + 1 -> Counter
* we inject now this
* Counter + 1 -> {Counter, OverflowFlag}
* Counter + OverflowFlag -> Counter
*/
ConstantInt *Zero = ConstantInt::get(Int8Ty, 0);
auto cf = IRB.CreateICmpEQ(Incr, Zero);
auto carry = IRB.CreateZExt(cf, Int8Ty);
Incr = IRB.CreateAdd(Incr, carry);
}
IRB.CreateStore(Incr, MapPtrIdx)
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
} /* non atomic case */
/* Update prev_loc history vector (by placing cur_loc at the head of the
vector and shuffle the other elements back by one) */
StoreInst *Store;
#ifdef HAVE_VECTOR_INTRINSICS
if (Ngram) {
Value *ShuffledPrevLoc = IRB.CreateShuffleVector(
PrevLoc, UndefValue::get(PrevLocTy), PrevLocShuffleMask);
Value *UpdatedPrevLoc = IRB.CreateInsertElement(
ShuffledPrevLoc, IRB.CreateLShr(CurLoc, (uint64_t)1), (uint64_t)0);
Store = IRB.CreateStore(UpdatedPrevLoc, AFLPrevLoc);
Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
} else
#endif
{
Store = IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1),
AFLPrevLoc);
Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
}
// in CTX mode we have to restore the original context for the caller -
// she might be calling other functions which need the correct CTX.
// Currently this is only needed for the Ubuntu clang-6.0 bug
if (instrument_ctx && has_calls) {
Instruction *Inst = BB.getTerminator();
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
IRBuilder<> Post_IRB(Inst);
StoreInst *RestoreCtx;
#ifdef HAVE_VECTOR_INTRINSICS
if (CtxK)
RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller);
else
#endif
RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext);
RestoreCtx->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
}
}
inst_blocks++;
}
}
if (DumpCFG) {
int fd;
#ifndef _WIN32
if ((fd = open(DumpCFGPath.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644)) <
0)
#else
if ((fd = _open(DumpCFGPath.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644)) <
0)
#endif
FATAL("Could not open/create CFG dump file.");
std::string cfg = "";
for (auto record = entry_bb.begin(); record != entry_bb.end(); record++) {
// Dump function BB entry points
cfg += formatv("$${0}+{1}\n", record->getFirst(),
bb_to_cur_loc[record->getSecond()]);
}
for (auto record = bb_to_cur_loc.begin(); record != bb_to_cur_loc.end();
record++) {
// Dump CFG information
auto current_bb = record->getFirst();
Function *calling_func = current_bb->getParent();
if (calling_func) {
auto function_name = calling_func->getName().str();
cfg += formatv("%%{0}", function_name);
} else
cfg += "%%__";
auto current_cur_loc = record->getSecond();
cfg += formatv("+{0}\n", current_cur_loc);
for (auto bb_successor = succ_begin(current_bb);
bb_successor != succ_end(current_bb); bb_successor++) {
cfg += formatv("->{0}\n", bb_to_cur_loc[*bb_successor]).str();
}
}
if (Debug) { errs() << "CFG: \n" << cfg; }
if (cfg.size() > 0 && write(fd, cfg.c_str(), cfg.length()) <= 0)
FATAL("Failed to dump CFG.\n");
}
/* Say something nice. */
/*if (!be_quiet) {
if (!inst_blocks)
WARNF("No instrumentation targets found.");
else {
char modeline[100];
snprintf(modeline, sizeof(modeline), "%s%s%s%s%s",
getenv("AFL_HARDEN") ? "hardened" : "non-hardened",
getenv("AFL_USE_ASAN") ? ", ASAN" : "",
getenv("AFL_USE_MSAN") ? ", MSAN" : "",
getenv("AFL_USE_CFISAN") ? ", CFISAN" : "",
getenv("AFL_USE_UBSAN") ? ", UBSAN" : "");
OKF("Instrumented %d locations (%s mode, ratio %u%%).", inst_blocks,
modeline, InstRatio);
}
}*/
if (Debug) {
if (!inst_blocks)
fprintf(stderr, "No instrumentation targets found.\n");
else
fprintf(stderr, "Instrumented %d locations (ratio %u%%).\n", inst_blocks,
(unsigned)InstRatio);
}
#ifdef USE_NEW_PM
return PA;
#else
return true;
#endif
}
#ifndef USE_NEW_PM
static void registerAFLPass(const PassManagerBuilder &,
legacy::PassManagerBase &PM) {
PM.add(new AFLCoverage());
}
static RegisterStandardPasses RegisterAFLPass(
PassManagerBuilder::EP_OptimizerLast, registerAFLPass);
static RegisterStandardPasses RegisterAFLPass0(
PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass);
#endif

View File

@ -30,8 +30,6 @@ pub enum LLVMPasses {
//CmpLogIns, //CmpLogIns,
/// The `CmpLog` pass /// The `CmpLog` pass
CmpLogRtn, CmpLogRtn,
/// The AFL coverage pass
AFLCoverage,
/// The Autotoken pass /// The Autotoken pass
AutoTokens, AutoTokens,
/// The Coverage Accouting (BB metric) pass /// The Coverage Accouting (BB metric) pass
@ -41,6 +39,8 @@ pub enum LLVMPasses {
#[cfg(unix)] #[cfg(unix)]
/// The `CmpLog` Instruction pass /// The `CmpLog` Instruction pass
CmpLogInstructions, CmpLogInstructions,
/// Instrument caller for sancov coverage
Ctx,
} }
impl LLVMPasses { impl LLVMPasses {
@ -50,8 +50,6 @@ impl LLVMPasses {
match self { match self {
LLVMPasses::CmpLogRtn => PathBuf::from(env!("OUT_DIR")) LLVMPasses::CmpLogRtn => PathBuf::from(env!("OUT_DIR"))
.join(format!("cmplog-routines-pass.{}", dll_extension())), .join(format!("cmplog-routines-pass.{}", dll_extension())),
LLVMPasses::AFLCoverage => PathBuf::from(env!("OUT_DIR"))
.join(format!("afl-coverage-pass.{}", dll_extension())),
LLVMPasses::AutoTokens => { LLVMPasses::AutoTokens => {
PathBuf::from(env!("OUT_DIR")).join(format!("autotokens-pass.{}", dll_extension())) PathBuf::from(env!("OUT_DIR")).join(format!("autotokens-pass.{}", dll_extension()))
} }
@ -63,6 +61,9 @@ impl LLVMPasses {
#[cfg(unix)] #[cfg(unix)]
LLVMPasses::CmpLogInstructions => PathBuf::from(env!("OUT_DIR")) LLVMPasses::CmpLogInstructions => PathBuf::from(env!("OUT_DIR"))
.join(format!("cmplog-instructions-pass.{}", dll_extension())), .join(format!("cmplog-instructions-pass.{}", dll_extension())),
LLVMPasses::Ctx => {
PathBuf::from(env!("OUT_DIR")).join(format!("ctx-pass.{}", dll_extension()))
}
} }
} }
} }

View File

@ -71,7 +71,7 @@ class CmpLogRoutines : public ModulePass {
#if USE_NEW_PM #if USE_NEW_PM
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
#else #else
bool runOnModule(Module &M) override; bool runOnModule(Module &M) override;
#if LLVM_VERSION_MAJOR < 4 #if LLVM_VERSION_MAJOR < 4
const char *getPassName() const override { const char *getPassName() const override {

224
libafl_cc/src/ctx-pass.cc Normal file
View File

@ -0,0 +1,224 @@
/*
LibAFL - Ctx LLVM pass
--------------------------------------------------
Written by Dongjia Zhang <toka@aflplus.plus>
Copyright 2022-2023 AFLplusplus Project. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include "common-llvm.h"
#ifndef _WIN32
#include <unistd.h>
#include <sys/time.h>
#else
#include <io.h>
#endif
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <list>
#include <string>
#include <fstream>
#include <set>
#include "llvm/Config/llvm-config.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/IRBuilder.h"
#if USE_NEW_PM
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/IR/PassManager.h"
#else
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#endif
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Pass.h"
#include "llvm/IR/Constants.h"
#include <iostream>
using namespace llvm;
#define MAP_SIZE LIBAFL_EDGES_MAP_SIZE
namespace {
#if USE_NEW_PM
class CtxPass : public PassInfoMixin<CtxPass> {
public:
CtxPass() {
#else
class CtxPass : public ModulePass {
public:
static char ID;
CtxPass() : ModulePass(ID) {
#endif
}
#if USE_NEW_PM
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
#else
bool runOnModule(Module &M) override;
#endif
protected:
uint32_t map_size = MAP_SIZE;
private:
bool isLLVMIntrinsicFn(StringRef &n) {
// Not interested in these LLVM's functions
if (n.startswith("llvm.")) {
return true;
} else {
return false;
}
}
};
} // namespace
#if USE_NEW_PM
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
return {LLVM_PLUGIN_API_VERSION, "CtxPass", "v0.1",
/* lambda to insert our pass into the pass pipeline. */
[](PassBuilder &PB) {
#if LLVM_VERSION_MAJOR <= 13
using OptimizationLevel = typename PassBuilder::OptimizationLevel;
#endif
PB.registerOptimizerLastEPCallback(
[](ModulePassManager &MPM, OptimizationLevel OL) {
MPM.addPass(CtxPass());
});
}};
}
#else
char CtxPass::ID = 0;
#endif
#if USE_NEW_PM
PreservedAnalyses CtxPass::run(Module &M, ModuleAnalysisManager &MAM) {
#else
bool CtxPass::runOnModule(Module &M) {
#endif
LLVMContext &C = M.getContext();
auto moduleName = M.getName();
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
uint32_t rand_seed;
rand_seed = time(NULL);
srand(rand_seed);
GlobalVariable *AFLContext = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx");
Value *PrevCtx =
NULL; // the ctx value up until now that we save on the stack
for (auto &F : M) {
int has_calls = 0;
if (isIgnoreFunction(&F)) { continue; }
if (F.size() < 1) { continue; }
for (auto &BB : F) {
BasicBlock::iterator IP = BB.getFirstInsertionPt();
IRBuilder<> IRB(&(*IP));
if (&BB == &F.getEntryBlock()) {
// if this is the first block..
LoadInst *PrevCtxLoad = IRB.CreateLoad(IRB.getInt32Ty(), AFLContext);
PrevCtxLoad->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
PrevCtx = PrevCtxLoad;
// now check for if calls exists
for (auto &BB_2 : F) {
if (has_calls) { break; }
for (auto &IN : BB_2) {
CallInst *callInst = nullptr;
if ((callInst = dyn_cast<CallInst>(&IN))) {
Function *Callee = callInst->getCalledFunction();
if (!Callee || Callee->size() < 1) {
continue;
} else {
has_calls = 1;
break;
}
}
}
}
if (has_calls) {
Value *NewCtx = ConstantInt::get(Int32Ty, RandBelow(map_size));
NewCtx = IRB.CreateXor(PrevCtx, NewCtx);
StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext);
StoreCtx->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
}
}
// Restore the ctx at the end of BB
Instruction *Inst = BB.getTerminator();
if (has_calls) {
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
IRBuilder<> Post_IRB(Inst);
StoreInst *RestoreCtx;
RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext);
RestoreCtx->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
}
}
}
}
#if USE_NEW_PM
auto PA = PreservedAnalyses::all();
return PA;
#else
return true;
#endif
}
#if USE_NEW_PM
#else
static void registerCtxPass(const PassManagerBuilder &,
legacy::PassManagerBase &PM) {
PM.add(new CtxPass());
}
static RegisterPass<CtxPass> X("ctx", "ctx instrumentation pass", false, false);
static RegisterStandardPasses RegisterCtxPass(
PassManagerBuilder::EP_OptimizerLast, registerCtxPass);
static RegisterStandardPasses RegisterCtxPass0(
PassManagerBuilder::EP_EnabledOnOptLevel0, registerCtxPass);
#endif

View File

@ -30,7 +30,7 @@ libafl_bolts = { path = "../../libafl_bolts", version = "0.11.2", default-featur
[build-dependencies] [build-dependencies]
cmake = "0.1" cmake = "0.1"
bindgen = "0.69" bindgen = "0.69.4"
regex = "1" regex = "1"
which = "4.4" which = "4.4"
symcc_libafl = { path = "../symcc_libafl", version = "0.11.2" } symcc_libafl = { path = "../symcc_libafl", version = "0.11.2" }

View File

@ -48,7 +48,7 @@ utf8-chars = "3.0.1"
env_logger = "0.10" env_logger = "0.10"
[build-dependencies] [build-dependencies]
bindgen = "0.68.1" bindgen = "0.69.4"
cc = { version = "1.0", features = ["parallel"] } cc = { version = "1.0", features = ["parallel"] }
[workspace] [workspace]

View File

@ -27,7 +27,7 @@ slirp = [] # build qemu with host libslirp (for user networking)
clippy = [] # special feature for clippy, don't use in normal projects§ clippy = [] # special feature for clippy, don't use in normal projects§
[dependencies] [dependencies]
bindgen = "0.68" bindgen = "0.69.4"
which = "4.4" which = "4.4"
json = "0.12" json = "0.12"
shell-words = "1.1" shell-words = "1.1"

View File

@ -480,7 +480,7 @@ char *strndup(const char *s, size_t n) {
QASAN_LOAD(s, l + 1); QASAN_LOAD(s, l + 1);
void *r = __libqasan_malloc(l + 1); void *r = __libqasan_malloc(l + 1);
__libqasan_memcpy(r, s, l); __libqasan_memcpy(r, s, l);
((char*)r)[l] = 0; ((char *)r)[l] = 0;
QASAN_DEBUG("\t\t = %p\n", r); QASAN_DEBUG("\t\t = %p\n", r);
return r; return r;

View File

@ -42,6 +42,8 @@ sancov_pcguard_edges = ["coverage"]
sancov_pcguard_hitcounts = ["coverage"] sancov_pcguard_hitcounts = ["coverage"]
sancov_value_profile = ["common"] sancov_value_profile = ["common"]
sancov_8bit = [] sancov_8bit = []
sancov_ngram4 = ["coverage"]
sancov_ctx = ["coverage"]
sancov_cmplog = ["common"] # Defines cmp and __sanitizer_weak_hook functions. Use libfuzzer_interceptors to define interceptors (only compatible with Linux) sancov_cmplog = ["common"] # Defines cmp and __sanitizer_weak_hook functions. Use libfuzzer_interceptors to define interceptors (only compatible with Linux)
sancov_pcguard = ["sancov_pcguard_hitcounts"] sancov_pcguard = ["sancov_pcguard_hitcounts"]
sanitizer_interfaces = [] sanitizer_interfaces = []
@ -56,7 +58,7 @@ whole_archive = [] # use +whole-archive to ensure the presence of weak symbols
cmplog_extended_instrumentation = [] # support for aflpp cmplog map, we will remove this once aflpp and libafl cmplog shares the same LLVM passes. cmplog_extended_instrumentation = [] # support for aflpp cmplog map, we will remove this once aflpp and libafl cmplog shares the same LLVM passes.
[build-dependencies] [build-dependencies]
bindgen = "0.68" bindgen = "0.69.4"
cc = { version = "1.0", features = ["parallel"] } cc = { version = "1.0", features = ["parallel"] }
rustversion = "1.0" rustversion = "1.0"
@ -65,6 +67,7 @@ libafl = { path = "../libafl", version = "0.11.2", default-features = false, fea
libafl_bolts = { path = "../libafl_bolts", version = "0.11.2", default-features = false, features = [] } libafl_bolts = { path = "../libafl_bolts", version = "0.11.2", default-features = false, features = [] }
libc = "0.2" libc = "0.2"
log = "0.4.20" log = "0.4.20"
rustversion = "1.0"
rangemap = "1.3" rangemap = "1.3"
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib

View File

@ -5,8 +5,17 @@ use std::{env, fs::File, io::Write, path::Path};
const TWO_MB: usize = 2_621_440; const TWO_MB: usize = 2_621_440;
const SIXTY_FIVE_KB: usize = 65_536; const SIXTY_FIVE_KB: usize = 65_536;
#[rustversion::nightly]
fn enable_nightly() {
println!("cargo:rustc-cfg=nightly");
}
#[rustversion::not(nightly)]
fn enable_nightly() {}
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn main() { fn main() {
enable_nightly();
let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = env::var_os("OUT_DIR").unwrap();
let out_dir = out_dir.to_string_lossy().to_string(); let out_dir = out_dir.to_string_lossy().to_string();
//let out_dir_path = Path::new(&out_dir); //let out_dir_path = Path::new(&out_dir);

View File

@ -33,7 +33,5 @@ uint8_t *__token_stop = &__stop_libafl_token;
#endif #endif
// #if defined(__ANDROID__) || defined(__HAIKU__) // #if defined(__ANDROID__) || defined(__HAIKU__)
MAYBE_THREAD_LOCAL prev_loc_t __afl_prev_loc[NGRAM_SIZE_MAX]; uint32_t __afl_prev_ctx;
MAYBE_THREAD_LOCAL prev_loc_t __afl_prev_caller[CTX_MAX_K];
MAYBE_THREAD_LOCAL uint32_t __afl_prev_ctx;
MAYBE_THREAD_LOCAL prev_loc_t __afl_acc_prev_loc; MAYBE_THREAD_LOCAL prev_loc_t __afl_acc_prev_loc;

View File

@ -3,6 +3,7 @@
#![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::broken_intra_doc_links)]
#![deny(clippy::all)] #![deny(clippy::all)]
#![deny(clippy::pedantic)] #![deny(clippy::pedantic)]
#![cfg_attr(nightly, feature(portable_simd))]
#![allow( #![allow(
clippy::unreadable_literal, clippy::unreadable_literal,
clippy::type_repetition_in_bounds, clippy::type_repetition_in_bounds,
@ -66,9 +67,19 @@ extern crate alloc;
include!(concat!(env!("OUT_DIR"), "/constants.rs")); include!(concat!(env!("OUT_DIR"), "/constants.rs"));
#[cfg(any(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts",))] #[cfg(any(
feature = "sancov_pcguard_edges",
feature = "sancov_pcguard_hitcounts",
feature = "sancov_ngram4",
feature = "sancov_ctx"
))]
pub mod sancov_pcguard; pub mod sancov_pcguard;
#[cfg(any(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts",))] #[cfg(any(
feature = "sancov_pcguard_edges",
feature = "sancov_pcguard_hitcounts",
feature = "sancov_ngram4",
feature = "sancov_ctx"
))]
pub use sancov_pcguard::*; pub use sancov_pcguard::*;
#[cfg(any(feature = "sancov_cmplog", feature = "sancov_value_profile"))] #[cfg(any(feature = "sancov_cmplog", feature = "sancov_value_profile"))]

View File

@ -1,8 +1,18 @@
//! [`LLVM` `PcGuard`](https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards) runtime for `LibAFL`. //! [`LLVM` `PcGuard`](https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards) runtime for `LibAFL`.
use crate::coverage::{EDGES_MAP, MAX_EDGES_NUM}; #[rustversion::nightly]
#[cfg(feature = "sancov_ngram4")]
use core::simd::num::SimdUint;
#[cfg(any(feature = "sancov_ngram4", feature = "sancov_ctx"))]
use libafl::executors::{hooks::ExecutorHook, HasObservers};
#[cfg(feature = "pointer_maps")] #[cfg(feature = "pointer_maps")]
use crate::coverage::{EDGES_MAP_PTR, EDGES_MAP_PTR_NUM}; use crate::coverage::{EDGES_MAP_PTR, EDGES_MAP_PTR_NUM};
use crate::{
coverage::{EDGES_MAP, MAX_EDGES_NUM},
EDGES_MAP_SIZE,
};
#[cfg(all(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))] #[cfg(all(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))]
#[cfg(not(any(doc, feature = "clippy")))] #[cfg(not(any(doc, feature = "clippy")))]
@ -10,14 +20,122 @@ compile_error!(
"the libafl_targets `sancov_pcguard_edges` and `sancov_pcguard_hitcounts` features are mutually exclusive." "the libafl_targets `sancov_pcguard_edges` and `sancov_pcguard_hitcounts` features are mutually exclusive."
); );
#[cfg(feature = "sancov_ngram4")]
#[rustversion::nightly]
type Ngram4 = core::simd::u32x4;
/// The array holding the previous locs. This is required for NGRAM-4 instrumentation
#[cfg(feature = "sancov_ngram4")]
#[rustversion::nightly]
pub static mut PREV_ARRAY: Ngram4 = Ngram4::from_array([0, 0, 0, 0]);
/// The hook to initialize ngram everytime we run the harness
#[cfg(feature = "sancov_ngram4")]
#[rustversion::nightly]
#[derive(Default, Debug, Clone, Copy)]
pub struct NgramHook {}
/// The hook to initialize ctx everytime we run the harness
#[cfg(feature = "sancov_ctx")]
#[derive(Default, Debug, Clone, Copy)]
pub struct CtxHook {}
#[cfg(feature = "sancov_ngram4")]
#[rustversion::nightly]
impl ExecutorHook for NgramHook {
fn init<E: HasObservers, S>(&mut self, _state: &mut S) {}
fn pre_exec<EM, I, S, Z>(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) {
unsafe {
PREV_ARRAY = Ngram4::from_array([0, 0, 0, 0]);
}
}
fn post_exec<EM, I, S, Z>(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) {
}
}
#[cfg(feature = "sancov_ctx")]
impl ExecutorHook for CtxHook {
fn init<E: HasObservers, S>(&mut self, _state: &mut S) {}
fn pre_exec<EM, I, S, Z>(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) {
unsafe {
__afl_prev_ctx = 0;
}
}
fn post_exec<EM, I, S, Z>(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) {
}
}
#[rustversion::nightly]
#[cfg(feature = "sancov_ngram4")]
unsafe fn update_ngram(mut pos: usize) -> usize {
#[cfg(feature = "sancov_ngram4")]
{
PREV_ARRAY = PREV_ARRAY.rotate_elements_right::<1>();
PREV_ARRAY.as_mut_array()[0] = pos as u32;
let reduced = PREV_ARRAY.reduce_xor() as usize;
pos ^= reduced;
pos %= EDGES_MAP_SIZE;
}
pos
}
#[rustversion::not(nightly)]
#[cfg(feature = "sancov_ngram4")]
unsafe fn update_ngram(pos: usize) -> usize {
pos
}
extern "C" {
/// The ctx variable
pub static mut __afl_prev_ctx: u32;
}
/// Callback for sancov `pc_guard` - usually called by `llvm` on each block or edge. /// Callback for sancov `pc_guard` - usually called by `llvm` on each block or edge.
/// ///
/// # Safety /// # Safety
/// Dereferences `guard`, reads the position from there, then dereferences the [`EDGES_MAP`] at that position. /// Dereferences `guard`, reads the position from there, then dereferences the [`EDGES_MAP`] at that position.
/// Should usually not be called directly. /// Should usually not be called directly.
#[no_mangle] #[no_mangle]
#[allow(unused_assignments)]
pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) { pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) {
let pos = *guard as usize; let mut pos = *guard as usize;
#[cfg(feature = "sancov_ngram4")]
{
pos = update_ngram(pos);
// println!("Wrinting to {} {}", pos, EDGES_MAP_SIZE);
}
#[cfg(feature = "sancov_ctx")]
{
pos ^= __afl_prev_ctx as usize;
// println!("Wrinting to {} {}", pos, EDGES_MAP_SIZE);
}
#[cfg(feature = "pointer_maps")] #[cfg(feature = "pointer_maps")]
{ {
#[cfg(feature = "sancov_pcguard_edges")] #[cfg(feature = "sancov_pcguard_edges")]
@ -72,7 +190,7 @@ pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32
#[cfg(not(feature = "pointer_maps"))] #[cfg(not(feature = "pointer_maps"))]
{ {
MAX_EDGES_NUM = MAX_EDGES_NUM.wrapping_add(1); MAX_EDGES_NUM = MAX_EDGES_NUM.wrapping_add(1);
assert!((MAX_EDGES_NUM <= EDGES_MAP.len()), "The number of edges reported by SanitizerCoverage exceed the size of the edges map ({}). Use the LIBAFL_EDGES_MAP_SIZE env to increase it at compile time.", EDGES_MAP.len()); // assert!((MAX_EDGES_NUM <= EDGES_MAP.len()), "The number of edges reported by SanitizerCoverage exceed the size of the edges map ({}). Use the LIBAFL_EDGES_MAP_SIZE env to increase it at compile time.", EDGES_MAP.len());
} }
} }
} }