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:
parent
8c773a6b85
commit
1a41e65a5b
17
.github/workflows/build_and_test.yml
vendored
17
.github/workflows/build_and_test.yml
vendored
@ -168,13 +168,26 @@ jobs:
|
||||
ubuntu-check:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Remove Dotnet & Haskell
|
||||
run: rm -rf /usr/share/dotnet && rm -rf /opt/ghc
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
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
|
||||
run: sudo apt purge llvm* clang*
|
||||
- name: Install and cache deps
|
||||
|
@ -21,7 +21,7 @@ debug = true
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1"
|
||||
bindgen = "0.63"
|
||||
bindgen = "0.69.4"
|
||||
cc = "1.0"
|
||||
|
||||
[dependencies]
|
||||
|
@ -26,7 +26,7 @@ nix = "0.27"
|
||||
libafl = { path = "../../libafl/" }
|
||||
libafl_bolts = { path = "../../libafl_bolts/" }
|
||||
libafl_cc = { path = "../../libafl_cc/" }
|
||||
libafl_targets = { path = "../../libafl_targets/" }
|
||||
libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] }
|
||||
|
||||
[lib]
|
||||
name = "libforkserver_libafl_cc"
|
||||
|
@ -24,9 +24,6 @@ pub fn main() {
|
||||
.parse_args(&args)
|
||||
.expect("Failed to parse the command line")
|
||||
// Enable libafl's coverage instrumentation
|
||||
.add_pass(LLVMPasses::AFLCoverage)
|
||||
.add_arg("-mllvm")
|
||||
.add_arg("-ctx") // Context sensitive coverage
|
||||
// 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_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : __afl_fuzz_alt_ptr)")
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "libfuzzer_libpng_ctx"
|
||||
name = "fuzzbench"
|
||||
version = "0.11.2"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2021"
|
||||
@ -7,6 +7,7 @@ edition = "2021"
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
no_link_main = ["libafl_targets/libfuzzer_no_link_main"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@ -14,19 +15,25 @@ codegen-units = 1
|
||||
opt-level = 3
|
||||
debug = true
|
||||
|
||||
[profile.release-fuzzbench]
|
||||
inherits = "release"
|
||||
debug = false
|
||||
strip = true
|
||||
|
||||
[build-dependencies]
|
||||
cc = { version = "1.0", features = ["parallel"] }
|
||||
which = "4.4"
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../libafl/", features = ["std", "derive", "llmp_compression", "introspection"] }
|
||||
libafl = { path = "../../libafl/" }
|
||||
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
|
||||
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 }
|
||||
|
||||
[lib]
|
||||
name = "libfuzzer_libpng"
|
||||
name = "fuzzbench"
|
||||
crate-type = ["staticlib"]
|
108
fuzzers/fuzzbench_ctx/Makefile.toml
Normal file
108
fuzzers/fuzzbench_ctx/Makefile.toml
Normal 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
|
||||
'''
|
19
fuzzers/fuzzbench_ctx/fuzz.c
Normal file
19
fuzzers/fuzzbench_ctx/fuzz.c
Normal 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);
|
||||
|
||||
}*/
|
@ -3,7 +3,7 @@ use std::env;
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses, ToolWrapper};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let mut 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();
|
||||
@ -16,16 +16,25 @@ pub fn main() {
|
||||
|
||||
dir.pop();
|
||||
|
||||
// Must be always present, even without --libafl
|
||||
args.push("-fsanitize-coverage=trace-pc-guard,trace-cmp".into());
|
||||
|
||||
let mut cc = ClangWrapper::new();
|
||||
|
||||
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
||||
cc.add_pass(LLVMPasses::AutoTokens);
|
||||
|
||||
if let Some(code) = cc
|
||||
.cpp(is_cpp)
|
||||
// silence the compiler wrapper output, needed for some configure scripts.
|
||||
.silence(true)
|
||||
// add arguments only if --libafl or --libafl-no-link are present
|
||||
.need_libafl_arg(true)
|
||||
.parse_args(&args)
|
||||
.expect("Failed to parse the command line")
|
||||
.add_pass(LLVMPasses::AFLCoverage)
|
||||
.add_passes_arg("-ctx") // Context sensitive coverage
|
||||
.link_staticlib(&dir, "libfuzzer_libpng")
|
||||
.link_staticlib(&dir, "fuzzbench")
|
||||
.add_pass(LLVMPasses::CmpLogRtn)
|
||||
.add_pass(LLVMPasses::Ctx)
|
||||
.run()
|
||||
.expect("Failed to run the wrapped compiler")
|
||||
{
|
407
fuzzers/fuzzbench_ctx/src/lib.rs
Normal file
407
fuzzers/fuzzbench_ctx/src/lib.rs
Normal 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(())
|
||||
}
|
34
fuzzers/fuzzbench_ctx/stub_rt.c
Normal file
34
fuzzers/fuzzbench_ctx/stub_rt.c
Normal 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;
|
||||
}
|
1
fuzzers/libfuzzer_libpng_ctx/.gitignore
vendored
1
fuzzers/libfuzzer_libpng_ctx/.gitignore
vendored
@ -1 +0,0 @@
|
||||
libpng-*
|
@ -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
|
||||
'''
|
@ -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 |
@ -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;
|
||||
}
|
@ -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:?}"),
|
||||
}
|
||||
}
|
@ -44,6 +44,9 @@ use crate::{
|
||||
/// 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>;
|
||||
|
||||
/// 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
|
||||
pub type OwnedInProcessExecutor<OT, S> = GenericInProcessExecutor<
|
||||
dyn FnMut(&<S as UsesInput>::Input) -> ExitKind,
|
||||
|
@ -254,6 +254,7 @@ where
|
||||
.ok_or_else(|| Error::key_not_found("MapObserver not found".to_string()))?;
|
||||
|
||||
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.
|
||||
let psmeta = state
|
||||
.metadata_map_mut()
|
||||
|
@ -404,10 +404,10 @@ pub const LIBAFL_CC_LLVM_VERSION: Option<usize> = None;
|
||||
|
||||
for pass in &[
|
||||
"cmplog-routines-pass.cc",
|
||||
"afl-coverage-pass.cc",
|
||||
"autotokens-pass.cc",
|
||||
"coverage-accounting-pass.cc",
|
||||
"cmplog-instructions-pass.cc",
|
||||
"ctx-pass.cc",
|
||||
] {
|
||||
build_pass(
|
||||
bindir_path,
|
||||
|
@ -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
|
@ -30,8 +30,6 @@ pub enum LLVMPasses {
|
||||
//CmpLogIns,
|
||||
/// The `CmpLog` pass
|
||||
CmpLogRtn,
|
||||
/// The AFL coverage pass
|
||||
AFLCoverage,
|
||||
/// The Autotoken pass
|
||||
AutoTokens,
|
||||
/// The Coverage Accouting (BB metric) pass
|
||||
@ -41,6 +39,8 @@ pub enum LLVMPasses {
|
||||
#[cfg(unix)]
|
||||
/// The `CmpLog` Instruction pass
|
||||
CmpLogInstructions,
|
||||
/// Instrument caller for sancov coverage
|
||||
Ctx,
|
||||
}
|
||||
|
||||
impl LLVMPasses {
|
||||
@ -50,8 +50,6 @@ impl LLVMPasses {
|
||||
match self {
|
||||
LLVMPasses::CmpLogRtn => PathBuf::from(env!("OUT_DIR"))
|
||||
.join(format!("cmplog-routines-pass.{}", dll_extension())),
|
||||
LLVMPasses::AFLCoverage => PathBuf::from(env!("OUT_DIR"))
|
||||
.join(format!("afl-coverage-pass.{}", dll_extension())),
|
||||
LLVMPasses::AutoTokens => {
|
||||
PathBuf::from(env!("OUT_DIR")).join(format!("autotokens-pass.{}", dll_extension()))
|
||||
}
|
||||
@ -63,6 +61,9 @@ impl LLVMPasses {
|
||||
#[cfg(unix)]
|
||||
LLVMPasses::CmpLogInstructions => PathBuf::from(env!("OUT_DIR"))
|
||||
.join(format!("cmplog-instructions-pass.{}", dll_extension())),
|
||||
LLVMPasses::Ctx => {
|
||||
PathBuf::from(env!("OUT_DIR")).join(format!("ctx-pass.{}", dll_extension()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
224
libafl_cc/src/ctx-pass.cc
Normal file
224
libafl_cc/src/ctx-pass.cc
Normal 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
|
@ -30,7 +30,7 @@ libafl_bolts = { path = "../../libafl_bolts", version = "0.11.2", default-featur
|
||||
|
||||
[build-dependencies]
|
||||
cmake = "0.1"
|
||||
bindgen = "0.69"
|
||||
bindgen = "0.69.4"
|
||||
regex = "1"
|
||||
which = "4.4"
|
||||
symcc_libafl = { path = "../symcc_libafl", version = "0.11.2" }
|
||||
|
@ -48,7 +48,7 @@ utf8-chars = "3.0.1"
|
||||
env_logger = "0.10"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.68.1"
|
||||
bindgen = "0.69.4"
|
||||
cc = { version = "1.0", features = ["parallel"] }
|
||||
|
||||
[workspace]
|
||||
|
@ -27,7 +27,7 @@ slirp = [] # build qemu with host libslirp (for user networking)
|
||||
clippy = [] # special feature for clippy, don't use in normal projects§
|
||||
|
||||
[dependencies]
|
||||
bindgen = "0.68"
|
||||
bindgen = "0.69.4"
|
||||
which = "4.4"
|
||||
json = "0.12"
|
||||
shell-words = "1.1"
|
||||
|
@ -480,7 +480,7 @@ char *strndup(const char *s, size_t n) {
|
||||
QASAN_LOAD(s, l + 1);
|
||||
void *r = __libqasan_malloc(l + 1);
|
||||
__libqasan_memcpy(r, s, l);
|
||||
((char*)r)[l] = 0;
|
||||
((char *)r)[l] = 0;
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
|
||||
return r;
|
||||
|
@ -42,6 +42,8 @@ sancov_pcguard_edges = ["coverage"]
|
||||
sancov_pcguard_hitcounts = ["coverage"]
|
||||
sancov_value_profile = ["common"]
|
||||
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_pcguard = ["sancov_pcguard_hitcounts"]
|
||||
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.
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.68"
|
||||
bindgen = "0.69.4"
|
||||
cc = { version = "1.0", features = ["parallel"] }
|
||||
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 = [] }
|
||||
libc = "0.2"
|
||||
log = "0.4.20"
|
||||
rustversion = "1.0"
|
||||
|
||||
rangemap = "1.3"
|
||||
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
||||
|
@ -5,8 +5,17 @@ use std::{env, fs::File, io::Write, path::Path};
|
||||
const TWO_MB: usize = 2_621_440;
|
||||
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)]
|
||||
fn main() {
|
||||
enable_nightly();
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let out_dir = out_dir.to_string_lossy().to_string();
|
||||
//let out_dir_path = Path::new(&out_dir);
|
||||
|
@ -33,7 +33,5 @@ uint8_t *__token_stop = &__stop_libafl_token;
|
||||
#endif
|
||||
|
||||
// #if defined(__ANDROID__) || defined(__HAIKU__)
|
||||
MAYBE_THREAD_LOCAL prev_loc_t __afl_prev_loc[NGRAM_SIZE_MAX];
|
||||
MAYBE_THREAD_LOCAL prev_loc_t __afl_prev_caller[CTX_MAX_K];
|
||||
MAYBE_THREAD_LOCAL uint32_t __afl_prev_ctx;
|
||||
uint32_t __afl_prev_ctx;
|
||||
MAYBE_THREAD_LOCAL prev_loc_t __afl_acc_prev_loc;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![deny(clippy::all)]
|
||||
#![deny(clippy::pedantic)]
|
||||
#![cfg_attr(nightly, feature(portable_simd))]
|
||||
#![allow(
|
||||
clippy::unreadable_literal,
|
||||
clippy::type_repetition_in_bounds,
|
||||
@ -66,9 +67,19 @@ extern crate alloc;
|
||||
|
||||
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;
|
||||
#[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::*;
|
||||
|
||||
#[cfg(any(feature = "sancov_cmplog", feature = "sancov_value_profile"))]
|
||||
|
@ -1,8 +1,18 @@
|
||||
//! [`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")]
|
||||
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(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."
|
||||
);
|
||||
|
||||
#[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.
|
||||
///
|
||||
/// # Safety
|
||||
/// Dereferences `guard`, reads the position from there, then dereferences the [`EDGES_MAP`] at that position.
|
||||
/// Should usually not be called directly.
|
||||
#[no_mangle]
|
||||
#[allow(unused_assignments)]
|
||||
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 = "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"))]
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user