diff options
57 files changed, 1391 insertions, 497 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..6c89bc6f 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -24,7 +24,7 @@ BIN_PATH = $(PREFIX)/bin HELPER_PATH = $(PREFIX)/lib/afl DOC_PATH = $(PREFIX)/share/doc/afl MISC_PATH = $(PREFIX)/share/afl -MAN_PATH = $(PREFIX)/man/man8 +MAN_PATH = $(PREFIX)/share/man/man8 PROGNAME = afl VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2) @@ -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..69e5bb74 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ <img align="right" src="https://raw.githubusercontent.com/andreafioraldi/AFLplusplus-website/master/static/logo_256x256.png" alt="AFL++ Logo"> - Release Version: [3.10c](https://github.com/AFLplusplus/AFLplusplus/releases) + Release Version: [3.11c](https://github.com/AFLplusplus/AFLplusplus/releases) - Github Version: 3.11a + Github Version: 3.12a Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus) @@ -175,7 +175,7 @@ If you want to build afl++ yourself you have many options. The easiest choice is to build and install everything: ```shell -sudo apt install build-essential python3-dev automake flex bison libglib2.0-dev libpixman-1-dev clang python3-setuptools clang llvm llvm-dev libstdc++-dev +sudo apt install build-essential python3-dev automake flex bison libglib2.0-dev libpixman-1-dev python3-setuptools clang lld llvm llvm-dev libstdc++-dev make distrib sudo make install ``` @@ -224,10 +224,9 @@ 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 +e.g.: `make ASAN_BUILD=1` ## Good examples and writeups @@ -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: @@ -304,7 +304,7 @@ Clickable README links for the chosen compiler: * [LTO mode - afl-clang-lto](instrumentation/README.lto.md) * [LLVM mode - afl-clang-fast](instrumentation/README.llvm.md) * [GCC_PLUGIN mode - afl-gcc-fast](instrumentation/README.gcc_plugin.md) - * GCC/CLANG mode (afl-gcc/afl-clang) have no README as they have no own features + * GCC/CLANG modes (afl-gcc/afl-clang) have no README as they have no own features You can select the mode for the afl-cc compiler by: 1. use a symlink to afl-cc: afl-gcc, afl-g++, afl-clang, afl-clang++, @@ -399,10 +399,19 @@ How to do this is described below. Then build the target. (Usually with `make`) -**NOTE**: sometimes configure and build systems are fickle and do not like -stderr output (and think this means a test failure) - which is something -afl++ like to do to show statistics. It is recommended to disable them via -`export AFL_QUIET=1`. +**NOTES** + +1. sometimes configure and build systems are fickle and do not like + stderr output (and think this means a test failure) - which is something + afl++ likes to do to show statistics. It is recommended to disable them via + `export AFL_QUIET=1`. + +2. sometimes configure and build systems error on warnings - these should be + disabled (e.g. `--disable-werror` for some configure scripts). + +3. in case the configure/build system complains about afl++'s compiler and + aborts then set `export AFL_NOOPT=1` which will then just behave like the + real compiler. This option has to be unset again before building the target! ##### configure @@ -484,8 +493,9 @@ default. #### c) Minimizing all corpus files The shorter the input files that still traverse the same path -within the target, the better the fuzzing will be. This is done with `afl-tmin` -however it is a long process as this has to be done for every file: +within the target, the better the fuzzing will be. This minimization +is done with `afl-tmin` however it is a long process as this has to +be done for every file: ``` mkdir input @@ -536,12 +546,10 @@ If you need to stop and re-start the fuzzing, use the same command line options mutation mode!) and switch the input directory with a dash (`-`): `afl-fuzz -i - -o output -- bin/target -d @@` -Note that afl-fuzz enforces memory limits to prevent the system to run out -of memory. By default this is 50MB for a process. If this is too little for -the target (which you can usually see by afl-fuzz bailing with the message -that it could not connect to the forkserver), then you can increase this -with the `-m` option, the value is in MB. To disable any memory limits -(beware!) set `-m none` - which is usually required for ASAN compiled targets. +Memory limits are not enforced by afl-fuzz by default and the system may run +out of memory. You can decrease the memory with the `-m` option, the value is +in MB. If this is too small for the target, you can usually see this by +afl-fuzz bailing with the message that it could not connect to the forkserver. Adding a dictionary is helpful. See the directory [dictionaries/](dictionaries/) if something is already included for your data format, and tell afl-fuzz to load @@ -554,7 +562,9 @@ afl-fuzz has a variety of options that help to workaround target quirks like specific locations for the input file (`-f`), not performing deterministic fuzzing (`-d`) and many more. Check out `afl-fuzz -h`. -afl-fuzz never stops fuzzing. To terminate afl++ simply press Control-C. +By default afl-fuzz never stops fuzzing. To terminate afl++ simply press Control-C +or send a signal SIGINT. You can limit the number of executions or approximate runtime +in seconds with options also. When you start afl-fuzz you will see a user interface that shows what the status is: 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..778d7487 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" } @@ -342,8 +346,10 @@ BEGIN { } else { stat_format = "-f '%z %N'" # *BSD, MacOS } - cmdline = "cd "in_dir" && find . \\( ! -name . -a -type d -prune \\) -o -type f -exec stat "stat_format" \\{\\} \\; | sort -k1n -k2r" - cmdline = "ls "in_dir" | (cd "in_dir" && xargs stat "stat_format" 2>/dev/null) | sort -k1n -k2r" + cmdline = "(cd "in_dir" && find . \\( ! -name . -a -type d -prune \\) -o -type f -exec stat "stat_format" \\{\\} + | sort -k1n -k2r)" + #cmdline = "ls "in_dir" | (cd "in_dir" && xargs stat "stat_format" 2>/dev/null) | sort -k1n -k2r" + #cmdline = "(cd "in_dir" && stat "stat_format" *) | sort -k1n -k2r" + #cmdline = "(cd "in_dir" && ls | xargs stat "stat_format" ) | sort -k1n -k2r" while (cmdline | getline) { sub(/^[0-9]+ (\.\/)?/,"",$0) infilesSmallToBig[i++] = $0 @@ -354,12 +360,12 @@ BEGIN { # Make sure that we're not dealing with a directory. - if (0 == system("test -d "in_dir"/"first_file)) { + if (0 == system("test -d ""\""in_dir"/"first_file"\"")) { print "[-] Error: The input directory is empty or contains subdirectories - please fix." > "/dev/stderr" exit 1 } - if (0 == system("ln "in_dir"/"first_file" "trace_dir"/.link_test")) { + if (0 == system("ln \""in_dir"/"first_file"\" "trace_dir"/.link_test")) { cp_tool = "ln" } else { cp_tool = "cp" @@ -374,7 +380,7 @@ BEGIN { if (!stdin_file) { system( "AFL_CMIN_ALLOW_ANY=1 "AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/.run_test\" -Z "extra_par" -- \""target_bin"\" "prog_args_string" <\""in_dir"/"first_file"\"") } else { - system("cp "in_dir"/"first_file" "stdin_file) + system("cp \""in_dir"/"first_file"\" "stdin_file) system( "AFL_CMIN_ALLOW_ANY=1 "AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/.run_test\" -Z "extra_par" -A \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null") } @@ -496,7 +502,7 @@ BEGIN { # copy file unless already done if (! (fn in file_already_copied)) { - system(cp_tool" "in_dir"/"fn" "out_dir"/"fn) + system(cp_tool" \""in_dir"/"fn"\" \""out_dir"/"fn"\"") file_already_copied[fn] = "" ++out_count #printf "tuple nr %d (%d cnt=%d) -> %s\n",tcnt,key,key_count[key],fn > trace_dir"/.log" 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/afl-system-config b/afl-system-config index 9905ac81..ae37a062 100755 --- a/afl-system-config +++ b/afl-system-config @@ -39,7 +39,7 @@ if [ "$PLATFORM" = "Linux" ] ; then echo Settings applied. dmesg | egrep -q 'nospectre_v2|spectre_v2=off' || { echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this: - echo ' /etc/default/grub:GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"' + echo ' /etc/default/grub:GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=0 l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off srbds=off noexec=off noexec32=off tsx=on tsx_async_abort=off arm64.nopauth audit=0 hardened_usercopy=off ssbd=force-off"' } DONE=1 fi 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..1c735a70 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -8,6 +8,36 @@ 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.11c (release) + - afl-fuzz: + - better auto detection of map size + - fix sanitizer settings (bug since 3.10c) + - fix an off-by-one overwrite in cmplog + - add non-unicode variants from unicode-looking dictionary entries + - Rust custom mutator API improvements + - Imported crash stats painted yellow on resume (only new ones are red) + - afl-cc: + - added AFL_NOOPT that will just pass everything to the normal + gcc/clang compiler without any changes - to pass weird configure + scripts + - 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) + - the compiler rt was added also in object building (-c) which + should have been fixed years ago but somewhere got lost :( + - 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" + - afl-cmin: support filenames with spaces + ### Version ++3.10c (release) - Mac OS ARM64 support diff --git a/docs/env_variables.md b/docs/env_variables.md index f6ed12d0..a20f1e42 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -26,6 +26,17 @@ Because (with the exception of the --afl-MODE command line option) the compile-time tools do not accept afl specific command-line options, they make fairly broad use of environmental variables instead: + - Some build/configure scripts break with afl++ compilers. To be able to + pass them, do: +``` + export CC=afl-cc + export CXX=afl-c++ + export AFL_NOOPT=1 + ./configure --disable-shared --disabler-werror + unset AFL_NOOPT + make +``` + - Most afl tools do not print any output if stdout/stderr are redirected. If you want to get the output into a file then set the `AFL_DEBUG` environment variable. diff --git a/docs/ideas.md b/docs/ideas.md index 08cb16ef..0130cf61 100644 --- a/docs/ideas.md +++ b/docs/ideas.md @@ -35,7 +35,9 @@ and documents something about it. In traditional fuzzing this is the coverage in the target, however we want to add various more observers, e.g. stack depth, heap usage, etc. - this is a topic for an experienced Rust developer. -# Generic ideas and wishlist +# Generic ideas and wishlist - NOT PART OF GSoC 2021 ! + +The below list is not part of GSoC 2021. ## Analysis software 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..cc8024ea 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.11c" /****************************************************** * * @@ -54,10 +54,10 @@ /* Number of potential positions from which we decide if cmplog becomes useless, default 8096 */ -#define CMPLOG_POSITIONS_MAX 8096U +#define CMPLOG_POSITIONS_MAX (12 * 1024) /* Maximum allowed fails per CMP value. Default: 128 */ -#define CMPLOG_FAIL_MAX 128 +#define CMPLOG_FAIL_MAX 96 /* Now non-cmplog configuration options */ diff --git a/include/envs.h b/include/envs.h index 143979c6..4d4d6b0e 100644 --- a/include/envs.h +++ b/include/envs.h @@ -80,7 +80,9 @@ static char *afl_environment_variables[] = { "AFL_LLVM_BLOCKLIST", "AFL_LLVM_CMPLOG", "AFL_LLVM_INSTRIM", + "AFL_LLVM_CALLER", "AFL_LLVM_CTX", + "AFL_LLVM_CTX_K", "AFL_LLVM_DICT2FILE", "AFL_LLVM_DOCUMENT_IDS", "AFL_LLVM_INSTRIM_LOOPHEAD", @@ -118,10 +120,12 @@ static char *afl_environment_variables[] = { "AFL_NO_PYTHON", "AFL_UNTRACER_FILE", "AFL_LLVM_USE_TRACE_PC", - "AFL_NO_X86", // not really an env but we dont want to warn on it "AFL_MAP_SIZE", "AFL_MAPSIZE", "AFL_MAX_DET_EXTRAS", + "AFL_NO_X86", // not really an env but we dont want to warn on it + "AFL_NOOPT", + "AFL_PASSTHROUGH", "AFL_PATH", "AFL_PERFORMANCE_FILE", "AFL_PRELOAD", @@ -141,6 +145,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..62de6ec5 100644 --- a/instrumentation/LLVMInsTrim.so.cc +++ b/instrumentation/LLVMInsTrim.so.cc @@ -38,7 +38,7 @@ typedef long double max_align_t; #include "MarkNodes.h" #include "afl-llvm-common.h" -#include "llvm-ngram-coverage.h" +#include "llvm-alternative-coverage.h" #include "config.h" #include "debug.h" @@ -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.cmplog.md b/instrumentation/README.cmplog.md index 5f855e1f..a796c7a7 100644 --- a/instrumentation/README.cmplog.md +++ b/instrumentation/README.cmplog.md @@ -1,10 +1,11 @@ # CmpLog instrumentation -The CmpLog instrumentation enables the logging of the comparisons operands in a +The CmpLog instrumentation enables logging of comparison operands in a shared memory. These values can be used by various mutators built on top of it. -At the moment we support the RedQueen mutator (input-2-state instructions only). +At the moment we support the RedQueen mutator (input-2-state instructions only), +for details see [the RedQueen paper](https://www.syssec.ruhr-uni-bochum.de/media/emma/veroeffentlichungen/2018/12/17/NDSS19-Redqueen.pdf). ## Build @@ -13,7 +14,7 @@ program. The first version is built using the regular AFL++ instrumentation. -The second one, the CmpLog binary, with setting AFL_LLVM_CMPLOG during the compilation. +The second one, the CmpLog binary, is built with setting AFL_LLVM_CMPLOG during the compilation. For example: @@ -26,11 +27,12 @@ export AFL_LLVM_CMPLOG=1 ./configure --cc=~/path/to/afl-clang-fast make cp ./program ./program.cmplog +unset AFL_LLVM_CMPLOG ``` ## Use -AFL++ has the new -c option that needs to be used to specify the CmpLog binary (the second +AFL++ has the new `-c` option that needs to be used to specify the CmpLog binary (the second build). For example: diff --git a/instrumentation/README.ctx.md b/instrumentation/README.ctx.md index caf2c09a..335e9921 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 +detrimental, 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/README.gcc_plugin.md b/instrumentation/README.gcc_plugin.md index 12449efd..230ceb73 100644 --- a/instrumentation/README.gcc_plugin.md +++ b/instrumentation/README.gcc_plugin.md @@ -3,16 +3,20 @@ See [../README.md](../README.md) for the general instruction manual. See [README.llvm.md](README.llvm.md) for the LLVM-based instrumentation. +This document describes how to build and use `afl-gcc-fast` and `afl-g++-fast`, +which instrument the target with the help of gcc plugins. + TLDR: - * `apt-get install gcc-VERSION-plugin-dev` - * `make` - * gcc and g++ must point to the gcc-VERSION you you have to set AFL_CC/AFL_CXX + * check the version of your gcc compiler: `gcc --version` + * `apt-get install gcc-VERSION-plugin-dev` or similar to install headers for gcc plugins + * `gcc` and `g++` must match the gcc-VERSION you installed headers for. You can set `AFL_CC`/`AFL_CXX` to point to these! - * just use afl-gcc-fast/afl-g++-fast normally like you would afl-clang-fast + * `make` + * just use `afl-gcc-fast`/`afl-g++-fast` normally like you would do with `afl-clang-fast` ## 1) Introduction -The code in this directory allows you to instrument programs for AFL using +The code in this directory allows to instrument programs for AFL using true compiler-level instrumentation, instead of the more crude assembly-level rewriting approach taken by afl-gcc and afl-clang. This has several interesting properties: @@ -27,10 +31,10 @@ several interesting properties: - The instrumentation is CPU-independent. At least in principle, you should be able to rely on it to fuzz programs on non-x86 architectures (after - building afl-fuzz with AFL_NOX86=1). + building `afl-fuzz` with `AFL_NOX86=1`). - Because the feature relies on the internals of GCC, it is gcc-specific - and will *not* work with LLVM (see ../llvm_mode for an alternative). + and will *not* work with LLVM (see [README.llvm.md](README.llvm.md) for an alternative). Once this implementation is shown to be sufficiently robust and portable, it will probably replace afl-gcc. For now, it can be built separately and @@ -41,29 +45,32 @@ The idea and much of the implementation comes from Laszlo Szekeres. ## 2) How to use In order to leverage this mechanism, you need to have modern enough GCC -(>= version 4.5.0) and the plugin headers installed on your system. That +(>= version 4.5.0) and the plugin development headers installed on your system. That should be all you need. On Debian machines, these headers can be acquired by installing the `gcc-VERSION-plugin-dev` packages. -To build the instrumentation itself, type 'make'. This will generate binaries -called afl-gcc-fast and afl-g++-fast in the parent directory. +To build the instrumentation itself, type `make`. This will generate binaries +called `afl-gcc-fast` and `afl-g++-fast` in the parent directory. The gcc and g++ compiler links have to point to gcc-VERSION - or set these -by pointing the environment variables AFL_CC/AFL_CXX to them. -If the CC/CXX have been overridden, those compilers will be used from -those wrappers without using AFL_CXX/AFL_CC settings. +by pointing the environment variables `AFL_CC`/`AFL_CXX` to them. +If the `CC`/`CXX` environment variables have been set, those compilers will be +preferred over those from the `AFL_CC`/`AFL_CXX` settings. Once this is done, you can instrument third-party code in a way similar to the standard operating mode of AFL, e.g.: - - CC=/path/to/afl/afl-gcc-fast ./configure [...options...] +``` + CC=/path/to/afl/afl-gcc-fast + CXX=/path/to/afl/afl-g++-fast + export CC CXX + ./configure [...options...] make +``` +Note: We also used `CXX` to set the C++ compiler to `afl-g++-fast` for C++ code. -Be sure to also include CXX set to afl-g++-fast for C++ code. - -The tool honors roughly the same environmental variables as afl-gcc (see -[env_variables.md](../docs/env_variables.md). This includes AFL_INST_RATIO, -AFL_USE_ASAN, AFL_HARDEN, and AFL_DONT_OPTIMIZE. +The tool honors roughly the same environmental variables as `afl-gcc` (see +[env_variables.md](../docs/env_variables.md). This includes `AFL_INST_RATIO`, +`AFL_USE_ASAN`, `AFL_HARDEN`, and `AFL_DONT_OPTIMIZE`. Note: if you want the GCC plugin to be installed on your system for all users, you need to build it before issuing 'make install' in the parent @@ -72,7 +79,7 @@ directory. ## 3) Gotchas, feedback, bugs This is an early-stage mechanism, so field reports are welcome. You can send bug -reports to afl@aflplus.plus +reports to afl@aflplus.plus. ## 4) Bonus feature #1: deferred initialization @@ -88,7 +95,7 @@ file before getting to the fuzzed data. In such cases, it's beneficial to initialize the forkserver a bit later, once most of the initialization work is already done, but before the binary attempts to read the fuzzed input and parse it; in some cases, this can offer a 10x+ -performance gain. You can implement delayed initialization in LLVM mode in a +performance gain. You can implement delayed initialization in GCC mode in a fairly simple way. First, locate a suitable location in the code where the delayed cloning can @@ -117,7 +124,7 @@ With the location selected, add this code in the appropriate spot: ``` You don't need the #ifdef guards, but they will make the program still work as -usual when compiled with a tool other than afl-gcc-fast/afl-clang-fast. +usual when compiled with a compiler other than afl-gcc-fast/afl-clang-fast. Finally, recompile the program with afl-gcc-fast (afl-gcc or afl-clang will *not* generate a deferred-initialization binary) - and you should be all set! @@ -127,7 +134,7 @@ Finally, recompile the program with afl-gcc-fast (afl-gcc or afl-clang will Some libraries provide APIs that are stateless, or whose state can be reset in between processing different input files. When such a reset is performed, a single long-lived process can be reused to try out multiple test cases, -eliminating the need for repeated fork() calls and the associated OS overhead. +eliminating the need for repeated `fork()` calls and the associated OS overhead. The basic structure of the program that does this would be: @@ -160,5 +167,9 @@ wary of memory leaks and the state of file descriptors. When running in this mode, the execution paths will inherently vary a bit depending on whether the input loop is being entered for the first time or executed again. To avoid spurious warnings, the feature implies -AFL_NO_VAR_CHECK and hides the "variable path" warnings in the UI. +`AFL_NO_VAR_CHECK` and hides the "variable path" warnings in the UI. + +## 6) Bonus feature #3: selective instrumentation +It can be more effective to fuzzing to only instrument parts of the code. +For details see [README.instrument_list.md](README.instrument_list.md). diff --git a/instrumentation/README.neverzero.md b/instrumentation/README.neverzero.md index 5c894d6e..49104e00 100644 --- a/instrumentation/README.neverzero.md +++ b/instrumentation/README.neverzero.md @@ -16,7 +16,7 @@ at a very little cost (one instruction per edge). (The alternative of saturated counters has been tested also and proved to be inferior in terms of path discovery.) -This is implemented in afl-gcc, however for llvm_mode this is optional if +This is implemented in afl-gcc and afl-gcc-fast, however for llvm_mode this is optional if the llvm version is below 9 - as there is a perfomance bug that is only fixed in version 9 and onwards. diff --git a/instrumentation/README.ngram.md b/instrumentation/README.ngram.md index de3ba432..da61ef32 100644 --- a/instrumentation/README.ngram.md +++ b/instrumentation/README.ngram.md @@ -10,8 +10,8 @@ by Jinghan Wang, et. al. Note that the original implementation (available [here](https://github.com/bitsecurerlab/afl-sensitive)) is built on top of AFL's QEMU mode. -This is essentially a port that uses LLVM vectorized instructions to achieve -the same results when compiling source code. +This is essentially a port that uses LLVM vectorized instructions (available from +llvm versions 4.0.1 and higher) to achieve the same results when compiling source code. In math the branch coverage is performed as follows: `map[current_location ^ prev_location[0] >> 1 ^ prev_location[1] >> 1 ^ ... up to n-1`] += 1` diff --git a/instrumentation/README.out_of_line.md b/instrumentation/README.out_of_line.md index aad215b6..2264f91f 100644 --- a/instrumentation/README.out_of_line.md +++ b/instrumentation/README.out_of_line.md @@ -1,18 +1,16 @@ -=========================================== -Using afl++ without inlined instrumentation -=========================================== +## Using afl++ without inlined instrumentation This file describes how you can disable inlining of instrumentation. By default, the GCC plugin will duplicate the effects of calling -__afl_trace (see afl-gcc-rt.o.c) in instrumented code, instead of +`__afl_trace` (see `afl-gcc-rt.o.c`) in instrumented code, instead of issuing function calls. The calls are presumed to be slower, more so because the rt file itself is not optimized by the compiler. -Setting AFL_GCC_OUT_OF_LINE=1 in the environment while compiling code +Setting `AFL_GCC_OUT_OF_LINE=1` in the environment while compiling code with the plugin will disable this inlining, issuing calls to the unoptimized runtime instead. diff --git a/instrumentation/README.persistent_mode.md b/instrumentation/README.persistent_mode.md index 2cf76adf..24f81ea0 100644 --- a/instrumentation/README.persistent_mode.md +++ b/instrumentation/README.persistent_mode.md @@ -16,7 +16,7 @@ Examples can be found in [utils/persistent_mode](../utils/persistent_mode). ## 2) TLDR; Example `fuzz_target.c`: -``` +```c #include "what_you_need_for_your_target.h" __AFL_FUZZ_INIT(); @@ -60,14 +60,14 @@ The speed increase is usually x10 to x20. If you want to be able to compile the target without afl-clang-fast/lto then add this just after the includes: -``` +```c #ifndef __AFL_FUZZ_TESTCASE_LEN ssize_t fuzz_len; #define __AFL_FUZZ_TESTCASE_LEN fuzz_len unsigned char fuzz_buf[1024000]; #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf #define __AFL_FUZZ_INIT() void sync(void); - #define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? + #define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0) #define __AFL_INIT() sync() #endif ``` @@ -75,7 +75,7 @@ add this just after the includes: ## 3) Deferred initialization AFL tries to optimize performance by executing the targeted binary just once, -stopping it just before main(), and then cloning this "main" process to get +stopping it just before `main()`, and then cloning this "main" process to get a steady supply of targets to fuzz. Although this approach eliminates much of the OS-, linker- and libc-level @@ -97,7 +97,7 @@ a location after: - The creation of any vital threads or child processes - since the forkserver can't clone them easily. - - The initialization of timers via setitimer() or equivalent calls. + - The initialization of timers via `setitimer()` or equivalent calls. - The creation of temporary files, network sockets, offset-sensitive file descriptors, and similar shared-state resources - but only provided that @@ -150,9 +150,9 @@ the impact of memory leaks and similar glitches; 1000 is a good starting point, and going much higher increases the likelihood of hiccups without giving you any real performance benefits. -A more detailed template is shown in ../utils/persistent_mode/. -Similarly to the previous mode, the feature works only with afl-clang-fast; #ifdef -guards can be used to suppress it when using other compilers. +A more detailed template is shown in `../utils/persistent_mode/.` +Similarly to the previous mode, the feature works only with afl-clang-fast; +`#ifdef` guards can be used to suppress it when using other compilers. Note that as with the previous mode, the feature is easy to misuse; if you do not fully reset the critical state, you may end up with false positives or @@ -161,7 +161,7 @@ wary of memory leaks and of the state of file descriptors. PS. Because there are task switches still involved, the mode isn't as fast as "pure" in-process fuzzing offered, say, by LLVM's LibFuzzer; but it is a lot -faster than the normal fork() model, and compared to in-process fuzzing, +faster than the normal `fork()` model, and compared to in-process fuzzing, should be a lot more robust. ## 5) Shared memory fuzzing @@ -174,17 +174,17 @@ Setting this up is very easy: After the includes set the following macro: -``` +```c __AFL_FUZZ_INIT(); ``` Directly at the start of main - or if you are using the deferred forkserver -with `__AFL_INIT()` then *after* `__AFL_INIT? : -``` +with `__AFL_INIT()` then *after* `__AFL_INIT()` : +```c unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; ``` Then as first line after the `__AFL_LOOP` while loop: -``` +```c int len = __AFL_FUZZ_TESTCASE_LEN; ``` and that is all! 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..cca38cd0 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -20,7 +20,7 @@ #include "config.h" #include "types.h" #include "cmplog.h" -#include "llvm-ngram-coverage.h" +#include "llvm-alternative-coverage.h" #include <stdio.h> #include <stdlib.h> @@ -34,6 +34,7 @@ #include <errno.h> #include <sys/mman.h> +#include <sys/syscall.h> #ifndef __HAIKU__ #include <sys/shm.h> #endif @@ -96,10 +97,12 @@ int __afl_selective_coverage_temp = 1; #if defined(__ANDROID__) || defined(__HAIKU__) PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX]; +PREV_LOC_T __afl_prev_caller[CTX_MAX_K]; u32 __afl_prev_ctx; u32 __afl_cmp_counter; #else __thread PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX]; +__thread PREV_LOC_T __afl_prev_caller[CTX_MAX_K]; __thread u32 __afl_prev_ctx; __thread u32 __afl_cmp_counter; #endif @@ -122,6 +125,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 +189,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 +235,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 +255,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,18 +315,23 @@ 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, " + "DEBUG: (1) id_str %s, __afl_area_ptr %p, __afl_area_initial %p, " + "__afl_area_ptr_dummy 0x%p, __afl_map_addr 0x%llx, MAP_SIZE %u, " + "__afl_final_loc %u, " "max_size_forkserver %u/0x%x\n", id_str == NULL ? "<null>" : id_str, __afl_area_ptr, - __afl_area_initial, __afl_map_addr, MAP_SIZE, __afl_final_loc, - FS_OPT_MAX_MAPSIZE, FS_OPT_MAX_MAPSIZE); + __afl_area_initial, __afl_area_ptr_dummy, __afl_map_addr, MAP_SIZE, + __afl_final_loc, FS_OPT_MAX_MAPSIZE, FS_OPT_MAX_MAPSIZE); + + } if (id_str) { - if (__afl_area_ptr && __afl_area_ptr != __afl_area_initial) { + if (__afl_area_ptr && __afl_area_ptr != __afl_area_initial && + __afl_area_ptr != __afl_area_ptr_dummy) { if (__afl_map_addr) { @@ -350,17 +376,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); @@ -439,6 +466,19 @@ static void __afl_map_shm(void) { __afl_area_ptr_backup = __afl_area_ptr; + if (__afl_debug) { + + fprintf(stderr, + "DEBUG: (2) id_str %s, __afl_area_ptr %p, __afl_area_initial %p, " + "__afl_area_ptr_dummy 0x%p, __afl_map_addr 0x%llx, MAP_SIZE " + "%u, __afl_final_loc %u, " + "max_size_forkserver %u/0x%x\n", + id_str == NULL ? "<null>" : id_str, __afl_area_ptr, + __afl_area_initial, __afl_area_ptr_dummy, __afl_map_addr, MAP_SIZE, + __afl_final_loc, FS_OPT_MAX_MAPSIZE, FS_OPT_MAX_MAPSIZE); + + } + if (__afl_selective_coverage) { if (__afl_map_size > MAP_INITIAL_SIZE) { @@ -467,7 +507,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 +516,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 +572,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 +652,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 +829,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 +882,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,11 +1117,14 @@ 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"); + } + } if (!init_done) { @@ -1060,6 +1164,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 +1193,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 +1213,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 +1281,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 +1303,40 @@ 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 instrumented 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 +1354,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 +1719,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) { + + if (unlikely(__asan_region_is_poisoned(ptr, len))) { return 0; } - char *p = (char *)ptr; - char *page = (char *)((uintptr_t)p & ~(sysconf(_SC_PAGE_SIZE) - 1)); + long r = syscall(__afl_dummy_fd[1], SYS_write, ptr, len); - int r = msync(page, (p - page) + len, MS_ASYNC); - if (r < 0) return errno != ENOMEM; - return 1; + 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 +1762,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 +1788,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 - 1; } @@ -1610,9 +1806,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 +1854,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 +1864,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 +1875,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 +1885,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-common.cc b/instrumentation/afl-llvm-common.cc index aa54f4f7..0fd3a011 100644 --- a/instrumentation/afl-llvm-common.cc +++ b/instrumentation/afl-llvm-common.cc @@ -62,13 +62,15 @@ bool isIgnoreFunction(const llvm::Function *F) { "sancov.", "__ubsan_", "ign.", - "__afl_", + "__afl", "_fini", - "__libc_csu", + "__libc_", "__asan", "__msan", "__cmplog", "__sancov", + "__cxx_", + "_GLOBAL", "msan.", "LLVMFuzzerM", "LLVMFuzzerC", 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..0f773aba 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -62,7 +62,7 @@ typedef long double max_align_t; #endif #include "afl-llvm-common.h" -#include "llvm-ngram-coverage.h" +#include "llvm-alternative-coverage.h" using namespace llvm; @@ -82,9 +82,10 @@ class AFLCoverage : public ModulePass { protected: uint32_t ngram_size = 0; + uint32_t ctx_k = 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; }; @@ -183,10 +184,16 @@ bool AFLCoverage::runOnModule(Module &M) { skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); unsigned PrevLocSize = 0; + unsigned PrevCallerSize = 0; char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE"); + char *ctx_k_str = getenv("AFL_LLVM_CTX_K"); + if (!ctx_k_str) ctx_k_str = getenv("AFL_CTX_K"); ctx_str = getenv("AFL_LLVM_CTX"); + caller_str = getenv("AFL_LLVM_CALLER"); + + bool instrument_ctx = ctx_str || caller_str; #ifdef AFL_HAVE_VECTOR_INTRINSICS /* Decide previous location vector size (must be a power of two) */ @@ -204,6 +211,31 @@ bool AFLCoverage::runOnModule(Module &M) { if (ngram_size) PrevLocSize = ngram_size - 1; else + PrevLocSize = 1; + + /* Decide K-ctx vector size (must be a power of two) */ + VectorType *PrevCallerTy = NULL; + + if (ctx_k_str) + if (sscanf(ctx_k_str, "%u", &ctx_k) != 1 || ctx_k < 1 || ctx_k > CTX_MAX_K) + FATAL("Bad value of AFL_CTX_K (must be between 1 and CTX_MAX_K (%u))", + CTX_MAX_K); + + if (ctx_k == 1) { + + ctx_k = 0; + instrument_ctx = true; + caller_str = ctx_k_str; // Enable CALLER instead + + } + + if (ctx_k) { + + PrevCallerSize = ctx_k; + instrument_ctx = true; + + } + #else if (ngram_size_str) #ifndef LLVM_VERSION_PATCH @@ -217,8 +249,20 @@ bool AFLCoverage::runOnModule(Module &M) { "%d.%d.%d!", LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH); #endif + if (ctx_k_str) + #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 - PrevLocSize = 1; #ifdef AFL_HAVE_VECTOR_INTRINSICS int PrevLocVecSize = PowerOf2Ceil(PrevLocSize); @@ -231,6 +275,17 @@ bool AFLCoverage::runOnModule(Module &M) { ); #endif +#ifdef AFL_HAVE_VECTOR_INTRINSICS + int PrevCallerVecSize = PowerOf2Ceil(PrevCallerSize); + if (ctx_k) + 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. */ @@ -238,9 +293,10 @@ bool AFLCoverage::runOnModule(Module &M) { new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); GlobalVariable *AFLPrevLoc; + GlobalVariable *AFLPrevCaller; 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"); @@ -275,6 +331,31 @@ bool AFLCoverage::runOnModule(Module &M) { #endif #ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ctx_k) + #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 AFL_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. */ @@ -288,13 +369,30 @@ bool AFLCoverage::runOnModule(Module &M) { PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, PrevLocSize)); Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle); + + Constant * PrevCallerShuffleMask = NULL; + SmallVector<Constant *, 32> PrevCallerShuffle = {UndefValue::get(Int32Ty)}; + + if (ctx_k) { + + 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 *Zero = ConstantInt::get(Int8Ty, 0); ConstantInt *One = ConstantInt::get(Int8Ty, 1); - LoadInst *PrevCtx = NULL; // CTX sensitive coverage + Value * PrevCtx = NULL; // CTX sensitive coverage + LoadInst *PrevCaller = NULL; // K-CTX coverage /* Instrument all the things! */ @@ -318,12 +416,30 @@ bool AFLCoverage::runOnModule(Module &M) { IRBuilder<> IRB(&(*IP)); // Context sensitive coverage - if (ctx_str && &BB == &F.getEntryBlock()) { + if (instrument_ctx && &BB == &F.getEntryBlock()) { + +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ctx_k) { + + PrevCaller = IRB.CreateLoad(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(AFLContext); + PrevCtxLoad->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + PrevCtx = PrevCtxLoad; - // load the context ID of the previous function and write to to a local - // variable on the stack - PrevCtx = IRB.CreateLoad(AFLContext); - PrevCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + } // does the function have calls? and is any of the calls larger than one // basic block? @@ -354,10 +470,32 @@ 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); - StoreCtx->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); + Value *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size)); +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ctx_k) { + + 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_str) NewCtx = IRB.CreateXor(PrevCtx, NewCtx); + StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext); + StoreCtx->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } } @@ -411,13 +549,20 @@ 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 (instrument_ctx && has_calls) { Instruction *Inst = BB.getTerminator(); if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) { IRBuilder<> Post_IRB(Inst); - StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + + StoreInst *RestoreCtx; + #ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ctx_k) + RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller); + else + #endif + RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); @@ -458,7 +603,7 @@ bool AFLCoverage::runOnModule(Module &M) { #endif PrevLocTrans = PrevLoc; - if (ctx_str) + if (instrument_ctx) PrevLocTrans = IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty); else @@ -545,13 +690,20 @@ 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 (instrument_ctx && has_calls) { Instruction *Inst = BB.getTerminator(); if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) { IRBuilder<> Post_IRB(Inst); - StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + + StoreInst *RestoreCtx; +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ctx_k) + RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller); + else +#endif + RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); 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/llvm-ngram-coverage.h b/instrumentation/llvm-alternative-coverage.h index 666839c8..0d7b3957 100644 --- a/instrumentation/llvm-ngram-coverage.h +++ b/instrumentation/llvm-alternative-coverage.h @@ -14,5 +14,8 @@ typedef u64 PREV_LOC_T; /* Maximum ngram size */ #define NGRAM_SIZE_MAX 16U +/* Maximum K for top-K context sensitivity */ +#define CTX_MAX_K 32U + #endif 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..38085389 100755 --- a/qemu_mode/build_qemu_support.sh +++ b/qemu_mode/build_qemu_support.sh @@ -59,51 +59,11 @@ if [ ! -f "../afl-showmap" ]; then fi -PREREQ_NOTFOUND= -for i in git wget sha384sum bison flex iconv patch pkg-config; do - - T=`command -v "$i" 2>/dev/null` - - if [ "$T" = "" ]; then - - echo "[-] Error: '$i' not found, please install first." - PREREQ_NOTFOUND=1 - - fi - -done - -PYTHONBIN=`command -v python3 || command -v python || command -v python2` - -if [ "$PYTHONBIN" = "" ]; then - echo "[-] Error: 'python' not found, please install using 'sudo apt install python3'." - PREREQ_NOTFOUND=1 -fi - - -if [ ! -d "/usr/include/glib-2.0/" -a ! -d "/usr/local/include/glib-2.0/" ]; then - - echo "[-] Error: devel version of 'glib2' not found, please install first." - PREREQ_NOTFOUND=1 - -fi - -if [ ! -d "/usr/include/pixman-1/" -a ! -d "/usr/local/include/pixman-1/" ]; then - - echo "[-] Error: devel version of 'pixman-1' not found, please install first." - PREREQ_NOTFOUND=1 - -fi - if echo "$CC" | grep -qF /afl-; then echo "[-] Error: do not use afl-gcc or afl-clang to compile this tool." - PREREQ_NOTFOUND=1 - -fi - -if [ "$PREREQ_NOTFOUND" = "1" ]; then exit 1 + fi echo "[+] All checks passed!" @@ -131,9 +91,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 @@ -233,7 +197,6 @@ QEMU_CONF_FLAGS=" \ --disable-xen \ --disable-xen-pci-passthrough \ --disable-xfsctl \ - --python=${PYTHONBIN} \ --target-list="${CPU_TARGET}-linux-user" \ --without-default-devices \ " @@ -372,6 +335,20 @@ if [ "$ORIG_CROSS" = "" ]; then then # works on Arch Linux CROSS=$CPU_TARGET-pc-linux-gnu-gcc fi + if ! command -v "$CROSS" > /dev/null && [ "$CPU_TARGET" = "i386" ] + then + CROSS=i686-linux-gnu-gcc + if ! command -v "$CROSS" > /dev/null + then # works on Arch Linux + CROSS=i686-pc-linux-gnu-gcc + fi + if ! command -v "$CROSS" > /dev/null && [ "`uname -m`" = "x86_64" ] + then # set -m32 + test "$CC" = "" && CC="gcc" + CROSS="$CC" + CROSS_FLAGS=-m32 + fi + fi fi if ! command -v "$CROSS" > /dev/null ; then @@ -387,13 +364,13 @@ if ! command -v "$CROSS" > /dev/null ; then echo "[!] Cross compiler $CROSS could not be found, cannot compile libcompcov libqasan and unsigaction" fi else - echo "[+] Building afl++ qemu support libraries with CC=$CROSS" + echo "[+] Building afl++ qemu support libraries with CC=\"$CROSS $CROSS_FLAGS\"" echo "[+] Building libcompcov ..." - make -C libcompcov CC=$CROSS && echo "[+] libcompcov ready" + make -C libcompcov CC="$CROSS $CROSS_FLAGS" && echo "[+] libcompcov ready" echo "[+] Building unsigaction ..." - make -C unsigaction CC=$CROSS && echo "[+] unsigaction ready" + make -C unsigaction CC="$CROSS $CROSS_FLAGS" && echo "[+] unsigaction ready" echo "[+] Building libqasan ..." - make -C libqasan CC=$CROSS && echo "[+] unsigaction ready" + make -C libqasan CC="$CROSS $CROSS_FLAGS" && echo "[+] unsigaction ready" fi echo "[+] All done for qemu_mode, enjoy!" 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..44654de0 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -22,7 +22,7 @@ #include "types.h" #include "debug.h" #include "alloc-inl.h" -#include "llvm-ngram-coverage.h" +#include "llvm-alternative-coverage.h" #include <stdio.h> #include <unistd.h> @@ -50,7 +50,7 @@ static u8 **cc_params; /* Parameters passed to the real CC */ static u32 cc_par_cnt = 1; /* Param count, including argv0 */ static u8 clang_mode; /* Invoked as afl-clang*? */ static u8 llvm_fullpath[PATH_MAX]; -static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode; +static u8 instrument_mode, instrument_opt_mode, ngram_size, ctx_k, lto_mode; static u8 compiler_mode, plusplus_mode, have_instr_env = 0; static u8 have_gcc, have_llvm, have_gcc_plugin, have_lto, have_instr_list = 0; static u8 * lto_flag = AFL_CLANG_FLTO, *argvnull; @@ -73,7 +73,9 @@ enum { INSTRUMENT_GCC = 6, INSTRUMENT_CLANG = 7, INSTRUMENT_OPT_CTX = 8, - INSTRUMENT_OPT_NGRAM = 16 + INSTRUMENT_OPT_NGRAM = 16, + INSTRUMENT_OPT_CALLER = 32, + INSTRUMENT_OPT_CTX_K = 64, }; @@ -88,7 +90,7 @@ char instrument_mode_string[18][18] = { "GCC", "CLANG", "CTX", - "", + "CALLER", "", "", "", @@ -938,7 +940,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - if (preprocessor_only) { + // prevent unnecessary build errors + cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; + + if (preprocessor_only || have_c) { /* In the preprocessor_only case (-E), we are not actually compiling at all but requesting the compiler to output preprocessed sources only. @@ -999,18 +1004,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #if !defined(__APPLE__) && !defined(__sun) - if (!shared_linking && !have_c) + if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif #if defined(USEMMAP) && !defined(__HAIKU__) - if (!have_c) cc_params[cc_par_cnt++] = "-lrt"; + cc_params[cc_par_cnt++] = "-lrt"; #endif - // prevent unnecessary build errors - cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; - } #endif @@ -1023,7 +1025,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { int main(int argc, char **argv, char **envp) { - int i; + int i, passthrough = 0; char *callname = argv[0], *ptr = NULL; if (getenv("AFL_DEBUG")) { @@ -1043,6 +1045,13 @@ int main(int argc, char **argv, char **envp) { } + if (getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) { + + passthrough = 1; + if (!debug) { be_quiet = 1; } + + } + if ((ptr = strrchr(callname, '/')) != NULL) callname = ptr + 1; argvnull = (u8 *)argv[0]; check_environment_vars(envp); @@ -1273,6 +1282,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")) { @@ -1286,6 +1296,26 @@ int main(int argc, char **argv, char **envp) { } + if (getenv("AFL_LLVM_CTX_K")) { + + ctx_k = atoi(getenv("AFL_LLVM_CTX_K")); + if (ctx_k < 1 || ctx_k > CTX_MAX_K) + FATAL("K-CTX instrumentation mode must be between 1 and CTX_MAX_K (%u)", + CTX_MAX_K); + if (ctx_k == 1) { + + setenv("AFL_LLVM_CALLER", "1", 1); + unsetenv("AFL_LLVM_CTX_K"); + instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + + } else { + + instrument_opt_mode |= INSTRUMENT_OPT_CTX_K; + + } + + } + if (getenv("AFL_LLVM_INSTRUMENT")) { u8 *ptr2 = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;"); @@ -1381,6 +1411,44 @@ int main(int argc, char **argv, char **envp) { } + if (strncasecmp(ptr2, "ctx-", strlen("ctx-")) == 0) { + + u8 *ptr3 = ptr2 + strlen("ctx-"); + while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) + ptr3++; + + if (!*ptr3) { + + if ((ptr3 = getenv("AFL_LLVM_CTX_K")) == NULL) + FATAL( + "you must set the K-CTX K with (e.g. for value 2) " + "AFL_LLVM_INSTRUMENT=ctx-2"); + + } + + ctx_k = atoi(ptr3); + if (ctx_k < 1 || ctx_k > CTX_MAX_K) + FATAL( + "K-CTX instrumentation option must be between 1 and CTX_MAX_K " + "(%u)", + CTX_MAX_K); + + if (ctx_k == 1) { + + instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + setenv("AFL_LLVM_CALLER", "1", 1); + unsetenv("AFL_LLVM_CTX_K"); + + } else { + + instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K); + u8 *ptr4 = alloc_printf("%u", ctx_k); + setenv("AFL_LLVM_CTX_K", ptr4, 1); + + } + + } + if (strncasecmp(ptr2, "ctx", strlen("ctx")) == 0) { instrument_opt_mode |= INSTRUMENT_OPT_CTX; @@ -1388,6 +1456,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 +1496,27 @@ 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_OPT_CTX) && + (instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { + + FATAL("you cannot set CTX and K-CTX together"); + + } + + if ((instrument_opt_mode & INSTRUMENT_OPT_CALLER) && + (instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { + + FATAL("you cannot set CALLER and K-CTX together"); + + } + if (instrument_opt_mode && instrument_mode == INSTRUMENT_DEFAULT && (compiler_mode == LLVM || compiler_mode == UNSET)) { @@ -1498,12 +1594,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 +1647,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) " @@ -1593,6 +1693,8 @@ int main(int argc, char **argv, char **envp) { " AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" " AFL_NO_BUILTIN: no builtins for string compare functions (for " "libtokencap.so)\n" + " AFL_NOOP: behave like a normal compiler (to pass configure " + "tests)\n" " AFL_PATH: path to instrumenting pass and runtime " "(afl-compiler-rt.*o)\n" " AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n" @@ -1644,15 +1746,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 +1874,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 +1884,18 @@ int main(int argc, char **argv, char **envp) { } else { - if (instrument_opt_mode == INSTRUMENT_OPT_CTX) + char *ptr2 = alloc_printf(" + NGRAM-%u", ngram_size); + char *ptr3 = alloc_printf(" + K-CTX-%u", ctx_k); - 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); + ptr = alloc_printf( + "%s%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 : "", + (instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : ""); + + ck_free(ptr2); + ck_free(ptr3); } @@ -1799,11 +1906,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( @@ -1897,7 +2007,16 @@ int main(int argc, char **argv, char **envp) { } - execvp(cc_params[0], (char **)cc_params); + if (passthrough) { + + argv[0] = cc_params[0]; + execvp(cc_params[0], (char **)argv); + + } else { + + execvp(cc_params[0], (char **)cc_params); + + } FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]); diff --git a/src/afl-common.c b/src/afl-common.c index a306fe5e..9f6eb564 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -682,6 +682,7 @@ void check_environment_vars(char **envp) { env[strlen(afl_environment_variables[i])] == '=') { match = 1; + if ((val = getenv(afl_environment_variables[i])) && !*val) { WARNF( @@ -1122,7 +1123,7 @@ u8 *u_stringify_time_diff(u8 *buf, u64 cur_ms, u64 event_ms) { /* Reads the map size from ENV */ u32 get_map_size(void) { - uint32_t map_size = (MAP_SIZE << 2); // needed for target ctors :( + uint32_t map_size = 8000000; // a very large default map char * ptr; if ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE"))) { @@ -1130,7 +1131,7 @@ u32 get_map_size(void) { map_size = atoi(ptr); if (!map_size || map_size > (1 << 29)) { - FATAL("illegal AFL_MAP_SIZE %u, must be between %u and %u", map_size, 32U, + FATAL("illegal AFL_MAP_SIZE %u, must be between %u and %u", map_size, 64U, 1U << 29); } diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index fd5edc98..68995388 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -481,27 +481,28 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* This should improve performance a bit, since it stops the linker from doing extra work post-fork(). */ - if (!getenv("LD_BIND_LAZY")) { setenv("LD_BIND_NOW", "1", 0); } + if (!getenv("LD_BIND_LAZY")) { setenv("LD_BIND_NOW", "1", 1); } /* Set sane defaults for ASAN if nothing else specified. */ - if (fsrv->debug == true && !getenv("ASAN_OPTIONS")) + if (!getenv("ASAN_OPTIONS")) setenv("ASAN_OPTIONS", "abort_on_error=1:" "detect_leaks=0:" "malloc_context_size=0:" "symbolize=0:" "allocator_may_return_null=1:" + "detect_odr_violation=0:" "handle_segv=0:" "handle_sigbus=0:" "handle_abort=0:" "handle_sigfpe=0:" "handle_sigill=0", - 0); + 1); /* Set sane defaults for UBSAN if nothing else specified. */ - if (fsrv->debug == true && !getenv("UBSAN_OPTIONS")) + if (!getenv("UBSAN_OPTIONS")) setenv("UBSAN_OPTIONS", "halt_on_error=1:" "abort_on_error=1:" @@ -513,7 +514,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "handle_abort=0:" "handle_sigfpe=0:" "handle_sigill=0", - 0); + 1); /* Envs for QASan */ setenv("QASAN_MAX_CALL_STACK", "0", 0); @@ -522,7 +523,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* MSAN is tricky, because it doesn't support abort_on_error=1 at this point. So, we do this in a very hacky way. */ - if (fsrv->debug == true && !getenv("MSAN_OPTIONS")) + if (!getenv("MSAN_OPTIONS")) setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" "symbolize=0:" @@ -535,7 +536,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "handle_abort=0:" "handle_sigfpe=0:" "handle_sigill=0", - 0); + 1); fsrv->init_child_func(fsrv, argv); @@ -820,7 +821,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, " - The target binary requires a large map and crashes before " "reporting.\n" - " Set a high value (e.g. AFL_MAP_SIZE=1024000) or use " + " Set a high value (e.g. AFL_MAP_SIZE=8000000) or use " "AFL_DEBUG=1 to see the\n" " message from the target binary\n\n" @@ -847,7 +848,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, " - The target binary requires a large map and crashes before " "reporting.\n" - " Set a high value (e.g. AFL_MAP_SIZE=1024000) or use " + " Set a high value (e.g. AFL_MAP_SIZE=8000000) or use " "AFL_DEBUG=1 to see the\n" " message from the target binary\n\n" @@ -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=8000000\n" "Otherwise there is a horrible bug in the fuzzer.\n" "Poke <afl-users@googlegroups.com> for troubleshooting tips.\n"); @@ -927,6 +930,11 @@ 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=8000000\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-init.c b/src/afl-fuzz-init.c index 3dbc4c65..ca2f75f1 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -828,7 +828,7 @@ void perform_dry_run(afl_state_t *afl) { for (idx = 0; idx < afl->queued_paths; idx++) { q = afl->queue_buf[idx]; - if (unlikely(q->disabled)) { continue; } + if (unlikely(!q || q->disabled)) { continue; } u8 res; s32 fd; @@ -1069,7 +1069,7 @@ void perform_dry_run(afl_state_t *afl) { } afl->max_depth = 0; - for (i = 0; i < afl->queued_paths; i++) { + for (i = 0; i < afl->queued_paths && likely(afl->queue_buf[i]); i++) { if (!afl->queue_buf[i]->disabled && afl->queue_buf[i]->depth > afl->max_depth) @@ -1136,10 +1136,11 @@ void perform_dry_run(afl_state_t *afl) { for (idx = 0; idx < afl->queued_paths; idx++) { q = afl->queue_buf[idx]; - if (q->disabled || q->cal_failed || !q->exec_cksum) { continue; } + if (!q || q->disabled || q->cal_failed || !q->exec_cksum) { continue; } u32 done = 0; - for (i = idx + 1; i < afl->queued_paths && !done; i++) { + for (i = idx + 1; + i < afl->queued_paths && !done && likely(afl->queue_buf[i]); i++) { struct queue_entry *p = afl->queue_buf[i]; if (p->disabled || p->cal_failed || !p->exec_cksum) { continue; } @@ -1191,7 +1192,7 @@ void perform_dry_run(afl_state_t *afl) { for (idx = 0; idx < afl->queued_paths; idx++) { - if (!afl->queue_buf[idx]->disabled && + if (afl->queue_buf[idx] && !afl->queue_buf[idx]->disabled && afl->queue_buf[idx]->depth > afl->max_depth) afl->max_depth = afl->queue_buf[idx]->depth; @@ -1247,7 +1248,7 @@ void pivot_inputs(afl_state_t *afl) { ACTF("Creating hard links for all input files..."); - for (i = 0; i < afl->queued_paths; i++) { + for (i = 0; i < afl->queued_paths && likely(afl->queue_buf[i]); i++) { q = afl->queue_buf[i]; @@ -2457,7 +2458,7 @@ void check_asan_opts(afl_state_t *afl) { } - if (!strstr(x, "symbolize=0")) { + if (!afl->debug && !strstr(x, "symbolize=0")) { FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index ad3e3b8e..b2f88205 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 @@ -324,7 +325,7 @@ static u8 check_if_text(afl_state_t *afl, struct queue_entry *q) { if (len >= MAX_FILE) len = MAX_FILE - 1; if ((fd = open(q->fname, O_RDONLY)) < 0) return 0; - buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len); + buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len + 1); comp = read(fd, buf, len); close(fd); if (comp != (ssize_t)len) return 0; 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-stats.c b/src/afl-fuzz-stats.c index bd856088..99059a2d 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -645,6 +645,13 @@ void show_stats(afl_state_t *afl) { #define SP10 SP5 SP5 #define SP20 SP10 SP10 + /* Since `total_crashes` does not get reloaded from disk on restart, + it indicates if we found crashes this round already -> paint red. + If it's 0, but `unique_crashes` is set from a past run, paint in yellow. */ + char *crash_color = afl->total_crashes ? cLRD + : afl->unique_crashes ? cYEL + : cRST; + /* Lord, forgive me this. */ SAYF(SET_G1 bSTG bLT bH bSTOP cCYA @@ -732,7 +739,7 @@ void show_stats(afl_state_t *afl) { u_stringify_time_diff(time_tmp, cur_ms, afl->last_crash_time); SAYF(bV bSTOP " last uniq crash : " cRST "%-33s " bSTG bV bSTOP " uniq crashes : %s%-6s" bSTG bV "\n", - time_tmp, afl->unique_crashes ? cLRD : cRST, tmp); + time_tmp, crash_color, tmp); sprintf(tmp, "%s%s", u_stringify_int(IB(0), afl->unique_hangs), (afl->unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : ""); @@ -815,15 +822,13 @@ void show_stats(afl_state_t *afl) { SAYF(bV bSTOP " total execs : " cRST "%-20s " bSTG bV bSTOP " new crashes : %s%-22s" bSTG bV "\n", - u_stringify_int(IB(0), afl->fsrv.total_execs), - afl->unique_crashes ? cLRD : cRST, tmp); + u_stringify_int(IB(0), afl->fsrv.total_execs), crash_color, tmp); } else { SAYF(bV bSTOP " total execs : " cRST "%-20s " bSTG bV bSTOP " total crashes : %s%-22s" bSTG bV "\n", - u_stringify_int(IB(0), afl->fsrv.total_execs), - afl->unique_crashes ? cLRD : cRST, tmp); + u_stringify_int(IB(0), afl->fsrv.total_execs), crash_color, tmp); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index a02eadb2..64e4b869 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -351,7 +351,7 @@ int main(int argc, char **argv_orig, char **envp) { exit_1 = !!afl->afl_env.afl_bench_just_one; SAYF(cCYA "afl-fuzz" VERSION cRST - " based on afl by Michal Zalewski and a big online community\n"); + " based on afl by Michal Zalewski and a large online community\n"); doc_path = access(DOC_PATH, F_OK) != 0 ? (u8 *)"docs" : (u8 *)DOC_PATH; @@ -1403,6 +1403,15 @@ int main(int argc, char **argv_orig, char **envp) { set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY); #endif + #ifdef __APPLE__ + if (pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0) != 0) { + + WARNF("general thread priority settings failed"); + + } + + #endif + init_count_class16(); if (afl->is_main_node && check_main_node_exists(afl) == 1) { @@ -1437,23 +1446,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 && @@ -1568,6 +1562,21 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->non_instrumented_mode || afl->fsrv.qemu_mode || afl->unicorn_mode) { + + map_size = afl->fsrv.map_size = MAP_SIZE; + afl->virgin_bits = ck_realloc(afl->virgin_bits, map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, map_size); + afl->top_rated = ck_realloc(afl->top_rated, map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, map_size); + afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, map_size); + afl->first_trace = ck_realloc(afl->first_trace, map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, map_size); + + } + afl->argv = use_argv; afl->fsrv.trace_bits = afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode); @@ -1575,42 +1584,44 @@ int main(int argc, char **argv_orig, char **envp) { if (!afl->non_instrumented_mode && !afl->fsrv.qemu_mode && !afl->unicorn_mode) { - afl->fsrv.map_size = 4194304; // dummy temporary value - setenv("AFL_MAP_SIZE", "4194304", 1); + if (map_size <= 8000000 && !afl->non_instrumented_mode && + !afl->fsrv.qemu_mode && !afl->unicorn_mode) { + + afl->fsrv.map_size = 8000000; // dummy temporary value + setenv("AFL_MAP_SIZE", "8000000", 1); + + } u32 new_map_size = afl_fsrv_get_mapsize( &afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); - if (new_map_size && new_map_size != 4194304) { - - // only reinitialize when it makes sense - if (map_size < new_map_size || - (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { - - OKF("Re-initializing maps to %u bytes", new_map_size); - - afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); - afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); - afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); - afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); - afl->top_rated = - ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); - afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); - afl->clean_trace_custom = - ck_realloc(afl->clean_trace_custom, new_map_size); - afl->first_trace = ck_realloc(afl->first_trace, new_map_size); - afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); - - afl_fsrv_kill(&afl->fsrv); - afl_shm_deinit(&afl->shm); - afl->fsrv.map_size = new_map_size; - afl->fsrv.trace_bits = - afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); - setenv("AFL_NO_AUTODICT", "1", 1); // loaded already - afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child); - - } + // only reinitialize when it makes sense + if ((map_size < new_map_size || + (new_map_size != MAP_SIZE && new_map_size < map_size && + map_size - new_map_size > MAP_SIZE))) { + + OKF("Re-initializing maps to %u bytes", new_map_size); + + afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); + afl->top_rated = + ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); + afl->clean_trace_custom = + ck_realloc(afl->clean_trace_custom, new_map_size); + afl->first_trace = ck_realloc(afl->first_trace, new_map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); + + afl_fsrv_kill(&afl->fsrv); + afl_shm_deinit(&afl->shm); + afl->fsrv.map_size = new_map_size; + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); + setenv("AFL_NO_AUTODICT", "1", 1); // loaded already + afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); map_size = new_map_size; @@ -1630,57 +1641,77 @@ int main(int argc, char **argv_orig, char **envp) { afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary; afl->cmplog_fsrv.init_child_func = cmplog_exec_child; - afl->cmplog_fsrv.map_size = 4194304; + if (map_size <= 8000000 && !afl->non_instrumented_mode && + !afl->fsrv.qemu_mode && !afl->unicorn_mode) { + + afl->cmplog_fsrv.map_size = 8000000; // dummy temporary value + setenv("AFL_MAP_SIZE", "8000000", 1); + + } u32 new_map_size = afl_fsrv_get_mapsize(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); - if (new_map_size && new_map_size != 4194304) { + // only reinitialize when it needs to be larger + if (map_size < new_map_size) { - // only reinitialize when it needs to be larger - if (map_size < new_map_size) { + OKF("Re-initializing maps to %u bytes due cmplog", new_map_size); - OKF("Re-initializing maps to %u bytes due cmplog", new_map_size); + afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); + afl->top_rated = + ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); + afl->clean_trace_custom = + ck_realloc(afl->clean_trace_custom, new_map_size); + afl->first_trace = ck_realloc(afl->first_trace, new_map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); - afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); - afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); - afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); - afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); - afl->top_rated = - ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); - afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); - afl->clean_trace_custom = - ck_realloc(afl->clean_trace_custom, new_map_size); - afl->first_trace = ck_realloc(afl->first_trace, new_map_size); - afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); + afl_fsrv_kill(&afl->fsrv); + afl_fsrv_kill(&afl->cmplog_fsrv); + afl_shm_deinit(&afl->shm); - afl_fsrv_kill(&afl->fsrv); - afl_fsrv_kill(&afl->cmplog_fsrv); - afl_shm_deinit(&afl->shm); - afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same - - afl->fsrv.trace_bits = - afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); - setenv("AFL_NO_AUTODICT", "1", 1); // loaded already - afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child); + afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same + map_size = new_map_size; - afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child); + setenv("AFL_NO_AUTODICT", "1", 1); // loaded already + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); + afl->cmplog_fsrv.trace_bits = afl->fsrv.trace_bits; + afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); - map_size = new_map_size; + } else { - } + afl->cmplog_fsrv.map_size = new_map_size; } - afl->cmplog_fsrv.map_size = map_size; - OKF("Cmplog forkserver successfully started"); } + 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:" diff --git a/test/test-custom-mutators.sh b/test/test-custom-mutators.sh index 24c95ac7..bae4220f 100755 --- a/test/test-custom-mutators.sh +++ b/test/test-custom-mutators.sh @@ -37,9 +37,9 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { echo "00000" > in/in # Run afl-fuzz w/ the C mutator - $ECHO "$GREY[*] running afl-fuzz for the C mutator, this will take approx 5 seconds" + $ECHO "$GREY[*] running afl-fuzz for the C mutator, this will take approx 10 seconds" { - AFL_CUSTOM_MUTATOR_LIBRARY=./libexamplemutator.so AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V1 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 + AFL_CUSTOM_MUTATOR_LIBRARY=./libexamplemutator.so AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 } >>errors 2>&1 # Check results @@ -57,9 +57,9 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { rm -rf out errors core.* # Run afl-fuzz w/ multiple C mutators - $ECHO "$GREY[*] running afl-fuzz with multiple custom C mutators, this will take approx 5 seconds" + $ECHO "$GREY[*] running afl-fuzz with multiple custom C mutators, this will take approx 10 seconds" { - AFL_CUSTOM_MUTATOR_LIBRARY="./libexamplemutator.so;./libexamplemutator2.so" AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V1 -m ${MEM_LIMIT} -i in -o out -- ./test-multiple-mutators >>errors 2>&1 + AFL_CUSTOM_MUTATOR_LIBRARY="./libexamplemutator.so;./libexamplemutator2.so" AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-multiple-mutators >>errors 2>&1 } >>errors 2>&1 test -n "$( ls out/default/crashes/id:000000* 2>/dev/null )" && { # TODO: update here @@ -76,11 +76,11 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { rm -rf out errors core.* # Run afl-fuzz w/ the Python mutator - $ECHO "$GREY[*] running afl-fuzz for the Python mutator, this will take approx 5 seconds" + $ECHO "$GREY[*] running afl-fuzz for the Python mutator, this will take approx 10 seconds" { export PYTHONPATH=${CUSTOM_MUTATOR_PATH} export AFL_PYTHON_MODULE=example - AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V5 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 + AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 unset PYTHONPATH unset AFL_PYTHON_MODULE } >>errors 2>&1 diff --git a/test/test-llvm.sh b/test/test-llvm.sh index 156b8920..aa36af1b 100755 --- a/test/test-llvm.sh +++ b/test/test-llvm.sh @@ -162,9 +162,9 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { test -e test-floatingpoint && { mkdir -p in echo ZZZZ > in/in - $ECHO "$GREY[*] running afl-fuzz with floating point splitting, this will take max. 30 seconds" + $ECHO "$GREY[*] running afl-fuzz with floating point splitting, this will take max. 45 seconds" { - AFL_BENCH_UNTIL_CRASH=1 AFL_NO_UI=1 ../afl-fuzz -s 1 -V30 -m ${MEM_LIMIT} -i in -o out -D -- ./test-floatingpoint >>errors 2>&1 + AFL_BENCH_UNTIL_CRASH=1 AFL_NO_UI=1 ../afl-fuzz -Z -s 123 -V50 -m ${MEM_LIMIT} -i in -o out -D -- ./test-floatingpoint >>errors 2>&1 } >>errors 2>&1 test -n "$( ls out/default/crashes/id:* 2>/dev/null )" && { $ECHO "$GREEN[+] llvm_mode laf-intel floatingpoint splitting feature works correctly" diff --git a/unicorn_mode/samples/speedtest/get_offsets.py b/unicorn_mode/samples/speedtest/get_offsets.py index c9dc76df..c9dc76df 100644..100755 --- a/unicorn_mode/samples/speedtest/get_offsets.py +++ b/unicorn_mode/samples/speedtest/get_offsets.py diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl deleted file mode 160000 -Subproject fb2fc9f25df32f17f6b6b859e4dbd70f9a857e0 diff --git a/utils/aflpp_driver/aflpp_driver.c b/utils/aflpp_driver/aflpp_driver.c index 6af79e14..9c97607c 100644 --- a/utils/aflpp_driver/aflpp_driver.c +++ b/utils/aflpp_driver/aflpp_driver.c @@ -204,7 +204,7 @@ int main(int argc, char **argv) { "To fuzz with afl-fuzz execute this:\n" " afl-fuzz [afl-flags] -- %s [-N]\n" "afl-fuzz will run N iterations before re-spawning the process (default: " - "1000)\n" + "INT_MAX)\n" "======================================================\n", argv[0], argv[0]); |