aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvan Hauser <vh@thc.org>2021-03-06 18:47:58 +0100
committerGitHub <noreply@github.com>2021-03-06 18:47:58 +0100
commit976cb3e36c130dc31fb189e9bb4f036730fca7ee (patch)
tree94143e3775e23597abe00b1ad9373c6c90b62632
parentbd0a23de73011a390714b9f3836a46443054fdd5 (diff)
parent9b3d8c327d33191b181219ffce411b40bdbe8902 (diff)
downloadafl++-976cb3e36c130dc31fb189e9bb4f036730fca7ee.tar.gz
Merge pull request #778 from AFLplusplus/dev
This fixes 3 different crash issues
-rw-r--r--.github/workflows/ci.yml4
-rw-r--r--.github/workflows/codeql-analysis.yml4
-rw-r--r--.github/workflows/rust_custom_mutator.yml30
-rw-r--r--GNUmakefile1
-rw-r--r--README.md2
-rw-r--r--TODO.md8
-rwxr-xr-xafl-cmin4
-rwxr-xr-xafl-cmin.bash1
-rw-r--r--custom_mutators/rust/custom_mutator/src/lib.rs164
-rw-r--r--custom_mutators/rust/example/src/example_mutator.rs1
-rw-r--r--custom_mutators/rust/example_lain/src/lain_mutator.rs2
-rw-r--r--docs/Changelog.md19
-rw-r--r--include/afl-fuzz.h1
-rw-r--r--include/config.h2
-rw-r--r--include/envs.h1
-rw-r--r--instrumentation/LLVMInsTrim.so.cc29
-rw-r--r--instrumentation/README.ctx.md22
-rw-r--r--instrumentation/SanitizerCoverageLTO.so.cc21
-rw-r--r--instrumentation/afl-compiler-rt.o.c251
-rw-r--r--instrumentation/afl-llvm-dict2file.so.cc23
-rw-r--r--instrumentation/afl-llvm-lto-instrumentation.so.cc21
-rw-r--r--instrumentation/afl-llvm-pass.so.cc18
-rw-r--r--instrumentation/cmplog-instructions-pass.cc14
-rw-r--r--instrumentation/compare-transform-pass.so.cc26
-rw-r--r--instrumentation/split-compares-pass.so.cc26
-rw-r--r--qemu_mode/QEMUAFL_VERSION2
-rw-r--r--qemu_mode/README.md7
-rwxr-xr-xqemu_mode/build_qemu_support.sh10
m---------qemu_mode/qemuafl0
-rw-r--r--src/afl-analyze.c1
-rw-r--r--src/afl-cc.c66
-rw-r--r--src/afl-forkserver.c15
-rw-r--r--src/afl-fuzz-extras.c149
-rw-r--r--src/afl-fuzz-queue.c47
-rw-r--r--src/afl-fuzz-redqueen.c16
-rw-r--r--src/afl-fuzz.c31
-rw-r--r--src/afl-showmap.c1
-rw-r--r--src/afl-tmin.c1
38 files changed, 798 insertions, 243 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 31cfceaf..8412fcbb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,8 +3,8 @@ name: CI
on:
push:
branches: [ stable, dev ]
- pull_request:
- branches: [ stable, dev ]
+# pull_request:
+# branches: [ stable, dev ]
jobs:
build:
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index eda8dfd0..e6c166f2 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -3,8 +3,8 @@ name: "CodeQL"
on:
push:
branches: [ stable, dev ]
- pull_request:
- branches: [ stable, dev ]
+# pull_request:
+# branches: [ stable, dev ]
jobs:
analyze:
diff --git a/.github/workflows/rust_custom_mutator.yml b/.github/workflows/rust_custom_mutator.yml
new file mode 100644
index 00000000..de2b184a
--- /dev/null
+++ b/.github/workflows/rust_custom_mutator.yml
@@ -0,0 +1,30 @@
+name: Rust Custom Mutators
+
+on:
+ push:
+ branches: [ stable, dev ]
+ pull_request:
+ branches: [ stable, dev ]
+
+jobs:
+ test:
+ name: Test Rust Custom Mutator Support
+ runs-on: '${{ matrix.os }}'
+ defaults:
+ run:
+ working-directory: custom_mutators/rust
+ strategy:
+ matrix:
+ os: [ubuntu-20.04]
+ steps:
+ - uses: actions/checkout@v2
+ - name: Install Rust Toolchain
+ uses: actions-rs/toolchain@v1
+ with:
+ toolchain: stable
+ - name: Check Code Compiles
+ run: cargo check
+ - name: Run General Tests
+ run: cargo test
+ - name: Run Tests for afl_internals feature flag
+ run: cd custom_mutator && cargo test --features=afl_internals \ No newline at end of file
diff --git a/GNUmakefile b/GNUmakefile
index 0420cdfc..871ee10d 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -364,7 +364,6 @@ help:
@echo NO_PYTHON - disable python support
@echo NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for normal fuzzing
@echo AFL_NO_X86 - if compiling on non-intel/amd platforms
- @echo NO_ARCH_OPT - builds afl++ without machine architecture optimizations
@echo "LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g. Debian)"
@echo "=========================================="
@echo e.g.: make ASAN_BUILD=1
diff --git a/README.md b/README.md
index 800c2121..6b11fee4 100644
--- a/README.md
+++ b/README.md
@@ -224,7 +224,6 @@ These build options exist:
* NO_PYTHON - disable python support
* NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for normal fuzzing
* AFL_NO_X86 - if compiling on non-intel/amd platforms
-* NO_ARCH_OPT - builds afl++ without machine architecture optimizations
* LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g. Debian)
e.g.: make ASAN_BUILD=1
@@ -239,6 +238,7 @@ Here are some good writeups to show how to effectively use AFL++:
* [https://securitylab.github.com/research/fuzzing-software-2](https://securitylab.github.com/research/fuzzing-software-2)
* [https://securitylab.github.com/research/fuzzing-sockets-FTP](https://securitylab.github.com/research/fuzzing-sockets-FTP)
* [https://securitylab.github.com/research/fuzzing-sockets-FreeRDP](https://securitylab.github.com/research/fuzzing-sockets-FreeRDP)
+ * [https://securitylab.github.com/research/fuzzing-apache-1](https://securitylab.github.com/research/fuzzing-apache-1)
If you are interested in fuzzing structured data (where you define what the
structure is), these links have you covered:
diff --git a/TODO.md b/TODO.md
index 4615c456..e5a678cf 100644
--- a/TODO.md
+++ b/TODO.md
@@ -2,12 +2,15 @@
## Roadmap 3.00+
- - AFL_MAP_SIZE for qemu_mode and unicorn_mode
- CPU affinity for many cores? There seems to be an issue > 96 cores
- afl-plot to support multiple plot_data
- afl_custom_fuzz_splice_optin()
- afl_custom_splice()
- intel-pt tracer
+ - better autodetection of shifting runtime timeout values
+ - cmplog: use colorization input for havoc?
+ - cmplog: too much tainted bytes, directly add to dict and skip?
+
## Further down the road
@@ -23,9 +26,12 @@ qemu_mode:
- add/implement AFL_QEMU_INST_LIBLIST and AFL_QEMU_NOINST_PROGRAM
- add/implement AFL_QEMU_INST_REGIONS as a list of _START/_END addresses
+
## Ideas
- LTO/sancov: write current edge to prev_loc and use that information when
using cmplog or __sanitizer_cov_trace_cmp*. maybe we can deduct by follow
up edge numbers that both following cmp paths have been found and then
disable working on this edge id -> cmplog_intelligence branch
+ - use cmplog colorization taint result for havoc locations?
+
diff --git a/afl-cmin b/afl-cmin
index 4ee79a79..7f8544eb 100755
--- a/afl-cmin
+++ b/afl-cmin
@@ -287,6 +287,10 @@ BEGIN {
exit 1
}
+ if (0 == system( "test -d "in_dir"/default" )) {
+ in_dir = in_dir "/default"
+ }
+
if (0 == system( "test -d "in_dir"/queue" )) {
in_dir = in_dir "/queue"
}
diff --git a/afl-cmin.bash b/afl-cmin.bash
index dae21939..5b2c3894 100755
--- a/afl-cmin.bash
+++ b/afl-cmin.bash
@@ -223,6 +223,7 @@ if [ ! -d "$IN_DIR" ]; then
exit 1
fi
+test -d "$IN_DIR/default" && IN_DIR="$IN_DIR/default"
test -d "$IN_DIR/queue" && IN_DIR="$IN_DIR/queue"
find "$OUT_DIR" -name 'id[:_]*' -maxdepth 1 -exec rm -- {} \; 2>/dev/null
diff --git a/custom_mutators/rust/custom_mutator/src/lib.rs b/custom_mutators/rust/custom_mutator/src/lib.rs
index b82af250..9444e4d1 100644
--- a/custom_mutators/rust/custom_mutator/src/lib.rs
+++ b/custom_mutators/rust/custom_mutator/src/lib.rs
@@ -1,3 +1,4 @@
+#![cfg(unix)]
//! Somewhat safe and somewhat ergonomic bindings for creating [AFL++](https://github.com/AFLplusplus/AFLplusplus) [custom mutators](https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/custom_mutators.md) in Rust.
//!
//! # Usage
@@ -23,7 +24,7 @@
//! The state is passed to [`CustomMutator::init`], when the feature is activated.
//!
//! _This is completely unsafe and uses automatically generated types extracted from the AFL++ source._
-use std::{ffi::CStr, fmt::Debug};
+use std::{fmt::Debug, path::Path};
#[cfg(feature = "afl_internals")]
#[doc(hidden)]
@@ -33,7 +34,7 @@ pub use custom_mutator_sys::afl_state;
#[doc(hidden)]
pub trait RawCustomMutator {
#[cfg(feature = "afl_internals")]
- fn init(afl: &'static afl_state, seed: c_uint) -> Self
+ fn init(afl: &'static afl_state, seed: u32) -> Self
where
Self: Sized;
#[cfg(not(feature = "afl_internals"))]
@@ -52,17 +53,17 @@ pub trait RawCustomMutator {
1
}
- fn queue_new_entry(&mut self, filename_new_queue: &CStr, _filename_orig_queue: Option<&CStr>) {}
+ fn queue_new_entry(&mut self, filename_new_queue: &Path, _filename_orig_queue: Option<&Path>) {}
- fn queue_get(&mut self, filename: &CStr) -> bool {
+ fn queue_get(&mut self, filename: &Path) -> bool {
true
}
- fn describe(&mut self, max_description: usize) -> Option<&CStr> {
- None
+ fn describe(&mut self, max_description: usize) -> Option<&str> {
+ Some(default_mutator_describe::<Self>(max_description))
}
- fn introspection(&mut self) -> Option<&CStr> {
+ fn introspection(&mut self) -> Option<&str> {
None
}
@@ -81,16 +82,17 @@ pub mod wrappers {
#[cfg(feature = "afl_internals")]
use custom_mutator_sys::afl_state;
- use core::slice;
use std::{
any::Any,
convert::TryInto,
- ffi::{c_void, CStr},
+ ffi::{c_void, CStr, OsStr},
mem::ManuallyDrop,
- os::raw::c_char,
+ os::{raw::c_char, unix::ffi::OsStrExt},
panic::catch_unwind,
+ path::Path,
process::abort,
ptr::null,
+ slice,
};
use crate::RawCustomMutator;
@@ -99,6 +101,10 @@ pub mod wrappers {
/// Also has some convenience functions for FFI conversions (from and to ptr) and tries to make misuse hard (see [`FFIContext::from`]).
struct FFIContext<M: RawCustomMutator> {
mutator: M,
+ /// buffer for storing the description returned by [`RawCustomMutator::describe`] as a CString
+ description_buffer: Vec<u8>,
+ /// buffer for storing the introspection returned by [`RawCustomMutator::introspect`] as a CString
+ introspection_buffer: Vec<u8>,
}
impl<M: RawCustomMutator> FFIContext<M> {
@@ -115,12 +121,16 @@ pub mod wrappers {
fn new(afl: &'static afl_state, seed: u32) -> Box<Self> {
Box::new(Self {
mutator: M::init(afl, seed),
+ description_buffer: Vec::new(),
+ introspection_buffer: Vec::new(),
})
}
#[cfg(not(feature = "afl_internals"))]
fn new(seed: u32) -> Box<Self> {
Box::new(Self {
mutator: M::init(seed),
+ description_buffer: Vec::new(),
+ introspection_buffer: Vec::new(),
})
}
}
@@ -242,9 +252,13 @@ pub mod wrappers {
if filename_new_queue.is_null() {
panic!("received null filename_new_queue in afl_custom_queue_new_entry");
}
- let filename_new_queue = unsafe { CStr::from_ptr(filename_new_queue) };
+ let filename_new_queue = Path::new(OsStr::from_bytes(
+ unsafe { CStr::from_ptr(filename_new_queue) }.to_bytes(),
+ ));
let filename_orig_queue = if !filename_orig_queue.is_null() {
- Some(unsafe { CStr::from_ptr(filename_orig_queue) })
+ Some(Path::new(OsStr::from_bytes(
+ unsafe { CStr::from_ptr(filename_orig_queue) }.to_bytes(),
+ )))
} else {
None
};
@@ -271,9 +285,14 @@ pub mod wrappers {
/// Internal function used in the macro
pub fn afl_custom_introspection_<M: RawCustomMutator>(data: *mut c_void) -> *const c_char {
match catch_unwind(|| {
- let mut context = FFIContext::<M>::from(data);
+ let context = &mut *FFIContext::<M>::from(data);
if let Some(res) = context.mutator.introspection() {
- res.as_ptr()
+ let buf = &mut context.introspection_buffer;
+ buf.clear();
+ buf.extend_from_slice(res.as_bytes());
+ buf.push(0);
+ // unwrapping here, as the error case should be extremely rare
+ CStr::from_bytes_with_nul(&buf).unwrap().as_ptr()
} else {
null()
}
@@ -289,9 +308,14 @@ pub mod wrappers {
max_description_len: usize,
) -> *const c_char {
match catch_unwind(|| {
- let mut context = FFIContext::<M>::from(data);
+ let context = &mut *FFIContext::<M>::from(data);
if let Some(res) = context.mutator.describe(max_description_len) {
- res.as_ptr()
+ let buf = &mut context.description_buffer;
+ buf.clear();
+ buf.extend_from_slice(res.as_bytes());
+ buf.push(0);
+ // unwrapping here, as the error case should be extremely rare
+ CStr::from_bytes_with_nul(&buf).unwrap().as_ptr()
} else {
null()
}
@@ -310,9 +334,9 @@ pub mod wrappers {
let mut context = FFIContext::<M>::from(data);
assert!(!filename.is_null());
- context
- .mutator
- .queue_get(unsafe { CStr::from_ptr(filename) }) as u8
+ context.mutator.queue_get(Path::new(OsStr::from_bytes(
+ unsafe { CStr::from_ptr(filename) }.to_bytes(),
+ ))) as u8
}) {
Ok(ret) => ret,
Err(err) => panic_handler("afl_custom_queue_get", err),
@@ -516,21 +540,21 @@ pub trait CustomMutator {
fn queue_new_entry(
&mut self,
- filename_new_queue: &CStr,
- filename_orig_queue: Option<&CStr>,
+ filename_new_queue: &Path,
+ filename_orig_queue: Option<&Path>,
) -> Result<(), Self::Error> {
Ok(())
}
- fn queue_get(&mut self, filename: &CStr) -> Result<bool, Self::Error> {
+ fn queue_get(&mut self, filename: &Path) -> Result<bool, Self::Error> {
Ok(true)
}
- fn describe(&mut self, max_description: usize) -> Result<Option<&CStr>, Self::Error> {
- Ok(None)
+ fn describe(&mut self, max_description: usize) -> Result<Option<&str>, Self::Error> {
+ Ok(Some(default_mutator_describe::<Self>(max_description)))
}
- fn introspection(&mut self) -> Result<Option<&CStr>, Self::Error> {
+ fn introspection(&mut self) -> Result<Option<&str>, Self::Error> {
Ok(None)
}
}
@@ -593,7 +617,7 @@ where
}
}
- fn queue_new_entry(&mut self, filename_new_queue: &CStr, filename_orig_queue: Option<&CStr>) {
+ fn queue_new_entry(&mut self, filename_new_queue: &Path, filename_orig_queue: Option<&Path>) {
match self.queue_new_entry(filename_new_queue, filename_orig_queue) {
Ok(r) => r,
Err(e) => {
@@ -602,7 +626,7 @@ where
}
}
- fn queue_get(&mut self, filename: &CStr) -> bool {
+ fn queue_get(&mut self, filename: &Path) -> bool {
match self.queue_get(filename) {
Ok(r) => r,
Err(e) => {
@@ -612,7 +636,7 @@ where
}
}
- fn describe(&mut self, max_description: usize) -> Option<&CStr> {
+ fn describe(&mut self, max_description: usize) -> Option<&str> {
match self.describe(max_description) {
Ok(r) => r,
Err(e) => {
@@ -622,7 +646,7 @@ where
}
}
- fn introspection(&mut self) -> Option<&CStr> {
+ fn introspection(&mut self) -> Option<&str> {
match self.introspection() {
Ok(r) => r,
Err(e) => {
@@ -632,3 +656,85 @@ where
}
}
}
+
+/// the default value to return from [`CustomMutator::describe`].
+fn default_mutator_describe<T: ?Sized>(max_len: usize) -> &'static str {
+ truncate_str_unicode_safe(std::any::type_name::<T>(), max_len)
+}
+
+#[cfg(all(test, not(feature = "afl_internals")))]
+mod default_mutator_describe {
+ struct MyMutator;
+ use super::CustomMutator;
+ impl CustomMutator for MyMutator {
+ type Error = ();
+
+ fn init(_: u32) -> Result<Self, Self::Error> {
+ Ok(Self)
+ }
+
+ fn fuzz<'b, 's: 'b>(
+ &'s mut self,
+ _: &'b mut [u8],
+ _: Option<&[u8]>,
+ _: usize,
+ ) -> Result<Option<&'b [u8]>, Self::Error> {
+ unimplemented!()
+ }
+ }
+
+ #[test]
+ fn test_default_describe() {
+ assert_eq!(
+ MyMutator::init(0).unwrap().describe(64).unwrap().unwrap(),
+ "custom_mutator::default_mutator_describe::MyMutator"
+ );
+ }
+}
+
+/// little helper function to truncate a `str` to a maximum of bytes while retaining unicode safety
+fn truncate_str_unicode_safe(s: &str, max_len: usize) -> &str {
+ if s.len() <= max_len {
+ s
+ } else {
+ if let Some((last_index, _)) = s
+ .char_indices()
+ .take_while(|(index, _)| *index <= max_len)
+ .last()
+ {
+ &s[..last_index]
+ } else {
+ ""
+ }
+ }
+}
+
+#[cfg(test)]
+mod truncate_test {
+ use super::truncate_str_unicode_safe;
+
+ #[test]
+ fn test_truncate() {
+ for (max_len, input, expected_output) in &[
+ (0usize, "a", ""),
+ (1, "a", "a"),
+ (1, "ä", ""),
+ (2, "ä", "ä"),
+ (3, "äa", "äa"),
+ (4, "äa", "äa"),
+ (1, "👎", ""),
+ (2, "👎", ""),
+ (3, "👎", ""),
+ (4, "👎", "👎"),
+ (1, "abc", "a"),
+ (2, "abc", "ab"),
+ ] {
+ let actual_output = truncate_str_unicode_safe(input, *max_len);
+ assert_eq!(
+ &actual_output, expected_output,
+ "{:#?} truncated to {} bytes should be {:#?}, but is {:#?}",
+ input, max_len, expected_output, actual_output
+ );
+ }
+ }
+}
diff --git a/custom_mutators/rust/example/src/example_mutator.rs b/custom_mutators/rust/example/src/example_mutator.rs
index 9b9d4997..c4711dd1 100644
--- a/custom_mutators/rust/example/src/example_mutator.rs
+++ b/custom_mutators/rust/example/src/example_mutator.rs
@@ -1,3 +1,4 @@
+#![cfg(unix)]
#![allow(unused_variables)]
use custom_mutator::{export_mutator, CustomMutator};
diff --git a/custom_mutators/rust/example_lain/src/lain_mutator.rs b/custom_mutators/rust/example_lain/src/lain_mutator.rs
index 22e5fe73..7099aeae 100644
--- a/custom_mutators/rust/example_lain/src/lain_mutator.rs
+++ b/custom_mutators/rust/example_lain/src/lain_mutator.rs
@@ -1,3 +1,5 @@
+#![cfg(unix)]
+
use custom_mutator::{export_mutator, CustomMutator};
use lain::{
mutator::Mutator,
diff --git a/docs/Changelog.md b/docs/Changelog.md
index f3e15b6a..3ca4a20b 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -8,6 +8,25 @@
Want to stay in the loop on major new features? Join our mailing list by
sending a mail to <afl-users+subscribe@googlegroups.com>.
+### Version ++3.11a (dev)
+ - afl-fuzz
+ - add non-unicode variants from unicode-looking dictionary entries
+ - Rust custom mutator API improvements
+ - afl-cc
+ - fixed a crash that can occur with ASAN + CMPLOG together plus
+ better support for unicode (thanks to @stbergmann for reporting!)
+ - fixed a crash in LAF transform for empty strings
+ - handle erroneous setups in which multiple afl-compiler-rt are
+ compiled into the target. This now also supports dlopen()
+ instrumented libs loaded before the forkserver and even after the
+ forkserver is started (then with collisions though)
+ - Renamed CTX to CALLER, added correct/real CTX implementation to CLASSIC
+ - qemu_mode
+ - added AFL_QEMU_EXCLUDE_RANGES env by @realmadsci, thanks!
+ - if no new/updated checkout is wanted, build with:
+ NO_CHECKOUT=1 ./build_qemu_support.sh
+ - we no longer perform a "git drop"
+
### Version ++3.10c (release)
- Mac OS ARM64 support
diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h
index 3531d672..5003b563 100644
--- a/include/afl-fuzz.h
+++ b/include/afl-fuzz.h
@@ -1062,6 +1062,7 @@ u8 has_new_bits_unclassified(afl_state_t *, u8 *);
void load_extras_file(afl_state_t *, u8 *, u32 *, u32 *, u32);
void load_extras(afl_state_t *, u8 *);
void dedup_extras(afl_state_t *);
+void deunicode_extras(afl_state_t *);
void add_extra(afl_state_t *afl, u8 *mem, u32 len);
void maybe_add_auto(afl_state_t *, u8 *, u32);
void save_auto(afl_state_t *);
diff --git a/include/config.h b/include/config.h
index c583f23b..a3aa9a4a 100644
--- a/include/config.h
+++ b/include/config.h
@@ -26,7 +26,7 @@
/* Version string: */
// c = release, a = volatile github dev, e = experimental branch
-#define VERSION "++3.10c"
+#define VERSION "++3.11a"
/******************************************************
* *
diff --git a/include/envs.h b/include/envs.h
index 143979c6..26f4de90 100644
--- a/include/envs.h
+++ b/include/envs.h
@@ -141,6 +141,7 @@ static char *afl_environment_variables[] = {
"AFL_QEMU_PERSISTENT_RETADDR_OFFSET",
"AFL_QEMU_PERSISTENT_EXITS",
"AFL_QEMU_INST_RANGES",
+ "AFL_QEMU_EXCLUDE_RANGES",
"AFL_QEMU_SNAPSHOT",
"AFL_QUIET",
"AFL_RANDOM_ALLOC_CANARY",
diff --git a/instrumentation/LLVMInsTrim.so.cc b/instrumentation/LLVMInsTrim.so.cc
index 948f8f3a..f0de6536 100644
--- a/instrumentation/LLVMInsTrim.so.cc
+++ b/instrumentation/LLVMInsTrim.so.cc
@@ -135,7 +135,7 @@ struct InsTrim : public ModulePass {
unsigned int PrevLocSize = 0;
char * ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE");
if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE");
- char *ctx_str = getenv("AFL_LLVM_CTX");
+ char *caller_str = getenv("AFL_LLVM_CALLER");
#ifdef AFL_HAVE_VECTOR_INTRINSICS
unsigned int ngram_size = 0;
@@ -197,9 +197,9 @@ struct InsTrim : public ModulePass {
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
GlobalVariable *AFLPrevLoc;
GlobalVariable *AFLContext = NULL;
- LoadInst * PrevCtx = NULL; // for CTX sensitive coverage
+ LoadInst * PrevCaller = NULL; // for CALLER sensitive coverage
- if (ctx_str)
+ if (caller_str)
#if defined(__ANDROID__) || defined(__HAIKU__)
AFLContext = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx");
@@ -398,11 +398,11 @@ struct InsTrim : public ModulePass {
unsigned int cur_loc;
// Context sensitive coverage
- if (ctx_str && &BB == &F.getEntryBlock()) {
+ if (caller_str && &BB == &F.getEntryBlock()) {
- PrevCtx = IRB.CreateLoad(AFLContext);
- PrevCtx->setMetadata(M.getMDKindID("nosanitize"),
- MDNode::get(C, None));
+ PrevCaller = IRB.CreateLoad(AFLContext);
+ PrevCaller->setMetadata(M.getMDKindID("nosanitize"),
+ MDNode::get(C, None));
// does the function have calls? and is any of the calls larger than
// one basic block?
@@ -441,7 +441,7 @@ struct InsTrim : public ModulePass {
}
- } // END of ctx_str
+ } // END of caller_str
if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; }
@@ -485,9 +485,9 @@ struct InsTrim : public ModulePass {
#endif
PrevLocTrans = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty());
- if (ctx_str)
+ if (caller_str)
PrevLocTrans =
- IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty);
+ IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCaller), Int32Ty);
/* Load SHM pointer */
LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
@@ -535,16 +535,17 @@ struct InsTrim : public ModulePass {
IRB.CreateStore(Incr, MapPtrIdx)
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
- if (ctx_str && has_calls) {
+ if (caller_str && has_calls) {
- // in CTX mode we have to restore the original context for the
+ // in CALLER mode we have to restore the original context for the
// caller - she might be calling other functions which need the
- // correct CTX
+ // correct CALLER
Instruction *Inst = BB.getTerminator();
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
IRBuilder<> Post_IRB(Inst);
- StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext);
+ StoreInst * RestoreCtx =
+ Post_IRB.CreateStore(PrevCaller, AFLContext);
RestoreCtx->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
diff --git a/instrumentation/README.ctx.md b/instrumentation/README.ctx.md
index caf2c09a..577b3e5f 100644
--- a/instrumentation/README.ctx.md
+++ b/instrumentation/README.ctx.md
@@ -4,14 +4,19 @@
This is an LLVM-based implementation of the context sensitive branch coverage.
-Basically every function gets its own ID and that ID is combined with the
-edges of the called functions.
+Basically every function gets its own ID and, every time when an edge is logged,
+all the IDs in the callstack are hashed and combined with the edge transition
+hash to augment the classic edge coverage with the information about the
+calling context.
So if both function A and function B call a function C, the coverage
collected in C will be different.
In math the coverage is collected as follows:
-`map[current_location_ID ^ previous_location_ID >> 1 ^ previous_callee_ID] += 1`
+`map[current_location_ID ^ previous_location_ID >> 1 ^ hash_callstack_IDs] += 1`
+
+The callstack hash is produced XOR-ing the function IDs to avoid explosion with
+recursive functions.
## Usage
@@ -20,3 +25,14 @@ Set the `AFL_LLVM_INSTRUMENT=CTX` or `AFL_LLVM_CTX=1` environment variable.
It is highly recommended to increase the MAP_SIZE_POW2 definition in
config.h to at least 18 and maybe up to 20 for this as otherwise too
many map collisions occur.
+
+## Caller Branch Coverage
+
+If the context sensitive coverage introduces too may collisions and becoming
+decremental, the user can choose to augment edge coverage with just the
+called function ID, instead of the entire callstack hash.
+
+In math the coverage is collected as follows:
+`map[current_location_ID ^ previous_location_ID >> 1 ^ previous_callee_ID] += 1`
+
+Set the `AFL_LLVM_INSTRUMENT=CALLER` or `AFL_LLVM_CALLER=1` environment variable.
diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc
index 3026abc8..13a5e5fd 100644
--- a/instrumentation/SanitizerCoverageLTO.so.cc
+++ b/instrumentation/SanitizerCoverageLTO.so.cc
@@ -733,7 +733,7 @@ bool ModuleSanitizerCoverage::instrumentModule(
Var->getInitializer())) {
HasStr2 = true;
- Str2 = Array->getAsString().str();
+ Str2 = Array->getRawDataValues().str();
}
@@ -809,7 +809,7 @@ bool ModuleSanitizerCoverage::instrumentModule(
Var->getInitializer())) {
HasStr1 = true;
- Str1 = Array->getAsString().str();
+ Str1 = Array->getRawDataValues().str();
}
@@ -849,15 +849,18 @@ bool ModuleSanitizerCoverage::instrumentModule(
thestring = Str2;
optLen = thestring.length();
+ if (optLen < 2 || (optLen == 2 && !thestring[1])) { continue; }
if (isMemcmp || isStrncmp || isStrncasecmp) {
Value * op2 = callInst->getArgOperand(2);
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
+
if (ilen) {
uint64_t literalLength = optLen;
optLen = ilen->getZExtValue();
+ if (optLen < 2) { continue; }
if (literalLength + 1 == optLen) { // add null byte
thestring.append("\0", 1);
addedNull = true;
@@ -872,17 +875,21 @@ bool ModuleSanitizerCoverage::instrumentModule(
// was not already added
if (!isMemcmp) {
- if (addedNull == false) {
+ if (addedNull == false && thestring[optLen - 1] != '\0') {
thestring.append("\0", 1); // add null byte
optLen++;
}
- // ensure we do not have garbage
- size_t offset = thestring.find('\0', 0);
- if (offset + 1 < optLen) optLen = offset + 1;
- thestring = thestring.substr(0, optLen);
+ if (!isStdString) {
+
+ // ensure we do not have garbage
+ size_t offset = thestring.find('\0', 0);
+ if (offset + 1 < optLen) optLen = offset + 1;
+ thestring = thestring.substr(0, optLen);
+
+ }
}
diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c
index ecb94cab..a702ec39 100644
--- a/instrumentation/afl-compiler-rt.o.c
+++ b/instrumentation/afl-compiler-rt.o.c
@@ -34,6 +34,7 @@
#include <errno.h>
#include <sys/mman.h>
+#include <sys/syscall.h>
#ifndef __HAIKU__
#include <sys/shm.h>
#endif
@@ -122,6 +123,21 @@ static u8 is_persistent;
static u8 _is_sancov;
+/* Debug? */
+
+static u32 __afl_debug;
+
+/* Already initialized markers */
+
+u32 __afl_already_initialized_shm;
+u32 __afl_already_initialized_forkserver;
+u32 __afl_already_initialized_first;
+u32 __afl_already_initialized_second;
+
+/* Dummy pipe for area_is_valid() */
+
+static int __afl_dummy_fd[2] = {2, 2};
+
/* ensure we kill the child on termination */
void at_exit(int signal) {
@@ -171,7 +187,7 @@ static void __afl_map_shm_fuzz() {
char *id_str = getenv(SHM_FUZZ_ENV_VAR);
- if (getenv("AFL_DEBUG")) {
+ if (__afl_debug) {
fprintf(stderr, "DEBUG: fuzzcase shmem %s\n", id_str ? id_str : "none");
@@ -217,7 +233,7 @@ static void __afl_map_shm_fuzz() {
__afl_fuzz_len = (u32 *)map;
__afl_fuzz_ptr = map + sizeof(u32);
- if (getenv("AFL_DEBUG")) {
+ if (__afl_debug) {
fprintf(stderr, "DEBUG: successfully got fuzzing shared memory\n");
@@ -237,6 +253,9 @@ static void __afl_map_shm_fuzz() {
static void __afl_map_shm(void) {
+ if (__afl_already_initialized_shm) return;
+ __afl_already_initialized_shm = 1;
+
// if we are not running in afl ensure the map exists
if (!__afl_area_ptr) { __afl_area_ptr = __afl_area_ptr_dummy; }
@@ -294,7 +313,7 @@ static void __afl_map_shm(void) {
early-stage __afl_area_initial region that is needed to allow some really
hacky .init code to work correctly in projects such as OpenSSL. */
- if (getenv("AFL_DEBUG"))
+ if (__afl_debug)
fprintf(stderr,
"DEBUG: id_str %s, __afl_area_ptr %p, __afl_area_initial %p, "
"__afl_map_addr 0x%llx, MAP_SIZE %u, __afl_final_loc %u, "
@@ -350,17 +369,18 @@ static void __afl_map_shm(void) {
}
- if (shm_base == MAP_FAILED) {
+ close(shm_fd);
+ shm_fd = -1;
- close(shm_fd);
- shm_fd = -1;
+ if (shm_base == MAP_FAILED) {
fprintf(stderr, "mmap() failed\n");
+ perror("mmap for map");
+
if (__afl_map_addr)
send_forkserver_error(FS_ERROR_MAP_ADDR);
else
send_forkserver_error(FS_ERROR_MMAP);
- perror("mmap for map");
exit(2);
@@ -467,7 +487,7 @@ static void __afl_map_shm(void) {
id_str = getenv(CMPLOG_SHM_ENV_VAR);
- if (getenv("AFL_DEBUG")) {
+ if (__afl_debug) {
fprintf(stderr, "DEBUG: cmplog id_str %s\n",
id_str == NULL ? "<null>" : id_str);
@@ -476,6 +496,12 @@ static void __afl_map_shm(void) {
if (id_str) {
+ if ((__afl_dummy_fd[1] = open("/dev/null", O_WRONLY)) < 0) {
+
+ if (pipe(__afl_dummy_fd) < 0) { __afl_dummy_fd[1] = 1; }
+
+ }
+
#ifdef USEMMAP
const char * shm_file_path = id_str;
int shm_fd = -1;
@@ -526,6 +552,58 @@ static void __afl_map_shm(void) {
}
+/* unmap SHM. */
+
+static void __afl_unmap_shm(void) {
+
+ if (!__afl_already_initialized_shm) return;
+
+ char *id_str = getenv(SHM_ENV_VAR);
+
+ if (id_str) {
+
+#ifdef USEMMAP
+
+ munmap((void *)__afl_area_ptr, __afl_map_size);
+
+#else
+
+ shmdt((void *)__afl_area_ptr);
+
+#endif
+
+ } else if ((!__afl_area_ptr || __afl_area_ptr == __afl_area_initial) &&
+
+ __afl_map_addr) {
+
+ munmap((void *)__afl_map_addr, __afl_map_size);
+
+ }
+
+ __afl_area_ptr = __afl_area_ptr_dummy;
+
+ id_str = getenv(CMPLOG_SHM_ENV_VAR);
+
+ if (id_str) {
+
+#ifdef USEMMAP
+
+ munmap((void *)__afl_cmp_map, __afl_map_size);
+
+#else
+
+ shmdt((void *)__afl_cmp_map);
+
+#endif
+
+ __afl_cmp_map = NULL;
+
+ }
+
+ __afl_already_initialized_shm = 0;
+
+}
+
#ifdef __linux__
static void __afl_start_snapshots(void) {
@@ -554,7 +632,7 @@ static void __afl_start_snapshots(void) {
if (read(FORKSRV_FD, &was_killed, 4) != 4) { _exit(1); }
- if (getenv("AFL_DEBUG")) {
+ if (__afl_debug) {
fprintf(stderr, "target forkserver recv: %08x\n", was_killed);
@@ -731,6 +809,9 @@ static void __afl_start_snapshots(void) {
static void __afl_start_forkserver(void) {
+ if (__afl_already_initialized_forkserver) return;
+ __afl_already_initialized_forkserver = 1;
+
struct sigaction orig_action;
sigaction(SIGTERM, NULL, &orig_action);
old_sigterm_handler = orig_action.sa_handler;
@@ -781,7 +862,7 @@ static void __afl_start_forkserver(void) {
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
- if (getenv("AFL_DEBUG")) {
+ if (__afl_debug) {
fprintf(stderr, "target forkserver recv: %08x\n", was_killed);
@@ -1016,7 +1097,7 @@ void __afl_manual_init(void) {
__afl_sharedmem_fuzzing = 0;
if (__afl_area_ptr == NULL) __afl_area_ptr = __afl_area_ptr_dummy;
- if (getenv("AFL_DEBUG"))
+ if (__afl_debug)
fprintf(stderr,
"DEBUG: disabled instrumentation because of "
"AFL_DISABLE_LLVM_INSTRUMENTATION\n");
@@ -1060,6 +1141,11 @@ __attribute__((constructor(CTOR_PRIO))) void __afl_auto_early(void) {
__attribute__((constructor(1))) void __afl_auto_second(void) {
+ if (__afl_already_initialized_second) return;
+ __afl_already_initialized_second = 1;
+
+ if (getenv("AFL_DEBUG")) { __afl_debug = 1; }
+
if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return;
u8 *ptr;
@@ -1084,17 +1170,18 @@ __attribute__((constructor(1))) void __afl_auto_second(void) {
}
-}
+} // ptr memleak report is a false positive
/* preset __afl_area_ptr #1 - at constructor level 0 global variables have
not been set */
__attribute__((constructor(0))) void __afl_auto_first(void) {
- if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return;
- u8 *ptr;
+ if (__afl_already_initialized_first) return;
+ __afl_already_initialized_first = 1;
- ptr = (u8 *)malloc(MAP_INITIAL_SIZE);
+ if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return;
+ u8 *ptr = (u8 *)malloc(MAP_INITIAL_SIZE);
if (ptr && (ssize_t)ptr != -1) {
@@ -1103,7 +1190,7 @@ __attribute__((constructor(0))) void __afl_auto_first(void) {
}
-}
+} // ptr memleak report is a false positive
/* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard.
It remains non-operational in the traditional, plugin-backed LLVM mode.
@@ -1171,11 +1258,13 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
_is_sancov = 1;
- if (getenv("AFL_DEBUG")) {
+ if (__afl_debug) {
fprintf(stderr,
- "Running __sanitizer_cov_trace_pc_guard_init: %p-%p (%lu edges)\n",
- start, stop, (unsigned long)(stop - start));
+ "Running __sanitizer_cov_trace_pc_guard_init: %p-%p (%lu edges) "
+ "after_fs=%u\n",
+ start, stop, (unsigned long)(stop - start),
+ __afl_already_initialized_forkserver);
}
@@ -1191,6 +1280,36 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
}
+ /* instrumented code is loaded *after* our forkserver is up. this is a
+ problem. We cannot prevent collisions then :( */
+ if (__afl_already_initialized_forkserver &&
+ __afl_final_loc + 1 + stop - start > __afl_map_size) {
+
+ if (__afl_debug)
+ fprintf(stderr, "Warning: new instrumneted code after the forkserver!\n");
+ __afl_final_loc = 2;
+
+ if (1 + stop - start > __afl_map_size) {
+
+ *(start++) = ++__afl_final_loc;
+
+ while (start < stop) {
+
+ if (R(100) < inst_ratio)
+ *start = ++__afl_final_loc % __afl_map_size;
+ else
+ *start = 0;
+
+ start++;
+
+ }
+
+ return;
+
+ }
+
+ }
+
/* Make sure that the first element in the range is always set - we use that
to avoid duplicate calls (which can happen as an artifact of the underlying
implementation in LLVM). */
@@ -1208,6 +1327,28 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
}
+ if (__afl_debug) {
+
+ fprintf(stderr,
+ "Done __sanitizer_cov_trace_pc_guard_init: __afl_final_loc = %u\n",
+ __afl_final_loc);
+
+ }
+
+ if (__afl_already_initialized_shm && __afl_final_loc > __afl_map_size) {
+
+ if (__afl_debug) {
+
+ fprintf(stderr, "Reinit shm necessary (+%u)\n",
+ __afl_final_loc - __afl_map_size);
+
+ }
+
+ __afl_unmap_shm();
+ __afl_map_shm();
+
+ }
+
}
///// CmpLog instrumentation
@@ -1551,17 +1692,42 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) {
}
+__attribute__((weak)) void *__asan_region_is_poisoned(void *beg, size_t size) {
+
+ return NULL;
+
+}
+
// POSIX shenanigan to see if an area is mapped.
// If it is mapped as X-only, we have a problem, so maybe we should add a check
// to avoid to call it on .text addresses
-static int area_is_mapped(void *ptr, size_t len) {
+static int area_is_valid(void *ptr, size_t len) {
- char *p = (char *)ptr;
- char *page = (char *)((uintptr_t)p & ~(sysconf(_SC_PAGE_SIZE) - 1));
+ if (unlikely(__asan_region_is_poisoned(ptr, len))) { return 0; }
- int r = msync(page, (p - page) + len, MS_ASYNC);
- if (r < 0) return errno != ENOMEM;
- return 1;
+ long r = syscall(__afl_dummy_fd[1], SYS_write, ptr, len);
+
+ if (unlikely(r <= 0 || r > len)) { // fail - maybe hitting asan boundary?
+
+ char *p = (char *)ptr;
+ long page_size = sysconf(_SC_PAGE_SIZE);
+ char *page = (char *)((uintptr_t)p & ~(page_size - 1)) + page_size;
+ if (page < p + len) { return 0; } // no isnt, return fail
+ len -= (p + len - page);
+ r = syscall(__afl_dummy_fd[1], SYS_write, p, len);
+
+ }
+
+ // partial writes - we return what was written.
+ if (likely(r >= 0 && r <= len)) {
+
+ return (int)r;
+
+ } else {
+
+ return 0;
+
+ }
}
@@ -1569,19 +1735,22 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) {
/*
u32 i;
- if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return;
+ if (area_is_valid(ptr1, 32) <= 0 || area_is_valid(ptr2, 32) <= 0) return;
fprintf(stderr, "rtn arg0=");
- for (i = 0; i < 24; i++)
+ for (i = 0; i < 32; i++)
fprintf(stderr, "%02x", ptr1[i]);
fprintf(stderr, " arg1=");
- for (i = 0; i < 24; i++)
+ for (i = 0; i < 32; i++)
fprintf(stderr, "%02x", ptr2[i]);
fprintf(stderr, "\n");
*/
if (unlikely(!__afl_cmp_map)) return;
-
- if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return;
+ int l1, l2;
+ if ((l1 = area_is_valid(ptr1, 32)) <= 0 ||
+ (l2 = area_is_valid(ptr2, 32)) <= 0)
+ return;
+ int len = MIN(l1, l2);
uintptr_t k = (uintptr_t)__builtin_return_address(0);
k = (k >> 4) ^ (k << 8);
@@ -1592,17 +1761,17 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) {
if (__afl_cmp_map->headers[k].type != CMP_TYPE_RTN) {
__afl_cmp_map->headers[k].type = CMP_TYPE_RTN;
- hits = 0;
__afl_cmp_map->headers[k].hits = 1;
- __afl_cmp_map->headers[k].shape = 31;
+ __afl_cmp_map->headers[k].shape = len - 1;
+ hits = 0;
} else {
hits = __afl_cmp_map->headers[k].hits++;
- if (__afl_cmp_map->headers[k].shape < 31) {
+ if (__afl_cmp_map->headers[k].shape < len) {
- __afl_cmp_map->headers[k].shape = 31;
+ __afl_cmp_map->headers[k].shape = len;
}
@@ -1610,9 +1779,9 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) {
hits &= CMP_MAP_RTN_H - 1;
__builtin_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v0,
- ptr1, 32);
+ ptr1, len);
__builtin_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1,
- ptr2, 32);
+ ptr2, len);
}
@@ -1658,7 +1827,8 @@ static u8 *get_llvm_stdstring(u8 *string) {
void __cmplog_rtn_gcc_stdstring_cstring(u8 *stdstring, u8 *cstring) {
if (unlikely(!__afl_cmp_map)) return;
- if (!area_is_mapped(stdstring, 32) || !area_is_mapped(cstring, 32)) return;
+ if (area_is_valid(stdstring, 32) <= 0 || area_is_valid(cstring, 32) <= 0)
+ return;
__cmplog_rtn_hook(get_gcc_stdstring(stdstring), cstring);
@@ -1667,7 +1837,7 @@ void __cmplog_rtn_gcc_stdstring_cstring(u8 *stdstring, u8 *cstring) {
void __cmplog_rtn_gcc_stdstring_stdstring(u8 *stdstring1, u8 *stdstring2) {
if (unlikely(!__afl_cmp_map)) return;
- if (!area_is_mapped(stdstring1, 32) || !area_is_mapped(stdstring2, 32))
+ if (area_is_valid(stdstring1, 32) <= 0 || area_is_valid(stdstring2, 32) <= 0)
return;
__cmplog_rtn_hook(get_gcc_stdstring(stdstring1),
@@ -1678,7 +1848,8 @@ void __cmplog_rtn_gcc_stdstring_stdstring(u8 *stdstring1, u8 *stdstring2) {
void __cmplog_rtn_llvm_stdstring_cstring(u8 *stdstring, u8 *cstring) {
if (unlikely(!__afl_cmp_map)) return;
- if (!area_is_mapped(stdstring, 32) || !area_is_mapped(cstring, 32)) return;
+ if (area_is_valid(stdstring, 32) <= 0 || area_is_valid(cstring, 32) <= 0)
+ return;
__cmplog_rtn_hook(get_llvm_stdstring(stdstring), cstring);
@@ -1687,7 +1858,7 @@ void __cmplog_rtn_llvm_stdstring_cstring(u8 *stdstring, u8 *cstring) {
void __cmplog_rtn_llvm_stdstring_stdstring(u8 *stdstring1, u8 *stdstring2) {
if (unlikely(!__afl_cmp_map)) return;
- if (!area_is_mapped(stdstring1, 32) || !area_is_mapped(stdstring2, 32))
+ if (area_is_valid(stdstring1, 32) <= 0 || area_is_valid(stdstring2, 32) <= 0)
return;
__cmplog_rtn_hook(get_llvm_stdstring(stdstring1),
diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc
index 19ef15f7..c954054b 100644
--- a/instrumentation/afl-llvm-dict2file.so.cc
+++ b/instrumentation/afl-llvm-dict2file.so.cc
@@ -357,6 +357,7 @@ bool AFLdict2filePass::runOnModule(Module &M) {
StringRef TmpStr;
bool HasStr1;
getConstantStringInfo(Str1P, TmpStr);
+
if (TmpStr.empty()) {
HasStr1 = false;
@@ -403,7 +404,7 @@ bool AFLdict2filePass::runOnModule(Module &M) {
dyn_cast<ConstantDataArray>(Var->getInitializer())) {
HasStr2 = true;
- Str2 = Array->getAsString().str();
+ Str2 = Array->getRawDataValues().str();
}
@@ -479,7 +480,7 @@ bool AFLdict2filePass::runOnModule(Module &M) {
dyn_cast<ConstantDataArray>(Var->getInitializer())) {
HasStr1 = true;
- Str1 = Array->getAsString().str();
+ Str1 = Array->getRawDataValues().str();
}
@@ -520,14 +521,18 @@ bool AFLdict2filePass::runOnModule(Module &M) {
optLen = thestring.length();
+ if (optLen < 2 || (optLen == 2 && !thestring[1])) { continue; }
+
if (isMemcmp || isStrncmp || isStrncasecmp) {
Value * op2 = callInst->getArgOperand(2);
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
+
if (ilen) {
uint64_t literalLength = optLen;
optLen = ilen->getZExtValue();
+ if (optLen < 2) { continue; }
if (literalLength + 1 == optLen) { // add null byte
thestring.append("\0", 1);
addedNull = true;
@@ -542,17 +547,21 @@ bool AFLdict2filePass::runOnModule(Module &M) {
// was not already added
if (!isMemcmp) {
- if (addedNull == false) {
+ if (addedNull == false && thestring[optLen - 1] != '\0') {
thestring.append("\0", 1); // add null byte
optLen++;
}
- // ensure we do not have garbage
- size_t offset = thestring.find('\0', 0);
- if (offset + 1 < optLen) optLen = offset + 1;
- thestring = thestring.substr(0, optLen);
+ if (!isStdString) {
+
+ // ensure we do not have garbage
+ size_t offset = thestring.find('\0', 0);
+ if (offset + 1 < optLen) optLen = offset + 1;
+ thestring = thestring.substr(0, optLen);
+
+ }
}
diff --git a/instrumentation/afl-llvm-lto-instrumentation.so.cc b/instrumentation/afl-llvm-lto-instrumentation.so.cc
index 137bae2c..50306224 100644
--- a/instrumentation/afl-llvm-lto-instrumentation.so.cc
+++ b/instrumentation/afl-llvm-lto-instrumentation.so.cc
@@ -519,7 +519,7 @@ bool AFLLTOPass::runOnModule(Module &M) {
Var->getInitializer())) {
HasStr2 = true;
- Str2 = Array->getAsString().str();
+ Str2 = Array->getRawDataValues().str();
}
@@ -595,7 +595,7 @@ bool AFLLTOPass::runOnModule(Module &M) {
Var->getInitializer())) {
HasStr1 = true;
- Str1 = Array->getAsString().str();
+ Str1 = Array->getRawDataValues().str();
}
@@ -635,15 +635,18 @@ bool AFLLTOPass::runOnModule(Module &M) {
thestring = Str2;
optLen = thestring.length();
+ if (optLen < 2 || (optLen == 2 && !thestring[1])) { continue; }
if (isMemcmp || isStrncmp || isStrncasecmp) {
Value * op2 = callInst->getArgOperand(2);
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
+
if (ilen) {
uint64_t literalLength = optLen;
optLen = ilen->getZExtValue();
+ if (optLen < 2) { continue; }
if (literalLength + 1 == optLen) { // add null byte
thestring.append("\0", 1);
addedNull = true;
@@ -658,17 +661,21 @@ bool AFLLTOPass::runOnModule(Module &M) {
// was not already added
if (!isMemcmp) {
- if (addedNull == false) {
+ if (addedNull == false && thestring[optLen - 1] != '\0') {
thestring.append("\0", 1); // add null byte
optLen++;
}
- // ensure we do not have garbage
- size_t offset = thestring.find('\0', 0);
- if (offset + 1 < optLen) optLen = offset + 1;
- thestring = thestring.substr(0, optLen);
+ if (!isStdString) {
+
+ // ensure we do not have garbage
+ size_t offset = thestring.find('\0', 0);
+ if (offset + 1 < optLen) optLen = offset + 1;
+ thestring = thestring.substr(0, optLen);
+
+ }
}
diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc
index 16fd9c94..33898aec 100644
--- a/instrumentation/afl-llvm-pass.so.cc
+++ b/instrumentation/afl-llvm-pass.so.cc
@@ -84,7 +84,7 @@ class AFLCoverage : public ModulePass {
uint32_t ngram_size = 0;
uint32_t map_size = MAP_SIZE;
uint32_t function_minimum_size = 1;
- char * ctx_str = NULL, *skip_nozero = NULL;
+ char * ctx_str = NULL, *caller_str = NULL, *skip_nozero = NULL;
};
@@ -187,6 +187,7 @@ bool AFLCoverage::runOnModule(Module &M) {
char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE");
if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE");
ctx_str = getenv("AFL_LLVM_CTX");
+ caller_str = getenv("AFL_LLVM_CALLER");
#ifdef AFL_HAVE_VECTOR_INTRINSICS
/* Decide previous location vector size (must be a power of two) */
@@ -240,7 +241,7 @@ bool AFLCoverage::runOnModule(Module &M) {
GlobalVariable *AFLPrevLoc;
GlobalVariable *AFLContext = NULL;
- if (ctx_str)
+ if (ctx_str || caller_str)
#if defined(__ANDROID__) || defined(__HAIKU__)
AFLContext = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx");
@@ -318,7 +319,7 @@ bool AFLCoverage::runOnModule(Module &M) {
IRBuilder<> IRB(&(*IP));
// Context sensitive coverage
- if (ctx_str && &BB == &F.getEntryBlock()) {
+ if ((ctx_str || caller_str) && &BB == &F.getEntryBlock()) {
// load the context ID of the previous function and write to to a local
// variable on the stack
@@ -354,8 +355,9 @@ bool AFLCoverage::runOnModule(Module &M) {
// if yes we store a context ID for this function in the global var
if (has_calls) {
- ConstantInt *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size));
- StoreInst * StoreCtx = IRB.CreateStore(NewCtx, AFLContext);
+ Value *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size));
+ if (ctx_str) NewCtx = IRB.CreateXor(PrevCtx, NewCtx);
+ StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext);
StoreCtx->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
@@ -411,7 +413,7 @@ bool AFLCoverage::runOnModule(Module &M) {
// 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 (ctx_str && has_calls) {
+ if ((ctx_str || caller_str) && has_calls) {
Instruction *Inst = BB.getTerminator();
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
@@ -458,7 +460,7 @@ bool AFLCoverage::runOnModule(Module &M) {
#endif
PrevLocTrans = PrevLoc;
- if (ctx_str)
+ if (ctx_str || caller_str)
PrevLocTrans =
IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty);
else
@@ -545,7 +547,7 @@ bool AFLCoverage::runOnModule(Module &M) {
// 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 (ctx_str && has_calls) {
+ if ((ctx_str || caller_str) && has_calls) {
Instruction *Inst = BB.getTerminator();
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc
index dbca9afa..ad334d3b 100644
--- a/instrumentation/cmplog-instructions-pass.cc
+++ b/instrumentation/cmplog-instructions-pass.cc
@@ -418,7 +418,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
IntegerType * intTyOp0 = NULL;
IntegerType * intTyOp1 = NULL;
unsigned max_size = 0, cast_size = 0;
- unsigned char attr = 0, do_cast = 0;
+ unsigned char attr = 0;
std::vector<Value *> args;
CmpInst *cmpInst = dyn_cast<CmpInst>(selectcmpInst);
@@ -484,7 +484,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
max_size = 128;
attr += 8;
- do_cast = 1;
} else {
@@ -503,12 +502,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
if (!max_size || max_size < 16) { continue; }
- if (max_size % 8) {
-
- max_size = (((max_size / 8) + 1) * 8);
- do_cast = 1;
-
- }
+ if (max_size % 8) { max_size = (((max_size / 8) + 1) * 8); }
if (max_size > 128) {
@@ -521,7 +515,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
}
max_size = 128;
- do_cast = 1;
}
@@ -537,7 +530,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
break;
default:
cast_size = 128;
- do_cast = 1;
}
@@ -574,7 +566,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
}
// fprintf(stderr, "_ExtInt(%u) castTo %u with attr %u didcast %u\n",
- // max_size, cast_size, attr, do_cast);
+ // max_size, cast_size, attr);
switch (cast_size) {
diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc
index bd524a69..3ecba4e6 100644
--- a/instrumentation/compare-transform-pass.so.cc
+++ b/instrumentation/compare-transform-pass.so.cc
@@ -229,9 +229,9 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
dyn_cast<ConstantDataArray>(Var->getInitializer())) {
HasStr2 = true;
- Str2 = Array->getAsString();
+ Str2 = Array->getRawDataValues();
valueMap[Str2P] = new std::string(Str2.str());
- fprintf(stderr, "glo2 %s\n", Str2.str().c_str());
+ // fprintf(stderr, "glo2 %s\n", Str2.str().c_str());
}
@@ -254,7 +254,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
Var->getInitializer())) {
HasStr1 = true;
- Str1 = Array->getAsString();
+ Str1 = Array->getRawDataValues();
valueMap[Str1P] = new std::string(Str1.str());
// fprintf(stderr, "glo1 %s\n", Str1.str().c_str());
@@ -316,7 +316,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
uint64_t len = ilen->getZExtValue();
// if len is zero this is a pointless call but allow real
// implementation to worry about that
- if (!len) continue;
+ if (len < 2) continue;
if (isMemcmp) {
@@ -420,15 +420,29 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
}
+ if (TmpConstStr.length() < 2 ||
+ (TmpConstStr.length() == 2 && !TmpConstStr[1])) {
+
+ continue;
+
+ }
+
// add null termination character implicit in c strings
- TmpConstStr.append("\0", 1);
+ if (!isMemcmp && TmpConstStr[TmpConstStr.length() - 1]) {
+
+ TmpConstStr.append("\0", 1);
+
+ }
// in the unusual case the const str has embedded null
// characters, the string comparison functions should terminate
// at the first null
- if (!isMemcmp)
+ if (!isMemcmp) {
+
TmpConstStr.assign(TmpConstStr, 0, TmpConstStr.find('\0') + 1);
+ }
+
constStrLen = TmpConstStr.length();
// prefer use of StringRef (in comparison to std::string a StringRef has
// built-in runtime bounds checking, which makes debugging easier)
diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc
index 80cd90ba..b02a89fb 100644
--- a/instrumentation/split-compares-pass.so.cc
+++ b/instrumentation/split-compares-pass.so.cc
@@ -149,8 +149,11 @@ bool SplitComparesTransform::simplifyFPCompares(Module &M) {
auto op1 = FcmpInst->getOperand(1);
/* find out what the new predicate is going to be */
- auto pred = dyn_cast<CmpInst>(FcmpInst)->getPredicate();
+ auto cmp_inst = dyn_cast<CmpInst>(FcmpInst);
+ if (!cmp_inst) { continue; }
+ auto pred = cmp_inst->getPredicate();
CmpInst::Predicate new_pred;
+
switch (pred) {
case CmpInst::FCMP_UGE:
@@ -276,8 +279,11 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
auto op1 = IcmpInst->getOperand(1);
/* find out what the new predicate is going to be */
- auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate();
+ auto cmp_inst = dyn_cast<CmpInst>(IcmpInst);
+ if (!cmp_inst) { continue; }
+ auto pred = cmp_inst->getPredicate();
CmpInst::Predicate new_pred;
+
switch (pred) {
case CmpInst::ICMP_UGE:
@@ -412,8 +418,11 @@ bool SplitComparesTransform::simplifyIntSignedness(Module &M) {
IntegerType *IntType = IntegerType::get(C, bitw);
/* get the new predicate */
- auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate();
+ auto cmp_inst = dyn_cast<CmpInst>(IcmpInst);
+ if (!cmp_inst) { continue; }
+ auto pred = cmp_inst->getPredicate();
CmpInst::Predicate new_pred;
+
if (pred == CmpInst::ICMP_SGT) {
new_pred = CmpInst::ICMP_UGT;
@@ -603,6 +612,10 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
if (op_size != op1->getType()->getPrimitiveSizeInBits()) { continue; }
const unsigned int sizeInBits = op0->getType()->getPrimitiveSizeInBits();
+
+ // BUG FIXME TODO: u64 does not work for > 64 bit ... e.g. 80 and 128 bit
+ if (sizeInBits > 64) { continue; }
+
const unsigned int precision = sizeInBits == 32 ? 24
: sizeInBits == 64 ? 53
: sizeInBits == 128 ? 113
@@ -610,8 +623,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
: sizeInBits == 80 ? 65
: sizeInBits - 8;
- const unsigned shiftR_exponent = precision - 1;
- // BUG FIXME TODO: u64 does not work for > 64 bit ... e.g. 80 and 128 bit
+ const unsigned shiftR_exponent = precision - 1;
const unsigned long long mask_fraction =
(1ULL << (shiftR_exponent - 1)) | ((1ULL << (shiftR_exponent - 1)) - 1);
const unsigned long long mask_exponent =
@@ -1113,7 +1125,9 @@ size_t SplitComparesTransform::splitIntCompares(Module &M, unsigned bitw) {
auto op0 = IcmpInst->getOperand(0);
auto op1 = IcmpInst->getOperand(1);
- auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate();
+ auto cmp_inst = dyn_cast<CmpInst>(IcmpInst);
+ if (!cmp_inst) { continue; }
+ auto pred = cmp_inst->getPredicate();
BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst));
diff --git a/qemu_mode/QEMUAFL_VERSION b/qemu_mode/QEMUAFL_VERSION
index 1152380c..a7f25da3 100644
--- a/qemu_mode/QEMUAFL_VERSION
+++ b/qemu_mode/QEMUAFL_VERSION
@@ -1 +1 @@
-e36a30ebca
+d1ca56b84e
diff --git a/qemu_mode/README.md b/qemu_mode/README.md
index bc4c1d2c..a14cbe64 100644
--- a/qemu_mode/README.md
+++ b/qemu_mode/README.md
@@ -99,6 +99,13 @@ Just set AFL_QEMU_INST_RANGES=A,B,C...
The format of the items in the list is either a range of addresses like 0x123-0x321
or a module name like module.so (that is matched in the mapped object filename).
+Alternatively you can tell QEMU to ignore part of an address space for instrumentation.
+
+Just set AFL_QEMU_EXCLUDE_RANGES=A,B,C...
+
+The format of the items on the list is the same as for AFL_QEMU_INST_RANGES, and excluding ranges
+takes priority over any included ranges or AFL_INST_LIBS.
+
## 7) CompareCoverage
CompareCoverage is a sub-instrumentation with effects similar to laf-intel.
diff --git a/qemu_mode/build_qemu_support.sh b/qemu_mode/build_qemu_support.sh
index 815e77d6..4d3d9bf6 100755
--- a/qemu_mode/build_qemu_support.sh
+++ b/qemu_mode/build_qemu_support.sh
@@ -131,9 +131,13 @@ test -d qemuafl || { echo "[-] Not checked out, please install git or check your
echo "[+] Got qemuafl."
cd "qemuafl" || exit 1
-echo "[*] Checking out $QEMUAFL_VERSION"
-sh -c 'git stash && git stash drop' 1>/dev/null 2>/dev/null
-git checkout "$QEMUAFL_VERSION" || echo Warning: could not check out to commit $QEMUAFL_VERSION
+if [ -n "$NO_CHECKOUT" ]; then
+ echo "[*] Skipping checkout to $QEMUAFL_VERSION"
+else
+ echo "[*] Checking out $QEMUAFL_VERSION"
+ sh -c 'git stash' 1>/dev/null 2>/dev/null
+ git checkout "$QEMUAFL_VERSION" || echo Warning: could not check out to commit $QEMUAFL_VERSION
+fi
echo "[*] Making sure imported headers matches"
cp "../../include/config.h" "./qemuafl/imported/" || exit 1
diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl
-Subproject e36a30ebca57ca433a5d6e20b1a32975aabb761
+Subproject d1ca56b84e78f821406eef28d836918edfc8d61
diff --git a/src/afl-analyze.c b/src/afl-analyze.c
index 20aef2da..d46ecb8d 100644
--- a/src/afl-analyze.c
+++ b/src/afl-analyze.c
@@ -785,6 +785,7 @@ static void set_up_environment(void) {
"abort_on_error=1:"
"detect_leaks=0:"
"allocator_may_return_null=1:"
+ "detect_odr_violation=0:"
"symbolize=0:"
"handle_segv=0:"
"handle_sigbus=0:"
diff --git a/src/afl-cc.c b/src/afl-cc.c
index c3910e6d..ab794877 100644
--- a/src/afl-cc.c
+++ b/src/afl-cc.c
@@ -73,7 +73,8 @@ enum {
INSTRUMENT_GCC = 6,
INSTRUMENT_CLANG = 7,
INSTRUMENT_OPT_CTX = 8,
- INSTRUMENT_OPT_NGRAM = 16
+ INSTRUMENT_OPT_NGRAM = 16,
+ INSTRUMENT_OPT_CALLER = 32,
};
@@ -88,7 +89,7 @@ char instrument_mode_string[18][18] = {
"GCC",
"CLANG",
"CTX",
- "",
+ "CALLER",
"",
"",
"",
@@ -1273,6 +1274,7 @@ int main(int argc, char **argv, char **envp) {
}
if (getenv("AFL_LLVM_CTX")) instrument_opt_mode |= INSTRUMENT_OPT_CTX;
+ if (getenv("AFL_LLVM_CALLER")) instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
if (getenv("AFL_LLVM_NGRAM_SIZE")) {
@@ -1388,6 +1390,13 @@ int main(int argc, char **argv, char **envp) {
}
+ if (strncasecmp(ptr2, "caller", strlen("caller")) == 0) {
+
+ instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
+ setenv("AFL_LLVM_CALLER", "1", 1);
+
+ }
+
if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) {
u8 *ptr3 = ptr2 + strlen("ngram");
@@ -1421,6 +1430,13 @@ int main(int argc, char **argv, char **envp) {
}
+ if ((instrument_opt_mode & INSTRUMENT_OPT_CTX) &&
+ (instrument_opt_mode & INSTRUMENT_OPT_CALLER)) {
+
+ FATAL("you cannot set CTX and CALLER together");
+
+ }
+
if (instrument_opt_mode && instrument_mode == INSTRUMENT_DEFAULT &&
(compiler_mode == LLVM || compiler_mode == UNSET)) {
@@ -1498,12 +1514,13 @@ int main(int argc, char **argv, char **envp) {
" CLASSIC %s no yes module yes yes "
"yes\n"
" - NORMAL\n"
+ " - CALLER\n"
" - CTX\n"
" - NGRAM-{2-16}\n"
" INSTRIM no yes module yes yes "
" yes\n"
" - NORMAL\n"
- " - CTX\n"
+ " - CALLER\n"
" - NGRAM-{2-16}\n"
" [GCC_PLUGIN] gcc plugin: %s%s\n"
" CLASSIC DEFAULT no yes no no no "
@@ -1550,7 +1567,10 @@ int main(int argc, char **argv, char **envp) {
NATIVE_MSG
" CLASSIC: decision target instrumentation (README.llvm.md)\n"
- " CTX: CLASSIC + callee context (instrumentation/README.ctx.md)\n"
+ " CALLER: CLASSIC + single callee context "
+ "(instrumentation/README.ctx.md)\n"
+ " CTX: CLASSIC + full callee context "
+ "(instrumentation/README.ctx.md)\n"
" NGRAM-x: CLASSIC + previous path "
"((instrumentation/README.ngram.md)\n"
" INSTRIM: Dominator tree (for LLVM <= 6.0) "
@@ -1644,15 +1664,17 @@ int main(int argc, char **argv, char **envp) {
" AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen "
"mutator)\n"
" AFL_LLVM_INSTRUMENT: set instrumentation mode:\n"
- " CLASSIC, INSTRIM, PCGUARD, LTO, GCC, CLANG, CTX, NGRAM-2 ... "
- "NGRAM-16\n"
+ " CLASSIC, INSTRIM, PCGUARD, LTO, GCC, CLANG, CALLER, CTX, "
+ "NGRAM-2 ..-16\n"
" You can also use the old environment variables instead:\n"
" AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n"
" AFL_LLVM_INSTRIM: use light weight instrumentation InsTrim\n"
" AFL_LLVM_INSTRIM_LOOPHEAD: optimize loop tracing for speed "
"(option to INSTRIM)\n"
- " AFL_LLVM_CTX: use context sensitive coverage (for CLASSIC and "
- "INSTRIM)\n"
+ " AFL_LLVM_CALLER: use single context sensitive coverage (for "
+ "CLASSIC)\n"
+ " AFL_LLVM_CTX: use full context sensitive coverage (for "
+ "CLASSIC)\n"
" AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for "
"CLASSIC & INSTRIM)\n");
@@ -1770,7 +1792,7 @@ int main(int argc, char **argv, char **envp) {
}
if (instrument_opt_mode && compiler_mode != LLVM)
- FATAL("CTX and NGRAM can only be used in LLVM mode");
+ FATAL("CTX, CALLER and NGRAM can only be used in LLVM mode");
if (!instrument_opt_mode) {
@@ -1780,15 +1802,14 @@ int main(int argc, char **argv, char **envp) {
} else {
- if (instrument_opt_mode == INSTRUMENT_OPT_CTX)
+ char *ptr2 = alloc_printf(" + NGRAM-%u", ngram_size);
+ ptr = alloc_printf(
+ "%s%s%s%s", instrument_mode_string[instrument_mode],
+ (instrument_opt_mode & INSTRUMENT_OPT_CTX) ? " + CTX" : "",
+ (instrument_opt_mode & INSTRUMENT_OPT_CALLER) ? " + CALLER" : "",
+ (instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : "");
- ptr = alloc_printf("%s + CTX", instrument_mode_string[instrument_mode]);
- else if (instrument_opt_mode == INSTRUMENT_OPT_NGRAM)
- ptr = alloc_printf("%s + NGRAM-%u",
- instrument_mode_string[instrument_mode], ngram_size);
- else
- ptr = alloc_printf("%s + CTX + NGRAM-%u",
- instrument_mode_string[instrument_mode], ngram_size);
+ ck_free(ptr2);
}
@@ -1799,11 +1820,14 @@ int main(int argc, char **argv, char **envp) {
"(requires LLVM 11 or higher)");
#endif
- if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC &&
- instrument_mode != INSTRUMENT_CFG)
+ if (instrument_opt_mode && instrument_mode == INSTRUMENT_CFG &&
+ instrument_opt_mode & INSTRUMENT_OPT_CTX)
+ FATAL("CFG instrumentation mode supports NGRAM and CALLER, but not CTX.");
+ else if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC)
+ // we will drop CFG/INSTRIM in the future so do not advertise
FATAL(
- "CTX and NGRAM instrumentation options can only be used with LLVM and "
- "CFG or CLASSIC instrumentation modes!");
+ "CALLER, CTX and NGRAM instrumentation options can only be used with "
+ "the LLVM CLASSIC instrumentation mode.");
if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO"))
FATAL(
diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c
index fd5edc98..6f08f9f4 100644
--- a/src/afl-forkserver.c
+++ b/src/afl-forkserver.c
@@ -492,6 +492,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
"malloc_context_size=0:"
"symbolize=0:"
"allocator_may_return_null=1:"
+ "detect_odr_violation=0:"
"handle_segv=0:"
"handle_sigbus=0:"
"handle_abort=0:"
@@ -908,10 +909,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
} else if (!fsrv->mem_limit) {
SAYF("\n" cLRD "[-] " cRST
- "Hmm, looks like the target binary terminated before we could"
- " complete a handshake with the injected code.\n"
- "If the target was compiled with afl-clang-lto and AFL_LLVM_MAP_ADDR"
- " then recompiling without this parameter.\n"
+ "Hmm, looks like the target binary terminated before we could complete"
+ " a\n"
+ "handshake with the injected code.\n"
+ "Most likely the target has a huge coverage map, retry with setting"
+ " the\n"
+ "environment variable AFL_MAP_SIZE=4194304\n"
"Otherwise there is a horrible bug in the fuzzer.\n"
"Poke <afl-users@googlegroups.com> for troubleshooting tips.\n");
@@ -927,6 +930,10 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
"explanations:\n\n"
"%s"
+
+ " - Most likely the target has a huge coverage map, retry with setting the\n"
+ " environment variable AFL_MAP_SIZE=4194304\n\n"
+
" - The current memory limit (%s) is too restrictive, causing an "
"OOM\n"
" fault in the dynamic linker. This can be fixed with the -m "
diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c
index 7ecad233..52100fa1 100644
--- a/src/afl-fuzz-extras.c
+++ b/src/afl-fuzz-extras.c
@@ -387,6 +387,130 @@ static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) {
}
+/* add an extra/dict/token - no checks performed, no sorting */
+
+static void add_extra_nocheck(afl_state_t *afl, u8 *mem, u32 len) {
+
+ afl->extras = afl_realloc((void **)&afl->extras,
+ (afl->extras_cnt + 1) * sizeof(struct extra_data));
+
+ if (unlikely(!afl->extras)) { PFATAL("alloc"); }
+
+ afl->extras[afl->extras_cnt].data = ck_alloc(len);
+ afl->extras[afl->extras_cnt].len = len;
+ memcpy(afl->extras[afl->extras_cnt].data, mem, len);
+ afl->extras_cnt++;
+
+ /* We only want to print this once */
+
+ if (afl->extras_cnt == afl->max_det_extras + 1) {
+
+ WARNF("More than %u tokens - will use them probabilistically.",
+ afl->max_det_extras);
+
+ }
+
+}
+
+/* Sometimes strings in input is transformed to unicode internally, so for
+ fuzzing we should attempt to de-unicode if it looks like simple unicode */
+
+void deunicode_extras(afl_state_t *afl) {
+
+ if (!afl->extras_cnt) return;
+
+ u32 i, j, orig_cnt = afl->extras_cnt;
+ u8 buf[64];
+
+ for (i = 0; i < orig_cnt; ++i) {
+
+ if (afl->extras[i].len < 6 || afl->extras[i].len > 64 ||
+ afl->extras[i].len % 2) {
+
+ continue;
+
+ }
+
+ u32 k = 0, z1 = 0, z2 = 0, z3 = 0, z4 = 0, half = afl->extras[i].len >> 1;
+ u32 quarter = half >> 1;
+
+ for (j = 0; j < afl->extras[i].len; ++j) {
+
+ switch (j % 4) {
+
+ case 2:
+ if (!afl->extras[i].data[j]) { ++z3; }
+ // fall through
+ case 0:
+ if (!afl->extras[i].data[j]) { ++z1; }
+ break;
+ case 3:
+ if (!afl->extras[i].data[j]) { ++z4; }
+ // fall through
+ case 1:
+ if (!afl->extras[i].data[j]) { ++z2; }
+ break;
+
+ }
+
+ }
+
+ if ((z1 < half && z2 < half) || z1 + z2 == afl->extras[i].len) { continue; }
+
+ // also maybe 32 bit unicode?
+ if (afl->extras[i].len % 4 == 0 && afl->extras[i].len >= 12 &&
+ (z3 == quarter || z4 == quarter) && z1 + z2 == quarter * 3) {
+
+ for (j = 0; j < afl->extras[i].len; ++j) {
+
+ if (z4 < quarter) {
+
+ if (j % 4 == 3) { buf[k++] = afl->extras[i].data[j]; }
+
+ } else if (z3 < quarter) {
+
+ if (j % 4 == 2) { buf[k++] = afl->extras[i].data[j]; }
+
+ } else if (z2 < half) {
+
+ if (j % 4 == 1) { buf[k++] = afl->extras[i].data[j]; }
+
+ } else {
+
+ if (j % 4 == 0) { buf[k++] = afl->extras[i].data[j]; }
+
+ }
+
+ }
+
+ add_extra_nocheck(afl, buf, k);
+ k = 0;
+
+ }
+
+ for (j = 0; j < afl->extras[i].len; ++j) {
+
+ if (z1 < half) {
+
+ if (j % 2 == 0) { buf[k++] = afl->extras[i].data[j]; }
+
+ } else {
+
+ if (j % 2 == 1) { buf[k++] = afl->extras[i].data[j]; }
+
+ }
+
+ }
+
+ add_extra_nocheck(afl, buf, k);
+
+ }
+
+ qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data),
+ compare_extras_len);
+
+}
+
/* Removes duplicates from the loaded extras. This can happen if multiple files
are loaded */
@@ -396,9 +520,9 @@ void dedup_extras(afl_state_t *afl) {
u32 i, j, orig_cnt = afl->extras_cnt;
- for (i = 0; i < afl->extras_cnt - 1; i++) {
+ for (i = 0; i < afl->extras_cnt - 1; ++i) {
- for (j = i + 1; j < afl->extras_cnt; j++) {
+ for (j = i + 1; j < afl->extras_cnt; ++j) {
restart_dedup:
@@ -462,30 +586,11 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) {
}
- afl->extras = afl_realloc((void **)&afl->extras,
- (afl->extras_cnt + 1) * sizeof(struct extra_data));
-
- if (unlikely(!afl->extras)) { PFATAL("alloc"); }
-
- afl->extras[afl->extras_cnt].data = ck_alloc(len);
- afl->extras[afl->extras_cnt].len = len;
-
- memcpy(afl->extras[afl->extras_cnt].data, mem, len);
-
- afl->extras_cnt++;
+ add_extra_nocheck(afl, mem, len);
qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data),
compare_extras_len);
- /* We only want to print this once */
-
- if (afl->extras_cnt == afl->max_det_extras + 1) {
-
- WARNF("More than %u tokens - will use them probabilistically.",
- afl->max_det_extras);
-
- }
-
}
/* Maybe add automatic extra. */
diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c
index ad3e3b8e..835aba40 100644
--- a/src/afl-fuzz-queue.c
+++ b/src/afl-fuzz-queue.c
@@ -198,34 +198,35 @@ void create_alias_table(afl_state_t *afl) {
while (nS)
afl->alias_probability[S[--nS]] = 1;
-#ifdef INTROSPECTION
- u8 fn[PATH_MAX];
- snprintf(fn, PATH_MAX, "%s/introspection_corpus.txt", afl->out_dir);
- FILE *f = fopen(fn, "a");
- if (f) {
+ /*
+ #ifdef INTROSPECTION
+ u8 fn[PATH_MAX];
+ snprintf(fn, PATH_MAX, "%s/introspection_corpus.txt", afl->out_dir);
+ FILE *f = fopen(fn, "a");
+ if (f) {
+
+ for (i = 0; i < n; i++) {
+
+ struct queue_entry *q = afl->queue_buf[i];
+ fprintf(
+ f,
+ "entry=%u name=%s favored=%s variable=%s disabled=%s len=%u "
+ "exec_us=%u "
+ "bitmap_size=%u bitsmap_size=%u tops=%u weight=%f perf_score=%f\n",
+ i, q->fname, q->favored ? "true" : "false",
+ q->var_behavior ? "true" : "false", q->disabled ? "true" : "false",
+ q->len, (u32)q->exec_us, q->bitmap_size, q->bitsmap_size, q->tc_ref,
+ q->weight, q->perf_score);
- for (i = 0; i < n; i++) {
+ }
- struct queue_entry *q = afl->queue_buf[i];
- fprintf(
- f,
- "entry=%u name=%s favored=%s variable=%s disabled=%s len=%u "
- "exec_us=%u "
- "bitmap_size=%u bitsmap_size=%u tops=%u weight=%f perf_score=%f\n",
- i, q->fname, q->favored ? "true" : "false",
- q->var_behavior ? "true" : "false", q->disabled ? "true" : "false",
- q->len, (u32)q->exec_us, q->bitmap_size, q->bitsmap_size, q->tc_ref,
- q->weight, q->perf_score);
+ fprintf(f, "\n");
+ fclose(f);
}
- fprintf(f, "\n");
- fclose(f);
-
- }
-
-#endif
-
+ #endif
+ */
/*
fprintf(stderr, " entry alias probability perf_score weight
filename\n"); for (u32 i = 0; i < n; ++i) fprintf(stderr, " %5u %5u %11u
diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c
index 1ab5f996..9bfbf95b 100644
--- a/src/afl-fuzz-redqueen.c
+++ b/src/afl-fuzz-redqueen.c
@@ -1853,7 +1853,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
}
static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl,
- u8 *o_pattern, u8 *changed_val, u32 idx,
+ u8 *o_pattern, u8 *changed_val, u8 plen, u32 idx,
u32 taint_len, u8 *orig_buf, u8 *buf, u8 *cbuf,
u32 len, u8 lvl, u8 *status) {
@@ -1866,7 +1866,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl,
u8 save[40];
u32 saved_idx = idx, pre, from = 0, to = 0, i, j;
- u32 its_len = MIN((u32)32, len - idx);
+ u32 its_len = MIN((u32)plen, len - idx);
its_len = MIN(its_len, taint_len);
u32 saved_its_len = its_len;
@@ -2365,9 +2365,9 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
status = 0;
- if (unlikely(rtn_extend_encoding(afl, o->v0, o->v1, orig_o->v0,
- orig_o->v1, idx, taint_len, orig_buf,
- buf, cbuf, len, lvl, &status))) {
+ if (unlikely(rtn_extend_encoding(
+ afl, o->v0, o->v1, orig_o->v0, orig_o->v1, SHAPE_BYTES(h->shape),
+ idx, taint_len, orig_buf, buf, cbuf, len, lvl, &status))) {
return 1;
@@ -2382,9 +2382,9 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
status = 0;
- if (unlikely(rtn_extend_encoding(afl, o->v1, o->v0, orig_o->v1,
- orig_o->v0, idx, taint_len, orig_buf,
- buf, cbuf, len, lvl, &status))) {
+ if (unlikely(rtn_extend_encoding(
+ afl, o->v1, o->v0, orig_o->v1, orig_o->v0, SHAPE_BYTES(h->shape),
+ idx, taint_len, orig_buf, buf, cbuf, len, lvl, &status))) {
return 1;
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index a02eadb2..09aff4fb 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -1437,23 +1437,8 @@ int main(int argc, char **argv_orig, char **envp) {
// read_foreign_testcases(afl, 1); for the moment dont do this
OKF("Loaded a total of %u seeds.", afl->queued_paths);
- load_auto(afl);
-
pivot_inputs(afl);
- if (extras_dir_cnt) {
-
- for (i = 0; i < extras_dir_cnt; i++) {
-
- load_extras(afl, extras_dir[i]);
-
- }
-
- dedup_extras(afl);
- OKF("Loaded a total of %u extras.", afl->extras_cnt);
-
- }
-
if (!afl->timeout_given) { find_timeout(afl); } // only for resumes!
if ((afl->tmp_dir = afl->afl_env.afl_tmpdir) != NULL &&
@@ -1681,6 +1666,22 @@ int main(int argc, char **argv_orig, char **envp) {
}
+ load_auto(afl);
+
+ if (extras_dir_cnt) {
+
+ for (i = 0; i < extras_dir_cnt; i++) {
+
+ load_extras(afl, extras_dir[i]);
+
+ }
+
+ }
+
+ deunicode_extras(afl);
+ dedup_extras(afl);
+ if (afl->extras_cnt) { OKF("Loaded a total of %u extras.", afl->extras_cnt); }
+
// after we have the correct bitmap size we can read the bitmap -B option
// and set the virgin maps
if (afl->in_bitmap) {
diff --git a/src/afl-showmap.c b/src/afl-showmap.c
index b40527d3..0fc76193 100644
--- a/src/afl-showmap.c
+++ b/src/afl-showmap.c
@@ -563,6 +563,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) {
"detect_leaks=0:"
"allocator_may_return_null=1:"
"symbolize=0:"
+ "detect_odr_violation=0:"
"handle_segv=0:"
"handle_sigbus=0:"
"handle_abort=0:"
diff --git a/src/afl-tmin.c b/src/afl-tmin.c
index 15336959..6d04c652 100644
--- a/src/afl-tmin.c
+++ b/src/afl-tmin.c
@@ -717,6 +717,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) {
"detect_leaks=0:"
"allocator_may_return_null=1:"
"symbolize=0:"
+ "detect_odr_violation=0:"
"handle_segv=0:"
"handle_sigbus=0:"
"handle_abort=0:"