about summary refs log tree commit diff
diff options
context:
space:
mode:
authorvan Hauser <vh@thc.org>2021-05-20 11:14:46 +0200
committerGitHub <noreply@github.com>2021-05-20 11:14:46 +0200
commitda2d4d8258d725f79c2daa22bf3b1a59c593e472 (patch)
tree20f32501c01a7d504e75c6dc933c8168ed183759
parent8b7a7b29c60f11cdf6226b3e418e87a5c3f5caac (diff)
downloadafl++-da2d4d8258d725f79c2daa22bf3b1a59c593e472.tar.gz
Push to stable (#927)
* 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

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>
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--README.md7
-rwxr-xr-xafl-cmin2
-rw-r--r--docs/Changelog.md5
-rw-r--r--docs/INSTALL.md35
-rw-r--r--docs/env_variables.md7
-rw-r--r--frida_mode/GNUmakefile12
-rw-r--r--frida_mode/Makefile4
-rw-r--r--frida_mode/README.md82
-rw-r--r--frida_mode/include/asan.h13
-rw-r--r--frida_mode/include/ctx.h11
-rw-r--r--frida_mode/include/frida_cmplog.h2
-rw-r--r--frida_mode/src/asan/asan.c24
-rw-r--r--frida_mode/src/asan/asan_arm.c28
-rw-r--r--frida_mode/src/asan/asan_arm64.c28
-rw-r--r--frida_mode/src/asan/asan_x64.c93
-rw-r--r--frida_mode/src/asan/asan_x86.c93
-rw-r--r--frida_mode/src/cmplog/cmplog.c6
-rw-r--r--frida_mode/src/cmplog/cmplog_x64.c174
-rw-r--r--frida_mode/src/cmplog/cmplog_x86.c266
-rw-r--r--frida_mode/src/ctx/ctx_x64.c114
-rw-r--r--frida_mode/src/ctx/ctx_x86.c81
-rw-r--r--frida_mode/src/instrument/instrument.c15
-rw-r--r--frida_mode/src/instrument/instrument_arm32.c3
-rw-r--r--frida_mode/src/instrument/instrument_x86.c66
-rw-r--r--frida_mode/src/lib/lib.c15
-rw-r--r--frida_mode/src/main.c37
-rw-r--r--frida_mode/src/persistent/persistent_x86.c233
-rw-r--r--frida_mode/src/stalker.c2
-rw-r--r--frida_mode/test/cmplog/GNUmakefile43
-rw-r--r--frida_mode/test/cmplog/Makefile12
-rw-r--r--frida_mode/test/cmplog/cmplog.c100
-rw-r--r--frida_mode/test/entry_point/GNUmakefile23
-rw-r--r--frida_mode/test/entry_point/Makefile6
-rw-r--r--frida_mode/test/entry_point/testinstr.c2
-rw-r--r--frida_mode/test/exe/GNUmakefile9
-rw-r--r--frida_mode/test/exe/Makefile6
-rw-r--r--frida_mode/test/fasan/GNUmakefile159
-rw-r--r--frida_mode/test/fasan/Makefile22
-rw-r--r--frida_mode/test/fasan/test.c90
-rw-r--r--frida_mode/test/png/GNUmakefile9
-rw-r--r--frida_mode/test/png/Makefile6
-rw-r--r--frida_mode/test/png/persistent/GNUmakefile23
-rw-r--r--frida_mode/test/png/persistent/Makefile6
-rwxr-xr-xfrida_mode/test/png/persistent/get_symbol_addr.py2
-rw-r--r--frida_mode/test/png/persistent/hook/GNUmakefile53
-rw-r--r--frida_mode/test/png/persistent/hook/Makefile12
-rw-r--r--frida_mode/test/png/persistent/hook/aflpp_qemu_driver_hook.c97
-rw-r--r--frida_mode/test/testinstr/GNUmakefile15
-rw-r--r--frida_mode/test/testinstr/Makefile9
-rw-r--r--include/envs.h2
-rw-r--r--include/forkserver.h2
-rw-r--r--qemu_mode/libqasan/hooks.c10
-rw-r--r--qemu_mode/libqasan/libqasan.c11
-rw-r--r--src/afl-cc.c12
-rw-r--r--src/afl-forkserver.c8
-rw-r--r--src/afl-fuzz-init.c18
-rw-r--r--src/afl-fuzz-one.c6
-rw-r--r--src/afl-fuzz-stats.c5
-rw-r--r--src/afl-fuzz.c107
-rw-r--r--src/afl-showmap.c100
61 files changed, 2130 insertions, 315 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a5f3429e..35051a20 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -19,7 +19,7 @@ jobs:
       - name: update
         run: sudo apt-get update && sudo apt-get upgrade -y
       - name: install packages
-        run: sudo apt-get install -y -m -f --install-suggests build-essential git libtool libtool-bin automake bison libglib2.0-0 clang llvm-dev libc++-dev findutils libcmocka-dev python3-dev python3-setuptools
+        run: sudo apt-get install -y -m -f --install-suggests build-essential git libtool libtool-bin automake bison libglib2.0-0 clang llvm-dev libc++-dev findutils libcmocka-dev python3-dev python3-setuptools ninja-build
       - name: compiler installed
         run: gcc -v ; echo ; clang -v
       - name: install gcc plugin
diff --git a/README.md b/README.md
index c16216bf..5d5510d2 100644
--- a/README.md
+++ b/README.md
@@ -90,16 +90,17 @@ behaviours and defaults:
 
   | Feature/Instrumentation  | afl-gcc | llvm      | gcc_plugin | frida_mode | qemu_mode        |unicorn_mode |
   | -------------------------|:-------:|:---------:|:----------:|:----------:|:----------------:|:------------:|
-  | NeverZero                | x86[_64]|     x(1)  |     x      |            |         x        |       x      |
-  | Persistent Mode          |         |     x     |     x      |     x      | x86[_64]/arm[64] |       x      |
+  | 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     |            |     x      | x86[_64]/arm[64] |              |
+  | 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      |
 
   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
diff --git a/afl-cmin b/afl-cmin
index 3f3a7517..adcbb221 100755
--- a/afl-cmin
+++ b/afl-cmin
@@ -123,6 +123,8 @@ function usage() {
 "AFL_KEEP_TRACES: leave the temporary <out_dir>/.traces directory\n" \
 "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n"
 "AFL_PATH: path for the afl-showmap binary if not found anywhere else\n" \
+"AFL_PRINT_FILENAMES: If set, the filename currently processed will be " \
+      "printed to stdout\n" \
 "AFL_SKIP_BIN_CHECK: skip check for target binary\n"
    exit 1
 }
diff --git a/docs/Changelog.md b/docs/Changelog.md
index ceb02bb9..1114a834 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -9,6 +9,7 @@ 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.13a (development)
+  - Note: plot_data switched to relative time from unix time in 3.10
   - frida_mode - new mode that uses frida to fuzz binary-only targets,
     it currently supports persistent mode and cmplog.
     thanks to @WorksButNotTested!
@@ -28,9 +29,12 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
     - ensure one fuzzer sync per cycle
     - fix afl_custom_queue_new_entry original file name when syncing
       from fuzzers
+    - on a crashing seed potentially the wrong input was disabled
     - added AFL_EXIT_ON_SEED_ISSUES env that will exit if a seed in
       -i dir crashes the target or results in a timeout. By default
       afl++ ignores these and uses them for splicing instead.
+    - added AFL_EXIT_ON_TIME env that will make afl-fuzz exit fuzzing after
+      no new paths have been found for n seconds
   - afl-cc:
     - We do not support llvm versions prior 6.0 anymore
     - Fix for -pie compiled binaries with default afl-clang-fast PCGUARD
@@ -48,6 +52,7 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
     MacOS shared memory
   - updated the grammar custom mutator to the newest version
   - add -d (add dead fuzzer stats) to afl-whatsup
+  - added AFL_PRINT_FILENAMES to afl-showmap/cmin to print the current filename
 
 ### Version ++3.12c (release)
   - afl-fuzz:
diff --git a/docs/INSTALL.md b/docs/INSTALL.md
index e3c06c9d..80d452f7 100644
--- a/docs/INSTALL.md
+++ b/docs/INSTALL.md
@@ -103,6 +103,41 @@ The llvm instrumentation requires a fully-operational installation of clang. The
 comes with Xcode is missing some of the essential headers and helper tools.
 See README.llvm.md for advice on how to build the compiler from scratch.
 
+MacOS X supports SYSV shared memory used by AFL's instrumentation, but the
+default settings aren't usable with AFL++. The default settings on 10.14 seem
+to be:
+
+```bash
+$ ipcs -M
+IPC status from <running system> as of XXX
+shminfo:
+        shmmax: 4194304 (max shared memory segment size)
+        shmmin:       1 (min shared memory segment size)
+        shmmni:      32 (max number of shared memory identifiers)
+        shmseg:       8 (max shared memory segments per process)
+        shmall:    1024 (max amount of shared memory in pages)
+```
+
+To temporarily change your settings to something minimally usable with AFL++,
+run these commands as root:
+
+```bash
+sysctl kern.sysv.shmmax=8388608
+sysctl kern.sysv.shmall=4096
+```
+
+If you're running more than one instance of AFL you likely want to make `shmall`
+bigger and increase `shmseg` as well:
+
+```bash
+sysctl kern.sysv.shmmax=8388608
+sysctl kern.sysv.shmseg=48
+sysctl kern.sysv.shmall=98304
+```
+
+See http://www.spy-hill.com/help/apple/SharedMemory.html for documentation for
+these settings and how to make them permanent.
+
 ## 4. Linux or *BSD on non-x86 systems
 
 Standard build will fail on non-x86 systems, but you should be able to
diff --git a/docs/env_variables.md b/docs/env_variables.md
index 8879db72..c3efa0c0 100644
--- a/docs/env_variables.md
+++ b/docs/env_variables.md
@@ -285,8 +285,8 @@ checks or alter some of the more exotic semantics of the tool:
     convenient for some types of automated jobs.
 
   - `AFL_EXIT_ON_TIME` Causes afl-fuzz to terminate if no new paths were 
-    found within a specified period of time. May be convenient for some 
-    types of automated jobs.
+    found within a specified period of time (in seconds). May be convenient 
+    for some types of automated jobs.
 
   - `AFL_EXIT_ON_SEED_ISSUES` will restore the vanilla afl-fuzz behaviour
     which does not allow crashes or timeout seeds in the initial -i corpus.
@@ -567,6 +567,9 @@ The corpus minimization script offers very little customization:
     a modest security risk on multi-user systems with rogue users, but should
     be safe on dedicated fuzzing boxes.
 
+  - `AFL_PRINT_FILENAMES` prints each filename to stdout, as it gets processed.
+    This can help when embedding `afl-cmin` or `afl-showmap` in other scripts scripting.
+
 ## 7) Settings for afl-tmin
 
 Virtually nothing to play with. Well, in QEMU mode (`-Q`), `AFL_PATH` will be
diff --git a/frida_mode/GNUmakefile b/frida_mode/GNUmakefile
index a15f5c32..bc77a451 100644
--- a/frida_mode/GNUmakefile
+++ b/frida_mode/GNUmakefile
@@ -19,11 +19,13 @@ RT_CFLAGS:=-Wno-unused-parameter \
 		   -Wno-sign-compare \
 		   -Wno-unused-function \
 		   -Wno-unused-result \
+		   -Wno-int-to-pointer-cast \
 
 LDFLAGS+=-shared \
 		 -lpthread \
 		 -lresolv \
 		 -ldl \
+		 -z noexecstack \
 
 ifdef DEBUG
 CFLAGS+=-Werror \
@@ -38,6 +40,8 @@ FRIDA_BUILD_DIR:=$(BUILD_DIR)frida/
 FRIDA_TRACE:=$(BUILD_DIR)afl-frida-trace.so
 FRIDA_TRACE_EMBEDDED:=$(BUILD_DIR)afl-frida-trace-embedded
 
+ifndef ARCH
+
 ARCH=$(shell uname -m)
 ifeq "$(ARCH)" "aarch64"
  ARCH:=arm64
@@ -46,6 +50,7 @@ endif
 ifeq "$(ARCH)" "i686"
  ARCH:=x86
 endif
+endif
 
 ifeq "$(shell uname)" "Darwin"
  OS:=macos
@@ -83,13 +88,16 @@ FRIDA_GUM_DEVKIT_COMPRESSED_TARBALL:=$(FRIDA_DIR)build/$(GUM_DEVKIT_FILENAME)
 AFL_COMPILER_RT_SRC:=$(ROOT)instrumentation/afl-compiler-rt.o.c
 AFL_COMPILER_RT_OBJ:=$(OBJ_DIR)afl-compiler-rt.o
 
-.PHONY: all clean format $(FRIDA_GUM)
+.PHONY: all 32 clean format $(FRIDA_GUM)
 
 ############################## ALL #############################################
 
 all: $(FRIDA_TRACE)
 	make -C $(ROOT)
 
+32:
+	CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
 $(BUILD_DIR):
 	mkdir -p $(BUILD_DIR)
 
@@ -161,11 +169,11 @@ $(foreach src,$(SOURCES),$(eval $(call BUILD_SOURCE,$(src),$(OBJ_DIR)$(notdir $(
 
 $(FRIDA_TRACE): $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_HEADER) $(OBJS) $(AFL_COMPILER_RT_OBJ) GNUmakefile | $(BUILD_DIR)
 	$(CC) \
-		-o $@ \
 		$(OBJS) \
 		$(GUM_DEVIT_LIBRARY) \
 		$(AFL_COMPILER_RT_OBJ) \
 		$(LDFLAGS) \
+		-o $@ \
 
 	cp -v $(FRIDA_TRACE) $(ROOT)
 
diff --git a/frida_mode/Makefile b/frida_mode/Makefile
index b6d64bff..6cd1a64e 100644
--- a/frida_mode/Makefile
+++ b/frida_mode/Makefile
@@ -2,6 +2,10 @@ 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
 
diff --git a/frida_mode/README.md b/frida_mode/README.md
index 9f574a4c..ecce0bfd 100644
--- a/frida_mode/README.md
+++ b/frida_mode/README.md
@@ -18,19 +18,19 @@ As FRIDA mode is new, it is missing a lot of features. The design is such that i
 should be possible to add these features in a similar manner to QEMU mode and
 perhaps leverage some of its design and implementation.
 
-  | Feature/Instrumentation  | frida-mode | Notes                                   |
-  | -------------------------|:----------:|:---------------------------------------:|
-  | NeverZero                |     x      |                                         |
-  | Persistent Mode          |     x      | (x64 only)(Only on function boundaries) |  
-  | LAF-Intel / CompCov      |     -      | (CMPLOG is better 90% of the time)      |
-  | CMPLOG                   |     x      | (x64 only)                              |
-  | Selective Instrumentation|     x      |                                         |
-  | Non-Colliding Coverage   |     -      |                                         |
-  | Ngram prev_loc Coverage  |     -      |                                         |
-  | Context Coverage         |     -      |                                         |
-  | Auto Dictionary          |     -      |                                         |
-  | Snapshot LKM Support     |     -      |                                         |
-  | In-Memory Test Cases     |     x      | (x64 only)                              |
+  | Feature/Instrumentation  | frida-mode | Notes                                        |
+  | -------------------------|:----------:|:--------------------------------------------:|
+  | NeverZero                |     x      |                                              |
+  | Persistent Mode          |     x      | (x86/x64 only)(Only on function boundaries)  |  
+  | LAF-Intel / CompCov      |     -      | (CMPLOG is better 90% of the time)           |
+  | CMPLOG                   |     x      | (x86/x64 only)                               |
+  | Selective Instrumentation|     x      |                                              |
+  | Non-Colliding Coverage   |     -      | (Not possible in binary-only instrumentation |
+  | Ngram prev_loc Coverage  |     -      |                                              |
+  | Context Coverage         |     -      |                                              |
+  | Auto Dictionary          |     -      |                                              |
+  | Snapshot LKM Support     |     -      |                                              |
+  | In-Memory Test Cases     |     x      | (x86/x64 only)                               |
 
 ## Compatibility
 Currently FRIDA mode supports Linux and macOS targets on both x86/x64
@@ -43,11 +43,17 @@ system does not support cross compilation.
 
 ## Getting Started
 
-To build everything run `make`.
+To build everything run `make`. To build for x86 run `make 32`. Note that in 
+x86 bit mode, it is not necessary for afl-fuzz to be built for 32-bit. However,
+the shared library for frida_mode must be since it is injected into the target
+process.
 
 Various tests can be found in subfolders within the `test/` directory. To use
 these, first run `make` to build any dependencies. Then run `make qemu` or
-`make frida` to run on either QEMU of FRIDA mode respectively.
+`make frida` to run on either QEMU of FRIDA mode respectively. To run frida 
+tests in 32-bit mode, run `make ARCH=x86 frida`. When switching between 
+architectures it may be necessary to run `make clean` first for a given build 
+target to remove previously generated binaries for a different architecture.
 
 ## Usage
 
@@ -130,9 +136,47 @@ them and they be inherited by the next child on fork.
 * `AFL_FRIDA_INST_TRACE` - Generate some logging when running instrumented code.
 Requires `AFL_FRIDA_INST_NO_OPTIMIZE`.
 
+## FASAN - Frida Address Sanitizer Mode
+Frida mode also supports FASAN. The design of this is actually quite simple and
+very similar to that used when instrumenting applications compiled from source.
+
+### Address Sanitizer Basics
+
+When Address Sanitizer is used to instrument programs built from source, the 
+compiler first adds a dependency (`DT_NEEDED` entry) for the Address Sanitizer
+dynamic shared object (DSO). This shared object contains the main logic for Address
+Sanitizer, including setting and managing up the shadow memory. It also provides
+replacement implementations for a number of functions in standard libraries. 
+
+These replacements include things like `malloc` and `free` which allows for those
+allocations to be marked in the shadow memory, but also a number of other fuctions.
+Consider `memcpy` for example, this is instrumented to validate the paramters 
+(test the source and destination buffers against the shadow memory. This is much 
+easier than instrumenting those standard libraries since, first it would require 
+you to re-compile them and secondly it would mean that the instrumentation would
+be applied at a more expensive granular level. Lastly, load-widening (typically 
+found in highy optimized code) can also make this instrumentation more difficult.
+
+Since the DSO is loaded before all of the standard libraries (in fact it insists
+on being first), the dynamic loader will use it to resolve imports from other
+modules which depend on it.
+
+### FASAN Implementation
+
+FASAN takes a similar approach. It requires the user to add the Address Sanitizer
+DSO to the `AFL_PRELOAD` environment variable such that it is loaded into the target.
+Again, it must be first in the list. This means that it is not necessary to 
+instrument the standard libraries to detect when an application has provided an 
+incorrect argument to `memcpy` for example. This avoids issues with load-widening 
+and should also mean a huge improvement in performance.
+
+FASAN then adds instrumentation for any instrucutions which use memory operands and
+then calls into the `__asan_loadN` and `__asan_storeN` functions provided by the DSO
+to validate memory accesses against the shadow memory.
+
 ## TODO
 
-The next features to be added are x86 support, integration with FuzzBench and
-support for ASAN. The intention is to achieve feature parity with QEMU mode in
-due course. Contributions are welcome, but please get in touch to ensure that
-efforts are deconflicted.
+The next features to be added are Aarch64 and Aarch32 support as well as looking at 
+potential performance improvements. The intention is to achieve feature parity with
+QEMU mode in due course. Contributions are welcome, but please get in touch to
+ensure that efforts are deconflicted.
diff --git a/frida_mode/include/asan.h b/frida_mode/include/asan.h
new file mode 100644
index 00000000..7a8726e0
--- /dev/null
+++ b/frida_mode/include/asan.h
@@ -0,0 +1,13 @@
+#ifndef _ASAN_H
+#define _ASAN_H
+
+#include "frida-gum.h"
+
+extern gboolean asan_initialized;
+
+void asan_init(void);
+void asan_arch_init(void);
+void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator);
+
+#endif
+
diff --git a/frida_mode/include/ctx.h b/frida_mode/include/ctx.h
new file mode 100644
index 00000000..cbcc892a
--- /dev/null
+++ b/frida_mode/include/ctx.h
@@ -0,0 +1,11 @@
+#ifndef _CTX_H
+#define _CTX_H
+
+#include "frida-gum.h"
+
+#if defined(__x86_64__) || defined(__i386__)
+gsize ctx_read_reg(GumCpuContext *ctx, x86_reg reg);
+#endif
+
+#endif
+
diff --git a/frida_mode/include/frida_cmplog.h b/frida_mode/include/frida_cmplog.h
index 28864c0e..b620a472 100644
--- a/frida_mode/include/frida_cmplog.h
+++ b/frida_mode/include/frida_cmplog.h
@@ -8,7 +8,7 @@ void cmplog_init(void);
 /* Functions to be implemented by the different architectures */
 void cmplog_instrument(const cs_insn *instr, GumStalkerIterator *iterator);
 
-gboolean cmplog_is_readable(void *addr, size_t size);
+gboolean cmplog_is_readable(guint64 addr, size_t size);
 
 #endif
 
diff --git a/frida_mode/src/asan/asan.c b/frida_mode/src/asan/asan.c
new file mode 100644
index 00000000..f78f690c
--- /dev/null
+++ b/frida_mode/src/asan/asan.c
@@ -0,0 +1,24 @@
+#include "frida-gum.h"
+
+#include "debug.h"
+
+#include "asan.h"
+
+gboolean asan_initialized = FALSE;
+
+void asan_init(void) {
+
+  if (getenv("AFL_USE_FASAN") != NULL) {
+
+    OKF("Frida ASAN mode enabled");
+    asan_arch_init();
+    asan_initialized = TRUE;
+
+  } else {
+
+    OKF("Frida ASAN mode disabled");
+
+  }
+
+}
+
diff --git a/frida_mode/src/asan/asan_arm.c b/frida_mode/src/asan/asan_arm.c
new file mode 100644
index 00000000..79475ced
--- /dev/null
+++ b/frida_mode/src/asan/asan_arm.c
@@ -0,0 +1,28 @@
+#include "frida-gum.h"
+
+#include "debug.h"
+
+#include "asan.h"
+#include "util.h"
+
+#if defined(__arm__)
+void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
+
+  UNUSED_PARAMETER(instr);
+  UNUSED_PARAMETER(iterator);
+  if (asan_initialized) {
+
+    FATAL("ASAN mode not supported on this architecture");
+
+  }
+
+}
+
+void asan_arch_init(void) {
+
+  FATAL("ASAN mode not supported on this architecture");
+
+}
+
+#endif
+
diff --git a/frida_mode/src/asan/asan_arm64.c b/frida_mode/src/asan/asan_arm64.c
new file mode 100644
index 00000000..6262ee18
--- /dev/null
+++ b/frida_mode/src/asan/asan_arm64.c
@@ -0,0 +1,28 @@
+#include "frida-gum.h"
+
+#include "debug.h"
+
+#include "asan.h"
+#include "util.h"
+
+#if defined(__aarch64__)
+void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
+
+  UNUSED_PARAMETER(instr);
+  UNUSED_PARAMETER(iterator);
+  if (asan_initialized) {
+
+    FATAL("ASAN mode not supported on this architecture");
+
+  }
+
+}
+
+void asan_arch_init(void) {
+
+  FATAL("ASAN mode not supported on this architecture");
+
+}
+
+#endif
+
diff --git a/frida_mode/src/asan/asan_x64.c b/frida_mode/src/asan/asan_x64.c
new file mode 100644
index 00000000..a2eabe3c
--- /dev/null
+++ b/frida_mode/src/asan/asan_x64.c
@@ -0,0 +1,93 @@
+#include <dlfcn.h>
+#include "frida-gum.h"
+
+#include "debug.h"
+
+#include "asan.h"
+#include "ctx.h"
+#include "util.h"
+
+#if defined(__x86_64__)
+
+typedef void (*asan_loadN_t)(uint64_t address, uint8_t size);
+typedef void (*asan_storeN_t)(uint64_t address, uint8_t size);
+
+asan_loadN_t  asan_loadN = NULL;
+asan_storeN_t asan_storeN = NULL;
+
+static void asan_callout(GumCpuContext *ctx, gpointer user_data) {
+
+  UNUSED_PARAMETER(user_data);
+
+  cs_x86_op * operand = (cs_x86_op *)user_data;
+  x86_op_mem *mem = &operand->mem;
+  gsize       base = 0;
+  gsize       index = 0;
+  gsize       address;
+  uint8_t     size;
+
+  if (mem->base != X86_REG_INVALID) { base = ctx_read_reg(ctx, mem->base); }
+
+  if (mem->index != X86_REG_INVALID) { index = ctx_read_reg(ctx, mem->index); }
+
+  address = base + (mem->scale * index) + mem->disp;
+  size = operand->size;
+
+  if (operand->access == CS_AC_READ) {
+
+    asan_loadN(address, size);
+
+  } else if (operand->access == CS_AC_WRITE) {
+
+    asan_storeN(address, size);
+
+  }
+
+}
+
+void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
+
+  UNUSED_PARAMETER(iterator);
+
+  cs_x86      x86 = instr->detail->x86;
+  cs_x86_op * operand;
+  x86_op_mem *mem;
+  cs_x86_op * ctx;
+
+  if (!asan_initialized) return;
+
+  if (instr->id == X86_INS_LEA) return;
+
+  if (instr->id == X86_INS_NOP) return;
+
+  for (uint8_t i = 0; i < x86.op_count; i++) {
+
+    operand = &x86.operands[i];
+
+    if (operand->type != X86_OP_MEM) { continue; }
+
+    mem = &operand->mem;
+    if (mem->segment != X86_REG_INVALID) { continue; }
+
+    ctx = g_malloc0(sizeof(cs_x86_op));
+    memcpy(ctx, operand, sizeof(cs_x86_op));
+    gum_stalker_iterator_put_callout(iterator, asan_callout, ctx, g_free);
+
+  }
+
+}
+
+void asan_arch_init(void) {
+
+  asan_loadN = (asan_loadN_t)dlsym(RTLD_DEFAULT, "__asan_loadN");
+  asan_storeN = (asan_loadN_t)dlsym(RTLD_DEFAULT, "__asan_storeN");
+  if (asan_loadN == NULL || asan_storeN == NULL) {
+
+    FATAL("Frida ASAN failed to find '__asan_loadN' or '__asan_storeN'");
+
+  }
+
+}
+
+#endif
+
diff --git a/frida_mode/src/asan/asan_x86.c b/frida_mode/src/asan/asan_x86.c
new file mode 100644
index 00000000..8490b490
--- /dev/null
+++ b/frida_mode/src/asan/asan_x86.c
@@ -0,0 +1,93 @@
+#include <dlfcn.h>
+#include "frida-gum.h"
+
+#include "debug.h"
+
+#include "asan.h"
+#include "ctx.h"
+#include "util.h"
+
+#if defined(__i386__)
+
+typedef void (*asan_loadN_t)(gsize address, uint8_t size);
+typedef void (*asan_storeN_t)(gsize address, uint8_t size);
+
+asan_loadN_t  asan_loadN = NULL;
+asan_storeN_t asan_storeN = NULL;
+
+static void asan_callout(GumCpuContext *ctx, gpointer user_data) {
+
+  UNUSED_PARAMETER(user_data);
+
+  cs_x86_op * operand = (cs_x86_op *)user_data;
+  x86_op_mem *mem = &operand->mem;
+  gsize       base = 0;
+  gsize       index = 0;
+  gsize       address;
+  uint8_t     size;
+
+  if (mem->base != X86_REG_INVALID) { base = ctx_read_reg(ctx, mem->base); }
+
+  if (mem->index != X86_REG_INVALID) { index = ctx_read_reg(ctx, mem->index); }
+
+  address = base + (mem->scale * index) + mem->disp;
+  size = operand->size;
+
+  if (operand->access == CS_AC_READ) {
+
+    asan_loadN(address, size);
+
+  } else if (operand->access == CS_AC_WRITE) {
+
+    asan_storeN(address, size);
+
+  }
+
+}
+
+void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
+
+  UNUSED_PARAMETER(iterator);
+
+  cs_x86      x86 = instr->detail->x86;
+  cs_x86_op * operand;
+  x86_op_mem *mem;
+  cs_x86_op * ctx;
+
+  if (!asan_initialized) return;
+
+  if (instr->id == X86_INS_LEA) return;
+
+  if (instr->id == X86_INS_NOP) return;
+
+  for (uint8_t i = 0; i < x86.op_count; i++) {
+
+    operand = &x86.operands[i];
+
+    if (operand->type != X86_OP_MEM) { continue; }
+
+    mem = &operand->mem;
+    if (mem->segment != X86_REG_INVALID) { continue; }
+
+    ctx = g_malloc0(sizeof(cs_x86_op));
+    memcpy(ctx, operand, sizeof(cs_x86_op));
+    gum_stalker_iterator_put_callout(iterator, asan_callout, ctx, g_free);
+
+  }
+
+}
+
+void asan_arch_init(void) {
+
+  asan_loadN = (asan_loadN_t)dlsym(RTLD_DEFAULT, "__asan_loadN");
+  asan_storeN = (asan_loadN_t)dlsym(RTLD_DEFAULT, "__asan_storeN");
+  if (asan_loadN == NULL || asan_storeN == NULL) {
+
+    FATAL("Frida ASAN failed to find '__asan_loadN' or '__asan_storeN'");
+
+  }
+
+}
+
+#endif
+
diff --git a/frida_mode/src/cmplog/cmplog.c b/frida_mode/src/cmplog/cmplog.c
index 3fab1951..7b11c350 100644
--- a/frida_mode/src/cmplog/cmplog.c
+++ b/frida_mode/src/cmplog/cmplog.c
@@ -53,7 +53,7 @@ static gboolean cmplog_contains(GumAddress inner_base, GumAddress inner_limit,
 
 }
 
-gboolean cmplog_is_readable(void *addr, size_t size) {
+gboolean cmplog_is_readable(guint64 addr, size_t size) {
 
   if (cmplog_ranges == NULL) FATAL("CMPLOG not initialized");
 
@@ -65,9 +65,9 @@ gboolean cmplog_is_readable(void *addr, size_t size) {
    * is lower than this. This should avoid some overhead when functions are
    * called where one of the parameters is a size, or a some other small value.
    */
-  if (GPOINTER_TO_SIZE(addr) < DEFAULT_MMAP_MIN_ADDR) { return false; }
+  if (addr < DEFAULT_MMAP_MIN_ADDR) { return false; }
 
-  GumAddress inner_base = GUM_ADDRESS(addr);
+  GumAddress inner_base = addr;
   GumAddress inner_limit = inner_base + size;
 
   for (guint i = 0; i < cmplog_ranges->len; i++) {
diff --git a/frida_mode/src/cmplog/cmplog_x64.c b/frida_mode/src/cmplog/cmplog_x64.c
index 9bf09ad5..9f56c32a 100644
--- a/frida_mode/src/cmplog/cmplog_x64.c
+++ b/frida_mode/src/cmplog/cmplog_x64.c
@@ -3,46 +3,12 @@
 #include "debug.h"
 #include "cmplog.h"
 
+#include "ctx.h"
 #include "frida_cmplog.h"
 #include "util.h"
 
 #if defined(__x86_64__)
 
-  #define X86_REG_8L(LABEL, REG)  \
-    case LABEL: {                 \
-                                  \
-      return REG & GUM_INT8_MASK; \
-                                  \
-    }
-
-  #define X86_REG_8H(LABEL, REG)          \
-    case LABEL: {                         \
-                                          \
-      return (REG & GUM_INT16_MASK) >> 8; \
-                                          \
-    }
-
-  #define X86_REG_16(LABEL, REG)     \
-    case LABEL: {                    \
-                                     \
-      return (REG & GUM_INT16_MASK); \
-                                     \
-    }
-
-  #define X86_REG_32(LABEL, REG)     \
-    case LABEL: {                    \
-                                     \
-      return (REG & GUM_INT32_MASK); \
-                                     \
-    }
-
-  #define X86_REG_64(LABEL, REG) \
-    case LABEL: {                \
-                                 \
-      return (REG);              \
-                                 \
-    }
-
 typedef struct {
 
   x86_op_type type;
@@ -65,123 +31,81 @@ typedef struct {
 
 } cmplog_pair_ctx_t;
 
-static guint64 cmplog_read_reg(GumX64CpuContext *ctx, x86_reg reg) {
-
-  switch (reg) {
-
-    X86_REG_8L(X86_REG_AL, ctx->rax)
-    X86_REG_8L(X86_REG_BL, ctx->rbx)
-    X86_REG_8L(X86_REG_CL, ctx->rcx)
-    X86_REG_8L(X86_REG_DL, ctx->rdx)
-    X86_REG_8L(X86_REG_BPL, ctx->rbp)
-    X86_REG_8L(X86_REG_SIL, ctx->rsi)
-    X86_REG_8L(X86_REG_DIL, ctx->rdi)
-
-    X86_REG_8H(X86_REG_AH, ctx->rax)
-    X86_REG_8H(X86_REG_BH, ctx->rbx)
-    X86_REG_8H(X86_REG_CH, ctx->rcx)
-    X86_REG_8H(X86_REG_DH, ctx->rdx)
-
-    X86_REG_16(X86_REG_AX, ctx->rax)
-    X86_REG_16(X86_REG_BX, ctx->rbx)
-    X86_REG_16(X86_REG_CX, ctx->rcx)
-    X86_REG_16(X86_REG_DX, ctx->rdx)
-    X86_REG_16(X86_REG_DI, ctx->rdi)
-    X86_REG_16(X86_REG_SI, ctx->rsi)
-    X86_REG_16(X86_REG_BP, ctx->rbp)
-
-    X86_REG_32(X86_REG_EAX, ctx->rax)
-    X86_REG_32(X86_REG_ECX, ctx->rcx)
-    X86_REG_32(X86_REG_EDX, ctx->rdx)
-    X86_REG_32(X86_REG_EBX, ctx->rbx)
-    X86_REG_32(X86_REG_ESP, ctx->rsp)
-    X86_REG_32(X86_REG_EBP, ctx->rbp)
-    X86_REG_32(X86_REG_ESI, ctx->rsi)
-    X86_REG_32(X86_REG_EDI, ctx->rdi)
-    X86_REG_32(X86_REG_R8D, ctx->r8)
-    X86_REG_32(X86_REG_R9D, ctx->r9)
-    X86_REG_32(X86_REG_R10D, ctx->r10)
-    X86_REG_32(X86_REG_R11D, ctx->r11)
-    X86_REG_32(X86_REG_R12D, ctx->r12)
-    X86_REG_32(X86_REG_R13D, ctx->r13)
-    X86_REG_32(X86_REG_R14D, ctx->r14)
-    X86_REG_32(X86_REG_R15D, ctx->r15)
-    X86_REG_32(X86_REG_EIP, ctx->rip)
-
-    X86_REG_64(X86_REG_RAX, ctx->rax)
-    X86_REG_64(X86_REG_RCX, ctx->rcx)
-    X86_REG_64(X86_REG_RDX, ctx->rdx)
-    X86_REG_64(X86_REG_RBX, ctx->rbx)
-    X86_REG_64(X86_REG_RSP, ctx->rsp)
-    X86_REG_64(X86_REG_RBP, ctx->rbp)
-    X86_REG_64(X86_REG_RSI, ctx->rsi)
-    X86_REG_64(X86_REG_RDI, ctx->rdi)
-    X86_REG_64(X86_REG_R8, ctx->r8)
-    X86_REG_64(X86_REG_R9, ctx->r9)
-    X86_REG_64(X86_REG_R10, ctx->r10)
-    X86_REG_64(X86_REG_R11, ctx->r11)
-    X86_REG_64(X86_REG_R12, ctx->r12)
-    X86_REG_64(X86_REG_R13, ctx->r13)
-    X86_REG_64(X86_REG_R14, ctx->r14)
-    X86_REG_64(X86_REG_R15, ctx->r15)
-    X86_REG_64(X86_REG_RIP, ctx->rip)
-
-    default:
-      FATAL("Failed to read register: %d", reg);
-      return 0;
+static gboolean cmplog_read_mem(GumCpuContext *ctx, uint8_t size,
+                                x86_op_mem *mem, gsize *val) {
 
-  }
+  gsize base = 0;
+  gsize index = 0;
+  gsize address;
 
-}
+  if (mem->base != X86_REG_INVALID) base = ctx_read_reg(ctx, mem->base);
 
-static guint64 cmplog_read_mem(GumX64CpuContext *ctx, x86_op_mem *mem) {
+  if (mem->index != X86_REG_INVALID) index = ctx_read_reg(ctx, mem->index);
 
-  guint64 base = 0;
-  guint64 index = 0;
-  guint64 address;
+  address = base + (index * mem->scale) + mem->disp;
 
-  if (mem->base != X86_REG_INVALID) base = cmplog_read_reg(ctx, mem->base);
+  if (!cmplog_is_readable(address, size)) { return FALSE; }
+
+  switch (size) {
+
+    case 1:
+      *val = *((guint8 *)GSIZE_TO_POINTER(address));
+      return TRUE;
+    case 2:
+      *val = *((guint16 *)GSIZE_TO_POINTER(address));
+      return TRUE;
+    case 4:
+      *val = *((guint32 *)GSIZE_TO_POINTER(address));
+      return TRUE;
+    case 8:
+      *val = *((guint64 *)GSIZE_TO_POINTER(address));
+      return TRUE;
+    default:
+      FATAL("Invalid operand size: %d\n", size);
 
-  if (mem->index != X86_REG_INVALID) index = cmplog_read_reg(ctx, mem->index);
+  }
 
-  address = base + (index * mem->scale) + mem->disp;
-  return address;
+  return FALSE;
 
 }
 
-static guint64 cmplog_get_operand_value(GumCpuContext *context,
-                                        cmplog_ctx_t * ctx) {
+static gboolean cmplog_get_operand_value(GumCpuContext *context,
+                                         cmplog_ctx_t *ctx, gsize *val) {
 
   switch (ctx->type) {
 
     case X86_OP_REG:
-      return cmplog_read_reg(context, ctx->reg);
+      *val = ctx_read_reg(context, ctx->reg);
+      return TRUE;
     case X86_OP_IMM:
-      return ctx->imm;
+      *val = ctx->imm;
+      return TRUE;
     case X86_OP_MEM:
-      return cmplog_read_mem(context, &ctx->mem);
+      return cmplog_read_mem(context, ctx->size, &ctx->mem, val);
     default:
       FATAL("Invalid operand type: %d\n", ctx->type);
 
   }
 
+  return FALSE;
+
 }
 
 static void cmplog_call_callout(GumCpuContext *context, gpointer user_data) {
 
   UNUSED_PARAMETER(user_data);
 
-  guint64 address = cmplog_read_reg(context, X86_REG_RIP);
-  guint64 rdi = cmplog_read_reg(context, X86_REG_RDI);
-  guint64 rsi = cmplog_read_reg(context, X86_REG_RSI);
+  gsize address = ctx_read_reg(context, X86_REG_RIP);
+  gsize rdi = ctx_read_reg(context, X86_REG_RDI);
+  gsize rsi = ctx_read_reg(context, X86_REG_RSI);
 
   if (((G_MAXULONG - rdi) < 32) || ((G_MAXULONG - rsi) < 32)) return;
 
+  if (!cmplog_is_readable(rdi, 32) || !cmplog_is_readable(rsi, 32)) return;
+
   void *ptr1 = GSIZE_TO_POINTER(rdi);
   void *ptr2 = GSIZE_TO_POINTER(rsi);
 
-  if (!cmplog_is_readable(ptr1, 32) || !cmplog_is_readable(ptr2, 32)) return;
-
   uintptr_t k = address;
 
   k = (k >> 4) ^ (k << 8);
@@ -245,10 +169,10 @@ static void cmplog_instrument_call(const cs_insn *     instr,
 
 }
 
-static void cmplog_handle_cmp_sub(GumCpuContext *context, guint64 operand1,
-                                  guint64 operand2, uint8_t size) {
+static void cmplog_handle_cmp_sub(GumCpuContext *context, gsize operand1,
+                                  gsize operand2, uint8_t size) {
 
-  guint64 address = cmplog_read_reg(context, X86_REG_RIP);
+  gsize address = ctx_read_reg(context, X86_REG_RIP);
 
   register uintptr_t k = (uintptr_t)address;
 
@@ -271,11 +195,13 @@ static void cmplog_handle_cmp_sub(GumCpuContext *context, guint64 operand1,
 static void cmplog_cmp_sub_callout(GumCpuContext *context, gpointer user_data) {
 
   cmplog_pair_ctx_t *ctx = (cmplog_pair_ctx_t *)user_data;
+  gsize              operand1;
+  gsize              operand2;
 
   if (ctx->operand1.size != ctx->operand2.size) FATAL("Operand size mismatch");
 
-  guint64 operand1 = cmplog_get_operand_value(context, &ctx->operand1);
-  guint64 operand2 = cmplog_get_operand_value(context, &ctx->operand2);
+  if (!cmplog_get_operand_value(context, &ctx->operand1, &operand1)) { return; }
+  if (!cmplog_get_operand_value(context, &ctx->operand2, &operand2)) { return; }
 
   cmplog_handle_cmp_sub(context, operand1, operand2, ctx->operand1.size);
 
diff --git a/frida_mode/src/cmplog/cmplog_x86.c b/frida_mode/src/cmplog/cmplog_x86.c
index 2401180c..a27df0af 100644
--- a/frida_mode/src/cmplog/cmplog_x86.c
+++ b/frida_mode/src/cmplog/cmplog_x86.c
@@ -1,17 +1,275 @@
 #include "frida-gum.h"
 
 #include "debug.h"
+#include "cmplog.h"
 
+#include "ctx.h"
 #include "frida_cmplog.h"
 #include "util.h"
 
 #if defined(__i386__)
+
+typedef struct {
+
+  x86_op_type type;
+  uint8_t     size;
+
+  union {
+
+    x86_op_mem mem;
+    x86_reg    reg;
+    int64_t    imm;
+
+  };
+
+} cmplog_ctx_t;
+
+typedef struct {
+
+  cmplog_ctx_t operand1;
+  cmplog_ctx_t operand2;
+
+} cmplog_pair_ctx_t;
+
+static gboolean cmplog_read_mem(GumCpuContext *ctx, uint8_t size,
+                                x86_op_mem *mem, gsize *val) {
+
+  gsize base = 0;
+  gsize index = 0;
+  gsize address;
+
+  if (mem->base != X86_REG_INVALID) base = ctx_read_reg(ctx, mem->base);
+
+  if (mem->index != X86_REG_INVALID) index = ctx_read_reg(ctx, mem->index);
+
+  address = base + (index * mem->scale) + mem->disp;
+
+  if (!cmplog_is_readable(address, size)) { return FALSE; }
+
+  switch (size) {
+
+    case 1:
+      *val = *((guint8 *)GSIZE_TO_POINTER(address));
+      return TRUE;
+    case 2:
+      *val = *((guint16 *)GSIZE_TO_POINTER(address));
+      return TRUE;
+    case 4:
+      *val = *((guint32 *)GSIZE_TO_POINTER(address));
+      return TRUE;
+    default:
+      FATAL("Invalid operand size: %d\n", size);
+
+  }
+
+  return FALSE;
+
+}
+
+static gboolean cmplog_get_operand_value(GumCpuContext *context,
+                                         cmplog_ctx_t *ctx, gsize *val) {
+
+  switch (ctx->type) {
+
+    case X86_OP_REG:
+      *val = ctx_read_reg(context, ctx->reg);
+      return TRUE;
+    case X86_OP_IMM:
+      *val = ctx->imm;
+      return TRUE;
+    case X86_OP_MEM:
+      return cmplog_read_mem(context, ctx->size, &ctx->mem, val);
+    default:
+      FATAL("Invalid operand type: %d\n", ctx->type);
+
+  }
+
+  return FALSE;
+
+}
+
+static void cmplog_call_callout(GumCpuContext *context, gpointer user_data) {
+
+  UNUSED_PARAMETER(user_data);
+
+  gsize  address = ctx_read_reg(context, X86_REG_EIP);
+  gsize *esp = (gsize *)ctx_read_reg(context, X86_REG_ESP);
+
+  if (!cmplog_is_readable(GPOINTER_TO_SIZE(esp), 12)) return;
+
+  /*
+   * This callout is place immediately before the call instruction, and hence
+   * the return address is not yet pushed on the top of the stack.
+   */
+  gsize arg1 = esp[0];
+  gsize arg2 = esp[1];
+
+  if (((G_MAXULONG - arg1) < 32) || ((G_MAXULONG - arg2) < 32)) return;
+
+  if (!cmplog_is_readable(arg1, 32) || !cmplog_is_readable(arg2, 32)) return;
+
+  void *ptr1 = GSIZE_TO_POINTER(arg1);
+  void *ptr2 = GSIZE_TO_POINTER(arg2);
+
+  uintptr_t k = address;
+
+  k = (k >> 4) ^ (k << 8);
+  k &= CMP_MAP_W - 1;
+
+  __afl_cmp_map->headers[k].type = CMP_TYPE_RTN;
+
+  u32 hits = __afl_cmp_map->headers[k].hits;
+  __afl_cmp_map->headers[k].hits = hits + 1;
+
+  __afl_cmp_map->headers[k].shape = 31;
+
+  hits &= CMP_MAP_RTN_H - 1;
+  gum_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v0, ptr1,
+             32);
+  gum_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1, ptr2,
+             32);
+
+}
+
+static void cmplog_instrument_put_operand(cmplog_ctx_t *ctx,
+                                          cs_x86_op *   operand) {
+
+  ctx->type = operand->type;
+  ctx->size = operand->size;
+  switch (operand->type) {
+
+    case X86_OP_REG:
+      gum_memcpy(&ctx->reg, &operand->reg, sizeof(x86_reg));
+      break;
+    case X86_OP_IMM:
+      gum_memcpy(&ctx->imm, &operand->imm, sizeof(int64_t));
+      break;
+    case X86_OP_MEM:
+      gum_memcpy(&ctx->mem, &operand->mem, sizeof(x86_op_mem));
+      break;
+    default:
+      FATAL("Invalid operand type: %d\n", operand->type);
+
+  }
+
+}
+
+static void cmplog_instrument_call(const cs_insn *     instr,
+                                   GumStalkerIterator *iterator) {
+
+  cs_x86     x86 = instr->detail->x86;
+  cs_x86_op *operand;
+
+  if (instr->id != X86_INS_CALL) return;
+
+  if (x86.op_count != 1) return;
+
+  operand = &x86.operands[0];
+
+  if (operand->type == X86_OP_INVALID) return;
+  if (operand->type == X86_OP_MEM && operand->mem.segment != X86_REG_INVALID)
+    return;
+
+  gum_stalker_iterator_put_callout(iterator, cmplog_call_callout, NULL, NULL);
+
+}
+
+static void cmplog_handle_cmp_sub(GumCpuContext *context, gsize operand1,
+                                  gsize operand2, uint8_t size) {
+
+  gsize address = ctx_read_reg(context, X86_REG_EIP);
+
+  register uintptr_t k = (uintptr_t)address;
+
+  k = (k >> 4) ^ (k << 8);
+  k &= CMP_MAP_W - 1;
+
+  __afl_cmp_map->headers[k].type = CMP_TYPE_INS;
+
+  u32 hits = __afl_cmp_map->headers[k].hits;
+  __afl_cmp_map->headers[k].hits = hits + 1;
+
+  __afl_cmp_map->headers[k].shape = (size - 1);
+
+  hits &= CMP_MAP_H - 1;
+  __afl_cmp_map->log[k][hits].v0 = operand1;
+  __afl_cmp_map->log[k][hits].v1 = operand2;
+
+}
+
+static void cmplog_cmp_sub_callout(GumCpuContext *context, gpointer user_data) {
+
+  cmplog_pair_ctx_t *ctx = (cmplog_pair_ctx_t *)user_data;
+  gsize              operand1;
+  gsize              operand2;
+
+  if (ctx->operand1.size != ctx->operand2.size) FATAL("Operand size mismatch");
+
+  if (!cmplog_get_operand_value(context, &ctx->operand1, &operand1)) { return; }
+  if (!cmplog_get_operand_value(context, &ctx->operand2, &operand2)) { return; }
+
+  cmplog_handle_cmp_sub(context, operand1, operand2, ctx->operand1.size);
+
+}
+
+static void cmplog_instrument_cmp_sub_put_callout(GumStalkerIterator *iterator,
+                                                  cs_x86_op *         operand1,
+                                                  cs_x86_op *operand2) {
+
+  cmplog_pair_ctx_t *ctx = g_malloc(sizeof(cmplog_pair_ctx_t));
+  if (ctx == NULL) return;
+
+  cmplog_instrument_put_operand(&ctx->operand1, operand1);
+  cmplog_instrument_put_operand(&ctx->operand2, operand2);
+
+  gum_stalker_iterator_put_callout(iterator, cmplog_cmp_sub_callout, ctx,
+                                   g_free);
+
+}
+
+static void cmplog_instrument_cmp_sub(const cs_insn *     instr,
+                                      GumStalkerIterator *iterator) {
+
+  cs_x86     x86 = instr->detail->x86;
+  cs_x86_op *operand1;
+  cs_x86_op *operand2;
+
+  switch (instr->id) {
+
+    case X86_INS_CMP:
+    case X86_INS_SUB:
+      break;
+    default:
+      return;
+
+  }
+
+  if (x86.op_count != 2) return;
+
+  operand1 = &x86.operands[0];
+  operand2 = &x86.operands[1];
+
+  if (operand1->type == X86_OP_INVALID) return;
+  if (operand2->type == X86_OP_INVALID) return;
+
+  if ((operand1->type == X86_OP_MEM) &&
+      (operand1->mem.segment != X86_REG_INVALID))
+    return;
+
+  if ((operand2->type == X86_OP_MEM) &&
+      (operand2->mem.segment != X86_REG_INVALID))
+    return;
+
+  cmplog_instrument_cmp_sub_put_callout(iterator, operand1, operand2);
+
+}
+
 void cmplog_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
 
-  UNUSED_PARAMETER(instr);
-  UNUSED_PARAMETER(iterator);
-  if (__afl_cmp_map == NULL) { return; }
-  FATAL("CMPLOG mode not supported on this architecture");
+  if (__afl_cmp_map == NULL) return;
+
+  cmplog_instrument_call(instr, iterator);
+  cmplog_instrument_cmp_sub(instr, iterator);
 
 }
 
diff --git a/frida_mode/src/ctx/ctx_x64.c b/frida_mode/src/ctx/ctx_x64.c
new file mode 100644
index 00000000..c5900533
--- /dev/null
+++ b/frida_mode/src/ctx/ctx_x64.c
@@ -0,0 +1,114 @@
+#include "frida-gum.h"
+
+#include "debug.h"
+
+#include "ctx.h"
+
+#if defined(__x86_64__)
+
+  #define X86_REG_8L(LABEL, REG)  \
+    case LABEL: {                 \
+                                  \
+      return REG & GUM_INT8_MASK; \
+                                  \
+    }
+
+  #define X86_REG_8H(LABEL, REG)          \
+    case LABEL: {                         \
+                                          \
+      return (REG & GUM_INT16_MASK) >> 8; \
+                                          \
+    }
+
+  #define X86_REG_16(LABEL, REG)     \
+    case LABEL: {                    \
+                                     \
+      return (REG & GUM_INT16_MASK); \
+                                     \
+    }
+
+  #define X86_REG_32(LABEL, REG)     \
+    case LABEL: {                    \
+                                     \
+      return (REG & GUM_INT32_MASK); \
+                                     \
+    }
+
+  #define X86_REG_64(LABEL, REG) \
+    case LABEL: {                \
+                                 \
+      return (REG);              \
+                                 \
+    }
+
+gsize ctx_read_reg(GumX64CpuContext *ctx, x86_reg reg) {
+
+  switch (reg) {
+
+    X86_REG_8L(X86_REG_AL, ctx->rax)
+    X86_REG_8L(X86_REG_BL, ctx->rbx)
+    X86_REG_8L(X86_REG_CL, ctx->rcx)
+    X86_REG_8L(X86_REG_DL, ctx->rdx)
+    X86_REG_8L(X86_REG_BPL, ctx->rbp)
+    X86_REG_8L(X86_REG_SIL, ctx->rsi)
+    X86_REG_8L(X86_REG_DIL, ctx->rdi)
+
+    X86_REG_8H(X86_REG_AH, ctx->rax)
+    X86_REG_8H(X86_REG_BH, ctx->rbx)
+    X86_REG_8H(X86_REG_CH, ctx->rcx)
+    X86_REG_8H(X86_REG_DH, ctx->rdx)
+
+    X86_REG_16(X86_REG_AX, ctx->rax)
+    X86_REG_16(X86_REG_BX, ctx->rbx)
+    X86_REG_16(X86_REG_CX, ctx->rcx)
+    X86_REG_16(X86_REG_DX, ctx->rdx)
+    X86_REG_16(X86_REG_DI, ctx->rdi)
+    X86_REG_16(X86_REG_SI, ctx->rsi)
+    X86_REG_16(X86_REG_BP, ctx->rbp)
+
+    X86_REG_32(X86_REG_EAX, ctx->rax)
+    X86_REG_32(X86_REG_ECX, ctx->rcx)
+    X86_REG_32(X86_REG_EDX, ctx->rdx)
+    X86_REG_32(X86_REG_EBX, ctx->rbx)
+    X86_REG_32(X86_REG_ESP, ctx->rsp)
+    X86_REG_32(X86_REG_EBP, ctx->rbp)
+    X86_REG_32(X86_REG_ESI, ctx->rsi)
+    X86_REG_32(X86_REG_EDI, ctx->rdi)
+    X86_REG_32(X86_REG_R8D, ctx->r8)
+    X86_REG_32(X86_REG_R9D, ctx->r9)
+    X86_REG_32(X86_REG_R10D, ctx->r10)
+    X86_REG_32(X86_REG_R11D, ctx->r11)
+    X86_REG_32(X86_REG_R12D, ctx->r12)
+    X86_REG_32(X86_REG_R13D, ctx->r13)
+    X86_REG_32(X86_REG_R14D, ctx->r14)
+    X86_REG_32(X86_REG_R15D, ctx->r15)
+    X86_REG_32(X86_REG_EIP, ctx->rip)
+
+    X86_REG_64(X86_REG_RAX, ctx->rax)
+    X86_REG_64(X86_REG_RCX, ctx->rcx)
+    X86_REG_64(X86_REG_RDX, ctx->rdx)
+    X86_REG_64(X86_REG_RBX, ctx->rbx)
+    X86_REG_64(X86_REG_RSP, ctx->rsp)
+    X86_REG_64(X86_REG_RBP, ctx->rbp)
+    X86_REG_64(X86_REG_RSI, ctx->rsi)
+    X86_REG_64(X86_REG_RDI, ctx->rdi)
+    X86_REG_64(X86_REG_R8, ctx->r8)
+    X86_REG_64(X86_REG_R9, ctx->r9)
+    X86_REG_64(X86_REG_R10, ctx->r10)
+    X86_REG_64(X86_REG_R11, ctx->r11)
+    X86_REG_64(X86_REG_R12, ctx->r12)
+    X86_REG_64(X86_REG_R13, ctx->r13)
+    X86_REG_64(X86_REG_R14, ctx->r14)
+    X86_REG_64(X86_REG_R15, ctx->r15)
+    X86_REG_64(X86_REG_RIP, ctx->rip)
+
+    default:
+      FATAL("Failed to read register: %d", reg);
+      return 0;
+
+  }
+
+}
+
+#endif
+
diff --git a/frida_mode/src/ctx/ctx_x86.c b/frida_mode/src/ctx/ctx_x86.c
new file mode 100644
index 00000000..45308272
--- /dev/null
+++ b/frida_mode/src/ctx/ctx_x86.c
@@ -0,0 +1,81 @@
+#include "frida-gum.h"
+
+#include "debug.h"
+
+#include "ctx.h"
+
+#if defined(__i386__)
+
+  #define X86_REG_8L(LABEL, REG)  \
+    case LABEL: {                 \
+                                  \
+      return REG & GUM_INT8_MASK; \
+                                  \
+    }
+
+  #define X86_REG_8H(LABEL, REG)          \
+    case LABEL: {                         \
+                                          \
+      return (REG & GUM_INT16_MASK) >> 8; \
+                                          \
+    }
+
+  #define X86_REG_16(LABEL, REG)     \
+    case LABEL: {                    \
+                                     \
+      return (REG & GUM_INT16_MASK); \
+                                     \
+    }
+
+  #define X86_REG_32(LABEL, REG)     \
+    case LABEL: {                    \
+                                     \
+      return (REG & GUM_INT32_MASK); \
+                                     \
+    }
+
+gsize ctx_read_reg(GumIA32CpuContext *ctx, x86_reg reg) {
+
+  switch (reg) {
+
+    X86_REG_8L(X86_REG_AL, ctx->eax)
+    X86_REG_8L(X86_REG_BL, ctx->ebx)
+    X86_REG_8L(X86_REG_CL, ctx->ecx)
+    X86_REG_8L(X86_REG_DL, ctx->edx)
+    X86_REG_8L(X86_REG_BPL, ctx->ebp)
+    X86_REG_8L(X86_REG_SIL, ctx->esi)
+    X86_REG_8L(X86_REG_DIL, ctx->edi)
+
+    X86_REG_8H(X86_REG_AH, ctx->eax)
+    X86_REG_8H(X86_REG_BH, ctx->ebx)
+    X86_REG_8H(X86_REG_CH, ctx->ecx)
+    X86_REG_8H(X86_REG_DH, ctx->edx)
+
+    X86_REG_16(X86_REG_AX, ctx->eax)
+    X86_REG_16(X86_REG_BX, ctx->ebx)
+    X86_REG_16(X86_REG_CX, ctx->ecx)
+    X86_REG_16(X86_REG_DX, ctx->edx)
+    X86_REG_16(X86_REG_DI, ctx->edi)
+    X86_REG_16(X86_REG_SI, ctx->esi)
+    X86_REG_16(X86_REG_BP, ctx->ebp)
+
+    X86_REG_32(X86_REG_EAX, ctx->eax)
+    X86_REG_32(X86_REG_ECX, ctx->ecx)
+    X86_REG_32(X86_REG_EDX, ctx->edx)
+    X86_REG_32(X86_REG_EBX, ctx->ebx)
+    X86_REG_32(X86_REG_ESP, ctx->esp)
+    X86_REG_32(X86_REG_EBP, ctx->ebp)
+    X86_REG_32(X86_REG_ESI, ctx->esi)
+    X86_REG_32(X86_REG_EDI, ctx->edi)
+    X86_REG_32(X86_REG_EIP, ctx->eip)
+
+    default:
+      FATAL("Failed to read register: %d", reg);
+      return 0;
+
+  }
+
+}
+
+#endif
+
diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c
index 971f80c0..67eadc3f 100644
--- a/frida_mode/src/instrument/instrument.c
+++ b/frida_mode/src/instrument/instrument.c
@@ -5,6 +5,7 @@
 #include "config.h"
 #include "debug.h"
 
+#include "asan.h"
 #include "entry.h"
 #include "frida_cmplog.h"
 #include "instrument.h"
@@ -33,7 +34,7 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context,
    */
   static char buffer[200];
   int         len;
-  guint64     current_pc = (guint64)user_data;
+  GumAddress  current_pc = GUM_ADDRESS(user_data);
   uint8_t *   cursor;
   uint64_t    value;
   if (unlikely(tracing)) {
@@ -85,8 +86,8 @@ static void instr_basic_block(GumStalkerIterator *iterator,
 
     if (begin) {
 
-      prefetch_write((void *)instr->address);
-      if (!range_is_excluded((void *)instr->address)) {
+      prefetch_write(GSIZE_TO_POINTER(instr->address));
+      if (!range_is_excluded(GSIZE_TO_POINTER(instr->address))) {
 
         if (optimize) {
 
@@ -94,8 +95,8 @@ static void instr_basic_block(GumStalkerIterator *iterator,
 
         } else {
 
-          gum_stalker_iterator_put_callout(iterator, on_basic_block,
-                                           (gpointer)instr->address, NULL);
+          gum_stalker_iterator_put_callout(
+              iterator, on_basic_block, GSIZE_TO_POINTER(instr->address), NULL);
 
         }
 
@@ -105,8 +106,9 @@ static void instr_basic_block(GumStalkerIterator *iterator,
 
     }
 
-    if (!range_is_excluded((void *)instr->address)) {
+    if (!range_is_excluded(GSIZE_TO_POINTER(instr->address))) {
 
+      asan_instrument(instr, iterator);
       cmplog_instrument(instr, iterator);
 
     }
@@ -142,6 +144,7 @@ void instrument_init(void) {
   transformer =
       gum_stalker_transformer_make_from_callback(instr_basic_block, NULL, NULL);
 
+  asan_init();
   cmplog_init();
 
 }
diff --git a/frida_mode/src/instrument/instrument_arm32.c b/frida_mode/src/instrument/instrument_arm32.c
index c2d720a7..1a3c40bb 100644
--- a/frida_mode/src/instrument/instrument_arm32.c
+++ b/frida_mode/src/instrument/instrument_arm32.c
@@ -3,6 +3,7 @@
 #include "debug.h"
 
 #include "instrument.h"
+#include "util.h"
 
 #if defined(__arm__)
 
@@ -15,6 +16,8 @@ gboolean instrument_is_coverage_optimize_supported(void) {
 void instrument_coverage_optimize(const cs_insn *   instr,
                                   GumStalkerOutput *output) {
 
+  UNUSED_PARAMETER(instr);
+  UNUSED_PARAMETER(output);
   FATAL("Optimized coverage not supported on this architecture");
 
 }
diff --git a/frida_mode/src/instrument/instrument_x86.c b/frida_mode/src/instrument/instrument_x86.c
index 5b8cbbba..585bb5b8 100644
--- a/frida_mode/src/instrument/instrument_x86.c
+++ b/frida_mode/src/instrument/instrument_x86.c
@@ -3,19 +3,81 @@
 #include "debug.h"
 
 #include "instrument.h"
+#include "util.h"
 
 #if defined(__i386__)
 
+static GumAddress current_log_impl = GUM_ADDRESS(0);
+
+static void instrument_coverage_function(GumX86Writer *cw) {
+
+  gum_x86_writer_put_pushfx(cw);
+  gum_x86_writer_put_push_reg(cw, GUM_REG_ECX);
+  gum_x86_writer_put_push_reg(cw, GUM_REG_EDX);
+
+  gum_x86_writer_put_mov_reg_address(cw, GUM_REG_ECX,
+                                     GUM_ADDRESS(&previous_pc));
+  gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_REG_EDX, GUM_REG_ECX);
+  gum_x86_writer_put_xor_reg_reg(cw, GUM_REG_EDX, GUM_REG_EDI);
+
+  gum_x86_writer_put_add_reg_imm(cw, GUM_REG_EDX, GUM_ADDRESS(__afl_area_ptr));
+
+  /* add byte ptr [edx], 1 */
+  uint8_t add_byte_ptr_edx_1[] = {0x80, 0x02, 0x01};
+  gum_x86_writer_put_bytes(cw, add_byte_ptr_edx_1, sizeof(add_byte_ptr_edx_1));
+
+  /* adc byte ptr [edx], 0 */
+  uint8_t adc_byte_ptr_edx_0[] = {0x80, 0x12, 0x00};
+  gum_x86_writer_put_bytes(cw, adc_byte_ptr_edx_0, sizeof(adc_byte_ptr_edx_0));
+
+  gum_x86_writer_put_shr_reg_u8(cw, GUM_REG_EDI, 1);
+  gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_REG_ECX, GUM_REG_EDI);
+
+  gum_x86_writer_put_pop_reg(cw, GUM_REG_EDX);
+  gum_x86_writer_put_pop_reg(cw, GUM_REG_ECX);
+  gum_x86_writer_put_popfx(cw);
+  gum_x86_writer_put_ret(cw);
+
+}
+
 gboolean instrument_is_coverage_optimize_supported(void) {
 
-  return false;
+  return true;
 
 }
 
 void instrument_coverage_optimize(const cs_insn *   instr,
                                   GumStalkerOutput *output) {
 
-  FATAL("Optimized coverage not supported on this architecture");
+  UNUSED_PARAMETER(instr);
+  UNUSED_PARAMETER(output);
+
+  guint64 current_pc = instr->address;
+  guint64 area_offset = (current_pc >> 4) ^ (current_pc << 8);
+  area_offset &= MAP_SIZE - 1;
+  GumX86Writer *cw = output->writer.x86;
+
+  if (current_log_impl == 0 ||
+      !gum_x86_writer_can_branch_directly_between(cw->pc, current_log_impl) ||
+      !gum_x86_writer_can_branch_directly_between(cw->pc + 128,
+                                                  current_log_impl)) {
+
+    gconstpointer after_log_impl = cw->code + 1;
+
+    gum_x86_writer_put_jmp_near_label(cw, after_log_impl);
+
+    current_log_impl = cw->pc;
+    instrument_coverage_function(cw);
+
+    gum_x86_writer_put_label(cw, after_log_impl);
+
+  }
+
+  // gum_x86_writer_put_breakpoint(cw);
+  gum_x86_writer_put_push_reg(cw, GUM_REG_EDI);
+  gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EDI, area_offset);
+  gum_x86_writer_put_call_address(cw, current_log_impl);
+  gum_x86_writer_put_pop_reg(cw, GUM_REG_EDI);
 
 }
 
diff --git a/frida_mode/src/lib/lib.c b/frida_mode/src/lib/lib.c
index c5045533..13a7d1e7 100644
--- a/frida_mode/src/lib/lib.c
+++ b/frida_mode/src/lib/lib.c
@@ -90,7 +90,8 @@ static void lib_read_text_section(lib_details_t *lib_details, Elf_Ehdr *hdr) {
 
   if (!found_preferred_base) { FATAL("Failed to find preferred load address"); }
 
-  OKF("Image preferred load address 0x%016lx", preferred_base);
+  OKF("Image preferred load address 0x%016" G_GSIZE_MODIFIER "x",
+      preferred_base);
 
   shdr = (Elf_Shdr *)((char *)hdr + hdr->e_shoff);
   shstrtab = &shdr[hdr->e_shstrndx];
@@ -107,15 +108,16 @@ static void lib_read_text_section(lib_details_t *lib_details, Elf_Ehdr *hdr) {
     if (curr->sh_name == 0) continue;
 
     section_name = &shstr[curr->sh_name];
-    OKF("Section: %2lu - base: 0x%016lX size: 0x%016lX %s", i, curr->sh_addr,
-        curr->sh_size, section_name);
+    OKF("Section: %2" G_GSIZE_MODIFIER "u - base: 0x%016" G_GSIZE_MODIFIER
+        "X size: 0x%016" G_GSIZE_MODIFIER "X %s",
+        i, curr->sh_addr, curr->sh_size, section_name);
     if (memcmp(section_name, text_name, sizeof(text_name)) == 0 &&
         text_base == 0) {
 
       text_base = lib_details->base_address + curr->sh_addr - preferred_base;
       text_limit = text_base + curr->sh_size;
-      OKF("> text_addr: 0x%016lX", text_base);
-      OKF("> text_limit: 0x%016lX", text_limit);
+      OKF("> text_addr: 0x%016" G_GINT64_MODIFIER "X", text_base);
+      OKF("> text_limit: 0x%016" G_GINT64_MODIFIER "X", text_limit);
 
     }
 
@@ -153,7 +155,8 @@ void lib_init(void) {
 
   lib_details_t lib_details;
   gum_process_enumerate_modules(lib_find_exe, &lib_details);
-  OKF("Executable: 0x%016lx - %s", lib_details.base_address, lib_details.path);
+  OKF("Executable: 0x%016" G_GINT64_MODIFIER "x - %s", lib_details.base_address,
+      lib_details.path);
   lib_get_text_section(&lib_details);
 
 }
diff --git a/frida_mode/src/main.c b/frida_mode/src/main.c
index e031dbed..21073cbe 100644
--- a/frida_mode/src/main.c
+++ b/frida_mode/src/main.c
@@ -75,16 +75,22 @@ static void on_main_os(int argc, char **argv, char **envp) {
 
 #endif
 
-static int *on_main(int argc, char **argv, char **envp) {
+static void embedded_init() {
 
-  void *fork_addr;
+  static gboolean initialized = false;
+  if (!initialized) {
 
-  on_main_os(argc, argv, envp);
+    gum_init_embedded();
+    initialized = true;
 
-  unintercept_self();
+  }
 
-  stalker_init();
+}
+
+void afl_frida_start() {
 
+  embedded_init();
+  stalker_init();
   lib_init();
   entry_init();
   instrument_init();
@@ -92,12 +98,23 @@ static int *on_main(int argc, char **argv, char **envp) {
   prefetch_init();
   ranges_init();
 
-  fork_addr = GSIZE_TO_POINTER(gum_module_find_export_by_name(NULL, "fork"));
+  void *fork_addr =
+      GSIZE_TO_POINTER(gum_module_find_export_by_name(NULL, "fork"));
   intercept(fork_addr, on_fork, NULL);
 
   stalker_start();
   entry_run();
 
+}
+
+static int *on_main(int argc, char **argv, char **envp) {
+
+  on_main_os(argc, argv, envp);
+
+  unintercept_self();
+
+  afl_frida_start();
+
   return main_fn(argc, argv, envp);
 
 }
@@ -149,13 +166,7 @@ static void intercept_main(void) {
 
 __attribute__((constructor)) static void init(void) {
 
-  gum_init_embedded();
-  if (!gum_stalker_is_supported()) {
-
-    gum_deinit_embedded();
-    FATAL("Failed to initialize embedded");
-
-  }
+  embedded_init();
 
   intercept_main();
 
diff --git a/frida_mode/src/persistent/persistent_x86.c b/frida_mode/src/persistent/persistent_x86.c
index 9d39c4e9..bd7171b9 100644
--- a/frida_mode/src/persistent/persistent_x86.c
+++ b/frida_mode/src/persistent/persistent_x86.c
@@ -1,9 +1,9 @@
 #include "frida-gum.h"
 
-#include "debug.h"
+#include "config.h"
 
+#include "instrument.h"
 #include "persistent.h"
-#include "util.h"
 
 #if defined(__i386__)
 
@@ -38,16 +38,239 @@ struct x86_regs {
 
 typedef struct x86_regs arch_api_regs;
 
+static arch_api_regs saved_regs = {0};
+static void *        saved_return = NULL;
+
 gboolean persistent_is_supported(void) {
 
-  return false;
+  return true;
+
+}
+
+static void instrument_persitent_save_regs(GumX86Writer *   cw,
+                                           struct x86_regs *regs) {
+
+  GumAddress regs_address = GUM_ADDRESS(regs);
+
+  /* Should be pushing FPU here, but meh */
+  gum_x86_writer_put_pushfx(cw);
+  gum_x86_writer_put_push_reg(cw, GUM_REG_EAX);
+
+  gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, regs_address);
+
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 1),
+                                            GUM_REG_EBX);
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 2),
+                                            GUM_REG_ECX);
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 3),
+                                            GUM_REG_EDX);
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 4),
+                                            GUM_REG_EDI);
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 5),
+                                            GUM_REG_ESI);
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 6),
+                                            GUM_REG_EBP);
+
+  /* Store RIP */
+  gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EBX,
+                                     GUM_ADDRESS(persistent_start));
+
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 7),
+                                            GUM_REG_EBX);
+
+  /* Store adjusted RSP */
+  gum_x86_writer_put_mov_reg_reg(cw, GUM_REG_EBX, GUM_REG_ESP);
+
+  /* RED_ZONE + Saved flags, RAX */
+  gum_x86_writer_put_add_reg_imm(cw, GUM_REG_EBX, (0x4 * 2));
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 8),
+                                            GUM_REG_EBX);
+
+  /* Save the flags */
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_ESP, 0x4);
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 9),
+                                            GUM_REG_EBX);
+
+  /* Save the RAX */
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_ESP, 0x0);
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 0),
+                                            GUM_REG_EBX);
+
+  /* Pop the saved values */
+  gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_ESP, GUM_REG_ESP, 0x8);
+
+}
+
+static void instrument_persitent_restore_regs(GumX86Writer *   cw,
+                                              struct x86_regs *regs) {
+
+  GumAddress regs_address = GUM_ADDRESS(regs);
+  gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, regs_address);
+
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ECX, GUM_REG_EAX,
+                                            (0x4 * 2));
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EDX, GUM_REG_EAX,
+                                            (0x4 * 3));
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EDI, GUM_REG_EAX,
+                                            (0x4 * 4));
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ESI, GUM_REG_EAX,
+                                            (0x4 * 5));
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBP, GUM_REG_EAX,
+                                            (0x4 * 6));
+
+  /* Don't restore RIP or RSP */
+
+  /* Restore RBX, RAX & Flags */
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_EAX,
+                                            (0x4 * 1));
+  gum_x86_writer_put_push_reg(cw, GUM_REG_EBX);
+
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_EAX,
+                                            (0x4 * 0));
+  gum_x86_writer_put_push_reg(cw, GUM_REG_EBX);
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_EAX,
+                                            (0x4 * 9));
+  gum_x86_writer_put_push_reg(cw, GUM_REG_EBX);
+
+  gum_x86_writer_put_popfx(cw);
+  gum_x86_writer_put_pop_reg(cw, GUM_REG_EAX);
+  gum_x86_writer_put_pop_reg(cw, GUM_REG_EBX);
+
+}
+
+static void instrument_save_ret(GumX86Writer *cw, void **saved_return_ptr) {
+
+  GumAddress saved_return_address = GUM_ADDRESS(saved_return_ptr);
+
+  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, saved_return_address);
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_ESP, 0x8);
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, 0, GUM_REG_EBX);
+
+  gum_x86_writer_put_pop_reg(cw, GUM_REG_EBX);
+  gum_x86_writer_put_pop_reg(cw, GUM_REG_EAX);
+
+}
+
+static void instrument_jump_ret(GumX86Writer *cw, void **saved_return_ptr) {
+
+  GumAddress saved_return_address = GUM_ADDRESS(saved_return_ptr);
+
+  /* Place holder for ret */
+  gum_x86_writer_put_push_reg(cw, GUM_REG_EAX);
+  gum_x86_writer_put_push_reg(cw, GUM_REG_EAX);
+
+  gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, saved_return_address);
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EAX, GUM_REG_EAX, 0);
+
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_ESP, 0x4, GUM_REG_EAX);
+  gum_x86_writer_put_pop_reg(cw, GUM_REG_EAX);
+  gum_x86_writer_put_ret(cw);
+
+}
+
+static int instrument_afl_persistent_loop_func(void) {
+
+  int ret = __afl_persistent_loop(persistent_count);
+  previous_pc = 0;
+  return ret;
+
+}
+
+static void instrument_afl_persistent_loop(GumX86Writer *cw) {
+
+  gum_x86_writer_put_call_address_with_arguments(
+      cw, GUM_CALL_CAPI, GUM_ADDRESS(instrument_afl_persistent_loop_func), 0);
+  gum_x86_writer_put_test_reg_reg(cw, GUM_REG_EAX, GUM_REG_EAX);
+
+}
+
+static void persistent_prologue_hook(GumX86Writer *cw, struct x86_regs *regs) {
+
+  if (hook == NULL) return;
+
+  gum_x86_writer_put_mov_reg_address(cw, GUM_REG_ECX,
+                                     GUM_ADDRESS(&__afl_fuzz_len));
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ECX, GUM_REG_ECX, 0);
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ECX, GUM_REG_ECX, 0);
+
+  gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EDX,
+                                     GUM_ADDRESS(&__afl_fuzz_ptr));
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EDX, GUM_REG_EDX, 0);
+
+  /* Base address is 64-bits (hence two zero arguments) */
+  gum_x86_writer_put_call_address_with_arguments(
+      cw, GUM_CALL_CAPI, GUM_ADDRESS(hook), 5, GUM_ARG_ADDRESS,
+      GUM_ADDRESS(regs), GUM_ARG_ADDRESS, GUM_ADDRESS(0), GUM_ARG_ADDRESS,
+      GUM_ADDRESS(0), GUM_ARG_REGISTER, GUM_REG_EDX, GUM_ARG_REGISTER,
+      GUM_REG_ECX);
 
 }
 
 void persistent_prologue(GumStalkerOutput *output) {
 
-  UNUSED_PARAMETER(output);
-  FATAL("Persistent mode not supported on this architecture");
+  /*
+   *  SAVE REGS
+   *  SAVE RET
+   *  POP RET
+   * loop:
+   *  CALL instrument_afl_persistent_loop
+   *  TEST EAX, EAX
+   *  JZ end:
+   *  call hook (optionally)
+   *  RESTORE REGS
+   *  call original
+   *  jmp loop:
+   *
+   * end:
+   *  JMP SAVED RET
+   *
+   * original:
+   *  INSTRUMENTED PERSISTENT FUNC
+   */
+
+  GumX86Writer *cw = output->writer.x86;
+
+  gconstpointer loop = cw->code + 1;
+
+  /* Stack must be 16-byte aligned per ABI */
+  instrument_persitent_save_regs(cw, &saved_regs);
+
+  /* Stash and pop the return value */
+  instrument_save_ret(cw, &saved_return);
+  gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_ESP, GUM_REG_ESP, (4));
+
+  /* loop: */
+  gum_x86_writer_put_label(cw, loop);
+
+  /* call instrument_prologue_func */
+  instrument_afl_persistent_loop(cw);
+
+  /* jz done */
+  gconstpointer done = cw->code + 1;
+  gum_x86_writer_put_jcc_near_label(cw, X86_INS_JE, done, GUM_UNLIKELY);
+
+  /* Optionally call the persistent hook */
+  persistent_prologue_hook(cw, &saved_regs);
+
+  instrument_persitent_restore_regs(cw, &saved_regs);
+  gconstpointer original = cw->code + 1;
+  /* call original */
+  gum_x86_writer_put_call_near_label(cw, original);
+  /* jmp loop */
+  gum_x86_writer_put_jmp_near_label(cw, loop);
+
+  /* done: */
+  gum_x86_writer_put_label(cw, done);
+
+  instrument_jump_ret(cw, &saved_return);
+
+  /* original: */
+  gum_x86_writer_put_label(cw, original);
+
+  gum_x86_writer_flush(cw);
 
 }
 
