diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..c9d1d41f01ef2c55bc1cad746e733fe0c84066bf
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,48 @@
+BINARY=passbook
+
+#VULNS=vuln1 vuln2 vuln3 vuln4 vuln5
+
+VBINARIES=$(BINARY) $(VULNS:%=${BINARY}-%)
+
+TARGETS=$(VBINARIES) $(VBINARIES:%=%-san) $(VBINARIES:%=%-fuzz) $(VBINARIES:%=%-cov)
+
+HEADERS=$(wildcard *.h)
+
+# allow the user to provide additional CFLAGS by doing e.g. CFLAGS=blah make
+CFLAGS += -W -Wall -Wpedantic -Wno-language-extension-token
+
+# allow the user to override what clang we use by doing e.g. CLANG=blah make
+CLANG ?= clang-6.0
+CC=$(CLANG)
+
+SAN_FLAGS ?= -fsanitize=address -fno-omit-frame-pointer
+FUZZ_FLAGS ?= -DPASSBOOK_LIBFUZZER -fsanitize=fuzzer,address -fno-omit-frame-pointer
+NO_STRICT_OVERFLOW_CFLAGS ?= -fwrapv -fno-strict-overflow -Wstrict-overflow
+COV_FLAGS ?= -fprofile-instr-generate -fcoverage-mapping
+
+default: $(TARGETS)
+
+.PHONY: default
+
+
+%: %.c $(HEADERS)
+	$(CC) $< $(CFLAGS) $(LDFLAGS) $(NO_STRICT_OVERFLOW_CFLAGS) -o $@ 
+
+
+%-san: %.c $(HEADERS)
+	$(CLANG) $< $(CFLAGS) $(LDFLAGS) $(SAN_FLAGS) $(NO_STRICT_OVERFLOW_CFLAGS) -o $@
+
+
+# build a self-fuzzing binary with libFuzzer
+# needs a recent clang version (e.g. clang-6.0)
+# then to run it:
+# ./passbook-fuzz ~/tmp/corpus/ ~/tmp/findings/ -timeout=5 -only-ascii=1 -dict=libfuzzer-dict -max_total_time=1200 -print_final_stats=1
+%-fuzz: %.c $(HEADERS)
+	$(CLANG) $< $(CFLAGS) $(LDFLAGS) $(FUZZ_FLAGS) $(NO_STRICT_OVERFLOW_CFLAGS) -o $@
+
+%-cov: %.c $(HEADERS)
+	$(CLANG) $< $(CFLAGS) $(LDFLAGS) $(COV_FLAGS) $(NO_STRICT_OVERFLOW_CFLAGS) -o $@
+
+clean:
+	rm -f $(TARGETS) *.profraw *.profdata
+
diff --git a/src/get_coverage.sh b/src/get_coverage.sh
new file mode 100755
index 0000000000000000000000000000000000000000..6768421d37de15b266e5c92b5bd437755313a514
--- /dev/null
+++ b/src/get_coverage.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+if [ -z ${TOOL_SUFFIX+x} ]
+then
+    # TOOL_SUFFIX not set
+    # try to intelligently set TOOL_SUFFIX
+    case $(uname) in
+        Linux)
+            TOOL_SUFFIX=-6.0
+            ;;
+        Darwin)
+            TOOL_SUFFIX=
+            ;;
+        *)
+            TOOL_SUFFIX=-6.0
+    esac
+fi
+
+
+CLANG=clang${TOOL_SUFFIX}
+LLVM_PROFDATA=llvm-profdata${TOOL_SUFFIX}
+LLVM_COV=llvm-cov${TOOL_SUFFIX}
+
+if [ -z "$(which ${CLANG})" ]
+then
+    echo "${CLANG} doesn't exist. Try setting TOOL_SUFFIX environment variable"
+    exit 1
+fi
+
+if [ -z "$(which ${LLVM_PROFDATA})" ]
+then
+    echo "${LLVM_PROFDATA} doesn't exist. Try setting TOOL_SUFFIX environment variable"
+    exit 1
+fi
+
+if [ -z "$(which ${LLVM_COV})" ]
+then
+    echo "${LLVM_COV} doesn't exist. Try setting TOOL_SUFFIX environment variable"
+    exit 1
+fi
+
+echo "using ${CLANG}, ${LLVM_PROFDATA} and ${LLVM_COV}"
+
+export LLVM_PROFILE_FILE="passbook-%m.profraw"
+
+if [ $# -lt 1 ]
+then
+    echo "Usage: $0 inputfile1 [inputfile2 ...]"
+    exit 1
+fi
+
+
+rm -f passbook*.profraw passbook.profdata
+
+echo "First re-building to make sure -DNDEBUG is turned on..."
+rm -f passbook-cov
+CLANG=${CLANG} CFLAGS="-DNDEBUG ${CFLAGS}" make passbook-cov
+
+./passbook-cov $*
+
+${LLVM_PROFDATA} merge -sparse passbook*.profraw -o passbook.profdata
+${LLVM_COV} show ./passbook-cov -instr-profile=passbook.profdata
+${LLVM_COV} report ./passbook-cov -instr-profile=passbook.profdata
diff --git a/src/passbook.c b/src/passbook.c
index a4e843882c826429691c03f7171ba35d01a1dee6..072b1ceb496f6a8a25c0a20b62c059b3505bd8d6 100644
--- a/src/passbook.c
+++ b/src/passbook.c
@@ -5,6 +5,13 @@
 
 #include "debug.h"
 
+#ifdef PASSBOOK_LIBFUZZER
+#include <stdint.h>
+const char LIBFUZZER_INPUT_FILE[] = "libFuzzerInput.tmp";
+/* turn off tracing to make it run faster */
+#define printf(...)
+#define fprintf(...)
+#endif
 
 const char INSTRUCTION_PUT[] = "put";
 
@@ -88,12 +95,19 @@ static node_t * node_insert(node_t *p, node_t *q){
   if (p == NULL){
     return q;
   }
-  
+  if (q == NULL){
+    return p;
+  }
   /* we store a pointer to a node pointer that remembers where in the 
      tree the new node needs to be added */
   node_t ** new = NULL;
   node_t * const start = p;
   while (new == NULL) {
+    assert(q->url != NULL);
+    assert(p->url != NULL);
+    printf("Addr of dodgy node: %08x\n",(unsigned int)q);
+    strlen(q->url);  // SEGV here
+    strlen(p->url);
     int ret = strcmp(q->url,p->url);
     if (ret == 0){
       assert (q->left == NULL && q->right == NULL && "illegal insertion");
@@ -123,6 +137,7 @@ static node_t * node_insert(node_t *p, node_t *q){
 /* returns a pointer to the tree with the node added or with the existing
    node updated if it was  already present */
 static node_t * put(node_t *p, const char *url, const cred_t cred){
+  strlen(url);
   return node_insert(p,node_new(url,cred));
 }
 
@@ -529,7 +544,17 @@ static int run(FILE *f){
   return 0;
 }
 
-
+#ifdef PASSBOOK_LIBFUZZER
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+  FILE *f = fopen(LIBFUZZER_INPUT_FILE,"w");
+  fwrite(Data,Size,1,f);
+  fclose(f);
+  f = fopen(LIBFUZZER_INPUT_FILE,"r");
+  run(f);
+  fclose(f);
+  return 0; /* libFuzzer wants 0 returned always */
+}
+#else
 int main(const int argc, const char * argv[]){
   cred_t cred1, cred2;
   cred1.password = "asdfa";
@@ -553,6 +578,7 @@ int main(const int argc, const char * argv[]){
   }
   
   for (int i = 1; i<argc; i++){
+    printf("Running on input file %s\n",argv[i]);
     FILE *f;
     if (strcmp(argv[i],"-") == 0){
       f = stdin;
@@ -566,7 +592,6 @@ int main(const int argc, const char * argv[]){
     int ans = run(f);
     if (ans < 0){
       fprintf(stderr,"Error\n");
-      exit(1);
     }
     /* do not close stdin */
     if (f != stdin){
@@ -575,3 +600,4 @@ int main(const int argc, const char * argv[]){
   }
   return 0;
 }
+#endif