diff options
author | van Hauser <vh@thc.org> | 2021-06-14 23:48:47 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-14 23:48:47 +0200 |
commit | 7bcd4e290111ca81d6d58d1b70696e9e9aaa5ac1 (patch) | |
tree | 507e51068fa67885de4ed2eed5c9022ca8f45d11 | |
parent | 48c878a76ddec2c133fd5708b185b2ac27740084 (diff) | |
download | afl++-7bcd4e290111ca81d6d58d1b70696e9e9aaa5ac1.tar.gz |
push to stable (#973)
* use atomic read-modify-write increment for LLVM CLASSIC * Change other LLVM modes to atomic increments * sync (#886) * Create FUNDING.yml * Update FUNDING.yml * moved custom_mutator examples * unicorn speedtest makefile cleanup * fixed example location * fix qdbi * update util readme * work in progress: not working correctly yet * Frida persistent (#880) * Added x64 support for persistent mode (function call only), in-memory teest cases and complog * Review changes, fix NeverZero and code to parse the .text section of the main executable. Excluded ranges TBC * Various minor fixes and finished support for AFL_INST_LIBS * Review changes Co-authored-by: Your Name <you@example.com> * nits * fix frida mode * Integer overflow/underflow fixes in libdislocator (#889) * libdislocator: fixing integer overflow in 'max_mem' variable and setting 'max_mem' type to 'size_t' * libdislocator: fixing potential integer underflow in 'total_mem' variable due to its different values in different threads * Bumped warnings up to the max and fixed remaining issues (#890) Co-authored-by: Your Name <you@example.com> * nits * frida mode - support non-pie * nits * nit * update grammar mutator * Fixes for aarch64, OSX and other minor issues (#891) Co-authored-by: Your Name <you@example.com> * nits * nits * fix PCGUARD, build aflpp_driver with fPIC * Added representative fuzzbench test and test for libxml (#893) * Added representative fuzzbench test and test for libxml * Added support for building FRIDA from source with FRIDA_SOURCE=1 Co-authored-by: Your Name <you@example.com> * nits * update changelog * typos * still not working * fixed potential double free in custom trim (#881) * error handling, freeing mem * frida: complog -> cmplog * fix statsd writing * let aflpp_qemu_driver_hook.so build fail gracefully * fix stdin trimming * Support for AFL_ENTRYPOINT (#898) Co-authored-by: Your Name <you@example.com> * remove the input file .cur_input at the end of the fuzzing, if AFL_TMPDIR is used * reverse push (#901) * Create FUNDING.yml * Update FUNDING.yml * disable QEMU static pie Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> * clarify that no modifications are required. * add new test for frida_mode (please review) * typos * fix persistent mode (64-bit) * set ARCH for linux intel 32-bit for frida-gum-devkit * prepare for 32-bit support (later) * not on qemu 3 anymore * unicorn mips fixes * instrumentation further move to C++11 (#900) * unicorn fixes * first working NeverZero implementation * more unicorn fixes * Fix memory errors when trim causes testcase growth (#881) (#903) * Revert "fixed potential double free in custom trim (#881)" This reverts commit e9d2f72382cab75832721d859c3e731da071435d. * Revert "fix custom trim for increasing data" This reverts commit 86a8ef168dda766d2f25f15c15c4d3ecf21d0667. * Fix memory errors when trim causes testcase growth Modify trim_case_custom to avoid writing into in_buf because some custom mutators can cause the testcase to grow rather than shrink. Instead of modifying in_buf directly, we write the update out to the disk when trimming is complete, and then the caller is responsible for refreshing the in-memory buffer from the file. This is still a bit sketchy because it does need to modify q->len in order to notify the upper layers that something changed, and it could end up telling upper layer code that the q->len is *bigger* than the buffer (q->testcase_buf) that contains it, which is asking for trouble down the line somewhere... * Fix an unlikely situation Put back some `unlikely()` calls that were in the e9d2f72382cab75832721d859c3e731da071435d commit that was reverted. * add some comments * typo * Exit on time (#904) * Variable AFL_EXIT_ON_TIME description has been added. Variables AFL_EXIT_ON_TIME and afl_exit_on_time has been added. afl->exit_on_time variable initialization has been added. The asignment of a value to the afl->afl_env.afl_exit_on_time variable from environment variables has been added. Code to exit on timeout if new path not found has been added. * Type of afl_exit_on_time variable has been changed. Variable exit_on_time has been added to the afl_state_t structure. * Command `export AFL_EXIT_WHEN_DONE=1` has been added. * Millisecond to second conversion has been added. Call get_cur_time() has been added. * Revert to using the saved current time value. * Useless check has been removed. * fix new path to custom-mutators * ensure crashes/README.txt exists * fix * Changes to bump FRIDA version and to clone FRIDA repo in to build directory rather than use a submodule as the FRIDA build scripts don't like it (#906) Co-authored-by: Your Name <you@example.com> * Fix numeric overflow in cmplog implementation (#907) Co-authored-by: Your Name <you@example.com> * testcase fixes for unicorn * remove merge conflict artifacts * fix afl-plot * Changes to remove binaries from frida_mode (#913) Co-authored-by: Your Name <you@example.com> * Frida cmplog fail fast (#914) * Changes to remove binaries from frida_mode * Changes to make cmplog fail fast Co-authored-by: Your Name <you@example.com> * afl-plot: relative time * arch linux and mac os support for afl-system-config * typo * code-format * update documentation * github workflow for qemu * OSX-specific improvements (#912) * Fix afl-cc to work correctly by default on OSX using xcode - CLANG_ENV_VAR must be set for afl-as to work - Use clang mode by default if no specific compiler selected * Add OSX-specific documentation for configuring shared memory * Fixes to memory operands for complog (#916) Co-authored-by: Your Name <you@example.com> * fix a few cur_time uses * added bounds check to pivot_inputs (fixes #921) * additional safety checks for restarts * restrict afl-showmap in_file size * fix seed crash disable * add warning for afl-showmap partial read * no core dumps * AFL_PRINT_FILENAMES added * more documentation for AFL_EXIT_ON_TIME * Flushing for AFL_PRINT_FILENAMES * FASAN Support (#918) * FASAN Support * Fix handling of Address Sanitizer DSO * Changes to identification of Address Sanitizer DSO Co-authored-by: Your Name <you@example.com> * Support for x86 (#920) Co-authored-by: Your Name <you@example.com> * Update frida_mode readme (#925) * libqasan: use syscalls for read and write * update readme * Minor integration tweaks (#926) Co-authored-by: Your Name <you@example.com> * merge * fix afl-fuzz.c frida preload * cleaned up AFL_PRINT_FILENAMES env * Changes to have persistent mode exit at the end of the loop (#928) Co-authored-by: Your Name <you@example.com> * fix llvm-dict2file * push to stable (#931) (#932) * sync (#886) * Create FUNDING.yml * Update FUNDING.yml * moved custom_mutator examples * unicorn speedtest makefile cleanup * fixed example location * fix qdbi * update util readme * Frida persistent (#880) * Added x64 support for persistent mode (function call only), in-memory teest cases and complog * Review changes, fix NeverZero and code to parse the .text section of the main executable. Excluded ranges TBC * Various minor fixes and finished support for AFL_INST_LIBS * Review changes Co-authored-by: Your Name <you@example.com> * nits * fix frida mode * Integer overflow/underflow fixes in libdislocator (#889) * libdislocator: fixing integer overflow in 'max_mem' variable and setting 'max_mem' type to 'size_t' * libdislocator: fixing potential integer underflow in 'total_mem' variable due to its different values in different threads * Bumped warnings up to the max and fixed remaining issues (#890) Co-authored-by: Your Name <you@example.com> * nits * frida mode - support non-pie * nits * nit * update grammar mutator * Fixes for aarch64, OSX and other minor issues (#891) Co-authored-by: Your Name <you@example.com> * nits * nits * fix PCGUARD, build aflpp_driver with fPIC * Added representative fuzzbench test and test for libxml (#893) * Added representative fuzzbench test and test for libxml * Added support for building FRIDA from source with FRIDA_SOURCE=1 Co-authored-by: Your Name <you@example.com> * nits * update changelog * typos * fixed potential double free in custom trim (#881) * error handling, freeing mem * frida: complog -> cmplog * fix statsd writing * let aflpp_qemu_driver_hook.so build fail gracefully * fix stdin trimming * Support for AFL_ENTRYPOINT (#898) Co-authored-by: Your Name <you@example.com> * remove the input file .cur_input at the end of the fuzzing, if AFL_TMPDIR is used * reverse push (#901) * Create FUNDING.yml * Update FUNDING.yml * disable QEMU static pie Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> * clarify that no modifications are required. * add new test for frida_mode (please review) * typos * fix persistent mode (64-bit) * set ARCH for linux intel 32-bit for frida-gum-devkit * prepare for 32-bit support (later) * not on qemu 3 anymore * unicorn mips fixes * instrumentation further move to C++11 (#900) * unicorn fixes * more unicorn fixes * Fix memory errors when trim causes testcase growth (#881) (#903) * Revert "fixed potential double free in custom trim (#881)" This reverts commit e9d2f72382cab75832721d859c3e731da071435d. * Revert "fix custom trim for increasing data" This reverts commit 86a8ef168dda766d2f25f15c15c4d3ecf21d0667. * Fix memory errors when trim causes testcase growth Modify trim_case_custom to avoid writing into in_buf because some custom mutators can cause the testcase to grow rather than shrink. Instead of modifying in_buf directly, we write the update out to the disk when trimming is complete, and then the caller is responsible for refreshing the in-memory buffer from the file. This is still a bit sketchy because it does need to modify q->len in order to notify the upper layers that something changed, and it could end up telling upper layer code that the q->len is *bigger* than the buffer (q->testcase_buf) that contains it, which is asking for trouble down the line somewhere... * Fix an unlikely situation Put back some `unlikely()` calls that were in the e9d2f72382cab75832721d859c3e731da071435d commit that was reverted. * typo * Exit on time (#904) * Variable AFL_EXIT_ON_TIME description has been added. Variables AFL_EXIT_ON_TIME and afl_exit_on_time has been added. afl->exit_on_time variable initialization has been added. The asignment of a value to the afl->afl_env.afl_exit_on_time variable from environment variables has been added. Code to exit on timeout if new path not found has been added. * Type of afl_exit_on_time variable has been changed. Variable exit_on_time has been added to the afl_state_t structure. * Command `export AFL_EXIT_WHEN_DONE=1` has been added. * Millisecond to second conversion has been added. Call get_cur_time() has been added. * Revert to using the saved current time value. * Useless check has been removed. * fix new path to custom-mutators * ensure crashes/README.txt exists * fix * Changes to bump FRIDA version and to clone FRIDA repo in to build directory rather than use a submodule as the FRIDA build scripts don't like it (#906) Co-authored-by: Your Name <you@example.com> * Fix numeric overflow in cmplog implementation (#907) Co-authored-by: Your Name <you@example.com> * testcase fixes for unicorn * remove merge conflict artifacts * fix afl-plot * Changes to remove binaries from frida_mode (#913) Co-authored-by: Your Name <you@example.com> * Frida cmplog fail fast (#914) * Changes to remove binaries from frida_mode * Changes to make cmplog fail fast Co-authored-by: Your Name <you@example.com> * afl-plot: relative time * arch linux and mac os support for afl-system-config * typo * code-format * update documentation * github workflow for qemu * OSX-specific improvements (#912) * Fix afl-cc to work correctly by default on OSX using xcode - CLANG_ENV_VAR must be set for afl-as to work - Use clang mode by default if no specific compiler selected * Add OSX-specific documentation for configuring shared memory * Fixes to memory operands for complog (#916) Co-authored-by: Your Name <you@example.com> * fix a few cur_time uses * added bounds check to pivot_inputs (fixes #921) * additional safety checks for restarts * restrict afl-showmap in_file size * fix seed crash disable * add warning for afl-showmap partial read * no core dumps * AFL_PRINT_FILENAMES added * more documentation for AFL_EXIT_ON_TIME * Flushing for AFL_PRINT_FILENAMES * FASAN Support (#918) * FASAN Support * Fix handling of Address Sanitizer DSO * Changes to identification of Address Sanitizer DSO Co-authored-by: Your Name <you@example.com> * Support for x86 (#920) Co-authored-by: Your Name <you@example.com> * Update frida_mode readme (#925) * libqasan: use syscalls for read and write * update readme * Minor integration tweaks (#926) Co-authored-by: Your Name <you@example.com> * merge * fix afl-fuzz.c frida preload * cleaned up AFL_PRINT_FILENAMES env * Changes to have persistent mode exit at the end of the loop (#928) Co-authored-by: Your Name <you@example.com> * fix llvm-dict2file Co-authored-by: Dominik Maier <domenukk@gmail.com> Co-authored-by: WorksButNotTested <62701594+WorksButNotTested@users.noreply.github.com> Co-authored-by: Your Name <you@example.com> Co-authored-by: Dmitry Zheregelya <zheregelya.d@gmail.com> Co-authored-by: hexcoder <hexcoder-@users.noreply.github.com> Co-authored-by: hexcoder- <heiko@hexco.de> Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> Co-authored-by: David CARLIER <devnexen@gmail.com> Co-authored-by: realmadsci <71108352+realmadsci@users.noreply.github.com> Co-authored-by: Roman M. Iudichev <SecNotice@ya.ru> Co-authored-by: Dustin Spicuzza <dustin@virtualroadside.com> Co-authored-by: Dominik Maier <domenukk@gmail.com> Co-authored-by: WorksButNotTested <62701594+WorksButNotTested@users.noreply.github.com> Co-authored-by: Your Name <you@example.com> Co-authored-by: Dmitry Zheregelya <zheregelya.d@gmail.com> Co-authored-by: hexcoder <hexcoder-@users.noreply.github.com> Co-authored-by: hexcoder- <heiko@hexco.de> Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> Co-authored-by: David CARLIER <devnexen@gmail.com> Co-authored-by: realmadsci <71108352+realmadsci@users.noreply.github.com> Co-authored-by: Roman M. Iudichev <SecNotice@ya.ru> Co-authored-by: Dustin Spicuzza <dustin@virtualroadside.com> * improve error msg * Added documentation for wine LoadLibrary workaround (#933) * Fix cmake target compilation command example (#934) - Fix typo DCMAKE_C_COMPILERC -> DCMAKE_C_COMPILER. - Add `cd build` after `mkdir build`. * showmap passes queue items in alphabetical order * added tmp files to gitignore * lenient dict parsing, no map size enum for binary fuzzing * added info about showmap queue directions * update binary-only doc * turn off map size detection if skip_bin_check is set * Typo * update docs * update afl-system-config * Set kill signal before using it in afl-showmap (#935) * fix afl-cc help output * add libafl to binary-only doc * update docs * less executions on variable paths * AFL_SKIP_CRASHES is obsolete since 3.0 * add AFL_TRY_AFFINITY * Typo * Typo * Typo/wording * tweaks * typos * fix afl-whatsup help output * fix afl-plot output * fix for MacOS * fix cmpcov doc for qemu * fix tmpfile removal * update dockerfile * Frida (#940) * Added re2 test * Added libpcap test * Fix validation of setting of ADDR_NO_RANDOMIZE * Added support for printing original and instrumented code Co-authored-by: Your Name <you@example.com> * Support for AFL_FRIDA_PERSISTENT_RET (#941) Co-authored-by: Your Name <you@example.com> * Changes to add missing exclusion of ranges (#943) Co-authored-by: Your Name <you@example.com> * add --afl-noopt to afl-cc * docs: fix link to README in QuickStartGuide (#946) * Support writing Stalker stats (#945) * Support writing Stalker stats * Fixed string handling in print functions Co-authored-by: Your Name <you@example.com> * afl-cmin help fix, aflpp_driver - + @@ support * fix for afl-showmap * support new env var AFL_LLVM_THREADSAFE_INST to enable atomic counters. add new test case for that. * add documentation for AFL_LLVM_THREADSAFE_INST * add support for AFL_LLVM_THREADSAFE_INST to other LLVM passes * add missing include for _exit() * threadsafe doc fixes, code format * Wording: "never zero" -> NeverZero * fix afl_custom_post_process with multiple custom mutators * fix docs * debug ck_write * fixed potential diff by 0 * fixes * fix classic threadsafe counters * v3.13c release * back push (#952) * Dev (#949) * use atomic read-modify-write increment for LLVM CLASSIC * Change other LLVM modes to atomic increments * sync (#886) * Create FUNDING.yml * Update FUNDING.yml * moved custom_mutator examples * unicorn speedtest makefile cleanup * fixed example location * fix qdbi * update util readme * work in progress: not working correctly yet * Frida persistent (#880) * Added x64 support for persistent mode (function call only), in-memory teest cases and complog * Review changes, fix NeverZero and code to parse the .text section of the main executable. Excluded ranges TBC * Various minor fixes and finished support for AFL_INST_LIBS * Review changes Co-authored-by: Your Name <you@example.com> * nits * fix frida mode * Integer overflow/underflow fixes in libdislocator (#889) * libdislocator: fixing integer overflow in 'max_mem' variable and setting 'max_mem' type to 'size_t' * libdislocator: fixing potential integer underflow in 'total_mem' variable due to its different values in different threads * Bumped warnings up to the max and fixed remaining issues (#890) Co-authored-by: Your Name <you@example.com> * nits * frida mode - support non-pie * nits * nit * update grammar mutator * Fixes for aarch64, OSX and other minor issues (#891) Co-authored-by: Your Name <you@example.com> * nits * nits * fix PCGUARD, build aflpp_driver with fPIC * Added representative fuzzbench test and test for libxml (#893) * Added representative fuzzbench test and test for libxml * Added support for building FRIDA from source with FRIDA_SOURCE=1 Co-authored-by: Your Name <you@example.com> * nits * update changelog * typos * still not working * fixed potential double free in custom trim (#881) * error handling, freeing mem * frida: complog -> cmplog * fix statsd writing * let aflpp_qemu_driver_hook.so build fail gracefully * fix stdin trimming * Support for AFL_ENTRYPOINT (#898) Co-authored-by: Your Name <you@example.com> * remove the input file .cur_input at the end of the fuzzing, if AFL_TMPDIR is used * reverse push (#901) * Create FUNDING.yml * Update FUNDING.yml * disable QEMU static pie Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> * clarify that no modifications are required. * add new test for frida_mode (please review) * typos * fix persistent mode (64-bit) * set ARCH for linux intel 32-bit for frida-gum-devkit * prepare for 32-bit support (later) * not on qemu 3 anymore * unicorn mips fixes * instrumentation further move to C++11 (#900) * unicorn fixes * first working NeverZero implementation * more unicorn fixes * Fix memory errors when trim causes testcase growth (#881) (#903) * Revert "fixed potential double free in custom trim (#881)" This reverts commit e9d2f72382cab75832721d859c3e731da071435d. * Revert "fix custom trim for increasing data" This reverts commit 86a8ef168dda766d2f25f15c15c4d3ecf21d0667. * Fix memory errors when trim causes testcase growth Modify trim_case_custom to avoid writing into in_buf because some custom mutators can cause the testcase to grow rather than shrink. Instead of modifying in_buf directly, we write the update out to the disk when trimming is complete, and then the caller is responsible for refreshing the in-memory buffer from the file. This is still a bit sketchy because it does need to modify q->len in order to notify the upper layers that something changed, and it could end up telling upper layer code that the q->len is *bigger* than the buffer (q->testcase_buf) that contains it, which is asking for trouble down the line somewhere... * Fix an unlikely situation Put back some `unlikely()` calls that were in the e9d2f72382cab75832721d859c3e731da071435d commit that was reverted. * add some comments * typo * Exit on time (#904) * Variable AFL_EXIT_ON_TIME description has been added. Variables AFL_EXIT_ON_TIME and afl_exit_on_time has been added. afl->exit_on_time variable initialization has been added. The asignment of a value to the afl->afl_env.afl_exit_on_time variable from environment variables has been added. Code to exit on timeout if new path not found has been added. * Type of afl_exit_on_time variable has been changed. Variable exit_on_time has been added to the afl_state_t structure. * Command `export AFL_EXIT_WHEN_DONE=1` has been added. * Millisecond to second conversion has been added. Call get_cur_time() has been added. * Revert to using the saved current time value. * Useless check has been removed. * fix new path to custom-mutators * ensure crashes/README.txt exists * fix * Changes to bump FRIDA version and to clone FRIDA repo in to build directory rather than use a submodule as the FRIDA build scripts don't like it (#906) Co-authored-by: Your Name <you@example.com> * Fix numeric overflow in cmplog implementation (#907) Co-authored-by: Your Name <you@example.com> * testcase fixes for unicorn * remove merge conflict artifacts * fix afl-plot * Changes to remove binaries from frida_mode (#913) Co-authored-by: Your Name <you@example.com> * Frida cmplog fail fast (#914) * Changes to remove binaries from frida_mode * Changes to make cmplog fail fast Co-authored-by: Your Name <you@example.com> * afl-plot: relative time * arch linux and mac os support for afl-system-config * typo * code-format * update documentation * github workflow for qemu * OSX-specific improvements (#912) * Fix afl-cc to work correctly by default on OSX using xcode - CLANG_ENV_VAR must be set for afl-as to work - Use clang mode by default if no specific compiler selected * Add OSX-specific documentation for configuring shared memory * Fixes to memory operands for complog (#916) Co-authored-by: Your Name <you@example.com> * fix a few cur_time uses * added bounds check to pivot_inputs (fixes #921) * additional safety checks for restarts * restrict afl-showmap in_file size * fix seed crash disable * add warning for afl-showmap partial read * no core dumps * AFL_PRINT_FILENAMES added * more documentation for AFL_EXIT_ON_TIME * Flushing for AFL_PRINT_FILENAMES * FASAN Support (#918) * FASAN Support * Fix handling of Address Sanitizer DSO * Changes to identification of Address Sanitizer DSO Co-authored-by: Your Name <you@example.com> * Support for x86 (#920) Co-authored-by: Your Name <you@example.com> * Update frida_mode readme (#925) * libqasan: use syscalls for read and write * update readme * Minor integration tweaks (#926) Co-authored-by: Your Name <you@example.com> * merge * fix afl-fuzz.c frida preload * cleaned up AFL_PRINT_FILENAMES env * Changes to have persistent mode exit at the end of the loop (#928) Co-authored-by: Your Name <you@example.com> * fix llvm-dict2file * push to stable (#931) (#932) * sync (#886) * Create FUNDING.yml * Update FUNDING.yml * moved custom_mutator examples * unicorn speedtest makefile cleanup * fixed example location * fix qdbi * update util readme * Frida persistent (#880) * Added x64 support for persistent mode (function call only), in-memory teest cases and complog * Review changes, fix NeverZero and code to parse the .text section of the main executable. Excluded ranges TBC * Various minor fixes and finished support for AFL_INST_LIBS * Review changes Co-authored-by: Your Name <you@example.com> * nits * fix frida mode * Integer overflow/underflow fixes in libdislocator (#889) * libdislocator: fixing integer overflow in 'max_mem' variable and setting 'max_mem' type to 'size_t' * libdislocator: fixing potential integer underflow in 'total_mem' variable due to its different values in different threads * Bumped warnings up to the max and fixed remaining issues (#890) Co-authored-by: Your Name <you@example.com> * nits * frida mode - support non-pie * nits * nit * update grammar mutator * Fixes for aarch64, OSX and other minor issues (#891) Co-authored-by: Your Name <you@example.com> * nits * nits * fix PCGUARD, build aflpp_driver with fPIC * Added representative fuzzbench test and test for libxml (#893) * Added representative fuzzbench test and test for libxml * Added support for building FRIDA from source with FRIDA_SOURCE=1 Co-authored-by: Your Name <you@example.com> * nits * update changelog * typos * fixed potential double free in custom trim (#881) * error handling, freeing mem * frida: complog -> cmplog * fix statsd writing * let aflpp_qemu_driver_hook.so build fail gracefully * fix stdin trimming * Support for AFL_ENTRYPOINT (#898) Co-authored-by: Your Name <you@example.com> * remove the input file .cur_input at the end of the fuzzing, if AFL_TMPDIR is used * reverse push (#901) * Create FUNDING.yml * Update FUNDING.yml * disable QEMU static pie Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> * clarify that no modifications are required. * add new test for frida_mode (please review) * typos * fix persistent mode (64-bit) * set ARCH for linux intel 32-bit for frida-gum-devkit * prepare for 32-bit support (later) * not on qemu 3 anymore * unicorn mips fixes * instrumentation further move to C++11 (#900) * unicorn fixes * more unicorn fixes * Fix memory errors when trim causes testcase growth (#881) (#903) * Revert "fixed potential double free in custom trim (#881)" This reverts commit e9d2f72382cab75832721d859c3e731da071435d. * Revert "fix custom trim for increasing data" This reverts commit 86a8ef168dda766d2f25f15c15c4d3ecf21d0667. * Fix memory errors when trim causes testcase growth Modify trim_case_custom to avoid writing into in_buf because some custom mutators can cause the testcase to grow rather than shrink. Instead of modifying in_buf directly, we write the update out to the disk when trimming is complete, and then the caller is responsible for refreshing the in-memory buffer from the file. This is still a bit sketchy because it does need to modify q->len in order to notify the upper layers that something changed, and it could end up telling upper layer code that the q->len is *bigger* than the buffer (q->testcase_buf) that contains it, which is asking for trouble down the line somewhere... * Fix an unlikely situation Put back some `unlikely()` calls that were in the e9d2f72382cab75832721d859c3e731da071435d commit that was reverted. * typo * Exit on time (#904) * Variable AFL_EXIT_ON_TIME description has been added. Variables AFL_EXIT_ON_TIME and afl_exit_on_time has been added. afl->exit_on_time variable initialization has been added. The asignment of a value to the afl->afl_env.afl_exit_on_time variable from environment variables has been added. Code to exit on timeout if new path not found has been added. * Type of afl_exit_on_time variable has been changed. Variable exit_on_time has been added to the afl_state_t structure. * Command `export AFL_EXIT_WHEN_DONE=1` has been added. * Millisecond to second conversion has been added. Call get_cur_time() has been added. * Revert to using the saved current time value. * Useless check has been removed. * fix new path to custom-mutators * ensure crashes/README.txt exists * fix * Changes to bump FRIDA version and to clone FRIDA repo in to build directory rather than use a submodule as the FRIDA build scripts don't like it (#906) Co-authored-by: Your Name <you@example.com> * Fix numeric overflow in cmplog implementation (#907) Co-authored-by: Your Name <you@example.com> * testcase fixes for unicorn * remove merge conflict artifacts * fix afl-plot * Changes to remove binaries from frida_mode (#913) Co-authored-by: Your Name <you@example.com> * Frida cmplog fail fast (#914) * Changes to remove binaries from frida_mode * Changes to make cmplog fail fast Co-authored-by: Your Name <you@example.com> * afl-plot: relative time * arch linux and mac os support for afl-system-config * typo * code-format * update documentation * github workflow for qemu * OSX-specific improvements (#912) * Fix afl-cc to work correctly by default on OSX using xcode - CLANG_ENV_VAR must be set for afl-as to work - Use clang mode by default if no specific compiler selected * Add OSX-specific documentation for configuring shared memory * Fixes to memory operands for complog (#916) Co-authored-by: Your Name <you@example.com> * fix a few cur_time uses * added bounds check to pivot_inputs (fixes #921) * additional safety checks for restarts * restrict afl-showmap in_file size * fix seed crash disable * add warning for afl-showmap partial read * no core dumps * AFL_PRINT_FILENAMES added * more documentation for AFL_EXIT_ON_TIME * Flushing for AFL_PRINT_FILENAMES * FASAN Support (#918) * FASAN Support * Fix handling of Address Sanitizer DSO * Changes to identification of Address Sanitizer DSO Co-authored-by: Your Name <you@example.com> * Support for x86 (#920) Co-authored-by: Your Name <you@example.com> * Update frida_mode readme (#925) * libqasan: use syscalls for read and write * update readme * Minor integration tweaks (#926) Co-authored-by: Your Name <you@example.com> * merge * fix afl-fuzz.c frida preload * cleaned up AFL_PRINT_FILENAMES env * Changes to have persistent mode exit at the end of the loop (#928) Co-authored-by: Your Name <you@example.com> * fix llvm-dict2file Co-authored-by: Dominik Maier <domenukk@gmail.com> Co-authored-by: WorksButNotTested <62701594+WorksButNotTested@users.noreply.github.com> Co-authored-by: Your Name <you@example.com> Co-authored-by: Dmitry Zheregelya <zheregelya.d@gmail.com> Co-authored-by: hexcoder <hexcoder-@users.noreply.github.com> Co-authored-by: hexcoder- <heiko@hexco.de> Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> Co-authored-by: David CARLIER <devnexen@gmail.com> Co-authored-by: realmadsci <71108352+realmadsci@users.noreply.github.com> Co-authored-by: Roman M. Iudichev <SecNotice@ya.ru> Co-authored-by: Dustin Spicuzza <dustin@virtualroadside.com> Co-authored-by: Dominik Maier <domenukk@gmail.com> Co-authored-by: WorksButNotTested <62701594+WorksButNotTested@users.noreply.github.com> Co-authored-by: Your Name <you@example.com> Co-authored-by: Dmitry Zheregelya <zheregelya.d@gmail.com> Co-authored-by: hexcoder <hexcoder-@users.noreply.github.com> Co-authored-by: hexcoder- <heiko@hexco.de> Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> Co-authored-by: David CARLIER <devnexen@gmail.com> Co-authored-by: realmadsci <71108352+realmadsci@users.noreply.github.com> Co-authored-by: Roman M. Iudichev <SecNotice@ya.ru> Co-authored-by: Dustin Spicuzza <dustin@virtualroadside.com> * improve error msg * Added documentation for wine LoadLibrary workaround (#933) * Fix cmake target compilation command example (#934) - Fix typo DCMAKE_C_COMPILERC -> DCMAKE_C_COMPILER. - Add `cd build` after `mkdir build`. * showmap passes queue items in alphabetical order * added tmp files to gitignore * lenient dict parsing, no map size enum for binary fuzzing * added info about showmap queue directions * update binary-only doc * turn off map size detection if skip_bin_check is set * Typo * update docs * update afl-system-config * Set kill signal before using it in afl-showmap (#935) * fix afl-cc help output * add libafl to binary-only doc * update docs * less executions on variable paths * AFL_SKIP_CRASHES is obsolete since 3.0 * add AFL_TRY_AFFINITY * Typo * Typo * Typo/wording * tweaks * typos * fix afl-whatsup help output * fix afl-plot output * fix for MacOS * fix cmpcov doc for qemu * fix tmpfile removal * update dockerfile * Frida (#940) * Added re2 test * Added libpcap test * Fix validation of setting of ADDR_NO_RANDOMIZE * Added support for printing original and instrumented code Co-authored-by: Your Name <you@example.com> * Support for AFL_FRIDA_PERSISTENT_RET (#941) Co-authored-by: Your Name <you@example.com> * Changes to add missing exclusion of ranges (#943) Co-authored-by: Your Name <you@example.com> * add --afl-noopt to afl-cc * docs: fix link to README in QuickStartGuide (#946) * Support writing Stalker stats (#945) * Support writing Stalker stats * Fixed string handling in print functions Co-authored-by: Your Name <you@example.com> * afl-cmin help fix, aflpp_driver - + @@ support * fix for afl-showmap * support new env var AFL_LLVM_THREADSAFE_INST to enable atomic counters. add new test case for that. * add documentation for AFL_LLVM_THREADSAFE_INST * add support for AFL_LLVM_THREADSAFE_INST to other LLVM passes * add missing include for _exit() * threadsafe doc fixes, code format * Wording: "never zero" -> NeverZero * fix afl_custom_post_process with multiple custom mutators * fix docs * debug ck_write * fixed potential diff by 0 * fixes * fix classic threadsafe counters Co-authored-by: van Hauser <vh@thc.org> Co-authored-by: Dominik Maier <domenukk@gmail.com> Co-authored-by: WorksButNotTested <62701594+WorksButNotTested@users.noreply.github.com> Co-authored-by: Your Name <you@example.com> Co-authored-by: Dmitry Zheregelya <zheregelya.d@gmail.com> Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> Co-authored-by: David CARLIER <devnexen@gmail.com> Co-authored-by: realmadsci <71108352+realmadsci@users.noreply.github.com> Co-authored-by: Roman M. Iudichev <SecNotice@ya.ru> Co-authored-by: Dustin Spicuzza <dustin@virtualroadside.com> Co-authored-by: 0x4d5a-ctf <51098072+0x4d5a-ctf@users.noreply.github.com> Co-authored-by: Tommy Chiang <oToToT@users.noreply.github.com> Co-authored-by: buherator <buherator@silentsignal.hu> Co-authored-by: Dag Heyman Kajevic <dag.heyman@gmail.com> * v3.13c release (#950) * use atomic read-modify-write increment for LLVM CLASSIC * Change other LLVM modes to atomic increments * sync (#886) * Create FUNDING.yml * Update FUNDING.yml * moved custom_mutator examples * unicorn speedtest makefile cleanup * fixed example location * fix qdbi * update util readme * work in progress: not working correctly yet * Frida persistent (#880) * Added x64 support for persistent mode (function call only), in-memory teest cases and complog * Review changes, fix NeverZero and code to parse the .text section of the main executable. Excluded ranges TBC * Various minor fixes and finished support for AFL_INST_LIBS * Review changes Co-authored-by: Your Name <you@example.com> * nits * fix frida mode * Integer overflow/underflow fixes in libdislocator (#889) * libdislocator: fixing integer overflow in 'max_mem' variable and setting 'max_mem' type to 'size_t' * libdislocator: fixing potential integer underflow in 'total_mem' variable due to its different values in different threads * Bumped warnings up to the max and fixed remaining issues (#890) Co-authored-by: Your Name <you@example.com> * nits * frida mode - support non-pie * nits * nit * update grammar mutator * Fixes for aarch64, OSX and other minor issues (#891) Co-authored-by: Your Name <you@example.com> * nits * nits * fix PCGUARD, build aflpp_driver with fPIC * Added representative fuzzbench test and test for libxml (#893) * Added representative fuzzbench test and test for libxml * Added support for building FRIDA from source with FRIDA_SOURCE=1 Co-authored-by: Your Name <you@example.com> * nits * update changelog * typos * still not working * fixed potential double free in custom trim (#881) * error handling, freeing mem * frida: complog -> cmplog * fix statsd writing * let aflpp_qemu_driver_hook.so build fail gracefully * fix stdin trimming * Support for AFL_ENTRYPOINT (#898) Co-authored-by: Your Name <you@example.com> * remove the input file .cur_input at the end of the fuzzing, if AFL_TMPDIR is used * reverse push (#901) * Create FUNDING.yml * Update FUNDING.yml * disable QEMU static pie Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> * clarify that no modifications are required. * add new test for frida_mode (please review) * typos * fix persistent mode (64-bit) * set ARCH for linux intel 32-bit for frida-gum-devkit * prepare for 32-bit support (later) * not on qemu 3 anymore * unicorn mips fixes * instrumentation further move to C++11 (#900) * unicorn fixes * first working NeverZero implementation * more unicorn fixes * Fix memory errors when trim causes testcase growth (#881) (#903) * Revert "fixed potential double free in custom trim (#881)" This reverts commit e9d2f72382cab75832721d859c3e731da071435d. * Revert "fix custom trim for increasing data" This reverts commit 86a8ef168dda766d2f25f15c15c4d3ecf21d0667. * Fix memory errors when trim causes testcase growth Modify trim_case_custom to avoid writing into in_buf because some custom mutators can cause the testcase to grow rather than shrink. Instead of modifying in_buf directly, we write the update out to the disk when trimming is complete, and then the caller is responsible for refreshing the in-memory buffer from the file. This is still a bit sketchy because it does need to modify q->len in order to notify the upper layers that something changed, and it could end up telling upper layer code that the q->len is *bigger* than the buffer (q->testcase_buf) that contains it, which is asking for trouble down the line somewhere... * Fix an unlikely situation Put back some `unlikely()` calls that were in the e9d2f72382cab75832721d859c3e731da071435d commit that was reverted. * add some comments * typo * Exit on time (#904) * Variable AFL_EXIT_ON_TIME description has been added. Variables AFL_EXIT_ON_TIME and afl_exit_on_time has been added. afl->exit_on_time variable initialization has been added. The asignment of a value to the afl->afl_env.afl_exit_on_time variable from environment variables has been added. Code to exit on timeout if new path not found has been added. * Type of afl_exit_on_time variable has been changed. Variable exit_on_time has been added to the afl_state_t structure. * Command `export AFL_EXIT_WHEN_DONE=1` has been added. * Millisecond to second conversion has been added. Call get_cur_time() has been added. * Revert to using the saved current time value. * Useless check has been removed. * fix new path to custom-mutators * ensure crashes/README.txt exists * fix * Changes to bump FRIDA version and to clone FRIDA repo in to build directory rather than use a submodule as the FRIDA build scripts don't like it (#906) Co-authored-by: Your Name <you@example.com> * Fix numeric overflow in cmplog implementation (#907) Co-authored-by: Your Name <you@example.com> * testcase fixes for unicorn * remove merge conflict artifacts * fix afl-plot * Changes to remove binaries from frida_mode (#913) Co-authored-by: Your Name <you@example.com> * Frida cmplog fail fast (#914) * Changes to remove binaries from frida_mode * Changes to make cmplog fail fast Co-authored-by: Your Name <you@example.com> * afl-plot: relative time * arch linux and mac os support for afl-system-config * typo * code-format * update documentation * github workflow for qemu * OSX-specific improvements (#912) * Fix afl-cc to work correctly by default on OSX using xcode - CLANG_ENV_VAR must be set for afl-as to work - Use clang mode by default if no specific compiler selected * Add OSX-specific documentation for configuring shared memory * Fixes to memory operands for complog (#916) Co-authored-by: Your Name <you@example.com> * fix a few cur_time uses * added bounds check to pivot_inputs (fixes #921) * additional safety checks for restarts * restrict afl-showmap in_file size * fix seed crash disable * add warning for afl-showmap partial read * no core dumps * AFL_PRINT_FILENAMES added * more documentation for AFL_EXIT_ON_TIME * Flushing for AFL_PRINT_FILENAMES * FASAN Support (#918) * FASAN Support * Fix handling of Address Sanitizer DSO * Changes to identification of Address Sanitizer DSO Co-authored-by: Your Name <you@example.com> * Support for x86 (#920) Co-authored-by: Your Name <you@example.com> * Update frida_mode readme (#925) * libqasan: use syscalls for read and write * update readme * Minor integration tweaks (#926) Co-authored-by: Your Name <you@example.com> * merge * fix afl-fuzz.c frida preload * cleaned up AFL_PRINT_FILENAMES env * Changes to have persistent mode exit at the end of the loop (#928) Co-authored-by: Your Name <you@example.com> * fix llvm-dict2file * push to stable (#931) (#932) * sync (#886) * Create FUNDING.yml * Update FUNDING.yml * moved custom_mutator examples * unicorn speedtest makefile cleanup * fixed example location * fix qdbi * update util readme * Frida persistent (#880) * Added x64 support for persistent mode (function call only), in-memory teest cases and complog * Review changes, fix NeverZero and code to parse the .text section of the main executable. Excluded ranges TBC * Various minor fixes and finished support for AFL_INST_LIBS * Review changes Co-authored-by: Your Name <you@example.com> * nits * fix frida mode * Integer overflow/underflow fixes in libdislocator (#889) * libdislocator: fixing integer overflow in 'max_mem' variable and setting 'max_mem' type to 'size_t' * libdislocator: fixing potential integer underflow in 'total_mem' variable due to its different values in different threads * Bumped warnings up to the max and fixed remaining issues (#890) Co-authored-by: Your Name <you@example.com> * nits * frida mode - support non-pie * nits * nit * update grammar mutator * Fixes for aarch64, OSX and other minor issues (#891) Co-authored-by: Your Name <you@example.com> * nits * nits * fix PCGUARD, build aflpp_driver with fPIC * Added representative fuzzbench test and test for libxml (#893) * Added representative fuzzbench test and test for libxml * Added support for building FRIDA from source with FRIDA_SOURCE=1 Co-authored-by: Your Name <you@example.com> * nits * update changelog * typos * fixed potential double free in custom trim (#881) * error handling, freeing mem * frida: complog -> cmplog * fix statsd writing * let aflpp_qemu_driver_hook.so build fail gracefully * fix stdin trimming * Support for AFL_ENTRYPOINT (#898) Co-authored-by: Your Name <you@example.com> * remove the input file .cur_input at the end of the fuzzing, if AFL_TMPDIR is used * reverse push (#901) * Create FUNDING.yml * Update FUNDING.yml * disable QEMU static pie Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> * clarify that no modifications are required. * add new test for frida_mode (please review) * typos * fix persistent mode (64-bit) * set ARCH for linux intel 32-bit for frida-gum-devkit * prepare for 32-bit support (later) * not on qemu 3 anymore * unicorn mips fixes * instrumentation further move to C++11 (#900) * unicorn fixes * more unicorn fixes * Fix memory errors when trim causes testcase growth (#881) (#903) * Revert "fixed potential double free in custom trim (#881)" This reverts commit e9d2f72382cab75832721d859c3e731da071435d. * Revert "fix custom trim for increasing data" This reverts commit 86a8ef168dda766d2f25f15c15c4d3ecf21d0667. * Fix memory errors when trim causes testcase growth Modify trim_case_custom to avoid writing into in_buf because some custom mutators can cause the testcase to grow rather than shrink. Instead of modifying in_buf directly, we write the update out to the disk when trimming is complete, and then the caller is responsible for refreshing the in-memory buffer from the file. This is still a bit sketchy because it does need to modify q->len in order to notify the upper layers that something changed, and it could end up telling upper layer code that the q->len is *bigger* than the buffer (q->testcase_buf) that contains it, which is asking for trouble down the line somewhere... * Fix an unlikely situation Put back some `unlikely()` calls that were in the e9d2f72382cab75832721d859c3e731da071435d commit that was reverted. * typo * Exit on time (#904) * Variable AFL_EXIT_ON_TIME description has been added. Variables AFL_EXIT_ON_TIME and afl_exit_on_time has been added. afl->exit_on_time variable initialization has been added. The asignment of a value to the afl->afl_env.afl_exit_on_time variable from environment variables has been added. Code to exit on timeout if new path not found has been added. * Type of afl_exit_on_time variable has been changed. Variable exit_on_time has been added to the afl_state_t structure. * Command `export AFL_EXIT_WHEN_DONE=1` has been added. * Millisecond to second conversion has been added. Call get_cur_time() has been added. * Revert to using the saved current time value. * Useless check has been removed. * fix new path to custom-mutators * ensure crashes/README.txt exists * fix * Changes to bump FRIDA version and to clone FRIDA repo in to build directory rather than use a submodule as the FRIDA build scripts don't like it (#906) Co-authored-by: Your Name <you@example.com> * Fix numeric overflow in cmplog implementation (#907) Co-authored-by: Your Name <you@example.com> * testcase fixes for unicorn * remove merge conflict artifacts * fix afl-plot * Changes to remove binaries from frida_mode (#913) Co-authored-by: Your Name <you@example.com> * Frida cmplog fail fast (#914) * Changes to remove binaries from frida_mode * Changes to make cmplog fail fast Co-authored-by: Your Name <you@example.com> * afl-plot: relative time * arch linux and mac os support for afl-system-config * typo * code-format * update documentation * github workflow for qemu * OSX-specific improvements (#912) * Fix afl-cc to work correctly by default on OSX using xcode - CLANG_ENV_VAR must be set for afl-as to work - Use clang mode by default if no specific compiler selected * Add OSX-specific documentation for configuring shared memory * Fixes to memory operands for complog (#916) Co-authored-by: Your Name <you@example.com> * fix a few cur_time uses * added bounds check to pivot_inputs (fixes #921) * additional safety checks for restarts * restrict afl-showmap in_file size * fix seed crash disable * add warning for afl-showmap partial read * no core dumps * AFL_PRINT_FILENAMES added * more documentation for AFL_EXIT_ON_TIME * Flushing for AFL_PRINT_FILENAMES * FASAN Support (#918) * FASAN Support * Fix handling of Address Sanitizer DSO * Changes to identification of Address Sanitizer DSO Co-authored-by: Your Name <you@example.com> * Support for x86 (#920) Co-authored-by: Your Name <you@example.com> * Update frida_mode readme (#925) * libqasan: use syscalls for read and write * update readme * Minor integration tweaks (#926) Co-authored-by: Your Name <you@example.com> * merge * fix afl-fuzz.c frida preload * cleaned up AFL_PRINT_FILENAMES env * Changes to have persistent mode exit at the end of the loop (#928) Co-authored-by: Your Name <you@example.com> * fix llvm-dict2file Co-authored-by: Dominik Maier <domenukk@gmail.com> Co-authored-by: WorksButNotTested <62701594+WorksButNotTested@users.noreply.github.com> Co-authored-by: Your Name <you@example.com> Co-authored-by: Dmitry Zheregelya <zheregelya.d@gmail.com> Co-authored-by: hexcoder <hexcoder-@users.noreply.github.com> Co-authored-by: hexcoder- <heiko@hexco.de> Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> Co-authored-by: David CARLIER <devnexen@gmail.com> Co-authored-by: realmadsci <71108352+realmadsci@users.noreply.github.com> Co-authored-by: Roman M. Iudichev <SecNotice@ya.ru> Co-authored-by: Dustin Spicuzza <dustin@virtualroadside.com> Co-authored-by: Dominik Maier <domenukk@gmail.com> Co-authored-by: WorksButNotTested <62701594+WorksButNotTested@users.noreply.github.com> Co-authored-by: Your Name <you@example.com> Co-authored-by: Dmitry Zheregelya <zheregelya.d@gmail.com> Co-authored-by: hexcoder <hexcoder-@users.noreply.github.com> Co-authored-by: hexcoder- <heiko@hexco.de> Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> Co-authored-by: David CARLIER <devnexen@gmail.com> Co-authored-by: realmadsci <71108352+realmadsci@users.noreply.github.com> Co-authored-by: Roman M. Iudichev <SecNotice@ya.ru> Co-authored-by: Dustin Spicuzza <dustin@virtualroadside.com> * improve error msg * Added documentation for wine LoadLibrary workaround (#933) * Fix cmake target compilation command example (#934) - Fix typo DCMAKE_C_COMPILERC -> DCMAKE_C_COMPILER. - Add `cd build` after `mkdir build`. * showmap passes queue items in alphabetical order * added tmp files to gitignore * lenient dict parsing, no map size enum for binary fuzzing * added info about showmap queue directions * update binary-only doc * turn off map size detection if skip_bin_check is set * Typo * update docs * update afl-system-config * Set kill signal before using it in afl-showmap (#935) * fix afl-cc help output * add libafl to binary-only doc * update docs * less executions on variable paths * AFL_SKIP_CRASHES is obsolete since 3.0 * add AFL_TRY_AFFINITY * Typo * Typo * Typo/wording * tweaks * typos * fix afl-whatsup help output * fix afl-plot output * fix for MacOS * fix cmpcov doc for qemu * fix tmpfile removal * update dockerfile * Frida (#940) * Added re2 test * Added libpcap test * Fix validation of setting of ADDR_NO_RANDOMIZE * Added support for printing original and instrumented code Co-authored-by: Your Name <you@example.com> * Support for AFL_FRIDA_PERSISTENT_RET (#941) Co-authored-by: Your Name <you@example.com> * Changes to add missing exclusion of ranges (#943) Co-authored-by: Your Name <you@example.com> * add --afl-noopt to afl-cc * docs: fix link to README in QuickStartGuide (#946) * Support writing Stalker stats (#945) * Support writing Stalker stats * Fixed string handling in print functions Co-authored-by: Your Name <you@example.com> * afl-cmin help fix, aflpp_driver - + @@ support * fix for afl-showmap * support new env var AFL_LLVM_THREADSAFE_INST to enable atomic counters. add new test case for that. * add documentation for AFL_LLVM_THREADSAFE_INST * add support for AFL_LLVM_THREADSAFE_INST to other LLVM passes * add missing include for _exit() * threadsafe doc fixes, code format * Wording: "never zero" -> NeverZero * fix afl_custom_post_process with multiple custom mutators * fix docs * debug ck_write * fixed potential diff by 0 * fixes * fix classic threadsafe counters * v3.13c release Co-authored-by: hexcoder- <heiko@hexco.de> Co-authored-by: Dominik Maier <domenukk@gmail.com> Co-authored-by: WorksButNotTested <62701594+WorksButNotTested@users.noreply.github.com> Co-authored-by: Your Name <you@example.com> Co-authored-by: Dmitry Zheregelya <zheregelya.d@gmail.com> Co-authored-by: hexcoder <hexcoder-@users.noreply.github.com> Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> Co-authored-by: David CARLIER <devnexen@gmail.com> Co-authored-by: realmadsci <71108352+realmadsci@users.noreply.github.com> Co-authored-by: Roman M. Iudichev <SecNotice@ya.ru> Co-authored-by: Dustin Spicuzza <dustin@virtualroadside.com> Co-authored-by: 0x4d5a-ctf <51098072+0x4d5a-ctf@users.noreply.github.com> Co-authored-by: Tommy Chiang <oToToT@users.noreply.github.com> Co-authored-by: buherator <buherator@silentsignal.hu> Co-authored-by: Dag Heyman Kajevic <dag.heyman@gmail.com> Co-authored-by: hexcoder <hexcoder-@users.noreply.github.com> Co-authored-by: Dominik Maier <domenukk@gmail.com> Co-authored-by: WorksButNotTested <62701594+WorksButNotTested@users.noreply.github.com> Co-authored-by: Your Name <you@example.com> Co-authored-by: Dmitry Zheregelya <zheregelya.d@gmail.com> Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> Co-authored-by: David CARLIER <devnexen@gmail.com> Co-authored-by: realmadsci <71108352+realmadsci@users.noreply.github.com> Co-authored-by: Roman M. Iudichev <SecNotice@ya.ru> Co-authored-by: Dustin Spicuzza <dustin@virtualroadside.com> Co-authored-by: 0x4d5a-ctf <51098072+0x4d5a-ctf@users.noreply.github.com> Co-authored-by: Tommy Chiang <oToToT@users.noreply.github.com> Co-authored-by: buherator <buherator@silentsignal.hu> Co-authored-by: Dag Heyman Kajevic <dag.heyman@gmail.com> Co-authored-by: hexcoder- <heiko@hexco.de> * v3.14a init * remove redundant unsetenv (#947) * update MacOS Install information * add missing clean action for frida_mode * ensure memory is there before free * adapt to incompatible LLVM 13 API * fix stupid typos * add fix info * build afl-compiler-rt even with broken llvm * fix -F with slash option * dynamic_list and afl-compiler-rt rework * detect partial linking in afl-cc * partial linking with -Wl * Add proper name and URL for Zafl (#959) * move link * add known frontends for supported compiler infrastructures * add Rust * fix ui fuzzing stage index (#960) * fix overflowing UI fields 'now processing' * restored timeout handling (with SIGALRM for now) * On non-Linux systems make clean may fail for frida_mode * give hint how to set env var for path to llvm-config tool * setting AFL_CC for test-llvm.sh on FreeBSD is not necessary anymore * remove -D from -M * write target errors to out_dir/error.txt * add changelog entry * add changelog * format * more info for error logging * Forkserver for afl-analyze (#963) * afl-analyze forkserver * added missing vars to forkserver * synchronized a bit more with afl-tmin * more debugging, runs now, but need to suppress target output * fix dev/null setting * afl-analyze info: Co-authored-by: hexcoder- <heiko@hexco.de> * proper newlines * reenable LLVM 3.8 ( Ubuntu 16.04 ) * FRIDA AARCH64 support (#965) Co-authored-by: Your Name <you@example.com> * adapt docs to minimum LLVM version * adapt to minimum llvm version * remove warning regarding core_pattern (was wrong/unnecessary anyway) * avoid code duplication, symlink header file * clippy fixes * add test cases for splitting integer comparisons * Revert "add test cases for splitting integer comparisons" This reverts commit e0aa411647e1a525a3a0488d929ec71611388d54. * add test cases for splitting integer comparisons * FRIDA - Remove need for AFL_FRIDA_PERSISTENT_RETADDR_OFFSET (#970) Co-authored-by: Your Name <you@example.com> * fix AFL_CAL_FAST * fix cmplog screen update crash * Frida complog fix (#971) * Fix complog issue with changing address space * Added support for printing command line and environment during startup * Review fixes Co-authored-by: Your Name <you@example.com> * Improve tracing support to include real addresses and edge ids and also support logging edges only once (#972) Co-authored-by: Your Name <you@example.com> * split-comparison llvm pass refactor for smaller compilation times (and a small bug fix) (#964) * Refactored split compare pass to be more efficient in LTO usage and allow splitting to other minimum bitwidths. Efficiency: avoid looping over the whole llvm module N times, when once is also enough. Bitwidth: Previously, due to fallthrough in switch-case, all comparisons were split to 8-bit, which might not be desirable e.g., 16 or 32 bit might be enough. So now all comparison are split until they are smaller or equal to the target bitwidth, which is controlled through the `AFL_LLVM_LAF_SPLIT_COMPARES_BITW` environment variable. * fixed miscompilation due to incorrectly trying to split a signed comparison operator * minor formatting updates and use IRBuilder when inserting multiple instructions * added @hexcoder-'s test-int_cases.c to make test * Avoid recursion; switch to smallvector in splitAndSimplify; use switch case for icmp type; * Fixed issue when splitting < where the inverse comparison was not further split * some cleanup * code format * fix to instrument global c++ namespace functions * update changelog * document frida changes * Fix typo in README.md (#974) * adapt for LLVM 3.8.0 * fix README * little inline * Add debug output to alert user to calibration progress/issues (#969) * aflppdriver help output * code format Co-authored-by: hexcoder- <heiko@hexco.de> Co-authored-by: Dominik Maier <domenukk@gmail.com> Co-authored-by: WorksButNotTested <62701594+WorksButNotTested@users.noreply.github.com> Co-authored-by: Your Name <you@example.com> Co-authored-by: Dmitry Zheregelya <zheregelya.d@gmail.com> Co-authored-by: hexcoder <hexcoder-@users.noreply.github.com> Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> Co-authored-by: David CARLIER <devnexen@gmail.com> Co-authored-by: realmadsci <71108352+realmadsci@users.noreply.github.com> Co-authored-by: Roman M. Iudichev <SecNotice@ya.ru> Co-authored-by: Dustin Spicuzza <dustin@virtualroadside.com> Co-authored-by: 0x4d5a-ctf <51098072+0x4d5a-ctf@users.noreply.github.com> Co-authored-by: Tommy Chiang <oToToT@users.noreply.github.com> Co-authored-by: buherator <buherator@silentsignal.hu> Co-authored-by: Dag Heyman Kajevic <dag.heyman@gmail.com> Co-authored-by: terrynini <terrynini38514@gmail.com> Co-authored-by: jdhiser <hiser@virginia.edu> Co-authored-by: yuan <ssspeed00@gmail.com> Co-authored-by: Michael Rodler <michael.rodler@paluno.uni-due.de> Co-authored-by: Artis <32833063+Artis24106@users.noreply.github.com>
37 files changed, 1922 insertions, 997 deletions
diff --git a/README.md b/README.md index bc547b3c..91f28118 100644 --- a/README.md +++ b/README.md @@ -88,20 +88,20 @@ behaviours and defaults: with laf-intel and redqueen, frida mode, unicorn mode, gcc plugin, full *BSD, Mac OS, Solaris and Android support and much, much, much more. - | Feature/Instrumentation | afl-gcc | llvm | gcc_plugin | frida_mode | qemu_mode |unicorn_mode | - | -------------------------|:-------:|:---------:|:----------:|:----------:|:----------------:|:------------:| - | Threadsafe counters | | x(3) | | | | | - | NeverZero | x86[_64]| x(1) | x | x | x | x | - | Persistent Mode | | x | x | x86[_64] | x86[_64]/arm[64] | x | - | LAF-Intel / CompCov | | x | | | x86[_64]/arm[64] | x86[_64]/arm | - | CmpLog | | x | | x86[_64] | x86[_64]/arm[64] | | - | Selective Instrumentation| | x | x | x | x | | - | Non-Colliding Coverage | | x(4) | | | (x)(5) | | - | Ngram prev_loc Coverage | | x(6) | | | | | - | Context Coverage | | x(6) | | | | | - | Auto Dictionary | | x(7) | | | | | - | Snapshot LKM Support | | (x)(8) | (x)(8) | | (x)(5) | | - | Shared Memory Testcases | | x | x | x | x | x | + | Feature/Instrumentation | afl-gcc | llvm | gcc_plugin | frida_mode | qemu_mode |unicorn_mode | + | -------------------------|:-------:|:---------:|:----------:|:----------------:|:----------------:|:----------------:| + | Threadsafe counters | | x(3) | | | | | + | NeverZero | x86[_64]| x(1) | x | x | x | x | + | Persistent Mode | | x | x | x86[_64]/arm64 | x86[_64]/arm[64] | x | + | LAF-Intel / CompCov | | x | | | x86[_64]/arm[64] | x86[_64]/arm[64] | + | CmpLog | | x | | x86[_64]/arm64 | x86[_64]/arm[64] | | + | Selective Instrumentation| | x | x | x | x | | + | Non-Colliding Coverage | | x(4) | | | (x)(5) | | + | Ngram prev_loc Coverage | | x(6) | | | | | + | Context Coverage | | x(6) | | | | | + | Auto Dictionary | | x(7) | | | | | + | Snapshot LKM Support | | (x)(8) | (x)(8) | | (x)(5) | | + | Shared Memory Testcases | | x | x | x86[_64]/arm64 | x | x | 1. default for LLVM >= 9.0, env var for older version due an efficiency bug in previous llvm versions 2. GCC creates non-performant code, hence it is disabled in gcc_plugin @@ -796,7 +796,7 @@ If you do not have to use Unicorn the following setup is recommended to use qemu_mode: * run 1 afl-fuzz -Q instance with CMPLOG (`-c 0` + `AFL_COMPCOV_LEVEL=2`) * run 1 afl-fuzz -Q instance with QASAN (`AFL_USE_QASAN=1`) - * run 1 afl-fuzz -Q instance with LAF (``AFL_PRELOAD=libcmpcov.so` + `AFL_COMPCOV_LEVEL=2`) + * run 1 afl-fuzz -Q instance with LAF (`AFL_PRELOAD=libcmpcov.so` + `AFL_COMPCOV_LEVEL=2`) Alternatively you can use frida_mode, just switch `-Q` with `-O` and remove the LAF instance. diff --git a/custom_mutators/radamsa/custom_mutator_helpers.h b/custom_mutators/radamsa/custom_mutator_helpers.h index e23c0b6a..f7532ef9 100644..120000 --- a/custom_mutators/radamsa/custom_mutator_helpers.h +++ b/custom_mutators/radamsa/custom_mutator_helpers.h @@ -1,342 +1 @@ -#ifndef CUSTOM_MUTATOR_HELPERS -#define CUSTOM_MUTATOR_HELPERS - -#include "config.h" -#include "types.h" -#include <stdlib.h> - -#define INITIAL_GROWTH_SIZE (64) - -#define RAND_BELOW(limit) (rand() % (limit)) - -/* Use in a struct: creates a name_buf and a name_size variable. */ -#define BUF_VAR(type, name) \ - type * name##_buf; \ - size_t name##_size; -/* this filles in `&structptr->something_buf, &structptr->something_size`. */ -#define BUF_PARAMS(struct, name) \ - (void **)&struct->name##_buf, &struct->name##_size - -typedef struct { - -} afl_t; - -static void surgical_havoc_mutate(u8 *out_buf, s32 begin, s32 end) { - - static s8 interesting_8[] = {INTERESTING_8}; - static s16 interesting_16[] = {INTERESTING_8, INTERESTING_16}; - static s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32}; - - switch (RAND_BELOW(12)) { - - case 0: { - - /* Flip a single bit somewhere. Spooky! */ - - s32 bit_idx = ((RAND_BELOW(end - begin) + begin) << 3) + RAND_BELOW(8); - - out_buf[bit_idx >> 3] ^= 128 >> (bit_idx & 7); - - break; - - } - - case 1: { - - /* Set byte to interesting value. */ - - u8 val = interesting_8[RAND_BELOW(sizeof(interesting_8))]; - out_buf[(RAND_BELOW(end - begin) + begin)] = val; - - break; - - } - - case 2: { - - /* Set word to interesting value, randomly choosing endian. */ - - if (end - begin < 2) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 1) break; - - switch (RAND_BELOW(2)) { - - case 0: - *(u16 *)(out_buf + byte_idx) = - interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]; - break; - case 1: - *(u16 *)(out_buf + byte_idx) = - SWAP16(interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]); - break; - - } - - break; - - } - - case 3: { - - /* Set dword to interesting value, randomly choosing endian. */ - - if (end - begin < 4) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 3) break; - - switch (RAND_BELOW(2)) { - - case 0: - *(u32 *)(out_buf + byte_idx) = - interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]; - break; - case 1: - *(u32 *)(out_buf + byte_idx) = - SWAP32(interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]); - break; - - } - - break; - - } - - case 4: { - - /* Set qword to interesting value, randomly choosing endian. */ - - if (end - begin < 8) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 7) break; - - switch (RAND_BELOW(2)) { - - case 0: - *(u64 *)(out_buf + byte_idx) = - (s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]; - break; - case 1: - *(u64 *)(out_buf + byte_idx) = SWAP64( - (s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]); - break; - - } - - break; - - } - - case 5: { - - /* Randomly subtract from byte. */ - - out_buf[(RAND_BELOW(end - begin) + begin)] -= 1 + RAND_BELOW(ARITH_MAX); - - break; - - } - - case 6: { - - /* Randomly add to byte. */ - - out_buf[(RAND_BELOW(end - begin) + begin)] += 1 + RAND_BELOW(ARITH_MAX); - - break; - - } - - case 7: { - - /* Randomly subtract from word, random endian. */ - - if (end - begin < 2) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 1) break; - - if (RAND_BELOW(2)) { - - *(u16 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u16 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u16 *)(out_buf + byte_idx) = - SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) - num); - - } - - break; - - } - - case 8: { - - /* Randomly add to word, random endian. */ - - if (end - begin < 2) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 1) break; - - if (RAND_BELOW(2)) { - - *(u16 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u16 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u16 *)(out_buf + byte_idx) = - SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) + num); - - } - - break; - - } - - case 9: { - - /* Randomly subtract from dword, random endian. */ - - if (end - begin < 4) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 3) break; - - if (RAND_BELOW(2)) { - - *(u32 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u32 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u32 *)(out_buf + byte_idx) = - SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) - num); - - } - - break; - - } - - case 10: { - - /* Randomly add to dword, random endian. */ - - if (end - begin < 4) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 3) break; - - if (RAND_BELOW(2)) { - - *(u32 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u32 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u32 *)(out_buf + byte_idx) = - SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) + num); - - } - - break; - - } - - case 11: { - - /* Just set a random byte to a random value. Because, - why not. We use XOR with 1-255 to eliminate the - possibility of a no-op. */ - - out_buf[(RAND_BELOW(end - begin) + begin)] ^= 1 + RAND_BELOW(255); - - break; - - } - - } - -} - -/* This function calculates the next power of 2 greater or equal its argument. - @return The rounded up power of 2 (if no overflow) or 0 on overflow. -*/ -static inline size_t next_pow2(size_t in) { - - if (in == 0 || in > (size_t)-1) - return 0; /* avoid undefined behaviour under-/overflow */ - size_t out = in - 1; - out |= out >> 1; - out |= out >> 2; - out |= out >> 4; - out |= out >> 8; - out |= out >> 16; - return out + 1; - -} - -/* This function makes sure *size is > size_needed after call. - It will realloc *buf otherwise. - *size will grow exponentially as per: - https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/ - Will return NULL and free *buf if size_needed is <1 or realloc failed. - @return For convenience, this function returns *buf. - */ -static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) { - - /* No need to realloc */ - if (likely(size_needed && *size >= size_needed)) return *buf; - - /* No initial size was set */ - if (size_needed < INITIAL_GROWTH_SIZE) size_needed = INITIAL_GROWTH_SIZE; - - /* grow exponentially */ - size_t next_size = next_pow2(size_needed); - - /* handle overflow */ - if (!next_size) { next_size = size_needed; } - - /* alloc */ - *buf = realloc(*buf, next_size); - *size = *buf ? next_size : 0; - - return *buf; - -} - -/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */ -static inline void afl_swap_bufs(void **buf1, size_t *size1, void **buf2, - size_t *size2) { - - void * scratch_buf = *buf1; - size_t scratch_size = *size1; - *buf1 = *buf2; - *size1 = *size2; - *buf2 = scratch_buf; - *size2 = scratch_size; - -} - -#undef INITIAL_GROWTH_SIZE - -#endif - +../examples/custom_mutator_helpers.h \ No newline at end of file diff --git a/docs/Changelog.md b/docs/Changelog.md index 6c851460..9f70535a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,20 +9,29 @@ 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.14a (release) - - Fix for llvm 13 - afl-fuzz: - fix -F when a '/' was part of the parameter + - fixed a crash for cmplog for very slow inputs - removed implied -D determinstic from -M main - - if the target becomes unavailable check out out/default/error.txt for - an indicator why - - afl-cc + - if the target becomes unavailable check out out/default/error.txt + for an indicator why + - AFL_CAL_FAST was a dead env, now does the same as AFL_FAST_CAL + - afl-cc: + - Update to COMPCOV/laf-intel that speeds up the instrumentation + process a lot - thanks to Michael Rodler/f0rki for the PR! + - Fix to instrument global namespace functions in c++ + - Fix for llvm 13 - support partial linking - - We do support llvm versions from 3.8 again - - afl_analyze - - fix timeout handling and support forkserver + - We do support llvm versions from 3.8 to 5.0 again + - frida_mode: + - fix for cmplog + - remove need for AFL_FRIDA_PERSISTENT_RETADDR_OFFSET + - feature parity of aarch64 with intel now (persistent, cmplog, + in-memory testcases, asan) + - afl_analyze: + - fix timeout handling + - add forkserver support for better performance - ensure afl-compiler-rt is built for gcc_module - - afl-analyze now uses the forkserver for increased performance - ### Version ++3.13c (release) - Note: plot_data switched to relative time from unix time in 3.10 diff --git a/docs/FAQ.md b/docs/FAQ.md index 714d50eb..ab0abe6c 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -3,6 +3,7 @@ ## Contents * [What is the difference between afl and afl++?](#what-is-the-difference-between-afl-and-afl) + * [I got a weird compile error from clang](#i-got-a-weird-compile-error-from-clang) * [How to improve the fuzzing speed?](#how-to-improve-the-fuzzing-speed) * [How do I fuzz a network service?](#how-do-i-fuzz-a-network-service) * [How do I fuzz a GUI program?](#how-do-i-fuzz-a-gui-program) @@ -35,6 +36,26 @@ flexible and feature rich guided fuzzer available as open source. And in independent fuzzing benchmarks it is one of the best fuzzers available, e.g. [Fuzzbench Report](https://www.fuzzbench.com/reports/2020-08-03/index.html) +## I got a weird compile error from clang + +If you see this kind of error when trying to instrument a target with afl-cc/ +afl-clang-fast/afl-clang-lto: +``` +/prg/tmp/llvm-project/build/bin/clang-13: symbol lookup error: /usr/local/bin/../lib/afl//cmplog-instructions-pass.so: undefined symbol: _ZNK4llvm8TypeSizecvmEv +clang-13: error: unable to execute command: No such file or directory +clang-13: error: clang frontend command failed due to signal (use -v to see invocation) +clang version 13.0.0 (https://github.com/llvm/llvm-project 1d7cf550721c51030144f3cd295c5789d51c4aad) +Target: x86_64-unknown-linux-gnu +Thread model: posix +InstalledDir: /prg/tmp/llvm-project/build/bin +clang-13: note: diagnostic msg: +******************** +``` +Then this means that your OS updated the clang installation from an upgrade +package and because of that the afl++ llvm plugins do not match anymore. + +Solution: `git pull ; make clean install` of afl++ + ## How to improve the fuzzing speed? 1. Use [llvm_mode](docs/llvm_mode/README.md): afl-clang-lto (llvm >= 11) or afl-clang-fast (llvm >= 9 recommended) diff --git a/docs/env_variables.md b/docs/env_variables.md index 38a67bc7..e058f377 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -108,9 +108,6 @@ make fairly broad use of environmental variables instead: - Setting `AFL_QUIET` will prevent afl-cc and afl-as banners from being displayed during compilation, in case you find them distracting. - - Setting `AFL_CAL_FAST` will speed up the initial calibration, if the - application is very slow. - ## 2) Settings for LLVM and LTO: afl-clang-fast / afl-clang-fast++ / afl-clang-lto / afl-clang-lto++ The native instrumentation helpers (instrumentation and gcc_plugin) accept a subset @@ -386,6 +383,7 @@ checks or alter some of the more exotic semantics of the tool: - `AFL_FAST_CAL` keeps the calibration stage about 2.5x faster (albeit less precise), which can help when starting a session against a slow target. + `AFL_CAL_FAST` works too. - The CPU widget shown at the bottom of the screen is fairly simplistic and may complain of high load prematurely, especially on systems with low core diff --git a/frida_mode/GNUmakefile b/frida_mode/GNUmakefile index a0387cac..329d9f7f 100644 --- a/frida_mode/GNUmakefile +++ b/frida_mode/GNUmakefile @@ -20,6 +20,7 @@ RT_CFLAGS:=-Wno-unused-parameter \ -Wno-unused-function \ -Wno-unused-result \ -Wno-int-to-pointer-cast \ + -Wno-pointer-sign \ LDFLAGS+=-shared \ -lpthread \ diff --git a/frida_mode/README.md b/frida_mode/README.md index d7dd72a0..296e6405 100644 --- a/frida_mode/README.md +++ b/frida_mode/README.md @@ -75,7 +75,6 @@ following options are currently supported: * `AFL_FRIDA_PERSISTENT_CNT` - See `AFL_QEMU_PERSISTENT_CNT` * `AFL_FRIDA_PERSISTENT_HOOK` - See `AFL_QEMU_PERSISTENT_HOOK` * `AFL_FRIDA_PERSISTENT_RET` - See `AFL_QEMU_PERSISTENT_RET` -* `AFL_FRIDA_PERSISTENT_RETADDR_OFFSET` - See `AFL_QEMU_PERSISTENT_RETADDR_OFFSET` To enable the powerful CMPLOG mechanism, set `-c 0` for `afl-fuzz`. @@ -156,16 +155,17 @@ instrumentation (the default where available). Required to use * `AFL_FRIDA_INST_NO_PREFETCH` - Disable prefetching. By default the child will report instrumented blocks back to the parent so that it can also instrument them and they be inherited by the next child on fork. -* `AFL_FRIDA_INST_TRACE` - Log to stdout the address of executed blocks -`AFL_FRIDA_INST_NO_OPTIMIZE`. +* `AFL_FRIDA_INST_TRACE` - Log to stdout the address of executed blocks, +requires `AFL_FRIDA_INST_NO_OPTIMIZE`. +* `AFL_FRIDA_INST_TRACE_UNIQUE` - As per `AFL_FRIDA_INST_TRACE`, but each edge +is logged only once, requires `AFL_FRIDA_INST_NO_OPTIMIZE`. * `AFL_FRIDA_OUTPUT_STDOUT` - Redirect the standard output of the target application to the named file (supersedes the setting of `AFL_DEBUG_CHILD`) * `AFL_FRIDA_OUTPUT_STDERR` - Redirect the standard error of the target application to the named file (supersedes the setting of `AFL_DEBUG_CHILD`) * `AFL_FRIDA_PERSISTENT_DEBUG` - Insert a Breakpoint into the instrumented code at `AFL_FRIDA_PERSISTENT_HOOK` and `AFL_FRIDA_PERSISTENT_RET` to allow the user -to determine the value of `AFL_FRIDA_PERSISTENT_RETADDR_OFFSET` using a -debugger. +to detect issues in the persistent loop using a debugger. ``` diff --git a/frida_mode/src/cmplog/cmplog.c b/frida_mode/src/cmplog/cmplog.c index 7b11c350..3df7d13d 100644 --- a/frida_mode/src/cmplog/cmplog.c +++ b/frida_mode/src/cmplog/cmplog.c @@ -1,3 +1,8 @@ +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <syscall.h> + #include "frida-gum.h" #include "debug.h" @@ -5,10 +10,13 @@ #include "util.h" #define DEFAULT_MMAP_MIN_ADDR (32UL << 10) +#define FD_TMP_MAX_SIZE 65536 extern struct cmp_map *__afl_cmp_map; static GArray *cmplog_ranges = NULL; +static int fd_tmp = -1; +static ssize_t fd_tmp_size = 0; static gboolean cmplog_range(const GumRangeDetails *details, gpointer user_data) { @@ -27,6 +35,40 @@ static gint cmplog_sort(gconstpointer a, gconstpointer b) { } +static int cmplog_create_temp(void) { + + const char *tmpdir = g_get_tmp_dir(); + OKF("CMPLOG Temporary directory: %s", tmpdir); + gchar *fname = g_strdup_printf("%s/frida-cmplog-XXXXXX", tmpdir); + OKF("CMPLOG Temporary file template: %s", fname); + int fd = mkstemp(fname); + OKF("CMPLOG Temporary file: %s", fname); + + if (fd < 0) { + + FATAL("Failed to create temp file: %s, errno: %d", fname, errno); + + } + + if (unlink(fname) < 0) { + + FATAL("Failed to unlink temp file: %s (%d), errno: %d", fname, fd, errno); + + } + + if (ftruncate(fd, 0) < 0) { + + FATAL("Failed to ftruncate temp file: %s (%d), errno: %d", fname, fd, + errno); + + } + + g_free(fname); + + return fd; + +} + void cmplog_init(void) { if (__afl_cmp_map != NULL) { OKF("CMPLOG mode enabled"); } @@ -44,6 +86,13 @@ void cmplog_init(void) { } + /* + * We can't use /dev/null or /dev/zero for this since it appears that they + * don't validate the input buffer. Persumably as an optimization because they + * don't actually write any data. The file will be deleted on close. + */ + fd_tmp = cmplog_create_temp(); + } static gboolean cmplog_contains(GumAddress inner_base, GumAddress inner_limit, @@ -67,6 +116,9 @@ gboolean cmplog_is_readable(guint64 addr, size_t size) { */ if (addr < DEFAULT_MMAP_MIN_ADDR) { return false; } + /* Check our addres/length don't wrap around */ + if (SIZE_MAX - addr < size) { return false; } + GumAddress inner_base = addr; GumAddress inner_limit = inner_base + size; @@ -81,6 +133,38 @@ gboolean cmplog_is_readable(guint64 addr, size_t size) { } + /* + * Our address map can change (e.g. stack growth), use write as a fallback to + * validate our address. + */ + ssize_t written = syscall(__NR_write, fd_tmp, (void *)addr, size); + + /* + * If the write succeeds, then the buffer must be valid otherwise it would + * return EFAULT + */ + if (written > 0) { + + fd_tmp_size += written; + if (fd_tmp_size > FD_TMP_MAX_SIZE) { + + /* + * Truncate the file, we don't want our temp file to continue growing! + */ + if (ftruncate(fd_tmp, 0) < 0) { + + FATAL("Failed to truncate fd_tmp (%d), errno: %d", fd_tmp, errno); + + } + + fd_tmp_size = 0; + + } + + if ((size_t)written == size) { return true; } + + } + return false; } diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c index f261e79a..ba82b89f 100644 --- a/frida_mode/src/instrument/instrument.c +++ b/frida_mode/src/instrument/instrument.c @@ -1,4 +1,6 @@ #include <unistd.h> +#include <sys/shm.h> +#include <sys/mman.h> #include "frida-gum.h" @@ -18,44 +20,50 @@ static gboolean tracing = false; static gboolean optimize = false; +static gboolean unique = false; static GumStalkerTransformer *transformer = NULL; __thread uint64_t previous_pc = 0; +static GumAddress previous_rip = 0; +static u8 * edges_notified = NULL; + +static void trace_debug(char *format, ...) { + + va_list ap; + char buffer[4096] = {0}; + int ret; + int len; + + va_start(ap, format); + ret = vsnprintf(buffer, sizeof(buffer) - 1, format, ap); + va_end(ap); + + if (ret < 0) { return; } + + len = strnlen(buffer, sizeof(buffer)); + + IGNORED_RETURN(write(STDOUT_FILENO, buffer, len)); + +} + __attribute__((hot)) static void on_basic_block(GumCpuContext *context, gpointer user_data) { UNUSED_PARAMETER(context); - /* - * This function is performance critical as it is called to instrument every - * basic block. By moving our print buffer to a global, we avoid it affecting - * the critical path with additional stack adjustments if tracing is not - * enabled. If tracing is enabled, then we're printing a load of diagnostic - * information so this overhead is unlikely to be noticeable. - */ - static char buffer[200]; - int len; - GumAddress current_pc = GUM_ADDRESS(user_data); - uint8_t * cursor; - uint64_t value; - if (unlikely(tracing)) { - - /* Avoid any functions which may cause an allocation since the target app - * may already be running inside malloc and it isn't designed to be - * re-entrant on a single thread */ - len = snprintf(buffer, sizeof(buffer), - "current_pc: 0x%016" G_GINT64_MODIFIER - "x, previous_pc: 0x%016" G_GINT64_MODIFIER "x\n", - current_pc, previous_pc); - IGNORED_RETURN(write(STDOUT_FILENO, buffer, len + 1)); + GumAddress current_rip = GUM_ADDRESS(user_data); + GumAddress current_pc; + GumAddress edge; + uint8_t * cursor; + uint64_t value; - } - - current_pc = (current_pc >> 4) ^ (current_pc << 8); + current_pc = (current_rip >> 4) ^ (current_rip << 8); current_pc &= MAP_SIZE - 1; - cursor = &__afl_area_ptr[current_pc ^ previous_pc]; + edge = current_pc ^ previous_pc; + + cursor = &__afl_area_ptr[edge]; value = *cursor; if (value == 0xff) { @@ -71,6 +79,23 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context, *cursor = value; previous_pc = current_pc >> 1; + if (unlikely(tracing)) { + + if (!unique || edges_notified[edge] == 0) { + + trace_debug("TRACE: edge: %10" G_GINT64_MODIFIER + "d, current_rip: 0x%016" G_GINT64_MODIFIER + "x, previous_rip: 0x%016" G_GINT64_MODIFIER "x\n", + edge, current_rip, previous_rip); + + } + + if (unique) { edges_notified[edge] = 1; } + + previous_rip = current_rip; + + } + } static void instr_basic_block(GumStalkerIterator *iterator, @@ -164,18 +189,28 @@ void instrument_init(void) { optimize = (getenv("AFL_FRIDA_INST_NO_OPTIMIZE") == NULL); tracing = (getenv("AFL_FRIDA_INST_TRACE") != NULL); + unique = (getenv("AFL_FRIDA_INST_TRACE_UNIQUE") != NULL); if (!instrument_is_coverage_optimize_supported()) optimize = false; OKF("Instrumentation - optimize [%c]", optimize ? 'X' : ' '); OKF("Instrumentation - tracing [%c]", tracing ? 'X' : ' '); + OKF("Instrumentation - unique [%c]", unique ? 'X' : ' '); if (tracing && optimize) { - FATAL("AFL_FRIDA_INST_OPTIMIZE and AFL_FRIDA_INST_TRACE are incompatible"); + FATAL("AFL_FRIDA_INST_TRACE requires AFL_FRIDA_INST_NO_OPTIMIZE"); + + } + + if (unique && optimize) { + + FATAL("AFL_FRIDA_INST_TRACE_UNIQUE requires AFL_FRIDA_INST_NO_OPTIMIZE"); } + if (unique) { tracing = TRUE; } + if (__afl_map_size != 0x10000) { FATAL("Bad map size: 0x%08x", __afl_map_size); @@ -185,6 +220,28 @@ void instrument_init(void) { transformer = gum_stalker_transformer_make_from_callback(instr_basic_block, NULL, NULL); + if (unique) { + + int shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600); + if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); } + + edges_notified = shmat(shm_id, NULL, 0); + g_assert(edges_notified != MAP_FAILED); + + /* + * Configure the shared memory region to be removed once the process dies. + */ + if (shmctl(shm_id, IPC_RMID, NULL) < 0) { + + FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno); + + } + + /* Clear it, not sure it's necessary, just seems like good practice */ + memset(edges_notified, '\0', MAP_SIZE); + + } + instrument_debug_init(); asan_init(); cmplog_init(); diff --git a/frida_mode/src/main.c b/frida_mode/src/main.c index 1ab9993f..7ff23755 100644 --- a/frida_mode/src/main.c +++ b/frida_mode/src/main.c @@ -1,4 +1,5 @@ #include <errno.h> +#include <fcntl.h> #include <unistd.h> #include <sys/types.h> @@ -27,6 +28,8 @@ #include "stats.h" #include "util.h" +#define PROC_MAX 65536 + #ifdef __APPLE__ extern mach_port_t mach_task_self(); extern GumAddress gum_darwin_find_entrypoint(mach_port_t task); @@ -78,7 +81,7 @@ static void on_main_os(int argc, char **argv, char **envp) { #endif -static void embedded_init() { +static void embedded_init(void) { static gboolean initialized = false; if (!initialized) { @@ -90,7 +93,84 @@ static void embedded_init() { } -void afl_frida_start() { +static void afl_print_cmdline(void) { + + char * buffer = g_malloc0(PROC_MAX); + gchar *fname = g_strdup_printf("/proc/%d/cmdline", getppid()); + int fd = open(fname, O_RDONLY); + + if (fd < 0) { + + FATAL("Failed to open /proc/self/cmdline, errno: (%d)", errno); + + } + + ssize_t bytes_read = read(fd, buffer, PROC_MAX - 1); + if (bytes_read < 0) { + + FATAL("Failed to read /proc/self/cmdline, errno: (%d)", errno); + + } + + int idx = 0; + + for (ssize_t i = 0; i < bytes_read; i++) { + + if (i == 0 || buffer[i - 1] == '\0') { + + OKF("AFL - COMMANDLINE: argv[%d] = %s", idx++, &buffer[i]); + + } + + } + + close(fd); + g_free(fname); + g_free(buffer); + +} + +static void afl_print_env(void) { + + char * buffer = g_malloc0(PROC_MAX); + gchar *fname = g_strdup_printf("/proc/%d/environ", getppid()); + int fd = open(fname, O_RDONLY); + + if (fd < 0) { + + FATAL("Failed to open /proc/self/cmdline, errno: (%d)", errno); + + } + + ssize_t bytes_read = read(fd, buffer, PROC_MAX - 1); + if (bytes_read < 0) { + + FATAL("Failed to read /proc/self/cmdline, errno: (%d)", errno); + + } + + int idx = 0; + + for (ssize_t i = 0; i < bytes_read; i++) { + + if (i == 0 || buffer[i - 1] == '\0') { + + OKF("AFL - ENVIRONMENT %3d: %s", idx++, &buffer[i]); + + } + + } + + close(fd); + g_free(fname); + g_free(buffer); + +} + +void afl_frida_start(void) { + + afl_print_cmdline(); + afl_print_env(); embedded_init(); stalker_init(); diff --git a/frida_mode/src/persistent/persistent.c b/frida_mode/src/persistent/persistent.c index 2ec5b9cc..243d501d 100644 --- a/frida_mode/src/persistent/persistent.c +++ b/frida_mode/src/persistent/persistent.c @@ -13,7 +13,6 @@ afl_persistent_hook_fn hook = NULL; guint64 persistent_start = 0; guint64 persistent_count = 0; guint64 persistent_ret = 0; -guint64 persistent_ret_offset = 0; gboolean persistent_debug = FALSE; void persistent_init(void) { @@ -23,8 +22,6 @@ void persistent_init(void) { persistent_start = util_read_address("AFL_FRIDA_PERSISTENT_ADDR"); persistent_count = util_read_num("AFL_FRIDA_PERSISTENT_CNT"); persistent_ret = util_read_address("AFL_FRIDA_PERSISTENT_RET"); - persistent_ret_offset = - util_read_address("AFL_FRIDA_PERSISTENT_RETADDR_OFFSET"); if (getenv("AFL_FRIDA_PERSISTENT_DEBUG") != NULL) { persistent_debug = TRUE; } @@ -44,14 +41,6 @@ void persistent_init(void) { } - if (persistent_ret_offset != 0 && persistent_ret == 0) { - - FATAL( - "AFL_FRIDA_PERSISTENT_RET must be specified if " - "AFL_FRIDA_PERSISTENT_RETADDR_OFFSET is"); - - } - if (persistent_start != 0 && persistent_count == 0) persistent_count = 1000; if (persistent_count != 0 && persistent_count < 100) @@ -68,8 +57,6 @@ void persistent_init(void) { OKF("Instrumentation - persistent ret [%c] (0x%016" G_GINT64_MODIFIER "X)", persistent_ret == 0 ? ' ' : 'X', persistent_ret); - OKF("Instrumentation - persistent ret offset [%c] (%" G_GINT64_MODIFIER "d)", - persistent_ret_offset == 0 ? ' ' : 'X', persistent_ret_offset); if (hook_name != NULL) { diff --git a/frida_mode/src/persistent/persistent_arm64.c b/frida_mode/src/persistent/persistent_arm64.c index b23693fe..d7c6c76b 100644 --- a/frida_mode/src/persistent/persistent_arm64.c +++ b/frida_mode/src/persistent/persistent_arm64.c @@ -268,13 +268,15 @@ static void instrument_persitent_restore_regs(GumArm64Writer * cw, ARM64_REG_X0, (16 * 14), GUM_INDEX_SIGNED_OFFSET); - /* Don't restore RIP or RSP, use x1-x3 as clobber */ - - /* LR & Adjusted SP (clobber x1) */ + /* LR & Adjusted SP (use x1 as clobber) */ gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X30, ARM64_REG_X1, ARM64_REG_X0, (16 * 15), GUM_INDEX_SIGNED_OFFSET); + gum_arm64_writer_put_mov_reg_reg(cw, ARM64_REG_SP, ARM64_REG_X1); + + /* Don't restore RIP use x1-x3 as clobber */ + /* PC (x2) & CPSR (x1) */ gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X2, ARM64_REG_X1, ARM64_REG_X0, (16 * 16), @@ -404,7 +406,6 @@ void persistent_prologue(GumStalkerOutput *output) { gconstpointer loop = cw->code + 1; - /* Stack must be 16-byte aligned per ABI */ instrument_persitent_save_regs(cw, &saved_regs); /* loop: */ @@ -450,9 +451,6 @@ void persistent_epilogue(GumStalkerOutput *output) { if (persistent_debug) { gum_arm64_writer_put_brk_imm(cw, 0); } - gum_arm64_writer_put_add_reg_reg_imm(cw, ARM64_REG_SP, ARM64_REG_SP, - persistent_ret_offset); - gum_arm64_writer_put_ldr_reg_address(cw, ARM64_REG_X0, GUM_ADDRESS(&saved_lr)); diff --git a/frida_mode/src/persistent/persistent_x64.c b/frida_mode/src/persistent/persistent_x64.c index 858ad38e..653acefe 100644 --- a/frida_mode/src/persistent/persistent_x64.c +++ b/frida_mode/src/persistent/persistent_x64.c @@ -43,6 +43,7 @@ struct x86_64_regs { typedef struct x86_64_regs arch_api_regs; static arch_api_regs saved_regs = {0}; +static gpointer saved_ret = NULL; gboolean persistent_is_supported(void) { @@ -104,7 +105,7 @@ static void instrument_persitent_save_regs(GumX86Writer * cw, /* RED_ZONE + Saved flags, RAX, alignment */ gum_x86_writer_put_add_reg_imm(cw, GUM_REG_RBX, - GUM_RED_ZONE_SIZE + (0x8 * 3)); + GUM_RED_ZONE_SIZE + (0x8 * 2)); gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 16), GUM_REG_RBX); @@ -159,7 +160,9 @@ static void instrument_persitent_restore_regs(GumX86Writer * cw, gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R15, GUM_REG_RAX, (0x8 * 14)); - /* Don't restore RIP or RSP */ + /* Don't restore RIP */ + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RSP, GUM_REG_RAX, + (0x8 * 16)); /* Restore RBX, RAX & Flags */ gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, @@ -242,6 +245,31 @@ static void persistent_prologue_hook(GumX86Writer * cw, } +static void instrument_persitent_save_ret(GumX86Writer *cw) { + + /* Stack usage by this function */ + gssize offset = GUM_RED_ZONE_SIZE + (3 * 8); + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, + -(GUM_RED_ZONE_SIZE)); + + gum_x86_writer_put_pushfx(cw); + gum_x86_writer_put_push_reg(cw, GUM_REG_RAX); + gum_x86_writer_put_push_reg(cw, GUM_REG_RBX); + + gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, GUM_ADDRESS(&saved_ret)); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RSP, + offset); + gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_REG_RAX, GUM_REG_RBX); + + gum_x86_writer_put_pop_reg(cw, GUM_REG_RBX); + gum_x86_writer_put_pop_reg(cw, GUM_REG_RAX); + gum_x86_writer_put_popfx(cw); + + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, + (GUM_RED_ZONE_SIZE)); + +} + void persistent_prologue(GumStalkerOutput *output) { /* @@ -268,11 +296,10 @@ void persistent_prologue(GumStalkerOutput *output) { gconstpointer loop = cw->code + 1; - /* Stack must be 16-byte aligned per ABI */ - instrument_persitent_save_regs(cw, &saved_regs); + /* Pop the return value */ + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, 8); - /* pop the return value */ - gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, (8)); + instrument_persitent_save_regs(cw, &saved_regs); /* loop: */ gum_x86_writer_put_label(cw, loop); @@ -304,6 +331,8 @@ void persistent_prologue(GumStalkerOutput *output) { /* original: */ gum_x86_writer_put_label(cw, original); + instrument_persitent_save_ret(cw); + if (persistent_debug) { gum_x86_writer_put_breakpoint(cw); } } @@ -314,9 +343,15 @@ void persistent_epilogue(GumStalkerOutput *output) { if (persistent_debug) { gum_x86_writer_put_breakpoint(cw); } - gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, - persistent_ret_offset); - gum_x86_writer_put_ret(cw); + /* The stack should be aligned when we re-enter our loop */ + gconstpointer zero = cw->code + 1; + gum_x86_writer_put_test_reg_u32(cw, GUM_REG_RSP, 0xF); + gum_x86_writer_put_jcc_near_label(cw, X86_INS_JE, zero, GUM_NO_HINT); + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, -8); + gum_x86_writer_put_label(cw, zero); + + gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, GUM_ADDRESS(&saved_ret)); + gum_x86_writer_put_jmp_reg_ptr(cw, GUM_REG_RAX); } diff --git a/frida_mode/src/persistent/persistent_x86.c b/frida_mode/src/persistent/persistent_x86.c index 0675edf4..7add6e99 100644 --- a/frida_mode/src/persistent/persistent_x86.c +++ b/frida_mode/src/persistent/persistent_x86.c @@ -39,6 +39,7 @@ struct x86_regs { typedef struct x86_regs arch_api_regs; static arch_api_regs saved_regs = {0}; +static gpointer saved_ret = NULL; gboolean persistent_is_supported(void) { @@ -117,7 +118,9 @@ static void instrument_persitent_restore_regs(GumX86Writer * cw, gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBP, GUM_REG_EAX, (0x4 * 6)); - /* Don't restore RIP or RSP */ + /* Don't restore RIP */ + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ESP, GUM_REG_EAX, + (0x4 * 8)); /* Restore RBX, RAX & Flags */ gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_EAX, @@ -184,6 +187,26 @@ static void persistent_prologue_hook(GumX86Writer *cw, struct x86_regs *regs) { } +static void instrument_persitent_save_ret(GumX86Writer *cw) { + + /* Stack usage by this function */ + gssize offset = (3 * 4); + + gum_x86_writer_put_pushfx(cw); + gum_x86_writer_put_push_reg(cw, GUM_REG_EAX); + gum_x86_writer_put_push_reg(cw, GUM_REG_EBX); + + gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, GUM_ADDRESS(&saved_ret)); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_ESP, + offset); + gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_REG_EAX, GUM_REG_EBX); + + gum_x86_writer_put_pop_reg(cw, GUM_REG_EBX); + gum_x86_writer_put_pop_reg(cw, GUM_REG_EAX); + gum_x86_writer_put_popfx(cw); + +} + void persistent_prologue(GumStalkerOutput *output) { /* @@ -210,11 +233,10 @@ void persistent_prologue(GumStalkerOutput *output) { gconstpointer loop = cw->code + 1; - /* Stack must be 16-byte aligned per ABI */ - instrument_persitent_save_regs(cw, &saved_regs); - /* Pop the return value */ - gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_ESP, GUM_REG_ESP, (4)); + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_ESP, GUM_REG_ESP, 4); + + instrument_persitent_save_regs(cw, &saved_regs); /* loop: */ gum_x86_writer_put_label(cw, loop); @@ -244,6 +266,8 @@ void persistent_prologue(GumStalkerOutput *output) { /* original: */ gum_x86_writer_put_label(cw, original); + instrument_persitent_save_ret(cw); + if (persistent_debug) { gum_x86_writer_put_breakpoint(cw); } } @@ -254,10 +278,8 @@ void persistent_epilogue(GumStalkerOutput *output) { if (persistent_debug) { gum_x86_writer_put_breakpoint(cw); } - gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_ESP, GUM_REG_ESP, - persistent_ret_offset); - - gum_x86_writer_put_ret(cw); + gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, GUM_ADDRESS(&saved_ret)); + gum_x86_writer_put_jmp_reg_ptr(cw, GUM_REG_EAX); } diff --git a/frida_mode/test/persistent_ret/GNUmakefile b/frida_mode/test/persistent_ret/GNUmakefile index 4c9d8a19..2de51d86 100644 --- a/frida_mode/test/persistent_ret/GNUmakefile +++ b/frida_mode/test/persistent_ret/GNUmakefile @@ -38,8 +38,6 @@ ifeq "$(ARCH)" "x86" AFL_FRIDA_PERSISTENT_RET=$(shell $(PWD)get_symbol_addr.py -f $(TESTINSTBIN) -s slow -b 0x56555000) endif -AFL_FRIDA_PERSISTENT_RETADDR_OFFSET:=0x50 - .PHONY: all 32 clean qemu frida all: $(TESTINSTBIN) @@ -76,7 +74,6 @@ frida: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE) frida_ret: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE) AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \ AFL_FRIDA_PERSISTENT_RET=$(AFL_FRIDA_PERSISTENT_RET) \ - AFL_FRIDA_PERSISTENT_RETADDR_OFFSET=$(AFL_FRIDA_PERSISTENT_RETADDR_OFFSET) \ $(ROOT)afl-fuzz \ -D \ -O \ @@ -89,7 +86,6 @@ debug: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE) gdb \ --ex 'set environment AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR)' \ --ex 'set environment AFL_FRIDA_PERSISTENT_RET=$(AFL_FRIDA_PERSISTENT_RET)' \ - --ex 'set environment AFL_FRIDA_PERSISTENT_RETADDR_OFFSET=$(AFL_FRIDA_PERSISTENT_RETADDR_OFFSET)' \ --ex 'set environment AFL_FRIDA_PERSISTENT_DEBUG=1' \ --ex 'set environment AFL_DEBUG_CHILD=1' \ --ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \ @@ -99,7 +95,6 @@ debug: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE) run: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE) AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \ AFL_FRIDA_PERSISTENT_RET=$(AFL_FRIDA_PERSISTENT_RET) \ - AFL_FRIDA_PERSISTENT_RETADDR_OFFSET=$(AFL_FRIDA_PERSISTENT_RETADDR_OFFSET) \ AFL_DEBUG_CHILD=1 \ LD_PRELOAD=$(ROOT)afl-frida-trace.so \ $(TESTINSTBIN) $(TESTINSTR_DATA_FILE) diff --git a/frida_mode/test/unstable/GNUmakefile b/frida_mode/test/unstable/GNUmakefile new file mode 100644 index 00000000..fed417a3 --- /dev/null +++ b/frida_mode/test/unstable/GNUmakefile @@ -0,0 +1,90 @@ +PWD:=$(shell pwd)/ +ROOT:=$(shell realpath $(PWD)../../..)/ +BUILD_DIR:=$(PWD)build/ +UNSTABLE_DATA_DIR:=$(BUILD_DIR)in/ +UNSTABLE_DATA_FILE:=$(UNSTABLE_DATA_DIR)in + +UNSTABLE_BIN:=$(BUILD_DIR)unstable +UNSTABLE_SRC:=$(PWD)unstable.c + +QEMU_OUT:=$(BUILD_DIR)qemu-out +FRIDA_OUT:=$(BUILD_DIR)frida-out + +ifndef ARCH + +ARCH=$(shell uname -m) +ifeq "$(ARCH)" "aarch64" + ARCH:=arm64 +endif + +ifeq "$(ARCH)" "i686" + ARCH:=x86 +endif +endif + +AFL_QEMU_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(UNSTABLE_BIN) -s run_test -b 0x4000000000) + +ifeq "$(ARCH)" "aarch64" + AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(UNSTABLE_BIN) -s run_test -b 0x0000aaaaaaaaa000) +endif + +ifeq "$(ARCH)" "x86_64" + AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(UNSTABLE_BIN) -s run_test -b 0x0000555555554000) +endif + +ifeq "$(ARCH)" "x86" + AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(UNSTABLE_BIN) -s run_test -b 0x56555000) +endif + +.PHONY: all 32 clean qemu frida + +all: $(UNSTABLE_BIN) + make -C $(ROOT)frida_mode/ + +32: + CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all + +$(BUILD_DIR): + mkdir -p $@ + +$(UNSTABLE_DATA_DIR): | $(BUILD_DIR) + mkdir -p $@ + +$(UNSTABLE_DATA_FILE): | $(UNSTABLE_DATA_DIR) + echo -n "000" > $@ + +$(UNSTABLE_BIN): $(UNSTABLE_SRC) | $(BUILD_DIR) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + +clean: + rm -rf $(BUILD_DIR) + + +qemu: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE) + AFL_QEMU_PERSISTENT_ADDR=$(AFL_QEMU_PERSISTENT_ADDR) \ + $(ROOT)afl-fuzz \ + -D \ + -Q \ + -i $(UNSTABLE_DATA_DIR) \ + -o $(QEMU_OUT) \ + -- \ + $(UNSTABLE_BIN) @@ + +frida: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE) + AFL_DEBUG=1 \ + AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \ + AFL_FRIDA_INST_TRACE_UNIQUE=1 \ + AFL_FRIDA_INST_NO_OPTIMIZE=1 \ + $(ROOT)afl-fuzz \ + -D \ + -O \ + -i $(UNSTABLE_DATA_DIR) \ + -o $(FRIDA_OUT) \ + -- \ + $(UNSTABLE_BIN) @@ + +debug: + gdb \ + --ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \ + --ex 'set disassembly-flavor intel' \ + --args $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE) diff --git a/frida_mode/test/unstable/Makefile b/frida_mode/test/unstable/Makefile new file mode 100644 index 00000000..f843af19 --- /dev/null +++ b/frida_mode/test/unstable/Makefile @@ -0,0 +1,19 @@ +all: + @echo trying to use GNU make... + @gmake all || echo please install GNUmake + +32: + @echo trying to use GNU make... + @gmake 32 || echo please install GNUmake + +clean: + @gmake clean + +qemu: + @gmake qemu + +frida: + @gmake frida + +debug: + @gmake debug diff --git a/frida_mode/test/unstable/get_symbol_addr.py b/frida_mode/test/unstable/get_symbol_addr.py new file mode 100755 index 00000000..1c46e010 --- /dev/null +++ b/frida_mode/test/unstable/get_symbol_addr.py @@ -0,0 +1,36 @@ +#!/usr/bin/python3 +import argparse +from elftools.elf.elffile import ELFFile + +def process_file(file, symbol, base): + with open(file, 'rb') as f: + elf = ELFFile(f) + symtab = elf.get_section_by_name('.symtab') + mains = symtab.get_symbol_by_name(symbol) + if len(mains) != 1: + print ("Failed to find main") + return 1 + + main_addr = mains[0]['st_value'] + main = base + main_addr + print ("0x%016x" % main) + return 0 + +def hex_value(x): + return int(x, 16) + +def main(): + parser = argparse.ArgumentParser(description='Process some integers.') + parser.add_argument('-f', '--file', dest='file', type=str, + help='elf file name', required=True) + parser.add_argument('-s', '--symbol', dest='symbol', type=str, + help='symbol name', required=True) + parser.add_argument('-b', '--base', dest='base', type=hex_value, + help='elf base address', required=True) + + args = parser.parse_args() + return process_file (args.file, args.symbol, args.base) + +if __name__ == "__main__": + ret = main() + exit(ret) diff --git a/frida_mode/test/unstable/unstable.c b/frida_mode/test/unstable/unstable.c new file mode 100644 index 00000000..67d56b73 --- /dev/null +++ b/frida_mode/test/unstable/unstable.c @@ -0,0 +1,67 @@ +/* + american fuzzy lop++ - a trivial program to test the build + -------------------------------------------------------- + Originally written by Michal Zalewski + Copyright 2014 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + http://www.apache.org/licenses/LICENSE-2.0 + */ + +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#ifdef __APPLE__ + #define TESTINSTR_SECTION +#else + #define TESTINSTR_SECTION __attribute__((section(".testinstr"))) +#endif + +void LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + + if (size < 1) return; + + int r = rand(); + if ((r % 2) == 0) { + printf ("Hooray all even\n"); + } else { + printf ("Hmm that's odd\n"); + } + + // we support three input cases + if (data[0] == '0') + printf("Looks like a zero to me!\n"); + else if (data[0] == '1') + printf("Pretty sure that is a one!\n"); + else + printf("Neither one or zero? How quaint!\n"); + +} + +void run_test(char * file) { + fprintf(stderr, "Running: %s\n", file); + FILE *f = fopen(file, "r"); + assert(f); + fseek(f, 0, SEEK_END); + size_t len = ftell(f); + fseek(f, 0, SEEK_SET); + unsigned char *buf = (unsigned char*)malloc(len); + size_t n_read = fread(buf, 1, len, f); + fclose(f); + assert(n_read == len); + LLVMFuzzerTestOneInput(buf, len); + free(buf); + fprintf(stderr, "Done: %s: (%zd bytes)\n", file, n_read); +} + +int main(int argc, char **argv) { + srand(1); + fprintf(stderr, "StandaloneFuzzTargetMain: running %d inputs\n", argc - 1); + for (int i = 1; i < argc; i++) { + run_test(argv[i]); + } +} diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 4aba3bdf..2920f905 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -478,9 +478,7 @@ typedef struct afl_state { u32 hang_tmout; /* Timeout used for hang det (ms) */ - u8 cal_cycles, /* Calibration cycles defaults */ - cal_cycles_long, /* Calibration cycles defaults */ - havoc_stack_pow2, /* HAVOC_STACK_POW2 */ + u8 havoc_stack_pow2, /* HAVOC_STACK_POW2 */ no_unlink, /* do not unlink cur_input */ debug, /* Debug mode */ custom_only, /* Custom mutator only mode */ diff --git a/include/envs.h b/include/envs.h index 15116fc1..54bb6597 100644 --- a/include/envs.h +++ b/include/envs.h @@ -60,6 +60,7 @@ static char *afl_environment_variables[] = { "AFL_FRIDA_INST_NO_PREFETCH", "AFL_FRIDA_INST_RANGES", "AFL_FRIDA_INST_TRACE", + "AFL_FRIDA_INST_UNSTABLE", "AFL_FRIDA_OUTPUT_STDOUT", "AFL_FRIDA_OUTPUT_STDERR", "AFL_FRIDA_PERSISTENT_ADDR", @@ -67,7 +68,6 @@ static char *afl_environment_variables[] = { "AFL_FRIDA_PERSISTENT_DEBUG", "AFL_FRIDA_PERSISTENT_HOOK", "AFL_FRIDA_PERSISTENT_RET", - "AFL_FRIDA_PERSISTENT_RETADDR_OFFSET", "AFL_FRIDA_STATS_FILE", "AFL_FRIDA_STATS_INTERVAL", "AFL_FRIDA_STATS_TRANSITIONS", diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc index af32e2f9..3239ea91 100644 --- a/instrumentation/afl-llvm-common.cc +++ b/instrumentation/afl-llvm-common.cc @@ -96,9 +96,8 @@ bool isIgnoreFunction(const llvm::Function *F) { static constexpr const char *ignoreSubstringList[] = { - "__asan", "__msan", "__ubsan", "__lsan", - "__san", "__sanitize", "__cxx", "_GLOBAL__", - "DebugCounter", "DwarfDebug", "DebugLoc" + "__asan", "__msan", "__ubsan", "__lsan", "__san", "__sanitize", + "__cxx", "DebugCounter", "DwarfDebug", "DebugLoc" }; diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc index b02a89fb..68f6c329 100644 --- a/instrumentation/split-compares-pass.so.cc +++ b/instrumentation/split-compares-pass.so.cc @@ -47,6 +47,10 @@ using namespace llvm; #include "afl-llvm-common.h" +// uncomment this toggle function verification at each step. horribly slow, but +// helps to pinpoint a potential problem in the splitting code. +//#define VERIFY_TOO_MUCH 1 + namespace { class SplitComparesTransform : public ModulePass { @@ -67,28 +71,101 @@ class SplitComparesTransform : public ModulePass { const char *getPassName() const override { #endif - return "simplifies and splits ICMP instructions"; + return "AFL_SplitComparesTransform"; } private: int enableFPSplit; - size_t splitIntCompares(Module &M, unsigned bitw); + unsigned target_bitwidth = 8; + + size_t count = 0; + size_t splitFPCompares(Module &M); - bool simplifyCompares(Module &M); bool simplifyFPCompares(Module &M); - bool simplifyIntSignedness(Module &M); size_t nextPowerOfTwo(size_t in); + using CmpWorklist = SmallVector<CmpInst *, 8>; + + /// simplify the comparison and then split the comparison until the + /// target_bitwidth is reached. + bool simplifyAndSplit(CmpInst *I, Module &M); + /// simplify a non-strict comparison (e.g., less than or equals) + bool simplifyOrEqualsCompare(CmpInst *IcmpInst, Module &M, + CmpWorklist &worklist); + /// simplify a signed comparison (signed less or greater than) + bool simplifySignedCompare(CmpInst *IcmpInst, Module &M, + CmpWorklist &worklist); + /// splits an icmp into nested icmps recursivly until target_bitwidth is + /// reached + bool splitCompare(CmpInst *I, Module &M, CmpWorklist &worklist); + + /// print an error to llvm's errs stream, but only if not ordered to be quiet + void reportError(const StringRef msg, Instruction *I, Module &M) { + + if (!be_quiet) { + + errs() << "[AFL++ SplitComparesTransform] ERROR: " << msg << "\n"; + if (debug) { + + if (I) { + + errs() << "Instruction = " << *I << "\n"; + if (auto BB = I->getParent()) { + + if (auto F = BB->getParent()) { + + if (F->hasName()) { + + errs() << "|-> in function " << F->getName() << " "; + + } + + } + + } + + } + + auto n = M.getName(); + if (n.size() > 0) { errs() << "in module " << n << "\n"; } + + } + + } + + } + + bool isSupportedBitWidth(unsigned bitw) { + + // IDK whether the icmp code works on other bitwidths. I guess not? So we + // try to avoid dealing with other weird icmp's that llvm might use (looking + // at you `icmp i0`). + switch (bitw) { + + case 8: + case 16: + case 32: + case 64: + case 128: + case 256: + return true; + default: + return false; + + } + + } + }; } // namespace char SplitComparesTransform::ID = 0; -/* This function splits FCMP instructions with xGE or xLE predicates into two - * FCMP instructions with predicate xGT or xLT and EQ */ +/// This function splits FCMP instructions with xGE or xLE predicates into two +/// FCMP instructions with predicate xGT or xLT and EQ bool SplitComparesTransform::simplifyFPCompares(Module &M) { LLVMContext & C = M.getContext(); @@ -221,292 +298,481 @@ bool SplitComparesTransform::simplifyFPCompares(Module &M) { } -/* This function splits ICMP instructions with xGE or xLE predicates into two - * ICMP instructions with predicate xGT or xLT and EQ */ -bool SplitComparesTransform::simplifyCompares(Module &M) { +/// This function splits ICMP instructions with xGE or xLE predicates into two +/// ICMP instructions with predicate xGT or xLT and EQ +bool SplitComparesTransform::simplifyOrEqualsCompare(CmpInst * IcmpInst, + Module & M, + CmpWorklist &worklist) { - LLVMContext & C = M.getContext(); - std::vector<Instruction *> icomps; - IntegerType * Int1Ty = IntegerType::getInt1Ty(C); + LLVMContext &C = M.getContext(); + IntegerType *Int1Ty = IntegerType::getInt1Ty(C); - /* iterate over all functions, bbs and instruction and add - * all integer comparisons with >= and <= predicates to the icomps vector */ - for (auto &F : M) { + /* find out what the new predicate is going to be */ + auto cmp_inst = dyn_cast<CmpInst>(IcmpInst); + if (!cmp_inst) { return false; } - if (!isInInstrumentList(&F)) continue; + BasicBlock *bb = IcmpInst->getParent(); - for (auto &BB : F) { + auto op0 = IcmpInst->getOperand(0); + auto op1 = IcmpInst->getOperand(1); - for (auto &IN : BB) { + CmpInst::Predicate pred = cmp_inst->getPredicate(); + CmpInst::Predicate new_pred; - CmpInst *selectcmpInst = nullptr; + switch (pred) { - if ((selectcmpInst = dyn_cast<CmpInst>(&IN))) { + case CmpInst::ICMP_UGE: + new_pred = CmpInst::ICMP_UGT; + break; + case CmpInst::ICMP_SGE: + new_pred = CmpInst::ICMP_SGT; + break; + case CmpInst::ICMP_ULE: + new_pred = CmpInst::ICMP_ULT; + break; + case CmpInst::ICMP_SLE: + new_pred = CmpInst::ICMP_SLT; + break; + default: // keep the compiler happy + return false; - if (selectcmpInst->getPredicate() == CmpInst::ICMP_UGE || - selectcmpInst->getPredicate() == CmpInst::ICMP_SGE || - selectcmpInst->getPredicate() == CmpInst::ICMP_ULE || - selectcmpInst->getPredicate() == CmpInst::ICMP_SLE) { + } - auto op0 = selectcmpInst->getOperand(0); - auto op1 = selectcmpInst->getOperand(1); + /* split before the icmp instruction */ + BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); + + /* the old bb now contains a unconditional jump to the new one (end_bb) + * we need to delete it later */ + + /* create the ICMP instruction with new_pred and add it to the old basic + * block bb it is now at the position where the old IcmpInst was */ + CmpInst *icmp_np = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), icmp_np); + + /* create a new basic block which holds the new EQ icmp */ + CmpInst *icmp_eq; + /* insert middle_bb before end_bb */ + BasicBlock *middle_bb = + BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); + icmp_eq = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, op0, op1); + middle_bb->getInstList().push_back(icmp_eq); + /* add an unconditional branch to the end of middle_bb with destination + * end_bb */ + BranchInst::Create(end_bb, middle_bb); + + /* replace the uncond branch with a conditional one, which depends on the + * new_pred icmp. True goes to end, false to the middle (injected) bb */ + auto term = bb->getTerminator(); + BranchInst::Create(end_bb, middle_bb, icmp_np, bb); + term->eraseFromParent(); + + /* replace the old IcmpInst (which is the first inst in end_bb) with a PHI + * inst to wire up the loose ends */ + PHINode *PN = PHINode::Create(Int1Ty, 2, ""); + /* the first result depends on the outcome of icmp_eq */ + PN->addIncoming(icmp_eq, middle_bb); + /* if the source was the original bb we know that the icmp_np yielded true + * hence we can hardcode this value */ + PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); + /* replace the old IcmpInst with our new and shiny PHI inst */ + BasicBlock::iterator ii(IcmpInst); + ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); + + worklist.push_back(icmp_np); + worklist.push_back(icmp_eq); - IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType()); - IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType()); + return true; - /* this is probably not needed but we do it anyway */ - if (!intTyOp0 || !intTyOp1) { continue; } +} - icomps.push_back(selectcmpInst); +/// Simplify a signed comparison operator by splitting it into a unsigned and +/// bit comparison. add all resulting comparisons to +/// the worklist passed as a reference. +bool SplitComparesTransform::simplifySignedCompare(CmpInst *IcmpInst, Module &M, + CmpWorklist &worklist) { - } + LLVMContext &C = M.getContext(); + IntegerType *Int1Ty = IntegerType::getInt1Ty(C); - } + BasicBlock *bb = IcmpInst->getParent(); - } + auto op0 = IcmpInst->getOperand(0); + auto op1 = IcmpInst->getOperand(1); - } + IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType()); + if (!intTyOp0) { return false; } + unsigned bitw = intTyOp0->getBitWidth(); + IntegerType *IntType = IntegerType::get(C, bitw); - } + /* get the new predicate */ + auto cmp_inst = dyn_cast<CmpInst>(IcmpInst); + if (!cmp_inst) { return false; } + auto pred = cmp_inst->getPredicate(); + CmpInst::Predicate new_pred; - if (!icomps.size()) { return false; } + if (pred == CmpInst::ICMP_SGT) { - for (auto &IcmpInst : icomps) { + new_pred = CmpInst::ICMP_UGT; - BasicBlock *bb = IcmpInst->getParent(); + } else { - auto op0 = IcmpInst->getOperand(0); - auto op1 = IcmpInst->getOperand(1); + new_pred = CmpInst::ICMP_ULT; - /* find out what the new predicate is going to be */ - auto cmp_inst = dyn_cast<CmpInst>(IcmpInst); - if (!cmp_inst) { continue; } - auto pred = cmp_inst->getPredicate(); - CmpInst::Predicate new_pred; + } - switch (pred) { + BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); + + /* create a 1 bit compare for the sign bit. to do this shift and trunc + * the original operands so only the first bit remains.*/ + Value *s_op0, *t_op0, *s_op1, *t_op1, *icmp_sign_bit; + + IRBuilder<> IRB(bb->getTerminator()); + s_op0 = IRB.CreateLShr(op0, ConstantInt::get(IntType, bitw - 1)); + t_op0 = IRB.CreateTruncOrBitCast(s_op0, Int1Ty); + s_op1 = IRB.CreateLShr(op1, ConstantInt::get(IntType, bitw - 1)); + t_op1 = IRB.CreateTruncOrBitCast(s_op1, Int1Ty); + /* compare of the sign bits */ + icmp_sign_bit = IRB.CreateICmp(CmpInst::ICMP_EQ, t_op0, t_op1); + + /* create a new basic block which is executed if the signedness bit is + * different */ + CmpInst * icmp_inv_sig_cmp; + BasicBlock *sign_bb = + BasicBlock::Create(C, "sign", end_bb->getParent(), end_bb); + if (pred == CmpInst::ICMP_SGT) { + + /* if we check for > and the op0 positive and op1 negative then the final + * result is true. if op0 negative and op1 pos, the cmp must result + * in false + */ + icmp_inv_sig_cmp = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, t_op0, t_op1); - case CmpInst::ICMP_UGE: - new_pred = CmpInst::ICMP_UGT; - break; - case CmpInst::ICMP_SGE: - new_pred = CmpInst::ICMP_SGT; - break; - case CmpInst::ICMP_ULE: - new_pred = CmpInst::ICMP_ULT; - break; - case CmpInst::ICMP_SLE: - new_pred = CmpInst::ICMP_SLT; - break; - default: // keep the compiler happy - continue; + } else { - } + /* just the inverse of the above statement */ + icmp_inv_sig_cmp = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, t_op0, t_op1); - /* split before the icmp instruction */ - BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); + } - /* the old bb now contains a unconditional jump to the new one (end_bb) - * we need to delete it later */ + sign_bb->getInstList().push_back(icmp_inv_sig_cmp); + BranchInst::Create(end_bb, sign_bb); - /* create the ICMP instruction with new_pred and add it to the old basic - * block bb it is now at the position where the old IcmpInst was */ - Instruction *icmp_np; - icmp_np = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), - icmp_np); + /* create a new bb which is executed if signedness is equal */ + CmpInst * icmp_usign_cmp; + BasicBlock *middle_bb = + BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); + /* we can do a normal unsigned compare now */ + icmp_usign_cmp = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1); - /* create a new basic block which holds the new EQ icmp */ - Instruction *icmp_eq; - /* insert middle_bb before end_bb */ - BasicBlock *middle_bb = - BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); - icmp_eq = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, op0, op1); - middle_bb->getInstList().push_back(icmp_eq); - /* add an unconditional branch to the end of middle_bb with destination - * end_bb */ - BranchInst::Create(end_bb, middle_bb); + middle_bb->getInstList().push_back(icmp_usign_cmp); + BranchInst::Create(end_bb, middle_bb); - /* replace the uncond branch with a conditional one, which depends on the - * new_pred icmp. True goes to end, false to the middle (injected) bb */ - auto term = bb->getTerminator(); - BranchInst::Create(end_bb, middle_bb, icmp_np, bb); - term->eraseFromParent(); + auto term = bb->getTerminator(); + /* if the sign is eq do a normal unsigned cmp, else we have to check the + * signedness bit */ + BranchInst::Create(middle_bb, sign_bb, icmp_sign_bit, bb); + term->eraseFromParent(); - /* replace the old IcmpInst (which is the first inst in end_bb) with a PHI - * inst to wire up the loose ends */ - PHINode *PN = PHINode::Create(Int1Ty, 2, ""); - /* the first result depends on the outcome of icmp_eq */ - PN->addIncoming(icmp_eq, middle_bb); - /* if the source was the original bb we know that the icmp_np yielded true - * hence we can hardcode this value */ - PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); - /* replace the old IcmpInst with our new and shiny PHI inst */ - BasicBlock::iterator ii(IcmpInst); - ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); + PHINode *PN = PHINode::Create(Int1Ty, 2, ""); - } + PN->addIncoming(icmp_usign_cmp, middle_bb); + PN->addIncoming(icmp_inv_sig_cmp, sign_bb); + + BasicBlock::iterator ii(IcmpInst); + ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); + + // save for later + worklist.push_back(icmp_usign_cmp); + + // signed comparisons are not supported by the splitting code, so we must not + // add it to the worklist. + // worklist.push_back(icmp_inv_sig_cmp); return true; } -/* this function transforms signed compares to equivalent unsigned compares */ -bool SplitComparesTransform::simplifyIntSignedness(Module &M) { +bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, + CmpWorklist &worklist) { - LLVMContext & C = M.getContext(); - std::vector<Instruction *> icomps; - IntegerType * Int1Ty = IntegerType::getInt1Ty(C); + auto pred = cmp_inst->getPredicate(); + switch (pred) { - /* iterate over all functions, bbs and instructions and add - * all signed compares to icomps vector */ - for (auto &F : M) { + case CmpInst::ICMP_EQ: + case CmpInst::ICMP_NE: + case CmpInst::ICMP_UGT: + case CmpInst::ICMP_ULT: + break; + default: + // unsupported predicate! + return false; - if (!isInInstrumentList(&F)) continue; + } - for (auto &BB : F) { + auto op0 = cmp_inst->getOperand(0); + auto op1 = cmp_inst->getOperand(1); - for (auto &IN : BB) { + // get bitwidth by checking the bitwidth of the first operator + IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType()); + if (!intTyOp0) { - CmpInst *selectcmpInst = nullptr; + // not an integer type + return false; - if ((selectcmpInst = dyn_cast<CmpInst>(&IN))) { + } - if (selectcmpInst->getPredicate() == CmpInst::ICMP_SGT || - selectcmpInst->getPredicate() == CmpInst::ICMP_SLT) { + unsigned bitw = intTyOp0->getBitWidth(); + if (bitw == target_bitwidth) { - auto op0 = selectcmpInst->getOperand(0); - auto op1 = selectcmpInst->getOperand(1); + // already the target bitwidth so we have to do nothing here. + return true; + + } + + LLVMContext &C = M.getContext(); + IntegerType *Int1Ty = IntegerType::getInt1Ty(C); + BasicBlock * bb = cmp_inst->getParent(); + IntegerType *OldIntType = IntegerType::get(C, bitw); + IntegerType *NewIntType = IntegerType::get(C, bitw / 2); + BasicBlock * end_bb = bb->splitBasicBlock(BasicBlock::iterator(cmp_inst)); + CmpInst * icmp_high, *icmp_low; - IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType()); - IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType()); + /* create the comparison of the top halves of the original operands */ + Value *s_op0, *op0_high, *s_op1, *op1_high; - /* see above */ - if (!intTyOp0 || !intTyOp1) { continue; } + IRBuilder<> IRB(bb->getTerminator()); - /* i think this is not possible but to lazy to look it up */ - if (intTyOp0->getBitWidth() != intTyOp1->getBitWidth()) { + s_op0 = IRB.CreateBinOp(Instruction::LShr, op0, + ConstantInt::get(OldIntType, bitw / 2)); + op0_high = IRB.CreateTruncOrBitCast(s_op0, NewIntType); - continue; + s_op1 = IRB.CreateBinOp(Instruction::LShr, op1, + ConstantInt::get(OldIntType, bitw / 2)); + op1_high = IRB.CreateTruncOrBitCast(s_op1, NewIntType); + icmp_high = cast<CmpInst>(IRB.CreateICmp(pred, op0_high, op1_high)); - } + PHINode *PN = nullptr; - icomps.push_back(selectcmpInst); + /* now we have to destinguish between == != and > < */ + switch (pred) { - } + case CmpInst::ICMP_EQ: + case CmpInst::ICMP_NE: { - } + /* transformation for == and != icmps */ + + /* create a compare for the lower half of the original operands */ + BasicBlock *cmp_low_bb = + BasicBlock::Create(C, "" /*"injected"*/, end_bb->getParent(), end_bb); + + Value * op0_low, *op1_low; + IRBuilder<> Builder(cmp_low_bb); + + op0_low = Builder.CreateTrunc(op0, NewIntType); + op1_low = Builder.CreateTrunc(op1, NewIntType); + icmp_low = cast<CmpInst>(Builder.CreateICmp(pred, op0_low, op1_low)); + + BranchInst::Create(end_bb, cmp_low_bb); + + /* dependent on the cmp of the high parts go to the end or go on with + * the comparison */ + auto term = bb->getTerminator(); + BranchInst *br = nullptr; + if (pred == CmpInst::ICMP_EQ) { + + br = BranchInst::Create(cmp_low_bb, end_bb, icmp_high, bb); + + } else { + + /* CmpInst::ICMP_NE */ + br = BranchInst::Create(end_bb, cmp_low_bb, icmp_high, bb); } + term->eraseFromParent(); + + /* create the PHI and connect the edges accordingly */ + PN = PHINode::Create(Int1Ty, 2, ""); + PN->addIncoming(icmp_low, cmp_low_bb); + Value *val = nullptr; + if (pred == CmpInst::ICMP_EQ) { + + val = ConstantInt::get(Int1Ty, 0); + + } else { + + /* CmpInst::ICMP_NE */ + val = ConstantInt::get(Int1Ty, 1); + + } + + PN->addIncoming(val, icmp_high->getParent()); + break; + } - } + case CmpInst::ICMP_UGT: + case CmpInst::ICMP_ULT: { + + /* transformations for < and > */ + + /* create a basic block which checks for the inverse predicate. + * if this is true we can go to the end if not we have to go to the + * bb which checks the lower half of the operands */ + Instruction *op0_low, *op1_low; + CmpInst * icmp_inv_cmp = nullptr; + BasicBlock * inv_cmp_bb = + BasicBlock::Create(C, "inv_cmp", end_bb->getParent(), end_bb); + if (pred == CmpInst::ICMP_UGT) { - if (!icomps.size()) { return false; } + icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, + op0_high, op1_high); - for (auto &IcmpInst : icomps) { + } else { - BasicBlock *bb = IcmpInst->getParent(); + icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, + op0_high, op1_high); - auto op0 = IcmpInst->getOperand(0); - auto op1 = IcmpInst->getOperand(1); + } - IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType()); - if (!intTyOp0) { continue; } - unsigned bitw = intTyOp0->getBitWidth(); - IntegerType *IntType = IntegerType::get(C, bitw); + inv_cmp_bb->getInstList().push_back(icmp_inv_cmp); + worklist.push_back(icmp_inv_cmp); - /* get the new predicate */ - auto cmp_inst = dyn_cast<CmpInst>(IcmpInst); - if (!cmp_inst) { continue; } - auto pred = cmp_inst->getPredicate(); - CmpInst::Predicate new_pred; + auto term = bb->getTerminator(); + term->eraseFromParent(); + BranchInst::Create(end_bb, inv_cmp_bb, icmp_high, bb); - if (pred == CmpInst::ICMP_SGT) { + /* create a bb which handles the cmp of the lower halves */ + BasicBlock *cmp_low_bb = + BasicBlock::Create(C, "" /*"injected"*/, end_bb->getParent(), end_bb); + op0_low = new TruncInst(op0, NewIntType); + cmp_low_bb->getInstList().push_back(op0_low); + op1_low = new TruncInst(op1, NewIntType); + cmp_low_bb->getInstList().push_back(op1_low); - new_pred = CmpInst::ICMP_UGT; + icmp_low = CmpInst::Create(Instruction::ICmp, pred, op0_low, op1_low); + cmp_low_bb->getInstList().push_back(icmp_low); + BranchInst::Create(end_bb, cmp_low_bb); - } else { + BranchInst::Create(end_bb, cmp_low_bb, icmp_inv_cmp, inv_cmp_bb); - new_pred = CmpInst::ICMP_ULT; + PN = PHINode::Create(Int1Ty, 3); + PN->addIncoming(icmp_low, cmp_low_bb); + PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); + PN->addIncoming(ConstantInt::get(Int1Ty, 0), inv_cmp_bb); + break; } - BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); + default: + return false; - /* create a 1 bit compare for the sign bit. to do this shift and trunc - * the original operands so only the first bit remains.*/ - Instruction *s_op0, *t_op0, *s_op1, *t_op1, *icmp_sign_bit; + } - s_op0 = BinaryOperator::Create(Instruction::LShr, op0, - ConstantInt::get(IntType, bitw - 1)); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op0); - t_op0 = new TruncInst(s_op0, Int1Ty); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_op0); + BasicBlock::iterator ii(cmp_inst); + ReplaceInstWithInst(cmp_inst->getParent()->getInstList(), ii, PN); - s_op1 = BinaryOperator::Create(Instruction::LShr, op1, - ConstantInt::get(IntType, bitw - 1)); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op1); - t_op1 = new TruncInst(s_op1, Int1Ty); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_op1); + // We split the comparison into low and high. If this isn't our target + // bitwidth we recursivly split the low and high parts again until we have + // target bitwidth. + if ((bitw / 2) > target_bitwidth) { - /* compare of the sign bits */ - icmp_sign_bit = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_op0, t_op1); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), - icmp_sign_bit); + worklist.push_back(icmp_high); + worklist.push_back(icmp_low); - /* create a new basic block which is executed if the signedness bit is - * different */ - Instruction *icmp_inv_sig_cmp; - BasicBlock * sign_bb = - BasicBlock::Create(C, "sign", end_bb->getParent(), end_bb); - if (pred == CmpInst::ICMP_SGT) { + } - /* if we check for > and the op0 positive and op1 negative then the final - * result is true. if op0 negative and op1 pos, the cmp must result - * in false - */ - icmp_inv_sig_cmp = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, t_op0, t_op1); + return true; - } else { +} + +bool SplitComparesTransform::simplifyAndSplit(CmpInst *I, Module &M) { + + CmpWorklist worklist; + + auto op0 = I->getOperand(0); + auto op1 = I->getOperand(1); + if (!op0 || !op1) { return false; } + auto op0Ty = dyn_cast<IntegerType>(op0->getType()); + if (!op0Ty || !isa<IntegerType>(op1->getType())) { return true; } + + unsigned bitw = op0Ty->getBitWidth(); + +#ifdef VERIFY_TOO_MUCH + auto F = I->getParent()->getParent(); +#endif + + // we run the comparison simplification on all compares regardless of their + // bitwidth. + if (I->getPredicate() == CmpInst::ICMP_UGE || + I->getPredicate() == CmpInst::ICMP_SGE || + I->getPredicate() == CmpInst::ICMP_ULE || + I->getPredicate() == CmpInst::ICMP_SLE) { - /* just the inverse of the above statement */ - icmp_inv_sig_cmp = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, t_op0, t_op1); + if (!simplifyOrEqualsCompare(I, M, worklist)) { + + reportError( + "Failed to simplify inequality or equals comparison " + "(UGE,SGE,ULE,SLE)", + I, M); } - sign_bb->getInstList().push_back(icmp_inv_sig_cmp); - BranchInst::Create(end_bb, sign_bb); + } else if (I->getPredicate() == CmpInst::ICMP_SGT || - /* create a new bb which is executed if signedness is equal */ - Instruction *icmp_usign_cmp; - BasicBlock * middle_bb = - BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); - /* we can do a normal unsigned compare now */ - icmp_usign_cmp = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1); - middle_bb->getInstList().push_back(icmp_usign_cmp); - BranchInst::Create(end_bb, middle_bb); + I->getPredicate() == CmpInst::ICMP_SLT) { - auto term = bb->getTerminator(); - /* if the sign is eq do a normal unsigned cmp, else we have to check the - * signedness bit */ - BranchInst::Create(middle_bb, sign_bb, icmp_sign_bit, bb); - term->eraseFromParent(); + if (!simplifySignedCompare(I, M, worklist)) { - PHINode *PN = PHINode::Create(Int1Ty, 2, ""); + reportError("Failed to simplify signed comparison (SGT,SLT)", I, M); + + } + + } + +#ifdef VERIFY_TOO_MUCH + if (verifyFunction(*F, &errs())) { + + reportError("simpliyfing compare lead to broken function", nullptr, M); + + } + +#endif + + // the simplification methods replace the original CmpInst and push the + // resulting new CmpInst into the worklist. If the worklist is empty then + // we only have to split the original CmpInst. + if (worklist.size() == 0) { worklist.push_back(I); } + + while (!worklist.empty()) { + + CmpInst *cmp = worklist.pop_back_val(); + // we split the simplified compares into comparisons with smaller bitwidths + // if they are larger than our target_bitwidth. + if (bitw > target_bitwidth) { + + if (!splitCompare(cmp, M, worklist)) { + + reportError("Failed to split comparison", cmp, M); + + } + +#ifdef VERIFY_TOO_MUCH + if (verifyFunction(*F, &errs())) { + + reportError("splitting compare lead to broken function", nullptr, M); + + } - PN->addIncoming(icmp_usign_cmp, middle_bb); - PN->addIncoming(icmp_inv_sig_cmp, sign_bb); +#endif - BasicBlock::iterator ii(IcmpInst); - ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); + } } + count++; return true; } @@ -1050,306 +1316,108 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { } -/* splits icmps of size bitw into two nested icmps with bitw/2 size each */ -size_t SplitComparesTransform::splitIntCompares(Module &M, unsigned bitw) { - - size_t count = 0; - - LLVMContext &C = M.getContext(); - - IntegerType *Int1Ty = IntegerType::getInt1Ty(C); - IntegerType *OldIntType = IntegerType::get(C, bitw); - IntegerType *NewIntType = IntegerType::get(C, bitw / 2); - - std::vector<Instruction *> icomps; - - if (bitw % 2) { return 0; } - - /* not supported yet */ - if (bitw > 64) { return 0; } - - /* get all EQ, NE, UGT, and ULT icmps of width bitw. if the - * functions simplifyCompares() and simplifyIntSignedness() - * were executed only these four predicates should exist */ - for (auto &F : M) { - - if (!isInInstrumentList(&F)) continue; - - for (auto &BB : F) { +bool SplitComparesTransform::runOnModule(Module &M) { - for (auto &IN : BB) { + char *bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW"); + if (!bitw_env) bitw_env = getenv("LAF_SPLIT_COMPARES_BITW"); + if (bitw_env) { target_bitwidth = atoi(bitw_env); } - CmpInst *selectcmpInst = nullptr; + enableFPSplit = getenv("AFL_LLVM_LAF_SPLIT_FLOATS") != NULL; - if ((selectcmpInst = dyn_cast<CmpInst>(&IN))) { + if ((isatty(2) && getenv("AFL_QUIET") == NULL) || + getenv("AFL_DEBUG") != NULL) { - if (selectcmpInst->getPredicate() == CmpInst::ICMP_EQ || - selectcmpInst->getPredicate() == CmpInst::ICMP_NE || - selectcmpInst->getPredicate() == CmpInst::ICMP_UGT || - selectcmpInst->getPredicate() == CmpInst::ICMP_ULT) { + errs() << "Split-compare-pass by laf.intel@gmail.com, extended by " + "heiko@hexco.de (splitting icmp to " + << target_bitwidth << " bit)\n"; - auto op0 = selectcmpInst->getOperand(0); - auto op1 = selectcmpInst->getOperand(1); + if (getenv("AFL_DEBUG") != NULL && !debug) { debug = 1; } - IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType()); - IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType()); + } else { - if (!intTyOp0 || !intTyOp1) { continue; } + be_quiet = 1; - /* check if the bitwidths are the one we are looking for */ - if (intTyOp0->getBitWidth() != bitw || - intTyOp1->getBitWidth() != bitw) { + } - continue; + if (enableFPSplit) { - } + count = splitFPCompares(M); - icomps.push_back(selectcmpInst); + /* + if (!be_quiet) { - } + errs() << "Split-floatingpoint-compare-pass: " << count + << " FP comparisons split\n"; } - } - - } + */ + simplifyFPCompares(M); } - if (!icomps.size()) { return 0; } - - for (auto &IcmpInst : icomps) { - - BasicBlock *bb = IcmpInst->getParent(); - - auto op0 = IcmpInst->getOperand(0); - auto op1 = IcmpInst->getOperand(1); - - auto cmp_inst = dyn_cast<CmpInst>(IcmpInst); - if (!cmp_inst) { continue; } - auto pred = cmp_inst->getPredicate(); - - BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); - - /* create the comparison of the top halves of the original operands */ - Instruction *s_op0, *op0_high, *s_op1, *op1_high, *icmp_high; - - s_op0 = BinaryOperator::Create(Instruction::LShr, op0, - ConstantInt::get(OldIntType, bitw / 2)); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op0); - op0_high = new TruncInst(s_op0, NewIntType); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), - op0_high); - - s_op1 = BinaryOperator::Create(Instruction::LShr, op1, - ConstantInt::get(OldIntType, bitw / 2)); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op1); - op1_high = new TruncInst(s_op1, NewIntType); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), - op1_high); - - icmp_high = CmpInst::Create(Instruction::ICmp, pred, op0_high, op1_high); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), - icmp_high); - - /* now we have to destinguish between == != and > < */ - if (pred == CmpInst::ICMP_EQ || pred == CmpInst::ICMP_NE) { - - /* transformation for == and != icmps */ - - /* create a compare for the lower half of the original operands */ - Instruction *op0_low, *op1_low, *icmp_low; - BasicBlock * cmp_low_bb = - BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); - - op0_low = new TruncInst(op0, NewIntType); - cmp_low_bb->getInstList().push_back(op0_low); - - op1_low = new TruncInst(op1, NewIntType); - cmp_low_bb->getInstList().push_back(op1_low); - - icmp_low = CmpInst::Create(Instruction::ICmp, pred, op0_low, op1_low); - cmp_low_bb->getInstList().push_back(icmp_low); - BranchInst::Create(end_bb, cmp_low_bb); - - /* dependent on the cmp of the high parts go to the end or go on with - * the comparison */ - auto term = bb->getTerminator(); - if (pred == CmpInst::ICMP_EQ) { - - BranchInst::Create(cmp_low_bb, end_bb, icmp_high, bb); - - } else { - - /* CmpInst::ICMP_NE */ - BranchInst::Create(end_bb, cmp_low_bb, icmp_high, bb); - - } - - term->eraseFromParent(); - - /* create the PHI and connect the edges accordingly */ - PHINode *PN = PHINode::Create(Int1Ty, 2, ""); - PN->addIncoming(icmp_low, cmp_low_bb); - if (pred == CmpInst::ICMP_EQ) { - - PN->addIncoming(ConstantInt::get(Int1Ty, 0), bb); + std::vector<CmpInst *> worklist; + /* iterate over all functions, bbs and instruction search for all integer + * compare instructions. Save them into the worklist for later. */ + for (auto &F : M) { - } else { + if (!isInInstrumentList(&F)) continue; - /* CmpInst::ICMP_NE */ - PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); + for (auto &BB : F) { - } + for (auto &IN : BB) { - /* replace the old icmp with the new PHI */ - BasicBlock::iterator ii(IcmpInst); - ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); + if (auto CI = dyn_cast<CmpInst>(&IN)) { - } else { + auto op0 = CI->getOperand(0); + auto op1 = CI->getOperand(1); + if (!op0 || !op1) { return false; } + auto iTy1 = dyn_cast<IntegerType>(op0->getType()); + if (iTy1 && isa<IntegerType>(op1->getType())) { - /* CmpInst::ICMP_UGT and CmpInst::ICMP_ULT */ - /* transformations for < and > */ + unsigned bitw = iTy1->getBitWidth(); + if (isSupportedBitWidth(bitw)) { worklist.push_back(CI); } - /* create a basic block which checks for the inverse predicate. - * if this is true we can go to the end if not we have to go to the - * bb which checks the lower half of the operands */ - Instruction *icmp_inv_cmp, *op0_low, *op1_low, *icmp_low; - BasicBlock * inv_cmp_bb = - BasicBlock::Create(C, "inv_cmp", end_bb->getParent(), end_bb); - if (pred == CmpInst::ICMP_UGT) { - - icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, - op0_high, op1_high); - - } else { + } - icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, - op0_high, op1_high); + } } - inv_cmp_bb->getInstList().push_back(icmp_inv_cmp); - - auto term = bb->getTerminator(); - term->eraseFromParent(); - BranchInst::Create(end_bb, inv_cmp_bb, icmp_high, bb); - - /* create a bb which handles the cmp of the lower halves */ - BasicBlock *cmp_low_bb = - BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); - op0_low = new TruncInst(op0, NewIntType); - cmp_low_bb->getInstList().push_back(op0_low); - op1_low = new TruncInst(op1, NewIntType); - cmp_low_bb->getInstList().push_back(op1_low); - - icmp_low = CmpInst::Create(Instruction::ICmp, pred, op0_low, op1_low); - cmp_low_bb->getInstList().push_back(icmp_low); - BranchInst::Create(end_bb, cmp_low_bb); - - BranchInst::Create(end_bb, cmp_low_bb, icmp_inv_cmp, inv_cmp_bb); - - PHINode *PN = PHINode::Create(Int1Ty, 3); - PN->addIncoming(icmp_low, cmp_low_bb); - PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); - PN->addIncoming(ConstantInt::get(Int1Ty, 0), inv_cmp_bb); - - BasicBlock::iterator ii(IcmpInst); - ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); - } - ++count; - } - return count; - -} - -bool SplitComparesTransform::runOnModule(Module &M) { - - int bitw = 64; - size_t count = 0; - - char *bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW"); - if (!bitw_env) bitw_env = getenv("LAF_SPLIT_COMPARES_BITW"); - if (bitw_env) { bitw = atoi(bitw_env); } - - enableFPSplit = getenv("AFL_LLVM_LAF_SPLIT_FLOATS") != NULL; - - if ((isatty(2) && getenv("AFL_QUIET") == NULL) || - getenv("AFL_DEBUG") != NULL) { + // now that we have a list of all integer comparisons we can start replacing + // them with the splitted alternatives. + for (auto CI : worklist) { - printf( - "Split-compare-pass by laf.intel@gmail.com, extended by " - "heiko@hexco.de\n"); - - } else { - - be_quiet = 1; + simplifyAndSplit(CI, M); } - if (enableFPSplit) { - - count = splitFPCompares(M); - - /* - if (!be_quiet) { - - errs() << "Split-floatingpoint-compare-pass: " << count - << " FP comparisons split\n"; + bool brokenDebug = false; + if (verifyModule( M, &errs() +#if LLVM_VERSION_MAJOR > 3 || (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 9) + ,&brokenDebug // 9th May 2016 +#endif + )) { - } - - */ - simplifyFPCompares(M); + reportError( + "Module Verifier failed! Consider reporting a bug with the AFL++ " + "project.", + nullptr, M); } - simplifyCompares(M); - - simplifyIntSignedness(M); + if (brokenDebug) { - switch (bitw) { - - case 64: - count += splitIntCompares(M, bitw); - if (debug) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " split\n"; - bitw >>= 1; -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) - [[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */ -#endif - case 32: - count += splitIntCompares(M, bitw); - if (debug) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " split\n"; - bitw >>= 1; -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) - [[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */ -#endif - case 16: - count += splitIntCompares(M, bitw); - if (debug) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " split\n"; - // bitw >>= 1; - break; - - default: - // if (!be_quiet) errs() << "NOT Running split-compare-pass \n"; - return false; - break; + reportError("Module Verifier reported broken Debug Infos - Stripping!", + nullptr, M); + StripDebugInfo(M); } - verifyModule(M); return true; } @@ -1373,3 +1441,8 @@ static RegisterStandardPasses RegisterSplitComparesTransPassLTO( registerSplitComparesPass); #endif +static RegisterPass<SplitComparesTransform> X("splitcompares", + "AFL++ split compares", + true /* Only looks at CFG */, + true /* Analysis Pass */); + diff --git a/src/afl-analyze.c b/src/afl-analyze.c index 606254d9..dbf2920f 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -167,7 +167,7 @@ static inline u8 anything_set(void) { static void at_exit_handler(void) { - unlink(fsrv.out_file); /* Ignore errors */ + unlink(fsrv.out_file); /* Ignore errors */ } @@ -643,12 +643,14 @@ static void set_up_environment(char **argv) { } - fsrv.out_file = alloc_printf("%s/.afl-analyze-temp-%u", use_dir, (u32)getpid()); + fsrv.out_file = + alloc_printf("%s/.afl-analyze-temp-%u", use_dir, (u32)getpid()); } unlink(fsrv.out_file); - fsrv.out_fd = open(fsrv.out_file, O_RDWR | O_CREAT | O_EXCL, DEFAULT_PERMISSION); + fsrv.out_fd = + open(fsrv.out_file, O_RDWR | O_CREAT | O_EXCL, DEFAULT_PERMISSION); if (fsrv.out_fd < 0) { PFATAL("Unable to create '%s'", fsrv.out_file); } @@ -1118,7 +1120,6 @@ int main(int argc, char **argv_orig, char **envp) { if (fsrv.target_path) { ck_free(fsrv.target_path); } if (in_data) { ck_free(in_data); } - exit(0); } diff --git a/src/afl-common.c b/src/afl-common.c index c61ce3d8..9ca2b3e8 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -751,6 +751,8 @@ void read_bitmap(u8 *fname, u8 *map, size_t len) { } +/* Get unix time in milliseconds */ + u64 get_cur_time(void) { struct timeval tv; diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index c3ce2edd..11adebf4 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -73,7 +73,7 @@ static int select_algorithm(afl_state_t *afl, u32 max_algorithm) { /* Helper to choose random block len for block operations in fuzz_one(). Doesn't return zero, provided that max_len is > 0. */ -static u32 choose_block_len(afl_state_t *afl, u32 limit) { +static inline u32 choose_block_len(afl_state_t *afl, u32 limit) { u32 min_value, max_value; u32 rlim = MIN(afl->queue_cycle, (u32)3); diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index b41ffa88..268f726c 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -252,7 +252,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 start_time = get_cur_time(); #endif - u32 screen_update = 1000000 / afl->queue_cur->exec_us; + u32 screen_update; u64 orig_hit_cnt, new_hit_cnt, exec_cksum; orig_hit_cnt = afl->queued_paths + afl->unique_crashes; @@ -261,6 +261,24 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, afl->stage_max = (len << 1); afl->stage_cur = 0; + if (likely(afl->queue_cur->exec_us)) { + + if (likely((100000 / 2) >= afl->queue_cur->exec_us)) { + + screen_update = 100000 / afl->queue_cur->exec_us; + + } else { + + screen_update = 1; + + } + + } else { + + screen_update = 100000; + + } + // in colorization we do not classify counts, hence we have to calculate // the original checksum. if (unlikely(get_exec_checksum(afl, buf, len, &exec_cksum))) { @@ -905,17 +923,16 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // test for arithmetic, eg. "if ((user_val - 0x1111) == 0x1234) ..." s64 diff = pattern - b_val; s64 o_diff = o_pattern - o_b_val; - /* - fprintf(stderr, "DIFF1 idx=%03u shape=%02u %llx-%llx=%lx\n", idx, - h->shape + 1, o_pattern, o_b_val, o_diff); - fprintf(stderr, "DIFF1 %016llx %llx-%llx=%lx\n", repl, pattern, - b_val, diff);*/ + /* fprintf(stderr, "DIFF1 idx=%03u shape=%02u %llx-%llx=%lx\n", idx, + h->shape + 1, o_pattern, o_b_val, o_diff); + fprintf(stderr, "DIFF1 %016llx %llx-%llx=%lx\n", repl, pattern, + b_val, diff); */ if (diff == o_diff && diff) { // this could be an arithmetic transformation u64 new_repl = (u64)((s64)repl - diff); - // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); + // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); if (unlikely(cmp_extend_encoding( afl, h, pattern, new_repl, o_pattern, repl, IS_TRANSFORM, idx, @@ -935,15 +952,17 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, diff = pattern ^ b_val; s64 o_diff = o_pattern ^ o_b_val; - /* fprintf(stderr, "DIFF2 idx=%03u shape=%02u %llx-%llx=%lx\n", - idx, h->shape + 1, o_pattern, o_b_val, o_diff); fprintf(stderr, - "DIFF2 %016llx %llx-%llx=%lx\n", repl, pattern, b_val, diff);*/ + /* fprintf(stderr, "DIFF2 idx=%03u shape=%02u %llx-%llx=%lx\n", + idx, h->shape + 1, o_pattern, o_b_val, o_diff); + fprintf(stderr, + "DIFF2 %016llx %llx-%llx=%lx\n", repl, pattern, b_val, diff); + */ if (diff == o_diff && diff) { // this could be a XOR transformation u64 new_repl = (u64)((s64)repl ^ diff); - // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); + // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); if (unlikely(cmp_extend_encoding( afl, h, pattern, new_repl, o_pattern, repl, IS_TRANSFORM, idx, @@ -982,15 +1001,17 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - /* fprintf(stderr, "DIFF3 idx=%03u shape=%02u %llx-%llx=%lx\n", - idx, h->shape + 1, o_pattern, o_b_val, o_diff); fprintf(stderr, - "DIFF3 %016llx %llx-%llx=%lx\n", repl, pattern, b_val, diff);*/ + /* fprintf(stderr, "DIFF3 idx=%03u shape=%02u %llx-%llx=%lx\n", + idx, h->shape + 1, o_pattern, o_b_val, o_diff); + fprintf(stderr, + "DIFF3 %016llx %llx-%llx=%lx\n", repl, pattern, b_val, diff); + */ if (o_diff && diff) { // this could be a lower to upper u64 new_repl = (repl & (0x5f5f5f5f5f5f5f5f & mask)); - // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); + // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); if (unlikely(cmp_extend_encoding( afl, h, pattern, new_repl, o_pattern, repl, IS_TRANSFORM, idx, @@ -1029,15 +1050,17 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - /* fprintf(stderr, "DIFF4 idx=%03u shape=%02u %llx-%llx=%lx\n", - idx, h->shape + 1, o_pattern, o_b_val, o_diff); fprintf(stderr, - "DIFF4 %016llx %llx-%llx=%lx\n", repl, pattern, b_val, diff);*/ + /* fprintf(stderr, "DIFF4 idx=%03u shape=%02u %llx-%llx=%lx\n", + idx, h->shape + 1, o_pattern, o_b_val, o_diff); + fprintf(stderr, + "DIFF4 %016llx %llx-%llx=%lx\n", repl, pattern, b_val, diff); + */ if (o_diff && diff) { // this could be a lower to upper u64 new_repl = (repl | (0x2020202020202020 & mask)); - // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); + // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); if (unlikely(cmp_extend_encoding( afl, h, pattern, new_repl, o_pattern, repl, IS_TRANSFORM, idx, @@ -1383,7 +1406,8 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - //#endif /* CMPLOG_SOLVE_ARITHMETIC + //#endif /* + // CMPLOG_SOLVE_ARITHMETIC return 0; @@ -2152,7 +2176,8 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, memcpy(buf + idx, tmp, i + 1); if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - // fprintf(stderr, "RTN ATTEMPT tohex %u result %u\n", tohex, *status); + // fprintf(stderr, "RTN ATTEMPT tohex %u result %u\n", tohex, + // *status); } @@ -2235,7 +2260,8 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, for (j = 0; j <= i; j++) buf[idx + j] = repl[j] - arith_val[j]; if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - // fprintf(stderr, "RTN ATTEMPT arith %u result %u\n", arith, *status); + // fprintf(stderr, "RTN ATTEMPT arith %u result %u\n", arith, + // *status); } @@ -2328,16 +2354,17 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, /* struct cmp_header *hh = &afl->orig_cmp_map->headers[key]; - fprintf(stderr, "RTN N hits=%u id=%u shape=%u attr=%u v0=", h->hits, h->id, - h->shape, h->attribute); for (j = 0; j < 8; j++) fprintf(stderr, "%02x", - o->v0[j]); fprintf(stderr, " v1="); for (j = 0; j < 8; j++) fprintf(stderr, - "%02x", o->v1[j]); fprintf(stderr, "\nRTN O hits=%u id=%u shape=%u attr=%u - o0=", hh->hits, hh->id, hh->shape, hh->attribute); for (j = 0; j < 8; j++) - fprintf(stderr, "%02x", orig_o->v0[j]); - fprintf(stderr, " o1="); - for (j = 0; j < 8; j++) - fprintf(stderr, "%02x", orig_o->v1[j]); - fprintf(stderr, "\n"); + fprintf(stderr, "RTN N hits=%u id=%u shape=%u attr=%u v0=", h->hits, + h->id, h->shape, h->attribute); + for (j = 0; j < 8; j++) fprintf(stderr, "%02x", o->v0[j]); + fprintf(stderr, " v1="); + for (j = 0; j < 8; j++) fprintf(stderr, "%02x", o->v1[j]); + fprintf(stderr, "\nRTN O hits=%u id=%u shape=%u attr=%u o0=", + hh->hits, hh->id, hh->shape, hh->attribute); + for (j = 0; j < 8; j++) fprintf(stderr, "%02x", orig_o->v0[j]); + fprintf(stderr, " o1="); + for (j = 0; j < 8; j++) fprintf(stderr, "%02x", orig_o->v1[j]); + fprintf(stderr, "\n"); */ t = taint; diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 493735ff..49856a9f 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -314,7 +314,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, ++q->cal_failed; afl->stage_name = "calibration"; - afl->stage_max = afl->fast_cal ? 3 : CAL_CYCLES; + afl->stage_max = afl->afl_env.afl_cal_fast ? 3 : CAL_CYCLES; /* Make sure the forkserver is up before we do anything, and let's not count its spin-up time toward binary calibration. */ @@ -355,6 +355,12 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { + if (unlikely(afl->debug)) { + + DEBUGF("calibration stage %d/%d\n", afl->stage_cur + 1, afl->stage_max); + + } + u64 cksum; write_to_testcase(afl, use_mem, q->len); @@ -402,8 +408,24 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, } + if (unlikely(!var_detected)) { + + // note: from_queue seems to only be set during initialization + if (afl->afl_env.afl_no_ui || from_queue) { + + WARNF("instability detected during calibration\n"); + + } else if (afl->debug) { + + DEBUGF("instability detected during calibration\n"); + + } + + } + var_detected = 1; - afl->stage_max = afl->fast_cal ? CAL_CYCLES : CAL_CYCLES_LONG; + afl->stage_max = + afl->afl_env.afl_cal_fast ? CAL_CYCLES : CAL_CYCLES_LONG; } else { diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 0658070e..b832c11e 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -96,8 +96,6 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->splicing_with = -1; /* Splicing with which test case? */ afl->cpu_to_bind = -1; afl->havoc_stack_pow2 = HAVOC_STACK_POW2; - afl->cal_cycles = CAL_CYCLES; - afl->cal_cycles_long = CAL_CYCLES_LONG; afl->hang_tmout = EXEC_TIMEOUT; afl->exit_on_time = 0; afl->stats_update_freq = 1; @@ -341,6 +339,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_cal_fast = get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_FAST_CAL", + + afl_environment_variable_len)) { + + afl->afl_env.afl_cal_fast = + get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_STATSD", afl_environment_variable_len)) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 9a3780fb..e9a67ac5 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1276,7 +1276,6 @@ int main(int argc, char **argv_orig, char **envp) { if (get_afl_env("AFL_NO_CPU_RED")) { afl->no_cpu_meter_red = 1; } if (get_afl_env("AFL_NO_ARITH")) { afl->no_arith = 1; } if (get_afl_env("AFL_SHUFFLE_QUEUE")) { afl->shuffle_queue = 1; } - if (get_afl_env("AFL_FAST_CAL")) { afl->fast_cal = 1; } if (get_afl_env("AFL_EXPAND_HAVOC_NOW")) { afl->expand_havoc = 1; } if (afl->afl_env.afl_autoresume) { @@ -1489,14 +1488,6 @@ int main(int argc, char **argv_orig, char **envp) { check_if_tty(afl); if (afl->afl_env.afl_force_ui) { afl->not_on_tty = 0; } - if (afl->afl_env.afl_cal_fast) { - - /* Use less calibration cycles, for slow applications */ - afl->cal_cycles = 3; - afl->cal_cycles_long = 5; - - } - if (afl->afl_env.afl_custom_mutator_only) { /* This ensures we don't proceed to havoc/splice */ diff --git a/test/test-basic.sh b/test/test-basic.sh index b4bb9df2..c39faa74 100755 --- a/test/test-basic.sh +++ b/test/test-basic.sh @@ -56,11 +56,6 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc CODE=1 } # now we want to be sure that afl-fuzz is working - # make sure core_pattern is set to core on linux - (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { - $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" - true - }) || # make sure crash reporter is disabled on Mac OS X (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" @@ -176,11 +171,6 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc CODE=1 } # now we want to be sure that afl-fuzz is working - # make sure core_pattern is set to core on linux - (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { - $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" - true - }) || # make sure crash reporter is disabled on Mac OS X (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" diff --git a/test/test-gcc-plugin.sh b/test/test-gcc-plugin.sh index 4c36b6c9..50d83e40 100755 --- a/test/test-gcc-plugin.sh +++ b/test/test-gcc-plugin.sh @@ -52,10 +52,6 @@ test -e ../afl-gcc-fast -a -e ../afl-compiler-rt.o && { CODE=1 } # now we want to be sure that afl-fuzz is working - (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { - $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" - true - }) || # make sure crash reporter is disabled on Mac OS X (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" diff --git a/test/test-int_cases.c b/test/test-int_cases.c new file mode 100644 index 00000000..c76206c5 --- /dev/null +++ b/test/test-int_cases.c @@ -0,0 +1,424 @@ +/* test cases for integer comparison transformations + * compile with -DINT_TYPE="signed char" + * or -DINT_TYPE="short" + * or -DINT_TYPE="int" + * or -DINT_TYPE="long" + * or -DINT_TYPE="long long" + */ + +#include <assert.h> + +int main() { + + volatile INT_TYPE a, b; + /* different values */ + a = -21; + b = -2; /* signs equal */ + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + a = 1; + b = 8; /* signs equal */ + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + if ((unsigned)(INT_TYPE)(~0) > 255) { /* short or bigger */ + volatile short a, b; + a = 2; + b = 256+1; /* signs equal */ + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + a = -1 - 256; + b = -8; /* signs equal */ + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + if ((unsigned)(INT_TYPE)(~0) > 65535) { /* int or bigger */ + volatile int a, b; + a = 2; + b = 65536+1; /* signs equal */ + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + a = -1 - 65536; + b = -8; /* signs equal */ + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + if ((unsigned)(INT_TYPE)(~0) > 4294967295) { /* long or bigger */ + volatile long a, b; + a = 2; + b = 4294967296+1; /* signs equal */ + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + a = -1 - 4294967296; + b = -8; /* signs equal */ + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + } + } + } + + a = -1; + b = 1; /* signs differ */ + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + a = -1; + b = 0; /* signs differ */ + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + a = -2; + b = 8; /* signs differ */ + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + a = -1; + b = -2; /* signs equal */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + a = 8; + b = 1; /* signs equal */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + if ((unsigned)(INT_TYPE)(~0) > 255) { + volatile short a, b; + a = 1 + 256; + b = 3; /* signs equal */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + a = -1; + b = -256; /* signs equal */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + if ((unsigned)(INT_TYPE)(~0) > 65535) { + volatile int a, b; + a = 1 + 65536; + b = 3; /* signs equal */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + a = -1; + b = -65536; /* signs equal */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + if ((unsigned)(INT_TYPE)(~0) > 4294967295) { + volatile long a, b; + a = 1 + 4294967296; + b = 3; /* signs equal */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + a = -1; + b = -4294967296; /* signs equal */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + } + } + } + + a = 1; + b = -1; /* signs differ */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + a = 0; + b = -1; /* signs differ */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + a = 8; + b = -2; /* signs differ */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + a = 1; + b = -2; /* signs differ */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + if ((unsigned)(INT_TYPE)(~0) > 255) { + volatile short a, b; + a = 1 + 256; + b = -2; /* signs differ */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + a = -1; + b = -2 - 256; /* signs differ */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + if ((unsigned)(INT_TYPE)(~0) > 65535) { + volatile int a, b; + a = 1 + 65536; + b = -2; /* signs differ */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + a = -1; + b = -2 - 65536; /* signs differ */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + if ((unsigned)(INT_TYPE)(~0) > 4294967295) { + volatile long a, b; + a = 1 + 4294967296; + b = -2; /* signs differ */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + a = -1; + b = -2 - 4294967296; /* signs differ */ + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + } + } + } + + /* equal values */ + a = 0; + b = 0; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + a = -0; + b = 0; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + a = 1; + b = 1; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + a = 5; + b = 5; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + a = -1; + b = -1; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + a = -5; + b = -5; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + if ((unsigned)(INT_TYPE)(~0) > 255) { + volatile short a, b; + a = 1 + 256; + b = 1 + 256; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + a = -2 - 256; + b = -2 - 256; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + if ((unsigned)(INT_TYPE)(~0) > 65535) { + volatile int a, b; + a = 1 + 65536; + b = 1 + 65536; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + a = -2 - 65536; + b = -2 - 65536; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + if ((unsigned)(INT_TYPE)(~0) > 4294967295) { + volatile long a, b; + a = 1 + 4294967296; + b = 1 + 4294967296; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + a = -2 - 4294967296; + b = -2 - 4294967296; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + } + } + } +} + diff --git a/test/test-llvm.sh b/test/test-llvm.sh index 7cdc83cb..8090e176 100755 --- a/test/test-llvm.sh +++ b/test/test-llvm.sh @@ -122,10 +122,6 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { CODE=1 } # now we want to be sure that afl-fuzz is working - (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { - $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" - true - }) || # make sure crash reporter is disabled on Mac OS X (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" @@ -190,6 +186,29 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { } rm -f test-instr.plain + $ECHO "$GREY[*] llvm_mode laf-intel/compcov testing splitting integer types (this might take some time)" + for testcase in ./test-int_cases.c ./test-uint_cases.c; do + for I in char short int long "long long"; do + for BITS in 8 16 32 64; do + bin="$testcase-split-$I-$BITS.compcov" + AFL_LLVM_INSTRUMENT=AFL AFL_DEBUG=1 AFL_LLVM_LAF_SPLIT_COMPARES_BITW=$BITS AFL_LLVM_LAF_SPLIT_COMPARES=1 ../afl-clang-fast -DINT_TYPE="$I" -o "$bin" "$testcase" > test.out 2>&1; + if ! test -e "$bin"; then + cat test.out + $ECHO "$RED[!] llvm_mode laf-intel/compcov integer splitting failed! ($testcase with type $I split to $BITS)!"; + CODE=1 + break + fi + if ! "$bin"; then + $ECHO "$RED[!] llvm_mode laf-intel/compcov integer splitting resulted in miscompilation (type $I split to $BITS)!"; + CODE=1 + break + fi + rm -f "$bin" test.out || true + done + done + done + rm -f test-int-split*.compcov test.out + AFL_LLVM_INSTRUMENT=AFL AFL_DEBUG=1 AFL_LLVM_LAF_SPLIT_SWITCHES=1 AFL_LLVM_LAF_TRANSFORM_COMPARES=1 AFL_LLVM_LAF_SPLIT_COMPARES=1 ../afl-clang-fast -o test-compcov.compcov test-compcov.c > test.out 2>&1 test -e test-compcov.compcov && test_compcov_binary_functionality ./test-compcov.compcov && { grep --binary-files=text -Eq " [ 123][0-9][0-9] location| [3-9][0-9] location" test.out && { diff --git a/test/test-uint_cases.c b/test/test-uint_cases.c new file mode 100644 index 00000000..a277e28a --- /dev/null +++ b/test/test-uint_cases.c @@ -0,0 +1,217 @@ +/* + * compile with -DINT_TYPE="char" + * or -DINT_TYPE="short" + * or -DINT_TYPE="int" + * or -DINT_TYPE="long" + * or -DINT_TYPE="long long" + */ + +#include <assert.h> + +int main() { + + volatile unsigned INT_TYPE a, b; + + a = 1; + b = 8; + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + if ((INT_TYPE)(~0) > 255) { + volatile unsigned short a, b; + a = 256+2; + b = 256+21; + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + a = 21; + b = 256+1; + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + if ((INT_TYPE)(~0) > 65535) { + volatile unsigned int a, b; + a = 65536+2; + b = 65536+21; + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + a = 21; + b = 65536+1; + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + } + + if ((INT_TYPE)(~0) > 4294967295) { + volatile unsigned long a, b; + a = 4294967296+2; + b = 4294967296+21; + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + + a = 21; + b = 4294967296+1; + assert((a < b)); + assert((a <= b)); + assert(!(a > b)); + assert(!(a >= b)); + assert((a != b)); + assert(!(a == b)); + } + } + + a = 8; + b = 1; + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + if ((INT_TYPE)(~0) > 255) { + volatile unsigned short a, b; + a = 256+2; + b = 256+1; + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + a = 256+2; + b = 6; + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + if ((INT_TYPE)(~0) > 65535) { + volatile unsigned int a, b; + a = 65536+2; + b = 65536+1; + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + a = 65536+2; + b = 6; + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + if ((INT_TYPE)(~0) > 4294967295) { + volatile unsigned long a, b; + a = 4294967296+2; + b = 4294967296+1; + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + a = 4294967296+2; + b = 6; + assert((a > b)); + assert((a >= b)); + assert(!(a < b)); + assert(!(a <= b)); + assert((a != b)); + assert(!(a == b)); + + } + } + } + + + a = 0; + b = 0; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + a = 1; + b = 1; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + if ((INT_TYPE)(~0) > 255) { + volatile unsigned short a, b; + a = 256+5; + b = 256+5; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + if ((INT_TYPE)(~0) > 65535) { + volatile unsigned int a, b; + a = 65536+5; + b = 65536+5; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + + if ((INT_TYPE)(~0) > 4294967295) { + volatile unsigned long a, b; + a = 4294967296+5; + b = 4294967296+5; + assert(!(a < b)); + assert((a <= b)); + assert(!(a > b)); + assert((a >= b)); + assert(!(a != b)); + assert((a == b)); + } + } + + } + +} + diff --git a/unicorn_mode/samples/speedtest/rust/src/main.rs b/unicorn_mode/samples/speedtest/rust/src/main.rs index 1e35ff0b..9ea1b873 100644 --- a/unicorn_mode/samples/speedtest/rust/src/main.rs +++ b/unicorn_mode/samples/speedtest/rust/src/main.rs @@ -48,7 +48,7 @@ fn parse_locs(loc_name: &str) -> Result<Vec<u64>, io::Error> { let contents = &read_file(&format!("../target.offsets.{}", loc_name))?; //println!("Read: {:?}", contents); Ok(str_from_u8_unchecked(&contents) - .split("\n") + .split('\n') .map(|x| { //println!("Trying to convert {}", &x[2..]); let result = u64::from_str_radix(&x[2..], 16); @@ -90,7 +90,8 @@ fn fuzz(input_file: &str) -> Result<(), uc_error> { let mut unicorn = Unicorn::new(Arch::X86, Mode::MODE_64, 0)?; let mut uc: UnicornHandle<'_, _> = unicorn.borrow(); - let binary = read_file(BINARY).expect(&format!("Could not read modem image: {}", BINARY)); + let binary = + read_file(BINARY).unwrap_or_else(|_| panic!("Could not read modem image: {}", BINARY)); let _aligned_binary_size = align(binary.len() as u64); // Apply constraints to the mutated input if binary.len() as u64 > CODE_SIZE_MAX { @@ -151,7 +152,7 @@ fn fuzz(input_file: &str) -> Result<(), uc_error> { already_allocated_malloc.set(true); }; - let already_allocated_free = already_allocated.clone(); + let already_allocated_free = already_allocated; // No real free, just set the "used"-flag to false. let hook_free = move |mut uc: UnicornHandle<'_, _>, addr, size| { if already_allocated_free.get() { @@ -190,7 +191,7 @@ fn fuzz(input_file: &str) -> Result<(), uc_error> { } for addr in parse_locs("magicfn").unwrap() { - uc.add_code_hook(addr, addr, Box::new(hook_magicfn.clone()))?; + uc.add_code_hook(addr, addr, Box::new(hook_magicfn))?; } let place_input_callback = @@ -225,7 +226,7 @@ fn fuzz(input_file: &str) -> Result<(), uc_error> { match ret { Ok(_) => {} - Err(e) => panic!(format!("found non-ok unicorn exit: {:?}", e)), + Err(e) => panic!("found non-ok unicorn exit: {:?}", e), } Ok(()) diff --git a/utils/aflpp_driver/aflpp_driver.c b/utils/aflpp_driver/aflpp_driver.c index c094c425..ff42f3b9 100644 --- a/utils/aflpp_driver/aflpp_driver.c +++ b/utils/aflpp_driver/aflpp_driver.c @@ -204,21 +204,23 @@ static int ExecuteFilesOnyByOne(int argc, char **argv) { int main(int argc, char **argv) { - printf( - "============================== INFO ================================\n" - "This binary is built for afl++.\n" - "To use with afl-cmin or afl-cmin.bash pass '-' as single command line " - "option\n" - "To run the target function on individual input(s) execute this:\n" - " %s INPUT_FILE1 [INPUT_FILE2 ... ]\n" - "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: " - "INT_MAX)\n" - "For stdin input processing, pass '-' as single command line option.\n" - "For file input processing, pass '@@' as single command line option.\n" - "===================================================================\n", - argv[0], argv[0]); + if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) + printf( + "============================== INFO ================================\n" + "This binary is built for afl++.\n" + "To use with afl-cmin or afl-cmin.bash pass '-' as single command line " + "option\n" + "To run the target function on individual input(s) execute this:\n" + " %s INPUT_FILE1 [INPUT_FILE2 ... ]\n" + "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: " + "INT_MAX)\n" + "For stdin input processing, pass '-' as single command line option.\n" + "For file input processing, pass '@@' as single command line option.\n" + "===================================================================\n", + argv[0], argv[0]); if (getenv("AFL_GDB")) { |