diff --git a/frida_mode/src/stalker.c b/frida_mode/src/stalker.c
index 81973e9c..63f3c529 100644
--- a/frida_mode/src/stalker.c
+++ b/frida_mode/src/stalker.c
@@ -7,6 +7,8 @@ static GumStalker *stalker = NULL;
 
 void stalker_init(void) {
 
+  if (!gum_stalker_is_supported()) { FATAL("Failed to initialize embedded"); }
+
   stalker = gum_stalker_new();
   if (stalker == NULL) { FATAL("Failed to initialize stalker"); }
 
diff --git a/frida_mode/test/cmplog/GNUmakefile b/frida_mode/test/cmplog/GNUmakefile
index c203fc5e..40de6a09 100644
--- a/frida_mode/test/cmplog/GNUmakefile
+++ b/frida_mode/test/cmplog/GNUmakefile
@@ -2,8 +2,8 @@ PWD:=$(shell pwd)/
 ROOT:=$(shell realpath $(PWD)../../../)/
 BUILD_DIR:=$(PWD)build/
 
-TEST_CMPLOG_DIR:=$(ROOT)qemu_mode/libcompcov/
-TEST_CMPLOG_OBJ=$(TEST_CMPLOG_DIR)compcovtest
+TEST_CMPLOG_SRC=$(PWD)cmplog.c
+TEST_CMPLOG_OBJ=$(BUILD_DIR)compcovtest
 
 TEST_BIN:=$(PWD)../../build/test
 
@@ -13,20 +13,14 @@ CMP_LOG_INPUT:=$(TEST_DATA_DIR)in
 QEMU_OUT:=$(BUILD_DIR)qemu-out
 FRIDA_OUT:=$(BUILD_DIR)frida-out
 
-ARCH=$(shell uname -m)
-ifeq "$(ARCH)" "aarch64"
- AFL_FRIDA_INST_RANGES=$(shell $(PWD)get_section_addrs.py -f $(TEST_CMPLOG_OBJ) -s .text -b 0x0000aaaaaaaaa000)
-endif
+.PHONY: all 32 clean qemu frida format
 
-ifeq "$(ARCH)" "x86_64"
- AFL_FRIDA_INST_RANGES=$(shell $(PWD)get_section_addrs.py -f $(TEST_CMPLOG_OBJ) -s .text -b 0x0000555555554000)
-endif
-
-.PHONY: all clean qemu frida
-
-all:
+all: $(TEST_CMPLOG_OBJ)
 	make -C $(ROOT)frida_mode/
 
+32:
+	CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
 $(BUILD_DIR):
 	mkdir -p $@
 
@@ -34,33 +28,42 @@ $(TEST_DATA_DIR): | $(BUILD_DIR)
 	mkdir -p $@
 
 $(CMP_LOG_INPUT): | $(TEST_DATA_DIR)
-	truncate -s 64 $@
+	echo -n "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" > $@
 
-$(TEST_CMPLOG_OBJ): $(TEST_CMPLOG_DIR)compcovtest.cc
-	make -C $(TEST_CMPLOG_DIR) compcovtest
+$(TEST_CMPLOG_OBJ): $(TEST_CMPLOG_SRC) | $(BUILD_DIR)
+	$(CXX) -std=c++11 -g $(CFLAGS) $(LDFLAGS) $< -o $@
 
 qemu: $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT)
 	$(ROOT)afl-fuzz \
