about summary refs log tree commit diff
diff options
context:
space:
mode:
authorWorksButNotTested <62701594+WorksButNotTested@users.noreply.github.com>2021-05-17 19:02:45 +0100
committerGitHub <noreply@github.com>2021-05-17 20:02:45 +0200
commite40c0c2da16f14dfddb5641f6f825903879534a9 (patch)
tree363135f288fd63253471a0455ebadd24a512a1ce
parent9d50ae7468970412177c9e08edf7f32ff9fdf1ce (diff)
downloadafl++-e40c0c2da16f14dfddb5641f6f825903879534a9.tar.gz
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>
-rw-r--r--frida_mode/include/asan.h13
-rw-r--r--frida_mode/include/ctx.h11
-rw-r--r--frida_mode/src/asan/asan.c24
-rw-r--r--frida_mode/src/asan/asan_arm.c22
-rw-r--r--frida_mode/src/asan/asan_arm64.c22
-rw-r--r--frida_mode/src/asan/asan_x64.c93
-rw-r--r--frida_mode/src/asan/asan_x86.c22
-rw-r--r--frida_mode/src/cmplog/cmplog_x64.c119
-rw-r--r--frida_mode/src/ctx/ctx_x64.c114
-rw-r--r--frida_mode/src/instrument/instrument.c3
-rw-r--r--frida_mode/test/fasan/GNUmakefile156
-rw-r--r--frida_mode/test/fasan/Makefile18
-rw-r--r--frida_mode/test/fasan/test.c85
-rw-r--r--include/envs.h1
-rw-r--r--include/forkserver.h2
-rw-r--r--src/afl-fuzz.c81
16 files changed, 664 insertions, 122 deletions
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..030d124a
--- /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__)
+guint64 ctx_read_reg(GumX64CpuContext *ctx, x86_reg reg);
+#endif
+
+#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..526017be
--- /dev/null
+++ b/frida_mode/src/asan/asan_arm.c
@@ -0,0 +1,22 @@
+#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");
+
+  }
+
+}
+
+#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..4e3fbafd
--- /dev/null
+++ b/frida_mode/src/asan/asan_arm64.c
@@ -0,0 +1,22 @@
+#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");
+
+  }
+
+}
+
+#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..bdf4ac30
--- /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"
+
+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;
+
+#if defined(__x86_64__)
+
+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;
+  uint64_t    base = 0;
+  uint64_t    index = 0;
+  uint64_t    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..b946b3bf
--- /dev/null
+++ b/frida_mode/src/asan/asan_x86.c
@@ -0,0 +1,22 @@
+#include "frida-gum.h"
+
+#include "debug.h"
+
+#include "asan.h"
+#include "util.h"
+
+#if defined(__i386__)
+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");
+
+  }
+
+}
+
+#endif
+
diff --git a/frida_mode/src/cmplog/cmplog_x64.c b/frida_mode/src/cmplog/cmplog_x64.c
index 4d8f243a..c3621a29 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,75 +31,6 @@ 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(GumX64CpuContext *ctx, uint8_t size,
                                 x86_op_mem *mem, guint64 *val) {
 
@@ -141,9 +38,9 @@ static gboolean cmplog_read_mem(GumX64CpuContext *ctx, uint8_t size,
   guint64 index = 0;
   guint64 address;
 
-  if (mem->base != X86_REG_INVALID) base = cmplog_read_reg(ctx, mem->base);
+  if (mem->base != X86_REG_INVALID) base = ctx_read_reg(ctx, mem->base);
 
-  if (mem->index != X86_REG_INVALID) index = cmplog_read_reg(ctx, mem->index);
+  if (mem->index != X86_REG_INVALID) index = ctx_read_reg(ctx, mem->index);
 
   address = base + (index * mem->scale) + mem->disp;
 
@@ -178,7 +75,7 @@ static gboolean cmplog_get_operand_value(GumCpuContext *context,
   switch (ctx->type) {
 
     case X86_OP_REG:
-      *val = cmplog_read_reg(context, ctx->reg);
+      *val = ctx_read_reg(context, ctx->reg);
       return TRUE;
     case X86_OP_IMM:
       *val = ctx->imm;
@@ -198,9 +95,9 @@ 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);
+  guint64 address = ctx_read_reg(context, X86_REG_RIP);
+  guint64 rdi = ctx_read_reg(context, X86_REG_RDI);
+  guint64 rsi = ctx_read_reg(context, X86_REG_RSI);
 
   if (((G_MAXULONG - rdi) < 32) || ((G_MAXULONG - rsi) < 32)) return;
 
@@ -275,7 +172,7 @@ static void cmplog_instrument_call(const cs_insn *     instr,
 static void cmplog_handle_cmp_sub(GumCpuContext *context, guint64 operand1,
                                   guint64 operand2, uint8_t size) {
 
-  guint64 address = cmplog_read_reg(context, X86_REG_RIP);
+  guint64 address = ctx_read_reg(context, X86_REG_RIP);
 
   register uintptr_t k = (uintptr_t)address;
 
diff --git a/frida_mode/src/ctx/ctx_x64.c b/frida_mode/src/ctx/ctx_x64.c
new file mode 100644
index 00000000..dec759f4
--- /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);              \
+                                 \
+    }
+
+guint64 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/instrument/instrument.c b/frida_mode/src/instrument/instrument.c
index 971f80c0..5c77ade6 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"
@@ -107,6 +108,7 @@ static void instr_basic_block(GumStalkerIterator *iterator,
 
     if (!range_is_excluded((void *)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/test/fasan/GNUmakefile b/frida_mode/test/fasan/GNUmakefile
new file mode 100644
index 00000000..22689395
--- /dev/null
+++ b/frida_mode/test/fasan/GNUmakefile
@@ -0,0 +1,156 @@
+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)" "x64"
+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 clean format frida-noasan frida debug run
+
+############################## ALL #############################################
+
+all: $(TEST_BIN)
+
+$(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=/usr/lib/llvm-10/lib/clang/10.0.0/lib/linux/libclang_rt.asan-x86_64.so \
+	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..a7bf44c7
--- /dev/null
+++ b/frida_mode/test/fasan/Makefile
@@ -0,0 +1,18 @@
+all:
+	@echo trying to use GNU make...
+	@gmake all || echo please install GNUmake
+
+clean:
+	@gmake clean
+
+frida-noasan:
+	@gmake frida-noasan
+
+frida:
+	@gmake frida
+
+debug:
+	@gmake debug
+
+run:
+	@gmake run
\ No newline at end of file
diff --git a/frida_mode/test/fasan/test.c b/frida_mode/test/fasan/test.c
new file mode 100644
index 00000000..a7d03017
--- /dev/null
+++ b/frida_mode/test/fasan/test.c
@@ -0,0 +1,85 @@
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#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) {
+
+  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/include/envs.h b/include/envs.h
index 9175005e..4fff1e3a 100644
--- a/include/envs.h
+++ b/include/envs.h
@@ -191,6 +191,7 @@ static char *afl_environment_variables[] = {
     "AFL_WINE_PATH",
     "AFL_NO_SNAPSHOT",
     "AFL_EXPAND_HAVOC_NOW",
+    "AFL_USE_FASAN",
     "AFL_USE_QASAN",
     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/src/afl-fuzz.c b/src/afl-fuzz.c
index a4599b4a..903068b2 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -328,6 +328,50 @@ 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) {
@@ -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;
 
@@ -1365,18 +1410,21 @@ int main(int argc, char **argv_orig, char **envp) {
     } else if (afl->fsrv.frida_mode) {
 
       afl_preload = getenv("AFL_PRELOAD");
-      u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
-      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");
+
+        fasan_check_afl_preload(afl_preload);
 
-        frida_afl_preload = alloc_printf("%s", frida_binary);
+        setenv("ASAN_OPTIONS", "detect_leaks=false", 1);
 
       }
 
+      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);
@@ -1391,11 +1439,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);
+
+    }
 
   }