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
19
.github/workflows/build_and_test.yml
vendored
19
.github/workflows/build_and_test.yml
vendored
@ -168,13 +168,26 @@ jobs:
|
|||||||
ubuntu-check:
|
ubuntu-check:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Remove Dotnet & Haskell
|
|
||||||
run: rm -rf /usr/share/dotnet && rm -rf /opt/ghc
|
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
components: llvm-tools
|
components: llvm-tools
|
||||||
|
- name: Free Disk Space (Ubuntu)
|
||||||
|
uses: jlumbroso/free-disk-space@main
|
||||||
|
with:
|
||||||
|
# this might remove tools that are actually needed,
|
||||||
|
# if set to "true" but frees about 6 GB
|
||||||
|
tool-cache: false
|
||||||
|
|
||||||
|
# all of these default to true, but feel free to set to
|
||||||
|
# "false" if necessary for your workflow
|
||||||
|
android: true
|
||||||
|
dotnet: true
|
||||||
|
haskell: true
|
||||||
|
large-packages: false
|
||||||
|
docker-images: true
|
||||||
|
swap-storage: true
|
||||||
- name: Remove existing clang and LLVM
|
- name: Remove existing clang and LLVM
|
||||||
run: sudo apt purge llvm* clang*
|
run: sudo apt purge llvm* clang*
|
||||||
- name: Install and cache deps
|
- name: Install and cache deps
|
||||||
|
@ -21,7 +21,7 @@ debug = true
|
|||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
bindgen = "0.63"
|
bindgen = "0.69.4"
|
||||||
cc = "1.0"
|
cc = "1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -26,7 +26,7 @@ nix = "0.27"
|
|||||||
libafl = { path = "../../libafl/" }
|
libafl = { path = "../../libafl/" }
|
||||||
libafl_bolts = { path = "../../libafl_bolts/" }
|
libafl_bolts = { path = "../../libafl_bolts/" }
|
||||||
libafl_cc = { path = "../../libafl_cc/" }
|
libafl_cc = { path = "../../libafl_cc/" }
|
||||||
libafl_targets = { path = "../../libafl_targets/" }
|
libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "libforkserver_libafl_cc"
|
name = "libforkserver_libafl_cc"
|
||||||
|
@ -24,9 +24,6 @@ pub fn main() {
|
|||||||
.parse_args(&args)
|
.parse_args(&args)
|
||||||
.expect("Failed to parse the command line")
|
.expect("Failed to parse the command line")
|
||||||
// Enable libafl's coverage instrumentation
|
// Enable libafl's coverage instrumentation
|
||||||
.add_pass(LLVMPasses::AFLCoverage)
|
|
||||||
.add_arg("-mllvm")
|
|
||||||
.add_arg("-ctx") // Context sensitive coverage
|
|
||||||
// Imitate afl-cc's compile definitions
|
// Imitate afl-cc's compile definitions
|
||||||
.add_arg("-D__AFL_FUZZ_INIT()=int __afl_sharedmem_fuzzing = 1;extern unsigned int *__afl_fuzz_len;extern unsigned char *__afl_fuzz_ptr;unsigned char __afl_fuzz_alt[1048576];unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;void libafl_start_forkserver(void)")
|
.add_arg("-D__AFL_FUZZ_INIT()=int __afl_sharedmem_fuzzing = 1;extern unsigned int *__afl_fuzz_len;extern unsigned char *__afl_fuzz_ptr;unsigned char __afl_fuzz_alt[1048576];unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;void libafl_start_forkserver(void)")
|
||||||
.add_arg("-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : __afl_fuzz_alt_ptr)")
|
.add_arg("-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : __afl_fuzz_alt_ptr)")
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "libfuzzer_libpng_ctx"
|
name = "fuzzbench"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@ -7,6 +7,7 @@ edition = "2021"
|
|||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = []
|
std = []
|
||||||
|
no_link_main = ["libafl_targets/libfuzzer_no_link_main"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
@ -14,19 +15,25 @@ codegen-units = 1
|
|||||||
opt-level = 3
|
opt-level = 3
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
[profile.release-fuzzbench]
|
||||||
|
inherits = "release"
|
||||||
|
debug = false
|
||||||
|
strip = true
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = { version = "1.0", features = ["parallel"] }
|
cc = { version = "1.0", features = ["parallel"] }
|
||||||
which = "4.4"
|
which = "4.4"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../../libafl/", features = ["std", "derive", "llmp_compression", "introspection"] }
|
libafl = { path = "../../libafl/" }
|
||||||
libafl_bolts = { path = "../../libafl_bolts/" }
|
libafl_bolts = { path = "../../libafl_bolts/" }
|
||||||
libafl_targets = { path = "../../libafl_targets/", features = ["libfuzzer"] }
|
libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "sancov_cmplog", "libfuzzer", "sancov_ctx"] }
|
||||||
# TODO Include it only when building cc
|
# TODO Include it only when building cc
|
||||||
libafl_cc = { path = "../../libafl_cc/" }
|
libafl_cc = { path = "../../libafl_cc/" }
|
||||||
clap = { version = "4.0", features = ["derive"] }
|
clap = { version = "4.0", features = ["default"] }
|
||||||
|
nix = { version = "0.27", features = ["fs"] }
|
||||||
mimalloc = { version = "*", default-features = false }
|
mimalloc = { version = "*", default-features = false }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "libfuzzer_libpng"
|
name = "fuzzbench"
|
||||||
crate-type = ["staticlib"]
|
crate-type = ["staticlib"]
|
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};
|
use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses, ToolWrapper};
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let args: Vec<String> = env::args().collect();
|
let mut args: Vec<String> = env::args().collect();
|
||||||
if args.len() > 1 {
|
if args.len() > 1 {
|
||||||
let mut dir = env::current_exe().unwrap();
|
let mut dir = env::current_exe().unwrap();
|
||||||
let wrapper_name = dir.file_name().unwrap().to_str().unwrap();
|
let wrapper_name = dir.file_name().unwrap().to_str().unwrap();
|
||||||
@ -16,16 +16,25 @@ pub fn main() {
|
|||||||
|
|
||||||
dir.pop();
|
dir.pop();
|
||||||
|
|
||||||
|
// Must be always present, even without --libafl
|
||||||
|
args.push("-fsanitize-coverage=trace-pc-guard,trace-cmp".into());
|
||||||
|
|
||||||
let mut cc = ClangWrapper::new();
|
let mut cc = ClangWrapper::new();
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
||||||
|
cc.add_pass(LLVMPasses::AutoTokens);
|
||||||
|
|
||||||
if let Some(code) = cc
|
if let Some(code) = cc
|
||||||
.cpp(is_cpp)
|
.cpp(is_cpp)
|
||||||
// silence the compiler wrapper output, needed for some configure scripts.
|
// silence the compiler wrapper output, needed for some configure scripts.
|
||||||
.silence(true)
|
.silence(true)
|
||||||
|
// add arguments only if --libafl or --libafl-no-link are present
|
||||||
|
.need_libafl_arg(true)
|
||||||
.parse_args(&args)
|
.parse_args(&args)
|
||||||
.expect("Failed to parse the command line")
|
.expect("Failed to parse the command line")
|
||||||
.add_pass(LLVMPasses::AFLCoverage)
|
.link_staticlib(&dir, "fuzzbench")
|
||||||
.add_passes_arg("-ctx") // Context sensitive coverage
|
.add_pass(LLVMPasses::CmpLogRtn)
|
||||||
.link_staticlib(&dir, "libfuzzer_libpng")
|
.add_pass(LLVMPasses::Ctx)
|
||||||
.run()
|
.run()
|
||||||
.expect("Failed to run the wrapped compiler")
|
.expect("Failed to run the wrapped compiler")
|
||||||
{
|
{
|
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
|
/// The process executor simply calls a target function, as mutable reference to a closure
|
||||||
pub type InProcessExecutor<'a, H, OT, S> = GenericInProcessExecutor<H, &'a mut H, (), OT, S>;
|
pub type InProcessExecutor<'a, H, OT, S> = GenericInProcessExecutor<H, &'a mut H, (), OT, S>;
|
||||||
|
|
||||||
|
/// The inprocess executor that allows hooks
|
||||||
|
pub type HookableInProcessExecutor<'a, H, HT, OT, S> =
|
||||||
|
GenericInProcessExecutor<H, &'a mut H, HT, OT, S>;
|
||||||
/// The process executor simply calls a target function, as boxed `FnMut` trait object
|
/// The process executor simply calls a target function, as boxed `FnMut` trait object
|
||||||
pub type OwnedInProcessExecutor<OT, S> = GenericInProcessExecutor<
|
pub type OwnedInProcessExecutor<OT, S> = GenericInProcessExecutor<
|
||||||
dyn FnMut(&<S as UsesInput>::Input) -> ExitKind,
|
dyn FnMut(&<S as UsesInput>::Input) -> ExitKind,
|
||||||
|
@ -254,6 +254,7 @@ where
|
|||||||
.ok_or_else(|| Error::key_not_found("MapObserver not found".to_string()))?;
|
.ok_or_else(|| Error::key_not_found("MapObserver not found".to_string()))?;
|
||||||
|
|
||||||
let mut bitmap_size = map.count_bytes();
|
let mut bitmap_size = map.count_bytes();
|
||||||
|
assert!(bitmap_size != 0);
|
||||||
bitmap_size = bitmap_size.max(1); // just don't make it 0 because we take log2 of it later.
|
bitmap_size = bitmap_size.max(1); // just don't make it 0 because we take log2 of it later.
|
||||||
let psmeta = state
|
let psmeta = state
|
||||||
.metadata_map_mut()
|
.metadata_map_mut()
|
||||||
|
@ -404,10 +404,10 @@ pub const LIBAFL_CC_LLVM_VERSION: Option<usize> = None;
|
|||||||
|
|
||||||
for pass in &[
|
for pass in &[
|
||||||
"cmplog-routines-pass.cc",
|
"cmplog-routines-pass.cc",
|
||||||
"afl-coverage-pass.cc",
|
|
||||||
"autotokens-pass.cc",
|
"autotokens-pass.cc",
|
||||||
"coverage-accounting-pass.cc",
|
"coverage-accounting-pass.cc",
|
||||||
"cmplog-instructions-pass.cc",
|
"cmplog-instructions-pass.cc",
|
||||||
|
"ctx-pass.cc",
|
||||||
] {
|
] {
|
||||||
build_pass(
|
build_pass(
|
||||||
bindir_path,
|
bindir_path,
|
||||||
|
@ -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,
|
//CmpLogIns,
|
||||||
/// The `CmpLog` pass
|
/// The `CmpLog` pass
|
||||||
CmpLogRtn,
|
CmpLogRtn,
|
||||||
/// The AFL coverage pass
|
|
||||||
AFLCoverage,
|
|
||||||
/// The Autotoken pass
|
/// The Autotoken pass
|
||||||
AutoTokens,
|
AutoTokens,
|
||||||
/// The Coverage Accouting (BB metric) pass
|
/// The Coverage Accouting (BB metric) pass
|
||||||
@ -41,6 +39,8 @@ pub enum LLVMPasses {
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
/// The `CmpLog` Instruction pass
|
/// The `CmpLog` Instruction pass
|
||||||
CmpLogInstructions,
|
CmpLogInstructions,
|
||||||
|
/// Instrument caller for sancov coverage
|
||||||
|
Ctx,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LLVMPasses {
|
impl LLVMPasses {
|
||||||
@ -50,8 +50,6 @@ impl LLVMPasses {
|
|||||||
match self {
|
match self {
|
||||||
LLVMPasses::CmpLogRtn => PathBuf::from(env!("OUT_DIR"))
|
LLVMPasses::CmpLogRtn => PathBuf::from(env!("OUT_DIR"))
|
||||||
.join(format!("cmplog-routines-pass.{}", dll_extension())),
|
.join(format!("cmplog-routines-pass.{}", dll_extension())),
|
||||||
LLVMPasses::AFLCoverage => PathBuf::from(env!("OUT_DIR"))
|
|
||||||
.join(format!("afl-coverage-pass.{}", dll_extension())),
|
|
||||||
LLVMPasses::AutoTokens => {
|
LLVMPasses::AutoTokens => {
|
||||||
PathBuf::from(env!("OUT_DIR")).join(format!("autotokens-pass.{}", dll_extension()))
|
PathBuf::from(env!("OUT_DIR")).join(format!("autotokens-pass.{}", dll_extension()))
|
||||||
}
|
}
|
||||||
@ -63,6 +61,9 @@ impl LLVMPasses {
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
LLVMPasses::CmpLogInstructions => PathBuf::from(env!("OUT_DIR"))
|
LLVMPasses::CmpLogInstructions => PathBuf::from(env!("OUT_DIR"))
|
||||||
.join(format!("cmplog-instructions-pass.{}", dll_extension())),
|
.join(format!("cmplog-instructions-pass.{}", dll_extension())),
|
||||||
|
LLVMPasses::Ctx => {
|
||||||
|
PathBuf::from(env!("OUT_DIR")).join(format!("ctx-pass.{}", dll_extension()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ class CmpLogRoutines : public ModulePass {
|
|||||||
#if USE_NEW_PM
|
#if USE_NEW_PM
|
||||||
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
|
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
|
||||||
#else
|
#else
|
||||||
bool runOnModule(Module &M) override;
|
bool runOnModule(Module &M) override;
|
||||||
|
|
||||||
#if LLVM_VERSION_MAJOR < 4
|
#if LLVM_VERSION_MAJOR < 4
|
||||||
const char *getPassName() const override {
|
const char *getPassName() const override {
|
||||||
|
224
libafl_cc/src/ctx-pass.cc
Normal file
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]
|
[build-dependencies]
|
||||||
cmake = "0.1"
|
cmake = "0.1"
|
||||||
bindgen = "0.69"
|
bindgen = "0.69.4"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
which = "4.4"
|
which = "4.4"
|
||||||
symcc_libafl = { path = "../symcc_libafl", version = "0.11.2" }
|
symcc_libafl = { path = "../symcc_libafl", version = "0.11.2" }
|
||||||
|
@ -48,7 +48,7 @@ utf8-chars = "3.0.1"
|
|||||||
env_logger = "0.10"
|
env_logger = "0.10"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
bindgen = "0.68.1"
|
bindgen = "0.69.4"
|
||||||
cc = { version = "1.0", features = ["parallel"] }
|
cc = { version = "1.0", features = ["parallel"] }
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
@ -27,7 +27,7 @@ slirp = [] # build qemu with host libslirp (for user networking)
|
|||||||
clippy = [] # special feature for clippy, don't use in normal projects§
|
clippy = [] # special feature for clippy, don't use in normal projects§
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bindgen = "0.68"
|
bindgen = "0.69.4"
|
||||||
which = "4.4"
|
which = "4.4"
|
||||||
json = "0.12"
|
json = "0.12"
|
||||||
shell-words = "1.1"
|
shell-words = "1.1"
|
||||||
|
@ -480,7 +480,7 @@ char *strndup(const char *s, size_t n) {
|
|||||||
QASAN_LOAD(s, l + 1);
|
QASAN_LOAD(s, l + 1);
|
||||||
void *r = __libqasan_malloc(l + 1);
|
void *r = __libqasan_malloc(l + 1);
|
||||||
__libqasan_memcpy(r, s, l);
|
__libqasan_memcpy(r, s, l);
|
||||||
((char*)r)[l] = 0;
|
((char *)r)[l] = 0;
|
||||||
QASAN_DEBUG("\t\t = %p\n", r);
|
QASAN_DEBUG("\t\t = %p\n", r);
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
|
@ -42,6 +42,8 @@ sancov_pcguard_edges = ["coverage"]
|
|||||||
sancov_pcguard_hitcounts = ["coverage"]
|
sancov_pcguard_hitcounts = ["coverage"]
|
||||||
sancov_value_profile = ["common"]
|
sancov_value_profile = ["common"]
|
||||||
sancov_8bit = []
|
sancov_8bit = []
|
||||||
|
sancov_ngram4 = ["coverage"]
|
||||||
|
sancov_ctx = ["coverage"]
|
||||||
sancov_cmplog = ["common"] # Defines cmp and __sanitizer_weak_hook functions. Use libfuzzer_interceptors to define interceptors (only compatible with Linux)
|
sancov_cmplog = ["common"] # Defines cmp and __sanitizer_weak_hook functions. Use libfuzzer_interceptors to define interceptors (only compatible with Linux)
|
||||||
sancov_pcguard = ["sancov_pcguard_hitcounts"]
|
sancov_pcguard = ["sancov_pcguard_hitcounts"]
|
||||||
sanitizer_interfaces = []
|
sanitizer_interfaces = []
|
||||||
@ -56,7 +58,7 @@ whole_archive = [] # use +whole-archive to ensure the presence of weak symbols
|
|||||||
cmplog_extended_instrumentation = [] # support for aflpp cmplog map, we will remove this once aflpp and libafl cmplog shares the same LLVM passes.
|
cmplog_extended_instrumentation = [] # support for aflpp cmplog map, we will remove this once aflpp and libafl cmplog shares the same LLVM passes.
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
bindgen = "0.68"
|
bindgen = "0.69.4"
|
||||||
cc = { version = "1.0", features = ["parallel"] }
|
cc = { version = "1.0", features = ["parallel"] }
|
||||||
rustversion = "1.0"
|
rustversion = "1.0"
|
||||||
|
|
||||||
@ -65,6 +67,7 @@ libafl = { path = "../libafl", version = "0.11.2", default-features = false, fea
|
|||||||
libafl_bolts = { path = "../libafl_bolts", version = "0.11.2", default-features = false, features = [] }
|
libafl_bolts = { path = "../libafl_bolts", version = "0.11.2", default-features = false, features = [] }
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
|
rustversion = "1.0"
|
||||||
|
|
||||||
rangemap = "1.3"
|
rangemap = "1.3"
|
||||||
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
||||||
|
@ -5,8 +5,17 @@ use std::{env, fs::File, io::Write, path::Path};
|
|||||||
const TWO_MB: usize = 2_621_440;
|
const TWO_MB: usize = 2_621_440;
|
||||||
const SIXTY_FIVE_KB: usize = 65_536;
|
const SIXTY_FIVE_KB: usize = 65_536;
|
||||||
|
|
||||||
|
#[rustversion::nightly]
|
||||||
|
fn enable_nightly() {
|
||||||
|
println!("cargo:rustc-cfg=nightly");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustversion::not(nightly)]
|
||||||
|
fn enable_nightly() {}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
fn main() {
|
fn main() {
|
||||||
|
enable_nightly();
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
let out_dir = out_dir.to_string_lossy().to_string();
|
let out_dir = out_dir.to_string_lossy().to_string();
|
||||||
//let out_dir_path = Path::new(&out_dir);
|
//let out_dir_path = Path::new(&out_dir);
|
||||||
|
@ -33,7 +33,5 @@ uint8_t *__token_stop = &__stop_libafl_token;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// #if defined(__ANDROID__) || defined(__HAIKU__)
|
// #if defined(__ANDROID__) || defined(__HAIKU__)
|
||||||
MAYBE_THREAD_LOCAL prev_loc_t __afl_prev_loc[NGRAM_SIZE_MAX];
|
uint32_t __afl_prev_ctx;
|
||||||
MAYBE_THREAD_LOCAL prev_loc_t __afl_prev_caller[CTX_MAX_K];
|
|
||||||
MAYBE_THREAD_LOCAL uint32_t __afl_prev_ctx;
|
|
||||||
MAYBE_THREAD_LOCAL prev_loc_t __afl_acc_prev_loc;
|
MAYBE_THREAD_LOCAL prev_loc_t __afl_acc_prev_loc;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#![deny(rustdoc::broken_intra_doc_links)]
|
#![deny(rustdoc::broken_intra_doc_links)]
|
||||||
#![deny(clippy::all)]
|
#![deny(clippy::all)]
|
||||||
#![deny(clippy::pedantic)]
|
#![deny(clippy::pedantic)]
|
||||||
|
#![cfg_attr(nightly, feature(portable_simd))]
|
||||||
#![allow(
|
#![allow(
|
||||||
clippy::unreadable_literal,
|
clippy::unreadable_literal,
|
||||||
clippy::type_repetition_in_bounds,
|
clippy::type_repetition_in_bounds,
|
||||||
@ -66,9 +67,19 @@ extern crate alloc;
|
|||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/constants.rs"));
|
include!(concat!(env!("OUT_DIR"), "/constants.rs"));
|
||||||
|
|
||||||
#[cfg(any(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts",))]
|
#[cfg(any(
|
||||||
|
feature = "sancov_pcguard_edges",
|
||||||
|
feature = "sancov_pcguard_hitcounts",
|
||||||
|
feature = "sancov_ngram4",
|
||||||
|
feature = "sancov_ctx"
|
||||||
|
))]
|
||||||
pub mod sancov_pcguard;
|
pub mod sancov_pcguard;
|
||||||
#[cfg(any(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts",))]
|
#[cfg(any(
|
||||||
|
feature = "sancov_pcguard_edges",
|
||||||
|
feature = "sancov_pcguard_hitcounts",
|
||||||
|
feature = "sancov_ngram4",
|
||||||
|
feature = "sancov_ctx"
|
||||||
|
))]
|
||||||
pub use sancov_pcguard::*;
|
pub use sancov_pcguard::*;
|
||||||
|
|
||||||
#[cfg(any(feature = "sancov_cmplog", feature = "sancov_value_profile"))]
|
#[cfg(any(feature = "sancov_cmplog", feature = "sancov_value_profile"))]
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
//! [`LLVM` `PcGuard`](https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards) runtime for `LibAFL`.
|
//! [`LLVM` `PcGuard`](https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards) runtime for `LibAFL`.
|
||||||
|
|
||||||
use crate::coverage::{EDGES_MAP, MAX_EDGES_NUM};
|
#[rustversion::nightly]
|
||||||
|
#[cfg(feature = "sancov_ngram4")]
|
||||||
|
use core::simd::num::SimdUint;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "sancov_ngram4", feature = "sancov_ctx"))]
|
||||||
|
use libafl::executors::{hooks::ExecutorHook, HasObservers};
|
||||||
|
|
||||||
#[cfg(feature = "pointer_maps")]
|
#[cfg(feature = "pointer_maps")]
|
||||||
use crate::coverage::{EDGES_MAP_PTR, EDGES_MAP_PTR_NUM};
|
use crate::coverage::{EDGES_MAP_PTR, EDGES_MAP_PTR_NUM};
|
||||||
|
use crate::{
|
||||||
|
coverage::{EDGES_MAP, MAX_EDGES_NUM},
|
||||||
|
EDGES_MAP_SIZE,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(all(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))]
|
#[cfg(all(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))]
|
||||||
#[cfg(not(any(doc, feature = "clippy")))]
|
#[cfg(not(any(doc, feature = "clippy")))]
|
||||||
@ -10,14 +20,122 @@ compile_error!(
|
|||||||
"the libafl_targets `sancov_pcguard_edges` and `sancov_pcguard_hitcounts` features are mutually exclusive."
|
"the libafl_targets `sancov_pcguard_edges` and `sancov_pcguard_hitcounts` features are mutually exclusive."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(feature = "sancov_ngram4")]
|
||||||
|
#[rustversion::nightly]
|
||||||
|
type Ngram4 = core::simd::u32x4;
|
||||||
|
|
||||||
|
/// The array holding the previous locs. This is required for NGRAM-4 instrumentation
|
||||||
|
#[cfg(feature = "sancov_ngram4")]
|
||||||
|
#[rustversion::nightly]
|
||||||
|
pub static mut PREV_ARRAY: Ngram4 = Ngram4::from_array([0, 0, 0, 0]);
|
||||||
|
|
||||||
|
/// The hook to initialize ngram everytime we run the harness
|
||||||
|
#[cfg(feature = "sancov_ngram4")]
|
||||||
|
#[rustversion::nightly]
|
||||||
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
|
pub struct NgramHook {}
|
||||||
|
|
||||||
|
/// The hook to initialize ctx everytime we run the harness
|
||||||
|
#[cfg(feature = "sancov_ctx")]
|
||||||
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
|
pub struct CtxHook {}
|
||||||
|
|
||||||
|
#[cfg(feature = "sancov_ngram4")]
|
||||||
|
#[rustversion::nightly]
|
||||||
|
impl ExecutorHook for NgramHook {
|
||||||
|
fn init<E: HasObservers, S>(&mut self, _state: &mut S) {}
|
||||||
|
fn pre_exec<EM, I, S, Z>(
|
||||||
|
&mut self,
|
||||||
|
_fuzzer: &mut Z,
|
||||||
|
_state: &mut S,
|
||||||
|
_mgr: &mut EM,
|
||||||
|
_input: &I,
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
|
PREV_ARRAY = Ngram4::from_array([0, 0, 0, 0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn post_exec<EM, I, S, Z>(
|
||||||
|
&mut self,
|
||||||
|
_fuzzer: &mut Z,
|
||||||
|
_state: &mut S,
|
||||||
|
_mgr: &mut EM,
|
||||||
|
_input: &I,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sancov_ctx")]
|
||||||
|
impl ExecutorHook for CtxHook {
|
||||||
|
fn init<E: HasObservers, S>(&mut self, _state: &mut S) {}
|
||||||
|
fn pre_exec<EM, I, S, Z>(
|
||||||
|
&mut self,
|
||||||
|
_fuzzer: &mut Z,
|
||||||
|
_state: &mut S,
|
||||||
|
_mgr: &mut EM,
|
||||||
|
_input: &I,
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
|
__afl_prev_ctx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn post_exec<EM, I, S, Z>(
|
||||||
|
&mut self,
|
||||||
|
_fuzzer: &mut Z,
|
||||||
|
_state: &mut S,
|
||||||
|
_mgr: &mut EM,
|
||||||
|
_input: &I,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustversion::nightly]
|
||||||
|
#[cfg(feature = "sancov_ngram4")]
|
||||||
|
unsafe fn update_ngram(mut pos: usize) -> usize {
|
||||||
|
#[cfg(feature = "sancov_ngram4")]
|
||||||
|
{
|
||||||
|
PREV_ARRAY = PREV_ARRAY.rotate_elements_right::<1>();
|
||||||
|
PREV_ARRAY.as_mut_array()[0] = pos as u32;
|
||||||
|
let reduced = PREV_ARRAY.reduce_xor() as usize;
|
||||||
|
pos ^= reduced;
|
||||||
|
pos %= EDGES_MAP_SIZE;
|
||||||
|
}
|
||||||
|
pos
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustversion::not(nightly)]
|
||||||
|
#[cfg(feature = "sancov_ngram4")]
|
||||||
|
unsafe fn update_ngram(pos: usize) -> usize {
|
||||||
|
pos
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
/// The ctx variable
|
||||||
|
pub static mut __afl_prev_ctx: u32;
|
||||||
|
}
|
||||||
|
|
||||||
/// Callback for sancov `pc_guard` - usually called by `llvm` on each block or edge.
|
/// Callback for sancov `pc_guard` - usually called by `llvm` on each block or edge.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Dereferences `guard`, reads the position from there, then dereferences the [`EDGES_MAP`] at that position.
|
/// Dereferences `guard`, reads the position from there, then dereferences the [`EDGES_MAP`] at that position.
|
||||||
/// Should usually not be called directly.
|
/// Should usually not be called directly.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
#[allow(unused_assignments)]
|
||||||
pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) {
|
pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) {
|
||||||
let pos = *guard as usize;
|
let mut pos = *guard as usize;
|
||||||
|
|
||||||
|
#[cfg(feature = "sancov_ngram4")]
|
||||||
|
{
|
||||||
|
pos = update_ngram(pos);
|
||||||
|
// println!("Wrinting to {} {}", pos, EDGES_MAP_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sancov_ctx")]
|
||||||
|
{
|
||||||
|
pos ^= __afl_prev_ctx as usize;
|
||||||
|
// println!("Wrinting to {} {}", pos, EDGES_MAP_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "pointer_maps")]
|
#[cfg(feature = "pointer_maps")]
|
||||||
{
|
{
|
||||||
#[cfg(feature = "sancov_pcguard_edges")]
|
#[cfg(feature = "sancov_pcguard_edges")]
|
||||||
@ -72,7 +190,7 @@ pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32
|
|||||||
#[cfg(not(feature = "pointer_maps"))]
|
#[cfg(not(feature = "pointer_maps"))]
|
||||||
{
|
{
|
||||||
MAX_EDGES_NUM = MAX_EDGES_NUM.wrapping_add(1);
|
MAX_EDGES_NUM = MAX_EDGES_NUM.wrapping_add(1);
|
||||||
assert!((MAX_EDGES_NUM <= EDGES_MAP.len()), "The number of edges reported by SanitizerCoverage exceed the size of the edges map ({}). Use the LIBAFL_EDGES_MAP_SIZE env to increase it at compile time.", EDGES_MAP.len());
|
// assert!((MAX_EDGES_NUM <= EDGES_MAP.len()), "The number of edges reported by SanitizerCoverage exceed the size of the edges map ({}). Use the LIBAFL_EDGES_MAP_SIZE env to increase it at compile time.", EDGES_MAP.len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user