-		-D \
 		-Q \
 		-i $(TEST_DATA_DIR) \
 		-o $(QEMU_OUT) \
 		-c 0 \
 		-l 3AT \
+		-Z \
 		-- \
 			$(TEST_CMPLOG_OBJ) @@
 
 frida: $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT)
-	XAFL_FRIDA_INST_RANGES=$(AFL_FRIDA_INST_RANGES) \
 	$(ROOT)afl-fuzz \
-		-D \
 		-O \
 		-i $(TEST_DATA_DIR) \
 		-o $(FRIDA_OUT) \
 		-c 0 \
 		-l 3AT \
+		-Z \
 		-- \
 			$(TEST_CMPLOG_OBJ) @@
 
+debug: $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT)
+	gdb \
+		--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
+		--ex 'set disassembly-flavor intel' \
+		--ex 'r $(CMP_LOG_INPUT)' \
+		--args $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT)
+
 clean:
-	rm -rf $(BUILD_DIR)
\ No newline at end of file
+	rm -rf $(BUILD_DIR)
+
+format:
+	cd $(ROOT) && echo $(TEST_CMPLOG_SRC) | xargs -L1 ./.custom-format.py -i
diff --git a/frida_mode/test/cmplog/Makefile b/frida_mode/test/cmplog/Makefile
index f322d1f5..606b43a5 100644
--- a/frida_mode/test/cmplog/Makefile
+++ b/frida_mode/test/cmplog/Makefile
@@ -2,6 +2,10 @@ 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
 
@@ -9,4 +13,10 @@ qemu:
 	@gmake qemu
 
 frida:
-	@gmake frida
\ No newline at end of file
+	@gmake frida
+
+format:
+	@gmake format
+
+debug:
+	@gmake debug
diff --git a/frida_mode/test/cmplog/cmplog.c b/frida_mode/test/cmplog/cmplog.c
new file mode 100644
index 00000000..99010645
--- /dev/null
+++ b/frida_mode/test/cmplog/cmplog.c
@@ -0,0 +1,100 @@
+/////////////////////////////////////////////////////////////////////////
+//
+// Author: Mateusz Jurczyk (mjurczyk@google.com)
+//
+// Copyright 2019-2020 Google LLC
+//
+// 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
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// solution: echo -ne 'The quick brown fox jumps over the lazy
+// dog\xbe\xba\xfe\xca\xbe\xba\xfe\xca\xde\xc0\xad\xde\xef\xbe' | ./compcovtest
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char **argv) {
+
+  char buffer[44] = {/* zero padding */};
+
+  FILE *file = stdin;
+
+  if (argc > 1) {
+
+    if ((file = fopen(argv[1], "r")) == NULL) {
+
+      perror(argv[1]);
+      exit(-1);
+
+    }
+
+  }
+
+  fread(buffer, 1, sizeof(buffer) - 1, file);
+
+  if (memcmp(&buffer[0], "The quick brown fox ", 20) != 0 ||
+      strncmp(&buffer[20], "jumps over ", 11) != 0 ||
+      strcmp(&buffer[31], "the lazy dog") != 0) {
+
+    if (argc > 1) { fclose(file); }
+    return 1;
+
+  }
+
+#if defined(__x86_64__)
+  uint64_t x = 0;
+  fread(&x, sizeof(x), 1, file);
+  if (x != 0xCAFEBABECAFEBABE) {
+
+    if (argc > 1) { fclose(file); }
+    return 2;
+
+  }
+
+#endif
+
+  uint32_t y = 0;
+  fread(&y, sizeof(y), 1, file);
+
+  if (y != 0xDEADC0DE) {
+
+    if (argc > 1) { fclose(file); }
+    return 3;
+
+  }
+
+  uint16_t z = 0;
+  fread(&z, sizeof(z), 1, file);
+
+  switch (z) {
+
+    case 0xBEEF:
+      break;
+
+    default:
+      if (argc > 1) { fclose(file); }
+      return 4;
+
+  }
+
+  printf("Puzzle solved, congrats!\n");
+  abort();
+
+  if (argc > 1) { fclose(file); }
+
+  return 0;
+
+}
+
diff --git a/frida_mode/test/entry_point/GNUmakefile b/frida_mode/test/entry_point/GNUmakefile
index 891827eb..c99bcecb 100644
--- a/frida_mode/test/entry_point/GNUmakefile
+++ b/frida_mode/test/entry_point/GNUmakefile
@@ -12,6 +12,18 @@ FRIDA_OUT:=$(BUILD_DIR)frida-out
 
 GET_SYMBOL_ADDR:=$(ROOT)frida_mode/test/png/persistent/get_symbol_addr.py
 
+ifndef ARCH
+
+ARCH=$(shell uname -m)
+ifeq "$(ARCH)" "aarch64"
+ ARCH:=arm64
+endif
+
+ifeq "$(ARCH)" "i686"
+ ARCH:=x86
+endif
+endif
+
 ARCH=$(shell uname -m)
 ifeq "$(ARCH)" "aarch64"
  AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x0000aaaaaaaaa000)
@@ -21,11 +33,18 @@ ifeq "$(ARCH)" "x86_64"
  AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x0000555555554000)
 endif
 
+ifeq "$(ARCH)" "x86"
+ AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x56555000)
+endif
+
 .PHONY: all clean qemu frida
 
 all: $(TESTINSTBIN)
 	make -C $(ROOT)frida_mode/
 
+32:
+	CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
 $(BUILD_DIR):
 	mkdir -p $@
 
@@ -36,7 +55,7 @@ $(TESTINSTR_DATA_FILE): | $(TESTINSTR_DATA_DIR)
 	echo -n "000" > $@
 
 $(TESTINSTBIN): $(TESTINSTSRC) | $(BUILD_DIR)
-	$(CC) -o $@ $<
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
 
 clean:
 	rm -rf $(BUILD_DIR)
@@ -58,4 +77,4 @@ frida_entry: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
 		-i $(TESTINSTR_DATA_DIR) \
 		-o $(FRIDA_OUT) \
 		-- \
-			$(TESTINSTBIN) @@
\ No newline at end of file
+			$(TESTINSTBIN) @@
diff --git a/frida_mode/test/entry_point/Makefile b/frida_mode/test/entry_point/Makefile
index 3b41b94e..75c57e66 100644
--- a/frida_mode/test/entry_point/Makefile
+++ b/frida_mode/test/entry_point/Makefile
@@ -2,6 +2,10 @@ 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
 
@@ -9,4 +13,4 @@ frida:
 	@gmake frida
 
 frida_entry:
-	@gmake frida
\ No newline at end of file
+	@gmake frida
diff --git a/frida_mode/test/entry_point/testinstr.c b/frida_mode/test/entry_point/testinstr.c
index a6c655f9..bd605c52 100644
--- a/frida_mode/test/entry_point/testinstr.c
+++ b/frida_mode/test/entry_point/testinstr.c
@@ -106,7 +106,9 @@ int run(char *file) {
 }
 
 void slow() {
+
   usleep(100000);
+
 }
 
 int main(int argc, char **argv) {
diff --git a/frida_mode/test/exe/GNUmakefile b/frida_mode/test/exe/GNUmakefile
index c543cca8..86e5a461 100644
--- a/frida_mode/test/exe/GNUmakefile
+++ b/frida_mode/test/exe/GNUmakefile
@@ -10,11 +10,14 @@ TESTINSTSRC:=$(PWD)testinstr.c
 QEMU_OUT:=$(BUILD_DIR)qemu-out
 FRIDA_OUT:=$(BUILD_DIR)frida-out
 
-.PHONY: all clean qemu frida
+.PHONY: all 32 clean qemu frida
 
 all: $(TESTINSTBIN)
 	make -C $(ROOT)frida_mode/
 
+32:
+	CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
 $(BUILD_DIR):
 	mkdir -p $@
 
@@ -25,7 +28,7 @@ $(TESTINSTR_DATA_FILE): | $(TESTINSTR_DATA_DIR)
 	echo -n "000" > $@
 
 $(TESTINSTBIN): $(TESTINSTSRC) | $(BUILD_DIR)
-	$(CC) -o $@ $< -no-pie
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -no-pie
 
 clean:
 	rm -rf $(BUILD_DIR)
@@ -47,4 +50,4 @@ frida: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
 		-i $(TESTINSTR_DATA_DIR) \
 		-o $(FRIDA_OUT) \
 		-- \
-			$(TESTINSTBIN) @@
\ No newline at end of file
+			$(TESTINSTBIN) @@
diff --git a/frida_mode/test/exe/Makefile b/frida_mode/test/exe/Makefile
index f322d1f5..4bef1ccb 100644
--- a/frida_mode/test/exe/Makefile
+++ b/frida_mode/test/exe/Makefile
@@ -2,6 +2,10 @@ 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
 
@@ -9,4 +13,4 @@ qemu:
 	@gmake qemu
 
 frida:
-	@gmake frida
\ No newline at end of file
+	@gmake frida
diff --git a/frida_mode/test/fasan/GNUmakefile b/frida_mode/test/fasan/GNUmakefile
new file mode 100644
index 00000000..08b271de
--- /dev/null
+++ b/frida_mode/test/fasan/GNUmakefile
@@ -0,0 +1,159 @@
+PWD:=$(shell pwd)/
+ROOT:=$(shell realpath $(PWD)../../..)/
+BUILD_DIR:=$(PWD)build/
+
+TEST_DATA_DIR:=$(BUILD_DIR)in/
+TEST_DATA_FILE:=$(TEST_DATA_DIR)in
+FRIDA_OUT:=$(BUILD_DIR)frida-out
+
+TEST_SRC:=$(PWD)/test.c
+TEST_BIN:=$(BUILD_DIR)test
+
+CFLAGS+=-fPIC \
+		-D_GNU_SOURCE \
+		-g \
+		-fno-omit-frame-pointer \
+		-Wno-stringop-overflow \
+
+LDFLAGS+=-ldl \
+
+ifdef DEBUG
+CFLAGS+=-Werror \
+		-Wall \
+		-Wextra \
+		-Wpointer-arith
+else
+CFLAGS+=-Wno-pointer-arith
+endif
+
+ifndef ARCH
+
+ARCH=$(shell uname -m)
+ifeq "$(ARCH)" "aarch64"
+ ARCH:=arm64
+endif
+
+ifeq "$(ARCH)" "i686"
+ ARCH:=x86
+endif
+endif
+
+ifeq "$(ARCH)" "x86"
+LIBASAN_FILE:=libclang_rt.asan-i386.so
+endif
+
+ifeq "$(ARCH)" "x86_64"
+LIBASAN_FILE:=libclang_rt.asan-x86_64.so
+endif
+
+ifeq "$(ARCH)" "aarch64"
+LIBASAN_FILE:=libclang_rt.asan-aarch64.so
+endif
+
+# LIBASAN:=/usr/lib/llvm-10/lib/clang/10.0.0/lib/linux/libclang_rt.asan-x86_64.so
+# LIBASAN:=/usr/lib/x86_64-linux-gnu/libasan.so.6.0.0
+
+LLVM_CONFIG ?= llvm-config
+ifeq "$(shell test -e '$(shell which $(LLVM_CONFIG))' && echo 1)" "1"
+  $(info Found llvm-config: '$(shell which $(LLVM_CONFIG))')
+else
+  $(warning Cannot find llvm-config)
+endif
+
+LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null)/
+$(info LLVM_BINDIR: $(LLVM_BINDIR))
+
+CLANG ?= $(LLVM_BINDIR)clang
+ifeq "$(shell test -e '$(CLANG)' && echo 1)" "1"
+  $(info Found clang: '$(CLANG)')
+else
+  $(warning Cannot find clang)
+endif
+
+CLANGVER = $(shell $(CLANG) --version | sed -E -ne '/^.*version\ (1?[0-9]\.[0-9]\.[0-9]).*/s//\1/p')
+$(info Clang version $(CLANGVER))
+
+LLVM_LIBDIR = $(shell $(LLVM_CONFIG) --libdir 2>/dev/null)
+$(info LLVM_LIBDIR: $(LLVM_LIBDIR))
+
+LIBASAN:=$(LLVM_LIBDIR)/clang/$(CLANGVER)/lib/linux/$(LIBASAN_FILE)
+
+ifeq "$(shell test -e '$(LIBASAN)' && echo 1)" "1"
+  $(info Found Address Sanitizer DSO: '$(LIBASAN)')
+else
+  $(error Error cannot find Address Sanitizer DSO)
+endif
+
+
+.PHONY: all 32 clean format frida-noasan frida debug run
+
+############################## ALL #############################################
+
+all: $(TEST_BIN)
+
+32:
+	CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
+$(TEST_BIN): $(TEST_SRC) GNUmakefile | $(BUILD_DIR)
+	$(CC) \
+		$(CFLAGS) \
+		$(LDFLAGS) \
+		-o $@ \
+		$<
+
+$(BUILD_DIR):
+	mkdir -p $(BUILD_DIR)
+
+############################# TESTS ############################################
+
+$(TEST_DATA_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(TEST_DATA_FILE): | $(TEST_DATA_DIR)
+	echo -n "TUODATM" > $@
+
+frida-noasan: $(TEST_BIN) $(TEST_DATA_FILE)
+		$(ROOT)afl-fuzz \
+			-D \
+			-O \
+			-i $(TEST_DATA_DIR) \
+			-o $(FRIDA_OUT) \
+			-- \
+				$(TEST_BIN)
+
+
+frida: $(TEST_BIN) $(TEST_DATA_FILE)
+	AFL_PRELOAD=$(LIBASAN) \
+	AFL_USE_FASAN=1 \
+	$(ROOT)afl-fuzz \
+		-D \
+		-O \
+		-i $(TEST_DATA_DIR) \
+		-o $(FRIDA_OUT) \
+		-- \
+			$(TEST_BIN)
+
+debug: $(TEST_BIN) $(TEST_DATA_FILE)
+	gdb \
+		--ex 'set environment LD_PRELOAD=$(LIBASAN):$(ROOT)afl-frida-trace.so' \
+		--ex 'set environment ASAN_OPTIONS=detect_leaks=false,halt_on_error=0' \
+		--ex 'set environment AFL_USE_FASAN=1' \
+		--ex 'set disassembly-flavor intel' \
+		--ex 'r < $(TEST_DATA_FILE)' \
+		--args $(TEST_BIN) \
+
+run: $(TEST_BIN) $(TEST_DATA_FILE)
+	LD_PRELOAD=$(LIBASAN):$(ROOT)afl-frida-trace.so \
+	ASAN_OPTIONS=detect_leaks=false \
+	AFL_USE_FASAN=1 \
+	$(TEST_BIN) < $(TEST_DATA_FILE)
+
+############################# CLEAN ############################################
+clean:
+	rm -rf $(BUILD_DIR)
+
+############################# FORMAT ###########################################
+format:
+	cd $(ROOT) && echo $(TEST_SRC) | xargs -L1 ./.custom-format.py -i
+
+############################# RUN #############################################
diff --git a/frida_mode/test/fasan/Makefile b/frida_mode/test/fasan/Makefile
new file mode 100644
index 00000000..3b4c71db
--- /dev/null
+++ b/frida_mode/test/fasan/Makefile
@@ -0,0 +1,22 @@
+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
+
+frida-noasan:
+	@gmake frida-noasan
+
+frida:
+	@gmake frida
+
+debug:
+	@gmake debug
+
+run:
+	@gmake run
diff --git a/frida_mode/test/fasan/test.c b/frida_mode/test/fasan/test.c
new file mode 100644
index 00000000..b9a119e6
--- /dev/null
+++ b/frida_mode/test/fasan/test.c
@@ -0,0 +1,90 @@
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#define UNUSED_PARAMETER(x) (void)(x)
+
+#define LOG(x)                              \
+  do {                                      \
+                                            \
+    char buf[] = x;                         \
+    write(STDOUT_FILENO, buf, sizeof(buf)); \
+                                            \
+  } while (false);
+
+void test(char data) {
+
+  char *buf = malloc(10);
+
+  if (buf == NULL) return;
+
+  switch (data) {
+
+    /* Underflow */
+    case 'U':
+      LOG("Underflow\n");
+      buf[-1] = '\0';
+      free(buf);
+      break;
+    /* Overflow */
+    case 'O':
+      LOG("Overflow\n");
+      buf[10] = '\0';
+      free(buf);
+      break;
+    /* Double free */
+    case 'D':
+      LOG("Double free\n");
+      free(buf);
+      free(buf);
+      break;
+    /* Use after free */
+    case 'A':
+      LOG("Use after free\n");
+      free(buf);
+      buf[0] = '\0';
+      break;
+    /* Test Limits (OK) */
+    case 'T':
+      LOG("Test-Limits - No Error\n");
+      buf[0] = 'A';
+      buf[9] = 'I';
+      free(buf);
+      break;
+    case 'M':
+      LOG("Memset too many\n");
+      memset(buf, '\0', 11);
+      free(buf);
+      break;
+    default:
+      LOG("Nop - No Error\n");
+      break;
+
+  }
+
+}
+
+int main(int argc, char **argv) {
+
+  UNUSED_PARAMETER(argc);
+  UNUSED_PARAMETER(argv);
+
+  char input = '\0';
+
+  if (read(STDIN_FILENO, &input, 1) < 0) {
+
+    LOG("Failed to read stdin\n");
+    return 1;
+
+  }
+
+  test(input);
+
+  LOG("DONE\n");
+  return 0;
+
+}
+
diff --git a/frida_mode/test/png/GNUmakefile b/frida_mode/test/png/GNUmakefile
index 515728c4..e05bade2 100644
--- a/frida_mode/test/png/GNUmakefile
+++ b/frida_mode/test/png/GNUmakefile
@@ -35,6 +35,9 @@ FRIDA_OUT:=$(BUILD_DIR)frida-out
 all: $(TEST_BIN)
 	make -C $(ROOT)frida_mode/
 
+32:
+	CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
 $(BUILD_DIR):
 	mkdir -p $@
 
@@ -46,7 +49,7 @@ $(HARNESS_FILE): | $(HARNESS_BUILD_DIR)
 	wget -O $@ $(HARNESS_URL)
 
 $(HARNESS_OBJ): $(HARNESS_FILE)
-	$(CC) -o $@ -c $<
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ -c $<
 
 ######### PNGTEST ########
 
@@ -57,7 +60,7 @@ $(PNGTEST_FILE): | $(PNGTEST_BUILD_DIR)
 	wget -O $@ $(PNGTEST_URL)
 
 $(PNGTEST_OBJ): $(PNGTEST_FILE) | $(LIBPNG_DIR)
-	$(CXX) -std=c++11 -I $(LIBPNG_DIR) -o $@ -c $<
+	$(CXX) $(CFLAGS) $(LDFLAGS) -std=c++11 -I $(LIBPNG_DIR) -o $@ -c $<
 
 ######### LIBPNG ########
 
@@ -80,6 +83,8 @@ $(LIBPNG_LIB): $(LIBPNG_MAKEFILE)
 
 $(TEST_BIN): $(HARNESS_OBJ) $(PNGTEST_OBJ) $(LIBPNG_LIB)
 	$(CXX) \
+		$(CFLAGS) \
+		$(LDFLAGS) \
 		-o $@ \
 		$(HARNESS_OBJ) $(PNGTEST_OBJ) $(LIBPNG_LIB) \
 		-lz \
diff --git a/frida_mode/test/png/Makefile b/frida_mode/test/png/Makefile
index f322d1f5..4bef1ccb 100644
--- a/frida_mode/test/png/Makefile
+++ b/frida_mode/test/png/Makefile
@@ -2,6 +2,10 @@ 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
 
@@ -9,4 +13,4 @@ qemu:
 	@gmake qemu
 
 frida:
-	@gmake frida
\ No newline at end of file
+	@gmake frida
diff --git a/frida_mode/test/png/persistent/GNUmakefile b/frida_mode/test/png/persistent/GNUmakefile
index 531f9bce..ca6f0ff2 100644
--- a/frida_mode/test/png/persistent/GNUmakefile
+++ b/frida_mode/test/png/persistent/GNUmakefile
@@ -8,6 +8,18 @@ TEST_DATA_DIR:=../build/libpng/libpng-1.2.56/contrib/pngsuite/
 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 $(TEST_BIN) -s main -b 0x4000000000)
 
 ARCH=$(shell uname -m)
@@ -19,11 +31,18 @@ ifeq "$(ARCH)" "x86_64"
  AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s main -b 0x0000555555554000)
 endif
 
-.PHONY: all clean qemu qemu_entry frida frida_entry
+ifeq "$(ARCH)" "x86"
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s main -b 0x56555000)
+endif
+
+.PHONY: all 32 clean qemu qemu_entry frida frida_entry
 
 all:
 	make -C $(ROOT)frida_mode/test/png/
 
+32:
+	CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
 $(BUILD_DIR):
 	mkdir -p $@
 
@@ -76,4 +95,4 @@ frida_entry: | $(BUILD_DIR)
 			$(TEST_BIN) @@
 
 clean:
-	rm -rf $(BUILD_DIR)
\ No newline at end of file
+	rm -rf $(BUILD_DIR)
diff --git a/frida_mode/test/png/persistent/Makefile b/frida_mode/test/png/persistent/Makefile
index 5fde63c2..cde0cf30 100644
--- a/frida_mode/test/png/persistent/Makefile
+++ b/frida_mode/test/png/persistent/Makefile
@@ -2,6 +2,10 @@ 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
 
@@ -15,4 +19,4 @@ frida:
 	@gmake frida
 
 frida_entry:
-	@gmake frida_entry
\ No newline at end of file
+	@gmake frida_entry
diff --git a/frida_mode/test/png/persistent/get_symbol_addr.py b/frida_mode/test/png/persistent/get_symbol_addr.py
index 6458c212..1c46e010 100755
--- a/frida_mode/test/png/persistent/get_symbol_addr.py
+++ b/frida_mode/test/png/persistent/get_symbol_addr.py
@@ -33,4 +33,4 @@ def main():
 
 if __name__ == "__main__":
     ret = main()
-    exit(ret)
\ No newline at end of file
+    exit(ret)
diff --git a/frida_mode/test/png/persistent/hook/GNUmakefile b/frida_mode/test/png/persistent/hook/GNUmakefile
index 4f55fe98..82f08fa4 100644
--- a/frida_mode/test/png/persistent/hook/GNUmakefile
+++ b/frida_mode/test/png/persistent/hook/GNUmakefile
@@ -2,8 +2,16 @@ PWD:=$(shell pwd)/
 ROOT:=$(shell realpath $(PWD)../../../../..)/
 BUILD_DIR:=$(PWD)build/
 
-AFLPP_DRIVER_HOOK_DIR=$(ROOT)utils/aflpp_driver/
-AFLPP_DRIVER_HOOK_OBJ=$(AFLPP_DRIVER_HOOK_DIR)aflpp_qemu_driver_hook.so
+AFLPP_DRIVER_HOOK_SRC=$(PWD)aflpp_qemu_driver_hook.c
+AFLPP_DRIVER_HOOK_OBJ=$(BUILD_DIR)aflpp_qemu_driver_hook.so
+
+CFLAGS+=-O3 \
+		-funroll-loops \
+		-g \
+		-fPIC \
+		-funroll-loops \
+
+LDFLAGS+=-shared \
 
 TEST_BIN:=$(PWD)../../build/test
 TEST_DATA_DIR:=../../build/libpng/libpng-1.2.56/contrib/pngsuite/
@@ -12,10 +20,21 @@ AFLPP_DRIVER_DUMMY_INPUT:=$(BUILD_DIR)in
 QEMU_OUT:=$(BUILD_DIR)qemu-out
 FRIDA_OUT:=$(BUILD_DIR)frida-out
 
-AFL_QEMU_PERSISTENT_ADDR=$(shell $(PWD)../get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x4000000000)
+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 $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x4000000000)
+
+ifeq "$(ARCH)" "aarch64"
  AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)../get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000aaaaaaaaa000)
 endif
 
@@ -23,6 +42,18 @@ ifeq "$(ARCH)" "x86_64"
  AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)../get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000555555554000)
 endif
 
+ifeq "$(ARCH)" "x86"
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)../get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x56555000)
+endif
+
+.PHONY: all 32 clean format qemu qemu_entry frida frida_entry debug
+
+all: $(AFLPP_DRIVER_HOOK_OBJ)
+	make -C $(ROOT)frida_mode/test/png/persistent/
+
+32:
+	CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
 .PHONY: all clean qemu qemu_entry frida frida_entry
 
 all:
@@ -37,8 +68,8 @@ $(TEST_DATA_DIR): | $(BUILD_DIR)
 $(AFLPP_DRIVER_DUMMY_INPUT): | $(BUILD_DIR)
 	truncate -s 1M $@
 
-$(AFLPP_DRIVER_HOOK_OBJ): | $(AFLPP_DRIVER_HOOK_DIR)
-	make -C $(AFLPP_DRIVER_HOOK_DIR)
+$(AFLPP_DRIVER_HOOK_OBJ): $(AFLPP_DRIVER_HOOK_SRC) | $(BUILD_DIR)
+	$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
 
 qemu: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
 	AFL_QEMU_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ) \
@@ -93,6 +124,18 @@ frida_entry: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
 		-- \
 			$(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT)
 
+debug:
+	echo $(AFL_FRIDA_PERSISTENT_ADDR)
+	gdb \
+		--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
+		--ex 'set environment AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ)' \
+		--ex 'set environment AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR)' \
+		--ex 'set disassembly-flavor intel' \
+		--args $(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT)
+
 clean:
 	rm -rf $(BUILD_DIR)
 
+format:
+	cd $(ROOT) && echo $(AFLPP_DRIVER_HOOK_SRC) | xargs -L1 ./.custom-format.py -i
+
diff --git a/frida_mode/test/png/persistent/hook/Makefile b/frida_mode/test/png/persistent/hook/Makefile
index 5fde63c2..983d009e 100644
--- a/frida_mode/test/png/persistent/hook/Makefile
+++ b/frida_mode/test/png/persistent/hook/Makefile
@@ -2,9 +2,16 @@ 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
 
+format:
+	@gmake format
+
 qemu:
 	@gmake qemu
 
@@ -15,4 +22,7 @@ frida:
 	@gmake frida
 
 frida_entry:
-	@gmake frida_entry
\ No newline at end of file
+	@gmake frida_entry
+
+debug:
+	@gmake debug
diff --git a/frida_mode/test/png/persistent/hook/aflpp_qemu_driver_hook.c b/frida_mode/test/png/persistent/hook/aflpp_qemu_driver_hook.c
new file mode 100644
index 00000000..059d438d
--- /dev/null
+++ b/frida_mode/test/png/persistent/hook/aflpp_qemu_driver_hook.c
@@ -0,0 +1,97 @@
+#include <stdint.h>
+#include <string.h>
+
+#if defined(__x86_64__)
+
+struct x86_64_regs {
+
+  uint64_t rax, rbx, rcx, rdx, rdi, rsi, rbp, r8, r9, r10, r11, r12, r13, r14,
+      r15;
+
+  union {
+
+    uint64_t rip;
+    uint64_t pc;
+
+  };
+
+  union {
+
+    uint64_t rsp;
+    uint64_t sp;
+
+  };
+
+  union {
+
+    uint64_t rflags;
+    uint64_t flags;
+
+  };
+
+  uint8_t zmm_regs[32][64];
+
+};
+
+void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base,
+                         uint8_t *input_buf, uint32_t input_buf_len) {
+
+  memcpy((void *)regs->rdi, input_buf, input_buf_len);
+  regs->rsi = input_buf_len;
+
+}
+
+#elif defined(__i386__)
+
+struct x86_regs {
+
+  uint32_t eax, ebx, ecx, edx, edi, esi, ebp;
+
+  union {
+
+    uint32_t eip;
+    uint32_t pc;
+
+  };
+
+  union {
+
+    uint32_t esp;
+    uint32_t sp;
+
+  };
+
+  union {
+
+    uint32_t eflags;
+    uint32_t flags;
+
+  };
+
+  uint8_t xmm_regs[8][16];
+
+};
+
+void afl_persistent_hook(struct x86_regs *regs, uint64_t guest_base,
+                         uint8_t *input_buf, uint32_t input_buf_len) {
+
+  void **esp = (void **)regs->esp;
+  void * arg1 = esp[1];
+  void **arg2 = &esp[2];
+  memcpy(arg1, input_buf, input_buf_len);
+  *arg2 = (void *)input_buf_len;
+
+}
+
+#else
+  #pragma error "Unsupported architecture"
+#endif
+
+int afl_persistent_hook_init(void) {
+
+  // 1 for shared memory input (faster), 0 for normal input (you have to use
+  // read(), input_buf will be NULL)
+  return 1;
+
+}
+
diff --git a/frida_mode/test/testinstr/GNUmakefile b/frida_mode/test/testinstr/GNUmakefile
index 4addbad8..a35073ab 100644
--- a/frida_mode/test/testinstr/GNUmakefile
+++ b/frida_mode/test/testinstr/GNUmakefile
@@ -10,11 +10,14 @@ TESTINSTSRC:=$(PWD)testinstr.c
 QEMU_OUT:=$(BUILD_DIR)qemu-out
 FRIDA_OUT:=$(BUILD_DIR)frida-out
 
-.PHONY: all clean qemu frida
+.PHONY: all 32 clean qemu frida
 
 all: $(TESTINSTBIN)
 	make -C $(ROOT)frida_mode/
 
+32:
+	CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
 $(BUILD_DIR):
 	mkdir -p $@
 
@@ -25,7 +28,7 @@ $(TESTINSTR_DATA_FILE): | $(TESTINSTR_DATA_DIR)
 	echo -n "000" > $@
 
 $(TESTINSTBIN): $(TESTINSTSRC) | $(BUILD_DIR)
-	$(CC) -o $@ $<
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
 
 clean:
 	rm -rf $(BUILD_DIR)
@@ -47,4 +50,10 @@ frida: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
 		-i $(TESTINSTR_DATA_DIR) \
 		-o $(FRIDA_OUT) \
 		-- \
-			$(TESTINSTBIN) @@
\ No newline at end of file
+			$(TESTINSTBIN) @@
+
+debug:
+	gdb \
+		--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
+		--ex 'set disassembly-flavor intel' \
+		--args $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
diff --git a/frida_mode/test/testinstr/Makefile b/frida_mode/test/testinstr/Makefile
index f322d1f5..f843af19 100644
--- a/frida_mode/test/testinstr/Makefile
+++ b/frida_mode/test/testinstr/Makefile
@@ -2,6 +2,10 @@ 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
 
@@ -9,4 +13,7 @@ qemu:
 	@gmake qemu
 
 frida:
-	@gmake frida
\ No newline at end of file
+	@gmake frida
+
+debug:
+	@gmake debug
diff --git a/include/envs.h b/include/envs.h
index 9175005e..f1314bad 100644
--- a/include/envs.h
+++ b/include/envs.h
@@ -191,7 +191,9 @@ static char *afl_environment_variables[] = {
     "AFL_WINE_PATH",
     "AFL_NO_SNAPSHOT",
     "AFL_EXPAND_HAVOC_NOW",
+    "AFL_USE_FASAN",
     "AFL_USE_QASAN",
+    "AFL_PRINT_FILENAMES",
     NULL
 
 };
diff --git a/include/forkserver.h b/include/forkserver.h
index 48db94c7..2baa6f0a 100644
--- a/include/forkserver.h
+++ b/include/forkserver.h
@@ -79,6 +79,8 @@ typedef struct afl_forkserver {
 
   bool frida_mode;                     /* if running in frida mode or not   */
 
+  bool frida_asan;                    /* if running with asan in frida mode */
+
   bool use_stdin;                       /* use stdin for sending data       */
 
   bool no_unlink;                       /* do not unlink cur_input          */
diff --git a/qemu_mode/libqasan/hooks.c b/qemu_mode/libqasan/hooks.c
index 0e6c3e08..c542521c 100644
--- a/qemu_mode/libqasan/hooks.c
+++ b/qemu_mode/libqasan/hooks.c
@@ -25,9 +25,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "libqasan.h"
 #include "map_macro.h"
+#include <unistd.h>
+#include <sys/syscall.h>
 
-ssize_t (*__lq_libc_write)(int, const void *, size_t);
-ssize_t (*__lq_libc_read)(int, void *, size_t);
 char *(*__lq_libc_fgets)(char *, int, FILE *);
 int (*__lq_libc_atoi)(const char *);
 long (*__lq_libc_atol)(const char *);
@@ -37,8 +37,6 @@ void __libqasan_init_hooks(void) {
 
   __libqasan_init_malloc();
 
-  __lq_libc_write = ASSERT_DLSYM(write);
-  __lq_libc_read = ASSERT_DLSYM(read);
   __lq_libc_fgets = ASSERT_DLSYM(fgets);
   __lq_libc_atoi = ASSERT_DLSYM(atoi);
   __lq_libc_atol = ASSERT_DLSYM(atol);
@@ -52,7 +50,7 @@ ssize_t write(int fd, const void *buf, size_t count) {
 
   QASAN_DEBUG("%14p: write(%d, %p, %zu)\n", rtv, fd, buf, count);
   QASAN_LOAD(buf, count);
-  ssize_t r = __lq_libc_write(fd, buf, count);
+  ssize_t r = syscall(SYS_write, fd, buf, count);
   QASAN_DEBUG("\t\t = %zd\n", r);
 
   return r;
@@ -65,7 +63,7 @@ ssize_t read(int fd, void *buf, size_t count) {
 
   QASAN_DEBUG("%14p: read(%d, %p, %zu)\n", rtv, fd, buf, count);
   QASAN_STORE(buf, count);
-  ssize_t r = __lq_libc_read(fd, buf, count);
+  ssize_t r = syscall(SYS_read, fd, buf, count);
   QASAN_DEBUG("\t\t = %zd\n", r);
 
   return r;
diff --git a/qemu_mode/libqasan/libqasan.c b/qemu_mode/libqasan/libqasan.c
index 9fc4ef7a..2ac0c861 100644
--- a/qemu_mode/libqasan/libqasan.c
+++ b/qemu_mode/libqasan/libqasan.c
@@ -61,9 +61,17 @@ void __libqasan_print_maps(void) {
 
 }
 
-/*__attribute__((constructor))*/ void __libqasan_init() {
+int __libqasan_is_initialized = 0;
+
+__attribute__((constructor)) void __libqasan_init() {
+
+  if (__libqasan_is_initialized) return;
+  __libqasan_is_initialized = 1;
 
   __libqasan_init_hooks();
+  
+  if (getenv("AFL_INST_LIBS") || getenv("QASAN_HOTPACH"))
+    __libqasan_hotpatch();
 
 #ifdef DEBUG
   __qasan_debug = getenv("QASAN_DEBUG") != NULL;
@@ -86,7 +94,6 @@ int __libc_start_main(int (*main)(int, char **, char **), int argc, char **argv,
   typeof(&__libc_start_main) orig = dlsym(RTLD_NEXT, "__libc_start_main");
 
   __libqasan_init();
-  if (getenv("AFL_INST_LIBS")) __libqasan_hotpatch();
 
   return orig(main, argc, argv, init, fini, rtld_fini, stack_end);
 
diff --git a/src/afl-cc.c b/src/afl-cc.c
index 09009334..ff7b5219 100644
--- a/src/afl-cc.c
+++ b/src/afl-cc.c
@@ -1574,7 +1574,12 @@ int main(int argc, char **argv, char **envp) {
     else if (have_gcc_plugin)
       compiler_mode = GCC_PLUGIN;
     else if (have_gcc)
+#ifdef __APPLE__
+      // on OSX clang masquerades as GCC
+      compiler_mode = CLANG;
+#else
       compiler_mode = GCC;
+#endif
     else if (have_lto)
       compiler_mode = LTO;
     else
@@ -1596,7 +1601,12 @@ int main(int argc, char **argv, char **envp) {
 
   }
 
-  if (compiler_mode == CLANG) { instrument_mode = INSTRUMENT_CLANG; }
+  if (compiler_mode == CLANG) {
+
+    instrument_mode = INSTRUMENT_CLANG;
+    setenv(CLANG_ENV_VAR, "1", 1);  // used by afl-as
+
+  }
 
   if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) {
 
diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c
index a07e78b4..3d472b36 100644
--- a/src/afl-forkserver.c
+++ b/src/afl-forkserver.c
@@ -451,8 +451,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
     /* Dumping cores is slow and can lead to anomalies if SIGKILL is delivered
        before the dump is complete. */
 
-    //    r.rlim_max = r.rlim_cur = 0;
-    //    setrlimit(RLIMIT_CORE, &r);                      /* Ignore errors */
+    if (!fsrv->debug) {
+
+      r.rlim_max = r.rlim_cur = 0;
+      setrlimit(RLIMIT_CORE, &r);                          /* Ignore errors */
+
+    }
 
     /* Isolate the process and configure standard descriptors. If out_file is
        specified, stdin is /dev/null; otherwise, out_fd is cloned instead. */
diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c
index cb586111..c43bcc2b 100644
--- a/src/afl-fuzz-init.c
+++ b/src/afl-fuzz-init.c
@@ -1044,18 +1044,16 @@ void perform_dry_run(afl_state_t *afl) {
 
         /* Remove from fuzzing queue but keep for splicing */
 
-        struct queue_entry *p = afl->queue;
+        if (!q->was_fuzzed) {
 
-        if (!p->was_fuzzed) {
-
-          p->was_fuzzed = 1;
+          q->was_fuzzed = 1;
           --afl->pending_not_fuzzed;
           --afl->active_paths;
 
         }
 
-        p->disabled = 1;
-        p->perf_score = 0;
+        q->disabled = 1;
+        q->perf_score = 0;
 
         u32 i = 0;
         while (unlikely(i < afl->queued_paths && afl->queue_buf[i] &&
@@ -1294,9 +1292,13 @@ void pivot_inputs(afl_state_t *afl) {
 
       if (src_str && sscanf(src_str + 1, "%06u", &src_id) == 1) {
 
-        struct queue_entry *s = afl->queue_buf[src_id];
+        if (src_id < afl->queued_paths) {
+
+          struct queue_entry *s = afl->queue_buf[src_id];
 
-        if (s) { q->depth = s->depth + 1; }
+          if (s) { q->depth = s->depth + 1; }
+
+        }
 
         if (afl->max_depth < q->depth) { afl->max_depth = q->depth; }
 
diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c
index 4eeb93de..4a3e7f33 100644
--- a/src/afl-fuzz-one.c
+++ b/src/afl-fuzz-one.c
@@ -562,7 +562,7 @@ u8 fuzz_one_original(afl_state_t *afl) {
       if (afl->cmplog_lvl == 3 ||
           (afl->cmplog_lvl == 2 && afl->queue_cur->tc_ref) ||
           !(afl->fsrv.total_execs % afl->queued_paths) ||
-          get_cur_time() - afl->last_path_time > 300000) {
+          get_cur_time() - afl->last_path_time > 300000) {  // 300 seconds
 
         if (input_to_state_stage(afl, in_buf, out_buf, len)) {
 
@@ -2013,7 +2013,7 @@ havoc_stage:
 
   }
 
-  if (unlikely(get_cur_time() - afl->last_path_time > 5000 &&
+  if (unlikely(get_cur_time() - afl->last_path_time > 5000 /* 5 seconds */ &&
                afl->ready_for_splicing_count > 1)) {
 
     /* add expensive havoc cases here if there is no findings in the last 5s */
@@ -3060,7 +3060,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) {
       if (afl->cmplog_lvl == 3 ||
           (afl->cmplog_lvl == 2 && afl->queue_cur->tc_ref) ||
           !(afl->fsrv.total_execs % afl->queued_paths) ||
-          get_cur_time() - afl->last_path_time > 300000) {
+          get_cur_time() - afl->last_path_time > 300000) {  // 300 seconds
 
         if (input_to_state_stage(afl, in_buf, out_buf, len)) {
 
diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c
index 313263f9..4884b942 100644
--- a/src/afl-fuzz-stats.c
+++ b/src/afl-fuzz-stats.c
@@ -368,7 +368,8 @@ void maybe_update_plot_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
                  afl->plot_prev_uh == afl->unique_hangs &&
                  afl->plot_prev_md == afl->max_depth &&
                  afl->plot_prev_ed == afl->fsrv.total_execs) ||
-                !afl->queue_cycle || get_cur_time() - afl->start_time <= 60))) {
+                !afl->queue_cycle ||
+                get_cur_time() - afl->start_time <= 60000))) {
 
     return;
 
@@ -393,7 +394,7 @@ void maybe_update_plot_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
   fprintf(afl->fsrv.plot_file,
           "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, %llu, "
           "%u\n",
-          (afl->prev_run_time + get_cur_time() - afl->start_time),
+          ((afl->prev_run_time + get_cur_time() - afl->start_time) / 1000),
           afl->queue_cycle - 1, afl->current_entry, afl->queued_paths,
           afl->pending_not_fuzzed, afl->pending_favored, bitmap_cvg,
           afl->unique_crashes, afl->unique_hangs, afl->max_depth, eps,
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index 8de3ed6b..5f939115 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -328,11 +328,55 @@ static int stricmp(char const *a, char const *b) {
 
 }
 
+static void fasan_check_afl_preload(char *afl_preload) {
+
+  char   first_preload[PATH_MAX + 1] = {0};
+  char * separator = strchr(afl_preload, ':');
+  size_t first_preload_len = PATH_MAX;
+  char * basename;
+  char   clang_runtime_prefix[] = "libclang_rt.asan-";
+
+  if (separator != NULL && (separator - afl_preload) < PATH_MAX) {
+
+    first_preload_len = separator - afl_preload;
+
+  }
+
+  strncpy(first_preload, afl_preload, first_preload_len);
+
+  basename = strrchr(first_preload, '/');
+  if (basename == NULL) {
+
+    basename = first_preload;
+
+  } else {
+
+    basename = basename + 1;
+
+  }
+
+  if (strncmp(basename, clang_runtime_prefix,
+              sizeof(clang_runtime_prefix) - 1) != 0) {
+
+    FATAL("Address Sanitizer DSO must be the first DSO in AFL_PRELOAD");
+
+  }
+
+  if (access(first_preload, R_OK) != 0) {
+
+    FATAL("Address Sanitizer DSO not found");
+
+  }
+
+  OKF("Found ASAN DSO: %s", first_preload);
+
+}
+
 /* Main entry point */
 
 int main(int argc, char **argv_orig, char **envp) {
 
-  s32 opt, i, auto_sync = 0 /*, user_set_cache = 0*/;
+  s32 opt, auto_sync = 0 /*, user_set_cache = 0*/;
   u64 prev_queued = 0;
   u32 sync_interval_cnt = 0, seek_to = 0, show_help = 0,
       map_size = get_map_size();
@@ -785,6 +829,7 @@ int main(int argc, char **argv_orig, char **envp) {
         }
 
         afl->fsrv.frida_mode = 1;
+        if (get_afl_env("AFL_USE_FASAN")) { afl->fsrv.frida_asan = 1; }
 
         break;
 
@@ -1369,18 +1414,26 @@ int main(int argc, char **argv_orig, char **envp) {
       OKF("Injecting %s ...", frida_binary);
       if (afl_preload) {
 
-        frida_afl_preload = alloc_printf("%s:%s", afl_preload, frida_binary);
+        if (afl->fsrv.frida_asan) {
 
-      } else {
+          OKF("Using Frida Address Sanitizer Mode");
 
-        frida_afl_preload = alloc_printf("%s", frida_binary);
+          fasan_check_afl_preload(afl_preload);
 
-      }
+          setenv("ASAN_OPTIONS", "detect_leaks=false", 1);
 
-      ck_free(frida_binary);
+        }
+
+        u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
+        OKF("Injecting %s ...", frida_binary);
+        frida_afl_preload = alloc_printf("%s:%s", afl_preload, frida_binary);
+
+        ck_free(frida_binary);
+
+        setenv("LD_PRELOAD", frida_afl_preload, 1);
+        setenv("DYLD_INSERT_LIBRARIES", frida_afl_preload, 1);
 
-      setenv("LD_PRELOAD", frida_afl_preload, 1);
-      setenv("DYLD_INSERT_LIBRARIES", frida_afl_preload, 1);
+      }
 
     } else {
 
@@ -1391,11 +1444,22 @@ int main(int argc, char **argv_orig, char **envp) {
 
   } else if (afl->fsrv.frida_mode) {
 
-    u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
-    OKF("Injecting %s ...", frida_binary);
-    setenv("LD_PRELOAD", frida_binary, 1);
-    setenv("DYLD_INSERT_LIBRARIES", frida_binary, 1);
-    ck_free(frida_binary);
+    if (afl->fsrv.frida_asan) {
+
+      OKF("Using Frida Address Sanitizer Mode");
+      FATAL(
+          "Address Sanitizer DSO must be loaded using AFL_PRELOAD in Frida "
+          "Address Sanitizer Mode");
+
+    } else {
+
+      u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
+      OKF("Injecting %s ...", frida_binary);
+      setenv("LD_PRELOAD", frida_binary, 1);
+      setenv("DYLD_INSERT_LIBRARIES", frida_binary, 1);
+      ck_free(frida_binary);
+
+    }
 
   }
 
@@ -1770,7 +1834,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
   if (extras_dir_cnt) {
 
-    for (i = 0; i < extras_dir_cnt; i++) {
+    for (u8 i = 0; i < extras_dir_cnt; i++) {
 
       load_extras(afl, extras_dir[i]);
 
@@ -1922,6 +1986,13 @@ int main(int argc, char **argv_orig, char **envp) {
 
         if (unlikely(seek_to)) {
 
+          if (unlikely(seek_to >= afl->queued_paths)) {
+
+            // This should never happen.
+            FATAL("BUG: seek_to location out of bounds!\n");
+
+          }
+
           afl->current_entry = seek_to;
           afl->queue_cur = afl->queue_buf[seek_to];
           seek_to = 0;
@@ -1940,8 +2011,10 @@ int main(int argc, char **argv_orig, char **envp) {
       /* If we had a full queue cycle with no new finds, try
          recombination strategies next. */
 
-      if (unlikely(afl->queued_paths == prev_queued &&
-                   (get_cur_time() - afl->start_time) >= 3600)) {
+      if (unlikely(afl->queued_paths == prev_queued
+                   /* FIXME TODO BUG: && (get_cur_time() - afl->start_time) >=
+                      3600 */
+                   )) {
 
         if (afl->use_splicing) {
 
@@ -2059,7 +2132,7 @@ int main(int argc, char **argv_orig, char **envp) {
         }
 
         // we must recalculate the scores of all queue entries
-        for (i = 0; i < (s32)afl->queued_paths; i++) {
+        for (u32 i = 0; i < afl->queued_paths; i++) {
 
           if (likely(!afl->queue_buf[i]->disabled)) {
 
diff --git a/src/afl-showmap.c b/src/afl-showmap.c
index 946b19cd..9b4d21a5 100644
--- a/src/afl-showmap.c
+++ b/src/afl-showmap.c
@@ -76,17 +76,18 @@ static u32 in_len;                     /* Input data length                 */
 
 static u32 map_size = MAP_SIZE;
 
-static u8 quiet_mode,                  /* Hide non-essential messages?      */
+static bool quiet_mode,                /* Hide non-essential messages?      */
     edges_only,                        /* Ignore hit counts?                */
     raw_instr_output,                  /* Do not apply AFL filters          */
     cmin_mode,                         /* Generate output in afl-cmin mode? */
     binary_mode,                       /* Write output as a binary map      */
     keep_cores,                        /* Allow coredumps?                  */
-    remove_shm = 1,                    /* remove shmem?                     */
+    remove_shm = true,                 /* remove shmem?                     */
     collect_coverage,                  /* collect coverage                  */
     have_coverage,                     /* have coverage?                    */
     no_classify,                       /* do not classify counts            */
-    debug;                             /* debug mode                        */
+    debug,                             /* debug mode                        */
+    print_filenames;                   /* print the current filename        */
 
 static volatile u8 stop_soon,          /* Ctrl-C pressed?                   */
     child_crashed;                     /* Child crashed?                    */
@@ -320,11 +321,11 @@ static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem,
   if (fsrv->trace_bits[0] == 1) {
 
     fsrv->trace_bits[0] = 0;
-    have_coverage = 1;
+    have_coverage = true;
 
   } else {
 
-    have_coverage = 0;
+    have_coverage = false;
 
   }
 
@@ -335,11 +336,11 @@ static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem,
   if (!fsrv->last_run_timed_out && !stop_soon &&
       WIFSIGNALED(fsrv->child_status)) {
 
-    child_crashed = 1;
+    child_crashed = true;
 
   } else {
 
-    child_crashed = 0;
+    child_crashed = false;
 
   }
 
@@ -375,6 +376,13 @@ static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem,
 
 static u32 read_file(u8 *in_file) {
 
+  if (print_filenames) {
+
+    SAYF("Processing %s\n", in_file);
+    fflush(stdout);
+
+  }
+
   struct stat st;
   s32         fd = open(in_file, O_RDONLY);
 
@@ -386,7 +394,18 @@ static u32 read_file(u8 *in_file) {
 
   }
 
-  in_len = st.st_size;
+  if (st.st_size > MAX_FILE) {
+
+    WARNF("Input file '%s' is too large, only reading %u bytes.", in_file,
+          MAX_FILE);
+    in_len = MAX_FILE;
+
+  } else {
+
+    in_len = st.st_size;
+
+  }
+
   in_data = ck_alloc_nozero(in_len);
 
   ck_read(fd, in_data, in_len, in_file);
@@ -504,11 +523,11 @@ static void showmap_run_target(afl_forkserver_t *fsrv, char **argv) {
   if (fsrv->trace_bits[0] == 1) {
 
     fsrv->trace_bits[0] = 0;
-    have_coverage = 1;
+    have_coverage = true;
 
   } else {
 
-    have_coverage = 0;
+    have_coverage = false;
 
   }
 
@@ -518,7 +537,7 @@ static void showmap_run_target(afl_forkserver_t *fsrv, char **argv) {
 
   if (!fsrv->last_run_timed_out && !stop_soon && WIFSIGNALED(status)) {
 
-    child_crashed = 1;
+    child_crashed = true;
 
   }
 
@@ -548,7 +567,7 @@ static void showmap_run_target(afl_forkserver_t *fsrv, char **argv) {
 static void handle_stop_sig(int sig) {
 
   (void)sig;
-  stop_soon = 1;
+  stop_soon = true;
   afl_fsrv_killall();
 
 }
@@ -731,6 +750,8 @@ static void usage(u8 *argv0) {
       "AFL_MAP_SIZE: the shared memory size for that target. must be >= the "
       "size the target was compiled for\n"
       "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n"
+      "AFL_PRINT_FILENAMES: If set, the filename currently processed will be "
+      "printed to stdout\n"
       "AFL_QUIET: do not print extra informational output\n",
       argv0, MEM_LIMIT, doc_path);
 
@@ -744,14 +765,17 @@ int main(int argc, char **argv_orig, char **envp) {
 
   // TODO: u64 mem_limit = MEM_LIMIT;                  /* Memory limit (MB) */
 
-  s32    opt, i;
-  u8     mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0;
+  s32  opt, i;
+  bool mem_limit_given = false, timeout_given = false, unicorn_mode = false,
+       use_wine = false;
   char **use_argv;
 
   char **argv = argv_cpy_dup(argc, argv_orig);
 
   afl_forkserver_t fsrv_var = {0};
-  if (getenv("AFL_DEBUG")) { debug = 1; }
+  if (getenv("AFL_DEBUG")) { debug = true; }
+  if (get_afl_env("AFL_PRINT_FILENAMES")) { print_filenames = true; }
+
   fsrv = &fsrv_var;
   afl_fsrv_init(fsrv);
   map_size = get_map_size();
@@ -759,19 +783,19 @@ int main(int argc, char **argv_orig, char **envp) {
 
   doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
 
-  if (getenv("AFL_QUIET") != NULL) { be_quiet = 1; }
+  if (getenv("AFL_QUIET") != NULL) { be_quiet = true; }
 
   while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqCZOQUWbcrsh")) > 0) {
 
     switch (opt) {
 
       case 's':
-        no_classify = 1;
+        no_classify = true;
         break;
 
       case 'C':
-        collect_coverage = 1;
-        quiet_mode = 1;
+        collect_coverage = true;
+        quiet_mode = true;
         break;
 
       case 'i':
@@ -790,7 +814,7 @@ int main(int argc, char **argv_orig, char **envp) {
         u8 suffix = 'M';
 
         if (mem_limit_given) { FATAL("Multiple -m options not supported"); }
-        mem_limit_given = 1;
+        mem_limit_given = true;
 
         if (!optarg) { FATAL("Wrong usage of -m"); }
 
@@ -851,7 +875,7 @@ int main(int argc, char **argv_orig, char **envp) {
       case 't':
 
         if (timeout_given) { FATAL("Multiple -t options not supported"); }
-        timeout_given = 1;
+        timeout_given = true;
 
         if (!optarg) { FATAL("Wrong usage of -t"); }
 
@@ -873,12 +897,12 @@ int main(int argc, char **argv_orig, char **envp) {
 
         if (edges_only) { FATAL("Multiple -e options not supported"); }
         if (raw_instr_output) { FATAL("-e and -r are mutually exclusive"); }
-        edges_only = 1;
+        edges_only = true;
         break;
 
       case 'q':
 
-        quiet_mode = 1;
+        quiet_mode = true;
         break;
 
       case 'Z':
@@ -886,8 +910,8 @@ int main(int argc, char **argv_orig, char **envp) {
         /* This is an undocumented option to write data in the syntax expected
            by afl-cmin. Nobody else should have any use for this. */
 
-        cmin_mode = 1;
-        quiet_mode = 1;
+        cmin_mode = true;
+        quiet_mode = true;
         break;
 
       case 'A':
@@ -899,7 +923,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
         if (fsrv->frida_mode) { FATAL("Multiple -O options not supported"); }
 
-        fsrv->frida_mode = 1;
+        fsrv->frida_mode = true;
 
         break;
 
@@ -907,21 +931,21 @@ int main(int argc, char **argv_orig, char **envp) {
 
         if (fsrv->qemu_mode) { FATAL("Multiple -Q options not supported"); }
 
-        fsrv->qemu_mode = 1;
+        fsrv->qemu_mode = true;
         break;
 
       case 'U':
 
         if (unicorn_mode) { FATAL("Multiple -U options not supported"); }
 
-        unicorn_mode = 1;
+        unicorn_mode = true;
         break;
 
       case 'W':                                           /* Wine+QEMU mode */
 
         if (use_wine) { FATAL("Multiple -W options not supported"); }
-        fsrv->qemu_mode = 1;
-        use_wine = 1;
+        fsrv->qemu_mode = true;
+        use_wine = true;
 
         break;
 
@@ -930,20 +954,20 @@ int main(int argc, char **argv_orig, char **envp) {
         /* Secret undocumented mode. Writes output in raw binary format
            similar to that dumped by afl-fuzz in <out_dir/queue/fuzz_bitmap. */
 
-        binary_mode = 1;
+        binary_mode = true;
         break;
 
       case 'c':
 
         if (keep_cores) { FATAL("Multiple -c options not supported"); }
-        keep_cores = 1;
+        keep_cores = true;
         break;
 
       case 'r':
 
         if (raw_instr_output) { FATAL("Multiple -r options not supported"); }
         if (edges_only) { FATAL("-e and -r are mutually exclusive"); }
-        raw_instr_output = 1;
+        raw_instr_output = true;
         break;
 
       case 'h':
@@ -1053,7 +1077,7 @@ int main(int argc, char **argv_orig, char **envp) {
   /* initialize cmplog_mode */
   shm_fuzz->cmplog_mode = 0;
   u8 *map = afl_shm_init(shm_fuzz, MAX_FILE + sizeof(u32), 1);
-  shm_fuzz->shmemfuzz_mode = 1;
+  shm_fuzz->shmemfuzz_mode = true;
   if (!map) { FATAL("BUG: Zero return from afl_shm_init."); }
 #ifdef USEMMAP
   setenv(SHM_FUZZ_ENV_VAR, shm_fuzz->g_shm_file_path, 1);
@@ -1062,7 +1086,7 @@ int main(int argc, char **argv_orig, char **envp) {
   setenv(SHM_FUZZ_ENV_VAR, shm_str, 1);
   ck_free(shm_str);
 #endif
-  fsrv->support_shmem_fuzz = 1;
+  fsrv->support_shmem_fuzz = true;
   fsrv->shmem_fuzz_len = (u32 *)map;
   fsrv->shmem_fuzz = map + sizeof(u32);
 
@@ -1114,7 +1138,7 @@ int main(int argc, char **argv_orig, char **envp) {
     struct stat statbuf;
 #endif
 
-    if (getenv("AFL_DEBUG_GDB")) wait_for_gdb = 1;
+    if (getenv("AFL_DEBUG_GDB")) wait_for_gdb = true;
 
     fsrv->dev_null_fd = open("/dev/null", O_RDWR);
     if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); }
@@ -1153,8 +1177,8 @@ int main(int argc, char **argv_orig, char **envp) {
 
       if ((coverage_map = (u8 *)malloc(map_size)) == NULL)
         FATAL("coult not grab memory");
-      edges_only = 0;
-      raw_instr_output = 1;
+      edges_only = false;
+      raw_instr_output = true;
 
     }