Add fuzzer

Signed-off-by: Guido Vranken <guidovranken@gmail.com>
diff --git a/Makefile.in b/Makefile.in
index 7487c4a..aabfda0 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -47,10 +47,13 @@
 # makefile variables
 
 CC	= @CC@
+CXX	= @CXX@
 INCDIR	= -Icrypto/include -I$(srcdir)/include -I$(srcdir)/crypto/include
 DEFS	= @DEFS@
 CPPFLAGS= @CPPFLAGS@
 CFLAGS	= @CFLAGS@
+srtp-fuzzer: CFLAGS += -g
+srtp-fuzzer: CPPFLAGS += -g
 LIBS	= @LIBS@
 LDFLAGS	= -L. @LDFLAGS@
 COMPILE = $(CC) $(DEFS) $(INCDIR) $(CPPFLAGS) $(CFLAGS)
@@ -241,6 +244,11 @@
 libsrtp2doc:
 	$(MAKE) -C doc
 
+# fuzzer
+
+srtp-fuzzer: libsrtp2.a
+	$(MAKE) -C fuzzer
+
 .PHONY: clean superclean distclean install
 
 install:
@@ -279,6 +287,7 @@
 	rm -rf *.pict *.jpg *.dat
 	rm -rf freed allocated tmp
 	$(MAKE) -C doc clean
+	$(MAKE) -C doc fuzzer
 
 superclean: clean
 	rm -rf crypto/include/config.h config.log config.cache config.status \
diff --git a/configure.ac b/configure.ac
index c0e0490..070bf57 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11,6 +11,7 @@
 dnl Checks for programs.
 AC_PROG_CC
 AC_PROG_CPP
+AC_PROG_CXX
 AC_ARG_VAR(
   [EXTRA_CFLAGS],
   [C compiler flags appended to the regular C compiler flags instead of overriding them])
@@ -200,6 +201,46 @@
 fi
 AC_MSG_RESULT([$enable_debug_logging])
 
+AC_MSG_CHECKING([whether to build with AddressSanitizer enabled])
+AC_ARG_ENABLE([asan],
+  [AS_HELP_STRING([--enable-asan], [Enable building with AddressSanitizer enabled])],
+  [], enable_asan=no)
+if test "$enable_asan" = "yes"; then
+   AC_DEFINE([ENABLE_ASAN], [1], [Define to build with AddressSanitizer enabled.])
+   [CFLAGS="$CFLAGS -fsanitize=address"]
+fi
+AC_MSG_RESULT([$enable_asan])
+
+AC_MSG_CHECKING([whether to build with UndefinedBehaviorSanitizer enabled])
+AC_ARG_ENABLE([ubsan],
+  [AS_HELP_STRING([--enable-ubsan], [Enable building with UndefinedBehaviorSanitizer enabled])],
+  [], enable_ubsan=no)
+if test "$enable_ubsan" = "yes"; then
+   AC_DEFINE([ENABLE_UBSAN], [1], [Define to build with UndefinedBehaviorSanitizer enabled.])
+   [CFLAGS="$CFLAGS -fsanitize=undefined"]
+fi
+AC_MSG_RESULT([$enable_ubsan])
+
+AC_MSG_CHECKING([whether to build with MemorySanitizer enabled])
+AC_ARG_ENABLE([msan],
+  [AS_HELP_STRING([--enable-msan], [Enable building with MemorySanitizer enabled])],
+  [], enable_msan=no)
+if test "$enable_msan" = "yes"; then
+   AC_DEFINE([ENABLE_MSAN], [1], [Define to build with MemorySanitizer enabled.])
+   [CFLAGS="$CFLAGS -fsanitize=memory"]
+fi
+AC_MSG_RESULT([$enable_msan])
+
+AC_MSG_CHECKING([whether to build with code coverage instrumentation])
+AC_ARG_ENABLE([code-coverage],
+  [AS_HELP_STRING([--enable-code-coverage], [Enable building with code coverage instrumentation])],
+  [], enable_code_coverage=no)
+if test "$enable_code_coverage" = "yes"; then
+   AC_DEFINE([ENABLE_CODE_COVERAGE], [1], [Define to build with code coverage instrumentation.])
+   [CFLAGS="$CFLAGS -fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp,trace-gep,trace-div,edge"]
+fi
+AC_MSG_RESULT([$enable_code_coverage])
+
 PKG_PROG_PKG_CONFIG
 AS_IF([test "x$PKG_CONFIG" != "x"], [PKG_CONFIG="$PKG_CONFIG --static"])
 
@@ -322,7 +363,7 @@
 
 AC_CONFIG_HEADER([crypto/include/config.h:config_in.h])
 
-AC_CONFIG_FILES([Makefile crypto/Makefile doc/Makefile libsrtp2.pc])
+AC_CONFIG_FILES([Makefile crypto/Makefile doc/Makefile fuzzer/Makefile libsrtp2.pc])
 AC_OUTPUT
 
 # This is needed when building outside the source dir.
diff --git a/crypto/kernel/alloc.c b/crypto/kernel/alloc.c
index 0b97db3..293c291 100644
--- a/crypto/kernel/alloc.c
+++ b/crypto/kernel/alloc.c
@@ -67,6 +67,7 @@
 
 #if defined(HAVE_STDLIB_H)
 
+extern void *fuzz_calloc(size_t nmemb, size_t size);
 void *srtp_crypto_alloc(size_t size)
 {
     void *ptr;
@@ -75,7 +76,7 @@
         return NULL;
     }
 
-    ptr = calloc(1, size);
+    ptr = fuzz_calloc(1, size);
 
     if (ptr) {
         debug_print(mod_alloc, "(location: %p) allocated", ptr);
@@ -87,11 +88,12 @@
     return ptr;
 }
 
+extern void fuzz_free(void* ptr);
 void srtp_crypto_free(void *ptr)
 {
     debug_print(mod_alloc, "(location: %p) freed", ptr);
 
-    free(ptr);
+    fuzz_free(ptr);
 }
 
 #else /* we need to define our own memory allocation routines */
diff --git a/fuzzer/Makefile.in b/fuzzer/Makefile.in
new file mode 100644
index 0000000..1418231
--- /dev/null
+++ b/fuzzer/Makefile.in
@@ -0,0 +1,34 @@
+# Makefile for libSRTP fuzzer
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+VPATH = @srcdir@
+
+CC	= @CC@
+CXX	= @CXX@
+INCDIR	= -Iinclude -I$(srcdir)/include -I$(top_srcdir)/include -I$(top_srcdir)/crypto/include/
+DEFS	= @DEFS@
+CPPFLAGS= @CPPFLAGS@
+CFLAGS	= @CFLAGS@
+LIBS	= @LIBS@
+LDFLAGS	= @LDFLAGS@ -L. -L..
+COMPILE = $(CC) $(DEFS) $(INCDIR) $(CPPFLAGS) $(CFLAGS)
+COMPILECXX = $(CXX) $(DEFS) $(INCDIR) $(CPPFLAGS) $(CFLAGS)
+CRYPTOLIB = -lsrtp2
+
+.PHONY: clean
+
+all : srtp-fuzzer
+
+mt19937.o: mt19937.cpp
+	$(COMPILECXX) -c -std=c++11 mt19937.cpp -o mt19937.o
+fuzzer.o: fuzzer.c fuzzer.h testmem.h
+	$(COMPILE) fuzzer.c -c -o fuzzer.o
+testmem.o: testmem.c
+	$(COMPILE) -O0 testmem.c -c -o testmem.o
+srtp-fuzzer: fuzzer.o mt19937.o testmem.o
+	$(COMPILECXX) $(LDFLAGS) fuzzer.o mt19937.o testmem.o libFuzzer.a $(CRYPTOLIB) $(LIBS) -o srtp-fuzzer
+
+clean:
+	rm -rf srtp-fuzzer *.o
diff --git a/fuzzer/README.md b/fuzzer/README.md
new file mode 100644
index 0000000..399a751
--- /dev/null
+++ b/fuzzer/README.md
@@ -0,0 +1,83 @@
+# libsrt fuzzer
+
+By Guido Vranken <guidovranken@gmail.com> -- https://guidovranken.wordpress.com/
+
+This is an advanced fuzzer for libSRTP (https://github.com/cisco/libsrtp). It implements several special techniques, described below, that are not often found in fuzzers or elsewhere. All are encouraged to transpose these ideas to their own fuzzers for the betterment of software security.
+
+Feel free to contact me for business enquiries.
+
+## Features
+
+### Portable PRNG
+
+```mt19937.c``` exports the C++11 Mersenne Twister implementaton. Hence, a modern C++ compiler is required to compile this file.
+
+This approach has the following advantages:
+
+- rand() is fickle -- its behavior eg. the sequence of numbers that it generates for a given seed, may differ across systems and libc's.
+- C++11 mt19937 is portable, meaning that its behavior will be consistent across platforms. This is important to keep the fuzzing corpus portable.
+- No need to implement a portable PRNG ourselves, or risk license incompatability by importing it from other projects.
+
+### Size 0 allocations
+
+To test whether allocations of size 0 eg. ```malloc(0)``` are ever dereferenced and written to, the custom allocater will return an intentionally invalid pointer pointer address for these requests.
+
+For more information, see the comments in ```fuzz_alloc()```.
+
+### Random allocation failures
+
+The custom allocator will periodically return ```NULL``` for heap requests. This tests the library's resilience and correct operation in the event of global memory shortages.
+
+The interval of ```NULL``` return values is deterministic as it relies on the PRNG, so for a given fuzzer input (that encodes the PRNG seed as well), behavior of that input with regards to allocator behaviour is consistent, allowing for reliable reproduction of bugs.
+
+### Detecting inadequate pointer arithmetic
+
+This feature is only available on 32 bit builds.
+
+Unless the ```--no_mmap``` flag is given, the fuzzer will use a special allocation technique for some of the allocation requests. It will use ```mmap()``` to reserve memory at the extremities of the virtual address space -- sometimes at 0x00010000 and sometimes at 0xFFFF0000. This approach can assist in detecting invalid or inadequate pointer arithmetic. For example, consider the following code:
+
+```c
+if ( start + n < end ) {
+    memset(start, 0, n);
+}
+```
+
+where ```start``` and ```end``` demarcate a memory region, and ```n``` is some positive integer.
+If ```n``` is a sufficiently large value, a pointer addition overflow will occur, leading to a page fault. By routinely placing allocations at the high virtual address ```0xFFFF0000```, the chances of detecting this bug are increased. So let's say ```start``` was previously allocated at ```0xFFFF0000```, and ```end``` is ```0xFFFF1000```, and ```n``` is 0xFFFFF. Then the expression effectively becomes:
+
+```c
+if ( 0xFFFF0000 + 0x000FFFFF < 0xFFFF1000 ) {
+    memset(0xFFFF0000, 0, 0x000FFFF);
+}
+```
+
+The addition ```0xFFFF0000 + 0x000FFFFF``` overflows so the result is ```0x000EFFFF```. Hence:
+
+```c
+if ( 0x000EFFFF < 0xFFFF1000 ) { // Expression resolves as true !
+```
+
+The subsequent ```memset``` is executed contrary to the programmer's intentions, and a segmentation fault will occur.
+
+While this is a corner case, it can not be ruled out that it might occur in a production environment. What's more, the analyst examining the crash can reason about how the value of ```n``` comes about in the first place, and concoct a crafted input that leads to a very high ```n``` value, making the "exploit" succeed even with average virtual addresses.
+
+Aside from using ```mmap``` to allocate at address ```0xFFFF0000```, the fuzzer will also place allocations at the low virtual address ```0x00010000``` to detect invalid pointer arithmetic involving subtraction:
+
+```c
+if ( end - n > start ) {
+```
+
+### Output memory testing
+
+```testmem.c``` exports ```fuzz_testmem```. All this function does is copy the input buffer to a newly allocated heap region, and then free that heap region. If AddressSanitizer is enabled, this ensures that the input buffer to ```fuzz_testmem``` is a legal memory region.
+If MemorySanitizer is enabled, then ``fuzz_testmem``` calls ```fuzz_testmem_msan````. The latter function writes the data at hand to ```/dev/null```. This is an nice trick to make MemorySanitizer evaluate this data, and crash if it contains uninitialized bytes.
+This function has been implemented in a separate file for a reason: from the perspective of an optimizing compiler, this is a meaningless operation, and as such it might be optimized away. Hence, this file must be compiled without optimizations (```-O0``` flag).
+
+### Generating proofs of concept
+
+By using the ```--debug``` command line argument, the fuzzer will output a C file that sets relevant libsrtp data structures to the values used internally by the fuzzer for a given input. This ought to make it easier for the analyst to determine the root cause of an issue.
+
+## Contributing
+
+When extending the current fuzzer, use variable types whose width is consistent across systems where possible. This is necessary to retain corpus portability. For example, use ```uint64_t``` rather than ```unsigned long```.
+
diff --git a/fuzzer/fuzzer.c b/fuzzer/fuzzer.c
new file mode 100644
index 0000000..7d8f8c6
--- /dev/null
+++ b/fuzzer/fuzzer.c
@@ -0,0 +1,1104 @@
+/* By Guido Vranken <guidovranken@gmail.com> -- https://guidovranken.wordpress.com/ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <limits.h>
+#include "srtp.h"
+#include "srtp_priv.h"
+#include "ekt.h"
+#include "fuzzer.h"
+#include "mt19937.h"
+#include "testmem.h"
+
+/* Global variables */
+static bool g_debug_print = false; /* Can be enabled with --debug */
+static bool g_no_align = false; /* Can be enabled with --no_align */
+static bool g_post_init = false; /* Set to true once past initialization phase */
+static bool g_write_input = false;
+
+#ifdef FUZZ_32BIT
+#include <sys/mman.h>
+static bool g_no_mmap = false; /* Can be enabled with --no_mmap */
+static void* g_mmap_allocation = NULL; /* Keeps current mmap() allocation address */
+static size_t g_mmap_allocation_size = 0; /* Keeps current mmap() allocation size */
+#endif
+
+/* Custom allocator functions */
+
+static void* fuzz_alloc(const size_t size, const bool do_zero)
+{
+    void* ret = NULL;
+#ifdef FUZZ_32BIT
+    bool do_malloc = true;
+#endif
+    bool do_mmap, mmap_high = true;
+
+    if ( size == 0 ) {
+        size_t ret;
+        /* Allocations of size 0 are not illegal, but are a bad practice, since
+         * writing just a single byte to this region constitutes undefined
+         * behavior per the C spec. glibc will return a small, valid memory region
+         * whereas OpenBSD will crash upon writing to it.
+         * Intentionally return a pointer to an invalid page to detect
+         * unsound code efficiently.
+         * fuzz_free is aware of this pointer range and will not attempt
+         * to free()/munmap() it.
+         */
+        ret = 0x01 + (fuzz_mt19937_get() % 1024);
+        return (void*)ret;
+    }
+
+    /* Don't do mmap()-based allocations during initialization */
+    if ( g_post_init == true ) {
+        /* Even extract these values if --no_mmap is specified.
+         * This keeps the PRNG output stream consistent across
+         * fuzzer configurations.
+         */
+        do_mmap = (fuzz_mt19937_get() % 64) == 0 ? true : false;
+        if ( do_mmap == true ) {
+            mmap_high = (fuzz_mt19937_get() % 2) == 0 ? true : false;
+        }
+    } else {
+        do_mmap = false;
+    }
+
+#ifdef FUZZ_32BIT
+    /* g_mmap_allocation must be NULL because we only support a single
+     * concurrent mmap allocation at a time
+     */
+    if ( g_mmap_allocation == NULL && g_no_mmap == false && do_mmap == true ) {
+        void* mmap_address;
+        if ( mmap_high == true ) {
+            mmap_address = (void*)0xFFFF0000;
+        } else {
+            mmap_address = (void*)0x00010000;
+        }
+        g_mmap_allocation_size = size;
+
+        ret = mmap(mmap_address, g_mmap_allocation_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+
+        if ( ret == MAP_FAILED ) {
+            /* That's okay -- just return NULL to the caller */
+
+            ret = NULL;
+
+            /* Reset this for the sake of cleanliness */
+            g_mmap_allocation_size = 0;
+        }
+        /* ret not being MAP_FAILED does not mean that ret is the requested
+         * address (mmap_address). That's okay. We're not going to perform
+         * a munmap() on it and call malloc() instead. It won't gain us
+         * anything.
+         */
+
+        g_mmap_allocation = ret;
+        do_malloc = false;
+    }
+
+    if ( do_malloc == true )
+#endif
+    {
+        ret = malloc(size);
+    }
+
+    /* Mimic calloc() if so requested */
+    if ( ret != NULL && do_zero ) {
+        memset(ret, 0, size);
+    }
+
+    return ret;
+}
+
+/* Internal allocations by this fuzzer must on one hand (sometimes)
+ * receive memory from mmap(), but on the other hand these requests for
+ * memory may not fail. By calling this function, the allocation is
+ * guaranteed to succeed; it first tries with fuzz_alloc(), which may
+ * fail if it uses mmap(), and if that is the case, memory is allocated
+ * via the libc allocator (malloc, calloc) which should always succeed */
+static void* fuzz_alloc_succeed(const size_t size, const bool do_zero)
+{
+    void* ret = fuzz_alloc(size, do_zero);
+    if ( ret == NULL ) {
+        if ( do_zero == false ) {
+            ret = malloc(size);
+        } else {
+            ret = calloc(1, size);
+        }
+    }
+
+    return ret;
+}
+
+void *fuzz_calloc(const size_t nmemb, const size_t size)
+{
+    /* We must be past srtp_init() to prevent that that function fails */
+    if ( g_post_init == true ) {
+        /* Fail 1 in 64 allocations on average to test whether the library
+         * can deal with this properly.
+         */
+        if ( (fuzz_mt19937_get() % 64) == 0 ) {
+            return NULL;
+        }
+    }
+
+    return fuzz_alloc(nmemb * size, true);
+}
+
+static bool fuzz_is_special_pointer(void* ptr)
+{
+    /* Special, invalid pointers introduced when code attempted
+     * to do size = 0 allocations.
+     */
+    if ( (size_t)ptr >= 0x01 && (size_t)ptr < (0x01 + 1024) ) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void fuzz_free(void* ptr)
+{
+    if ( fuzz_is_special_pointer(ptr) == true ) {
+        return;
+    }
+
+#ifdef FUZZ_32BIT
+    if ( g_post_init == true && ptr != NULL && ptr == g_mmap_allocation ) {
+        if ( munmap(g_mmap_allocation, g_mmap_allocation_size) == -1 ) {
+            /* Shouldn't happen */
+            abort();
+        }
+        g_mmap_allocation = NULL;
+    } else
+#endif
+    {
+        free(ptr);
+    }
+}
+
+static void fuzz_print_bytes(const char* variable, const uint8_t* data, size_t size)
+{
+    size_t i;
+    printf("const uint8_t %s [] = {", variable);
+    for (i = 0; i < size; i++) {
+        if ( i != 0 ) {
+            printf(", ");
+        }
+        printf("0x%02X", data[i]);
+    }
+    printf("};\n");
+}
+
+static void fuzz_print_ints(const char* variable, const int* data, size_t size)
+{
+    size_t i;
+    printf("const int %s [] = {", variable);
+    for (i = 0; i < size; i++) {
+        if ( i != 0 ) {
+            printf(", ");
+        }
+        printf("%d", data[i]);
+    }
+    printf("};\n");
+}
+
+static srtp_err_status_t fuzz_srtp_protect(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki)
+{
+    if ( g_debug_print == true ) {
+        printf("\t\t{\n");
+        printf("\t\tint len = %d;\n", *len);
+        printf("\t\tsrtp_protect(srtp_ctx, hdr, &len);\n");
+        printf("\t\t}\n");
+    }
+    return srtp_protect(srtp_sender, hdr, len);
+}
+
+static srtp_err_status_t fuzz_srtp_unprotect(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki)
+{
+    if ( g_debug_print == true ) {
+        printf("\t\t{\n");
+        printf("\t\tint len = %d;\n", *len);
+        printf("\t\tsrtp_unprotect(srtp_ctx, hdr, &len);\n");
+        printf("\t\t}\n");
+    }
+    return srtp_unprotect(srtp_sender, hdr, len);
+}
+
+static srtp_err_status_t fuzz_srtp_protect_rtcp(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki)
+{
+    if ( g_debug_print == true ) {
+        printf("\t\t{\n");
+        printf("\t\tint len = %d;\n", *len);
+        printf("\t\tsrtp_protect_rtcp(srtp_ctx, copy, &len);\n");
+        printf("\t\t}\n");
+    }
+    return srtp_protect_rtcp(srtp_sender, hdr, len);
+}
+
+static srtp_err_status_t fuzz_srtp_unprotect_rtcp(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki)
+{
+    if ( g_debug_print == true ) {
+        printf("\t\t{\n");
+        printf("\t\tint len = %d;\n", *len);
+        printf("\t\tsrtp_unprotect_rtcp(srtp_ctx, copy, &len);\n");
+        printf("\t\t}\n");
+    }
+    return srtp_unprotect_rtcp(srtp_sender, hdr, len);
+}
+
+static srtp_err_status_t fuzz_srtp_protect_mki(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki)
+{
+    if ( g_debug_print == true ) {
+        printf("\t\t{\n");
+        printf("\t\tint len = %d;\n", *len);
+        printf("\t\tsrtp_protect_mki(srtp_ctx, copy, &len, %u, %u);\n", use_mki, mki);
+        printf("\t\t}\n");
+    }
+    return srtp_protect_mki(srtp_sender, hdr, len, use_mki, mki);
+}
+
+static srtp_err_status_t fuzz_srtp_protect_rtcp_mki(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki)
+{
+    if ( g_debug_print == true ) {
+        printf("\t\t{\n");
+        printf("\t\tint len = %d;\n", *len);
+        printf("\t\tsrtp_protect_rtcp_mki(srtp_ctx, copy, &len, %u, %u);\n", use_mki, mki);
+        printf("\t\t}\n");
+    }
+    return srtp_protect_rtcp_mki(srtp_sender, hdr, len, use_mki, mki);
+}
+
+static srtp_err_status_t fuzz_srtp_unprotect_mki(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki)
+{
+    if ( g_debug_print == true ) {
+        printf("\t\t{\n");
+        printf("\t\tint len = %d;\n", *len);
+        printf("\t\tsrtp_unprotect_mki(srtp_ctx, copy, &len, %u);\n", use_mki);
+        printf("\t\t}\n");
+    }
+    return srtp_unprotect_mki(srtp_sender, hdr, len, use_mki);
+}
+
+static srtp_err_status_t fuzz_srtp_unprotect_rtcp_mki(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki)
+{
+    if ( g_debug_print == true ) {
+        printf("\t\t{\n");
+        printf("\t\tint len = %d;\n", *len);
+        printf("\t\tsrtp_unprotect_rtcp_mki(srtp_ctx, copy, &len, %u);\n", use_mki);
+        printf("\t\t}\n");
+    }
+    return srtp_unprotect_rtcp_mki(srtp_sender, hdr, len, use_mki);
+}
+
+/* Get protect length functions */
+
+static srtp_err_status_t fuzz_srtp_get_protect_length(const srtp_t srtp_ctx, uint8_t use_mki, unsigned int mki, uint32_t* length)
+{
+    if ( g_debug_print == true ) {
+        printf("\t\tsrtp_get_protect_trailer_length(srtp_ctx, 0, 0, &trailer_length);\n");
+    }
+    return srtp_get_protect_trailer_length(srtp_ctx, 0, 0, length);
+}
+
+static srtp_err_status_t fuzz_srtp_get_protect_rtcp_length(const srtp_t srtp_ctx, uint8_t use_mki, unsigned int mki, uint32_t* length)
+{
+    if ( g_debug_print == true ) {
+        printf("\t\tsrtp_get_protect_rtcp_trailer_length(srtp_ctx, 0, 0, &trailer_length);\n");
+    }
+    return srtp_get_protect_rtcp_trailer_length(srtp_ctx, 0, 0, length);
+}
+
+static srtp_err_status_t fuzz_srtp_get_protect_mki_length(const srtp_t srtp_ctx, uint8_t use_mki, unsigned int mki, uint32_t* length)
+{
+    if ( g_debug_print == true ) {
+        printf("\t\tsrtp_get_protect_trailer_length(srtp_ctx, %u, %u, &trailer_length);\n", use_mki, mki);
+    }
+    return srtp_get_protect_trailer_length(srtp_ctx, use_mki, mki, length);
+}
+
+static srtp_err_status_t fuzz_srtp_get_protect_rtcp_mki_length(const srtp_t srtp_ctx, uint8_t use_mki, unsigned int mki, uint32_t* length)
+{
+    if ( g_debug_print == true ) {
+        printf("\t\tsrtp_get_protect_rtcp_trailer_length(srtp_ctx, %u, %u, &trailer_length);\n", use_mki, mki);
+    }
+    return srtp_get_protect_rtcp_trailer_length(srtp_ctx, use_mki, mki, length);
+}
+
+static uint8_t* extract_key(const uint8_t **data, size_t* size, const size_t key_size)
+{
+    uint8_t* ret;
+    if ( *size < key_size ) {
+        return NULL;
+    }
+
+    ret = fuzz_alloc_succeed(key_size, false);
+    EXTRACT(ret, *data, *size, key_size);
+
+    return ret;
+}
+
+static srtp_master_key_t* extract_master_key(const uint8_t **data, size_t* size, const size_t key_size, bool simulate, bool* success)
+{
+    srtp_master_key_t* ret = NULL;
+    uint16_t mki_id_size;
+
+    if ( simulate == true ) {
+        *success = false;
+    }
+
+    EXTRACT_IF(&mki_id_size, *data, *size, sizeof(mki_id_size));
+
+    if ( *size < key_size + mki_id_size ) {
+        goto end;
+    }
+
+    if ( simulate == true ) {
+        *data += key_size + mki_id_size;
+        *size -= key_size + mki_id_size;
+        *success = true;
+        goto end;
+    }
+
+    ret = fuzz_alloc_succeed(sizeof(srtp_master_key_t), false);
+    ret->key = fuzz_alloc_succeed(key_size, false);
+
+    ret->mki_id = fuzz_alloc_succeed(mki_id_size, false);
+
+    EXTRACT(ret->key, *data, *size, key_size);
+    EXTRACT(ret->mki_id, *data, *size, mki_id_size);
+    ret->mki_size = mki_id_size; 
+end:
+    return ret;
+}
+
+static srtp_master_key_t** extract_master_keys(const uint8_t **data, size_t* size, const size_t key_size, unsigned long* num_master_keys)
+{
+    const uint8_t* data_orig = *data;
+    size_t size_orig = *size;
+    size_t i = 0;
+
+    srtp_master_key_t** ret = NULL;
+
+    *num_master_keys = 0;
+
+
+    /* First pass -- dry run, determine how many keys we want and can extract */
+    while ( 1 ) {
+        uint8_t do_extract_master_key;
+        bool success;
+        if ( *size < sizeof(do_extract_master_key) ) {
+            goto next;
+        }
+        EXTRACT(&do_extract_master_key, *data, *size, sizeof(do_extract_master_key));
+
+        /* Decide whether to extract another key */
+        if ( (do_extract_master_key % 2) == 0 ) {
+            break;
+        }
+
+        extract_master_key(data, size, key_size, true, &success);
+
+        if ( success == false ) {
+            break;
+        }
+
+        (*num_master_keys)++;
+    }
+
+next:
+    *data = data_orig;
+    *size = size_orig;
+
+    /* Allocate array of pointers */
+    ret = fuzz_alloc_succeed(*num_master_keys * sizeof(srtp_master_key_t*), false);
+
+    /* Second pass -- perform the actual extractions */
+    for (i = 0; i < *num_master_keys; i++) {
+        uint8_t do_extract_master_key;
+        EXTRACT_IF(&do_extract_master_key, *data, *size, sizeof(do_extract_master_key));
+
+        if ( (do_extract_master_key % 2) == 0 ) {
+            break;
+        }
+
+        ret[i] = extract_master_key(data, size, key_size, false, NULL);
+
+        if ( ret[i] == NULL ) {
+            /* Shouldn't happen */
+            abort();
+        }
+    }
+
+end:
+    return ret;
+}
+
+static srtp_ekt_policy_t extract_ekt_policy(const uint8_t** data, size_t *size)
+{
+    srtp_ekt_policy_t ret = NULL;
+    struct {
+        srtp_ekt_spi_t spi;
+        uint8_t key[16];
+
+    } params;
+
+    EXTRACT_IF(&params, *data, *size, sizeof(params));
+
+    ret = fuzz_alloc_succeed(sizeof(struct srtp_ekt_policy_ctx_t), false);
+
+    ret->spi = params.spi;
+
+    /* The only supported cipher type */
+    ret->ekt_cipher_type = SRTP_EKT_CIPHER_AES_128_ECB;
+
+    ret->ekt_key = fuzz_alloc_succeed(sizeof(params.key), false);
+    memcpy(ret->ekt_key, params.key, sizeof(params.key));
+
+    ret->next_ekt_policy = NULL;
+
+end:
+    return ret;
+}
+
+static srtp_policy_t* extract_policy(const uint8_t **data, size_t* size)
+{
+    srtp_policy_t* policy = NULL;
+    struct {
+        uint8_t srtp_crypto_policy_func;
+        uint64_t window_size;
+        uint8_t allow_repeat_tx;
+        uint8_t ssrc_type;
+        uint32_t ssrc_value;
+        uint8_t num_xtn_hdr;
+        uint8_t with_ekt;
+        srtp_ekt_spi_t ekt_spi;
+        uint8_t do_extract_key;
+        uint8_t do_extract_master_keys;
+    } params;
+
+    EXTRACT_IF(&params, *data, *size, sizeof(params));
+
+    params.srtp_crypto_policy_func %= sizeof(fuzz_srtp_crypto_policies) / sizeof(fuzz_srtp_crypto_policies[0]);
+    params.allow_repeat_tx %= 2;
+    params.ssrc_type %= sizeof(fuzz_ssrc_type_map) / sizeof(fuzz_ssrc_type_map[0]);
+    params.with_ekt %= 2;
+
+    policy = fuzz_alloc_succeed(sizeof(*policy), true);
+
+    fuzz_srtp_crypto_policies[params.srtp_crypto_policy_func].crypto_policy_func(&policy->rtp);
+    fuzz_srtp_crypto_policies[params.srtp_crypto_policy_func].crypto_policy_func(&policy->rtcp);
+
+    if ( policy->rtp.cipher_key_len > MAX_KEY_LEN ) {
+        /* Shouldn't happen */
+        abort();
+    }
+    
+    policy->ssrc.type = fuzz_ssrc_type_map[params.ssrc_type].srtp_ssrc_type;
+    policy->ssrc.value = params.ssrc_value;
+
+    if ( (params.do_extract_key % 2) == 0 ) {
+        policy->key = extract_key(data, size, policy->rtp.cipher_key_len);
+
+        if ( policy->key == NULL ) {
+            fuzz_free(policy);
+            return NULL;
+        }
+    }
+
+    if ( params.num_xtn_hdr != 0 ) {
+        const size_t xtn_hdr_size = params.num_xtn_hdr * sizeof(int);
+        if ( *size < xtn_hdr_size ) {
+            fuzz_free(policy->key);
+            fuzz_free(policy);
+            return NULL;
+        }
+        policy->enc_xtn_hdr = fuzz_alloc_succeed(xtn_hdr_size, false);
+        EXTRACT(policy->enc_xtn_hdr, *data, *size, xtn_hdr_size);
+        policy->enc_xtn_hdr_count = params.num_xtn_hdr;
+    }
+
+    if ( (params.do_extract_master_keys % 2) == 0 ) {
+        policy->keys = extract_master_keys(data, size, policy->rtp.cipher_key_len, &policy->num_master_keys);
+        if ( policy->keys == NULL ) {
+            fuzz_free(policy->key);
+            fuzz_free(policy->enc_xtn_hdr);
+            fuzz_free(policy);
+            return NULL;
+        }
+    }
+
+    if ( params.with_ekt ) {
+        policy->ekt = extract_ekt_policy(data, size);
+    }
+
+    policy->window_size = params.window_size;
+    policy->allow_repeat_tx = params.allow_repeat_tx;
+    policy->next = NULL;
+
+end:
+    return policy;
+}
+
+static srtp_policy_t* extract_policies(const uint8_t **data, size_t* size)
+{
+    srtp_policy_t *curpolicy = NULL, *policy_chain = NULL;
+
+    curpolicy = extract_policy(data, size);
+    if ( curpolicy == NULL ) {
+        return NULL;
+    }
+
+    policy_chain = curpolicy;
+
+    while ( 1 ) {
+        uint8_t do_extract_policy;
+        EXTRACT_IF(&do_extract_policy, *data, *size, sizeof(do_extract_policy));
+
+        /* Decide whether to extract another policy */
+        if ( (do_extract_policy % 2) == 0 ) {
+            break;
+        }
+
+        curpolicy->next = extract_policy(data, size);
+        if ( curpolicy->next == NULL ) {
+            break;
+        }
+        curpolicy = curpolicy->next;
+    }
+
+end:
+    return policy_chain;
+}
+
+static uint32_t* extract_remove_stream_ssrc(const uint8_t** data, size_t* size, uint8_t* num_remove_stream)
+{
+    uint32_t* ret = NULL;
+    uint8_t _num_remove_stream;
+    size_t total_size;
+
+    *num_remove_stream = 0;
+
+    EXTRACT_IF(&_num_remove_stream, *data, *size, sizeof(_num_remove_stream));
+
+    if ( _num_remove_stream == 0 ) {
+        goto end;
+    }
+
+    total_size = _num_remove_stream * sizeof(uint32_t);
+
+    if ( *size < total_size ) {
+        goto end;
+    }
+
+    ret = fuzz_alloc_succeed(total_size, false);
+    EXTRACT(ret, *data, *size, total_size);
+
+    *num_remove_stream = _num_remove_stream;
+
+end:
+    return ret;
+}
+
+static uint32_t* extract_set_roc(const uint8_t** data, size_t* size, uint8_t* num_set_roc)
+{
+    uint32_t* ret = NULL;
+    uint8_t _num_set_roc;
+    size_t total_size;
+
+    *num_set_roc = 0;
+    EXTRACT_IF(&_num_set_roc, *data, *size, sizeof(_num_set_roc));
+    if ( _num_set_roc == 0 ) {
+        goto end;
+    }
+
+    /* Tuples of 2 uint32_t's */
+    total_size = _num_set_roc * sizeof(uint32_t) * 2;
+    
+    if ( *size < total_size ) {
+        goto end;
+    }
+
+    ret = fuzz_alloc_succeed(total_size, false);
+    EXTRACT(ret, *data, *size, total_size);
+
+    *num_set_roc = _num_set_roc;
+
+end:
+    return ret;
+}
+
+static void free_policies(srtp_policy_t* curpolicy)
+{
+    size_t i;
+    while ( curpolicy ) {
+        srtp_policy_t* next = curpolicy->next;
+
+        fuzz_free(curpolicy->key);
+
+        for (i = 0; i < curpolicy->num_master_keys; i++) {
+            fuzz_free(curpolicy->keys[i]->key);
+            fuzz_free(curpolicy->keys[i]->mki_id);
+            fuzz_free(curpolicy->keys[i]);
+        }
+
+        fuzz_free(curpolicy->keys);
+        fuzz_free(curpolicy->enc_xtn_hdr);
+
+        if ( curpolicy->ekt ) {
+            fuzz_free(curpolicy->ekt->ekt_key);
+            fuzz_free(curpolicy->ekt);
+        }
+
+        fuzz_free(curpolicy);
+
+        curpolicy = next;
+    }
+}
+
+static uint8_t* run_srtp_func(const srtp_t srtp_ctx, const uint8_t** data, size_t* size)
+{
+    uint8_t* ret = NULL;
+    uint8_t* copy = NULL, *copy_2 = NULL;
+
+    struct {
+        uint16_t size;
+        uint8_t srtp_func;
+        uint8_t use_mki;
+        uint32_t mki;
+        uint8_t stretch;
+    } params_1;
+
+    struct {
+        uint8_t srtp_func;
+        uint8_t use_mki;
+        uint32_t mki;
+    } params_2;
+    int ret_size;
+
+    if ( g_debug_print == true ) {
+        printf("\t{\n");
+        printf("\t\tuint32_t trailer_length;\n");
+        printf("\t\tuint8_t* copy = NULL;\n");
+    }
+
+    EXTRACT_IF(&params_1, *data, *size, sizeof(params_1));
+    params_1.srtp_func %= sizeof(srtp_funcs) / sizeof(srtp_funcs[0]);
+    params_1.use_mki %= 2;
+
+    if ( *size < params_1.size ) {
+        goto end;
+    }
+
+    /* Enforce 4 byte alignment */
+    if ( g_no_align == false ) {
+        params_1.size -= params_1.size % 4;
+    }
+
+    if ( params_1.size == 0 ) {
+        goto end;
+    }
+
+    ret_size = params_1.size;
+    if ( srtp_funcs[params_1.srtp_func].protect == true ) {
+        /* Intentionally not initialized to trigger MemorySanitizer, if applicable */
+        uint32_t alloc_size;
+
+        if ( srtp_funcs[params_1.srtp_func].get_length(srtp_ctx, params_1.use_mki, params_1.mki, &alloc_size) != srtp_err_status_ok ) {
+            goto end;
+        }
+
+        copy = fuzz_alloc_succeed(ret_size + alloc_size, false);
+
+        if ( g_debug_print == true ) {
+            printf("\t\tcopy = malloc(%d + trailer_length);\n", ret_size);
+        }
+    } else {
+        if ( g_debug_print == true ) {
+            printf("\t\tcopy = malloc(%d);\n", ret_size);
+        }
+        copy = fuzz_alloc_succeed(ret_size, false);
+    }
+
+    EXTRACT(copy, *data, *size, params_1.size);
+    if ( g_debug_print == true ) {
+        printf("\t\t{\n");
+        printf("\t\t\t");
+        fuzz_print_bytes("copycopy", copy, (size_t)params_1.size);
+        printf("\t\t\tmemcpy(copy, copycopy, %d);\n", params_1.size);
+        printf("\t\t}\n");
+    }
+
+    if ( srtp_funcs[params_1.srtp_func].srtp_func(srtp_ctx, copy, &ret_size, params_1.use_mki, params_1.mki) != srtp_err_status_ok ) {
+        fuzz_free(copy);
+        goto end;
+    }
+    //fuzz_free(copy);
+
+    fuzz_testmem(copy, ret_size);
+
+    ret = copy;
+
+    EXTRACT_IF(&params_2, *data, *size, sizeof(params_2));
+    params_2.srtp_func %= sizeof(srtp_funcs) / sizeof(srtp_funcs[0]);
+    params_2.use_mki %= 2;
+
+    if ( ret_size == 0 ) {
+        goto end;
+    }
+
+    if ( srtp_funcs[params_2.srtp_func].protect == true ) {
+        /* Intentionally not initialized to trigger MemorySanitizer, if applicable */
+        uint32_t alloc_size;
+
+        if ( srtp_funcs[params_2.srtp_func].get_length(srtp_ctx, params_2.use_mki, params_2.mki, &alloc_size) != srtp_err_status_ok ) {
+            goto end;
+        }
+
+        copy_2 = fuzz_alloc_succeed(ret_size + alloc_size, false);
+    } else {
+        copy_2 = fuzz_alloc_succeed(ret_size, false);
+    }
+
+    memcpy(copy_2, copy, ret_size);
+    fuzz_free(copy);
+    copy = copy_2;
+
+    if ( srtp_funcs[params_2.srtp_func].srtp_func(srtp_ctx, copy, &ret_size, params_2.use_mki, params_2.mki) != srtp_err_status_ok ) {
+        fuzz_free(copy);
+        ret = NULL;
+        goto end;
+    }
+
+    fuzz_testmem(copy, ret_size);
+
+    ret = copy;
+
+end:
+    return ret;
+}
+
+void fuzz_srtp_event_handler(srtp_event_data_t *data)
+{
+    fuzz_testmem(data, sizeof(srtp_event_data_t));
+    if ( data->session != NULL ) {
+        fuzz_testmem(data->session, sizeof(*data->session));
+    }
+}
+
+static void fuzz_write_input(const uint8_t* data, size_t size) {
+    FILE* fp = fopen("input.bin", "wb");
+
+    if ( fp == NULL ) {
+        /* Shouldn't happen */
+        abort();
+    }
+
+    if ( size != 0 && fwrite(data, size, 1, fp) != 1 ) {
+        printf("Cannot write\n");
+        /* Shouldn't happen */
+        abort();
+    }
+
+    fclose(fp);
+}
+static size_t fuzz_print_policy_chain(const srtp_policy_t* policy_chain)
+{
+    size_t i = 1, j;
+    while ( policy_chain ) {
+        printf("\tsrtp_policy_t* policy_%zu = malloc(sizeof(srtp_policy_t));\n", i);
+
+        /* ssrc */
+        printf("\tpolicy_%zu->ssrc.type = %d;\n", i, policy_chain->ssrc.type);
+        printf("\tpolicy_%zu->ssrc.value = %u;\n", i, policy_chain->ssrc.value);
+
+        /* rtp */
+        printf("\tpolicy_%zu->rtp.cipher_type = %u;\n", i, policy_chain->rtp.cipher_type);
+        printf("\tpolicy_%zu->rtp.cipher_key_len = %d;\n", i, policy_chain->rtp.cipher_key_len);
+        printf("\tpolicy_%zu->rtp.auth_type = %u;\n", i, policy_chain->rtp.auth_type);
+        printf("\tpolicy_%zu->rtp.auth_key_len = %d;\n", i, policy_chain->rtp.auth_key_len);
+        printf("\tpolicy_%zu->rtp.auth_tag_len = %d;\n", i, policy_chain->rtp.auth_tag_len);
+        printf("\tpolicy_%zu->rtp.sec_serv = %d;\n", i, policy_chain->rtp.sec_serv);
+
+        /* rctp */
+        printf("\tpolicy_%zu->rtcp.cipher_type = %u;\n", i, policy_chain->rtcp.cipher_type);
+        printf("\tpolicy_%zu->rtcp.cipher_key_len = %d;\n", i, policy_chain->rtcp.cipher_key_len);
+        printf("\tpolicy_%zu->rtcp.auth_type = %u;\n", i, policy_chain->rtcp.auth_type);
+        printf("\tpolicy_%zu->rtcp.auth_key_len = %d;\n", i, policy_chain->rtcp.auth_key_len);
+        printf("\tpolicy_%zu->rtcp.auth_tag_len = %d;\n", i, policy_chain->rtcp.auth_tag_len);
+        printf("\tpolicy_%zu->rtcp.sec_serv = %d;\n", i, policy_chain->rtcp.sec_serv);
+
+        /* key */
+        printf("\tpolicy_%zu->num_master_keys = %zu;\n", i, policy_chain->num_master_keys);
+        if ( fuzz_is_special_pointer(policy_chain->key) == true || policy_chain->key == NULL ) {
+            printf("\tpolicy_%zu->key = (void*)0x%zX;\n", i, (size_t)policy_chain->key);
+        } else {
+            printf("\tpolicy_%zu->key = malloc(policy_%zu->rtp.cipher_key_len);\n", i, i);
+            printf("\t{\n");
+            printf("\t\t");
+            fuzz_print_bytes("key", policy_chain->key, policy_chain->rtp.cipher_key_len);
+            printf("\t\tmemcpy(policy_%zu->key, key, %d);\n", i, policy_chain->rtp.cipher_key_len);
+            printf("\t}\n");
+        }
+
+        /* keys */
+        if ( fuzz_is_special_pointer(policy_chain->keys) == true || policy_chain->keys == NULL ) {
+            printf("\tpolicy_%zu->keys = (void*)0x%zX;\n", i, (size_t)policy_chain->keys);
+        } else {
+            size_t j;
+            printf("\tpolicy_%zu->keys = malloc(%zu * sizeof(srtp_master_key_t*));\n", i, policy_chain->num_master_keys);
+            for (j = 0; j < policy_chain->num_master_keys; j++) {
+                printf("\tpolicy_%zu->keys[%zu] = malloc(sizeof(srtp_master_key_t));\n", i, j);
+                if ( fuzz_is_special_pointer(policy_chain->keys[j]->key) == true || policy_chain->keys[j]->key == NULL ) {
+                    printf("\tpolicy_%zu->keys[%zu]->key = (void*)0x%zX;\n", i, j, (size_t)policy_chain->keys[j]->key);
+                } else {
+                    printf("\tpolicy_%zu->keys[%zu]->key = malloc(policy_%zu->rtp.cipher_key_len);\n", i, j, i);
+                    printf("\t{\n");
+                    printf("\t\t");
+                    fuzz_print_bytes("key", policy_chain->keys[j]->key, policy_chain->rtp.cipher_key_len);
+                    printf("\t\tmemcpy(policy_%zu->keys[%zu]->key, key, policy_%zu->rtp.cipher_key_len);\n", i, j, i);
+                    printf("\t}\n");
+                }
+                if ( fuzz_is_special_pointer(policy_chain->keys[j]->mki_id) == true || policy_chain->keys[j]->mki_id == NULL ) {
+                    printf("\tpolicy_%zu->keys[%zu]->mki_id = (void*)0x%zX;\n", i, j, (size_t)policy_chain->keys[j]->mki_id);
+                } else {
+                    printf("\tpolicy_%zu->keys[%zu]->mki_id = malloc(%d);\n", i, j, policy_chain->keys[j]->mki_size);
+                    printf("\t{\n");
+                    printf("\t\t");
+                    fuzz_print_bytes("key", policy_chain->keys[j]->mki_id, policy_chain->keys[j]->mki_size);
+                    printf("\t\tmemcpy(policy_%zu->keys[%zu]->mki_id, key, %d);\n", i, j, policy_chain->keys[j]->mki_size);
+                    printf("\t}\n");
+                    printf("\tpolicy_%zu->keys[%zu]->mki_size = %d;\n", i, j, policy_chain->keys[j]->mki_size);
+                }
+            }
+        }
+
+        /* ekt */
+        if ( fuzz_is_special_pointer(policy_chain->ekt) == true || policy_chain->ekt == NULL ) {
+            printf("\tpolicy_%zu->ekt = (void*)0x%zX;\n", i, (size_t)policy_chain->ekt);
+        } else {
+            printf("\tpolicy_%zu->ekt = malloc(struct srtp_ekt_policy_ctx_t);\n", i);
+            printf("\tpolicy_%zu->ekt->spi = %u;\n", i, policy_chain->ekt->spi);
+            printf("\tpolicy_%zu->ekt->ekt_cipher_type = %u;\n", i, policy_chain->ekt->ekt_cipher_type);
+            printf("\tpolicy_%zu->ekt->key = malloc(16);\n", i);
+            printf("\t{\n");
+            fuzz_print_bytes("ekt_key", policy_chain->ekt->ekt_key, 16);
+            printf("\tmemcpy(policy_%zu->ekt->ekt_key, ekt_key, 16);\n", i);
+            printf("\t}\n");
+        }
+
+        /* misc */
+        printf("\tpolicy_%zu->window_size = %lu;\n", i, policy_chain->window_size);
+        printf("\tpolicy_%zu->allow_repeat_tx = %d;\n", i, policy_chain->allow_repeat_tx);
+
+        /* enc_xtn_hdr */
+        if ( fuzz_is_special_pointer(policy_chain->enc_xtn_hdr) == true || policy_chain->enc_xtn_hdr == NULL ) {
+            printf("\tpolicy_%zu->enc_xtn_hdr = (void*)0x%zX;\n", i, (size_t)policy_chain->enc_xtn_hdr);
+        } else {
+            printf("\tpolicy_%zu->enc_xtn_hdr = malloc(%d * sizeof(int));\n", i, policy_chain->enc_xtn_hdr_count);
+            printf("\t{\n");
+            fuzz_print_ints("enc_xtn_hdr", policy_chain->enc_xtn_hdr, (size_t)policy_chain->enc_xtn_hdr_count);
+            printf("\tmemcpy(policy_%zu->enc_xtn_hdr, enc_xtn_hdr, %d * sizeof(int));\n", i, policy_chain->enc_xtn_hdr_count);
+            printf("\t}\n");
+        }
+        printf("\tpolicy_%zu->enc_xtn_hdr_count = %d;\n", i, policy_chain->enc_xtn_hdr_count);
+
+        printf("\tpolicy_%zu->next = NULL;\n", i);
+
+        printf("\t\n");
+
+        policy_chain = policy_chain->next;
+        i++;
+    }
+
+    for (j = 1; j < i - 1; j++) {
+        printf("policy_%zu->next = policy_%zu;\n", j, j + 1);
+    }
+
+    return i - 1;
+}
+
+int LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+    char** _argv = *argv;
+    int i;
+    bool no_custom_event_handler = false;
+
+    if ( srtp_init() != srtp_err_status_ok ) {
+        /* Shouldn't happen */
+        abort();
+    }
+
+    for (i = 0; i < *argc; i++) {
+        if ( strcmp("--debug", _argv[i]) == 0 ) {
+            g_debug_print = true;
+        }
+        else if ( strcmp("--no_align", _argv[i]) == 0 ) {
+            g_no_align = true;
+        }
+        else if ( strcmp("--no_custom_event_handler", _argv[i]) == 0 ) {
+            no_custom_event_handler = true;
+        }
+        else if ( strcmp("--write_input", _argv[i]) == 0 ) {
+            g_write_input = true;
+        }
+#ifdef FUZZ_32BIT
+        else if ( strcmp("--no_mmap", _argv[i]) == 0 ) {
+            g_no_mmap = true;
+        }
+#endif
+        else if ( strncmp("--", _argv[i], 2) == 0 ) {
+            printf("Invalid argument: %s\n", _argv[i]);
+            exit(0);
+        }
+    }
+
+    if ( no_custom_event_handler == false ) {
+        if ( srtp_install_event_handler(fuzz_srtp_event_handler) != srtp_err_status_ok ) {
+            /* Shouldn't happen */
+            abort();
+        }
+    }
+
+    /* Fully initialized -- past this point, simulated allocation failures
+     * are allowed to occur */
+    g_post_init = true;
+
+    return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+    uint8_t num_remove_stream;
+    uint32_t* remove_stream_ssrc = NULL;
+    uint8_t num_set_roc;
+    uint32_t* set_roc = NULL;
+    srtp_t srtp_ctx = NULL;
+    srtp_policy_t* policy_chain = NULL, *policy_chain_2 = NULL;
+    uint32_t randseed;
+    static bool firstrun = true;
+
+    if ( firstrun == true ) {
+        /* TODO version check etc and send it to MSAN */
+    }
+
+    if ( g_debug_print == true ) {
+        printf("#include <stdint.h>\n");
+        printf("#include <string.h>\n");
+        printf("#include \"srtp.h\"\n");
+        printf("#include \"srtp_priv.h\"\n");
+        printf("#include \"ekt.h\"\n");
+        printf("\n");
+        printf("void* fuzz_calloc(size_t x, size_t y) { return calloc(x, y); }\n");
+        printf("void fuzz_free(void* p) { free(p); }\n");
+        printf("\n");
+        printf("int main(void) {\n");
+        printf("\tsrtp_t srtp_ctx = NULL;\n");
+        printf("\tsrtp_init();\n");
+    }
+
+#ifdef FUZZ_32BIT
+    /* Free the mmap allocation made during the previous iteration, if applicable */
+    fuzz_free(g_mmap_allocation);
+#endif
+
+    if ( g_write_input == true ) {
+        fuzz_write_input(data, size);
+    }
+
+    EXTRACT_IF(&randseed, data, size, sizeof(randseed));
+    fuzz_mt19937_init(randseed);
+    srand(randseed);
+
+    /* policy_chain is used to initialize the srtp context with */
+    if ( (policy_chain = extract_policies(&data, &size)) == NULL ) {
+        goto end;
+    }
+    /* policy_chain_2 is used as an argument to srtp_update later on */
+    if ( (policy_chain_2 = extract_policies(&data, &size)) == NULL ) {
+        goto end;
+    }
+
+    if ( g_debug_print == true ) {
+        if ( fuzz_print_policy_chain(policy_chain) > 0 ) {
+            printf("\tsrtp_create(&srtp_ctx, policy_1);\n");
+        }
+    }
+
+    /* Create context */
+    if ( srtp_create(&srtp_ctx, policy_chain) != srtp_err_status_ok ) {
+        goto end;
+    }
+
+    //free_policies(policy_chain);
+    //policy_chain = NULL;
+
+    /* Don't check for NULL result -- no extractions is fine */
+    remove_stream_ssrc = extract_remove_stream_ssrc(&data, &size, &num_remove_stream);
+
+    /* Don't check for NULL result -- no extractions is fine */
+    set_roc = extract_set_roc(&data, &size, &num_set_roc);
+
+    {
+        uint8_t* ret;
+        int i = 0, j = 0;
+
+        while ( (ret = run_srtp_func(srtp_ctx, &data, &size)) != NULL ) {
+            fuzz_free(ret);
+
+            /* Keep removing streams until the set of SSRCs extracted from the
+             * fuzzer input is exhausted */
+            if ( i < num_remove_stream ) {
+                if ( g_debug_print == true ) {
+                    printf("srtp_remove_stream(srtp_ctx, %u);\n", remove_stream_ssrc[i]);
+                }
+                if ( srtp_remove_stream(srtp_ctx, remove_stream_ssrc[i]) != srtp_err_status_ok ) {
+                    goto end;
+                }
+                i++;
+            }
+
+            /* Keep setting and getting ROCs until the set of SSRC/ROC tuples
+             * extracted from the fuzzer input is exhausted */
+            if ( j < num_set_roc * 2 ) {
+                uint32_t roc;
+                if ( g_debug_print == true ) {
+                    printf("srtp_set_stream_roc(srtp_ctx, %u, %u);\n", set_roc[j], set_roc[j + 1]);
+                }
+                if ( srtp_set_stream_roc(srtp_ctx, set_roc[j], set_roc[j + 1]) != srtp_err_status_ok ) {
+                    goto end;
+                }
+                if ( g_debug_print == true ) {
+                    printf("srtp_get_stream_roc(srtp_ctx, %u);\n", set_roc[j + 1]);
+                }
+                if ( srtp_get_stream_roc(srtp_ctx, set_roc[j + 1], &roc) != srtp_err_status_ok ) {
+                    goto end;
+                }
+                j += 2;
+            }
+
+            if ( policy_chain_2 != NULL ) {
+                /* TODO srtp_update(srtp_ctx, policy_chain_2); */
+
+                /* Discard after using once */
+                free_policies(policy_chain_2);
+                policy_chain_2 = NULL;
+            }
+        }
+    }
+
+end:
+    free_policies(policy_chain);
+    free_policies(policy_chain_2);
+    fuzz_free(remove_stream_ssrc);
+    fuzz_free(set_roc);
+    if ( srtp_ctx != NULL ) {
+        srtp_dealloc(srtp_ctx);
+    }
+    fuzz_mt19937_destroy();
+    if ( g_debug_print == true ) {
+        printf("}\n");
+    }
+    return 0;
+}
diff --git a/fuzzer/fuzzer.h b/fuzzer/fuzzer.h
new file mode 100644
index 0000000..6a5d3d2
--- /dev/null
+++ b/fuzzer/fuzzer.h
@@ -0,0 +1,108 @@
+#define MAX_KEY_LEN 46
+#define EXTRACT(dest, src, srcsize, copysize) { \
+    memcpy((dest), (src), (copysize)); \
+    (src) += (copysize); \
+    (srcsize) -= (copysize); \
+}
+
+/* Extract data if src contains sufficient bytes, otherwise go to end */
+#define EXTRACT_IF(dest, src, srcsize, copysize) { \
+    if ( (srcsize) < (copysize) ) { \
+        goto end; \
+    } else { \
+        EXTRACT((dest), (src), (srcsize), (copysize)); \
+    } \
+}
+#include <stdint.h>
+#if UINTPTR_MAX == 0xffffffff
+#define FUZZ_32BIT
+#elif UINTPTR_MAX == 0xffffffffffffffff
+#else
+#error "Cannot detect word size"
+#endif
+
+typedef srtp_err_status_t (*fuzz_srtp_func)(srtp_t, void*, int*, uint8_t, unsigned int);
+typedef void (*fuzz_srtp_crypto_policy_func)(srtp_crypto_policy_t*);
+typedef srtp_err_status_t (*fuzz_srtp_get_length_func)(const srtp_t, uint8_t, unsigned int, uint32_t*);
+
+struct fuzz_srtp_params {
+    uint8_t srtp_func;
+    uint8_t srtp_crypto_policy_func;
+    uint16_t window_size;
+    uint8_t allow_repeat_tx;
+    uint8_t ssrc_type;
+    unsigned int ssrc_value;
+    uint8_t key[MAX_KEY_LEN];
+    uint8_t mki;
+};
+
+static srtp_err_status_t fuzz_srtp_protect(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki);
+static srtp_err_status_t fuzz_srtp_unprotect(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki);
+static srtp_err_status_t fuzz_srtp_protect_rtcp(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki);
+static srtp_err_status_t fuzz_srtp_unprotect_rtcp(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki);
+static srtp_err_status_t fuzz_srtp_protect_mki(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki);
+static srtp_err_status_t fuzz_srtp_protect_rtcp_mki(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki);
+static srtp_err_status_t fuzz_srtp_unprotect_mki(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki);
+static srtp_err_status_t fuzz_srtp_unprotect_rtcp_mki(srtp_t srtp_sender, void* hdr, int* len, uint8_t use_mki, unsigned int mki);
+
+static srtp_err_status_t fuzz_srtp_get_protect_length(const srtp_t srtp_ctx, uint8_t use_mki, unsigned int mki, uint32_t* length);
+static srtp_err_status_t fuzz_srtp_get_protect_mki_length(const srtp_t srtp_ctx, uint8_t use_mki, unsigned int mki, uint32_t* length);
+static srtp_err_status_t fuzz_srtp_get_protect_rtcp_length(const srtp_t srtp_ctx, uint8_t use_mki, unsigned int mki, uint32_t* length);
+static srtp_err_status_t fuzz_srtp_get_protect_rtcp_mki_length(const srtp_t srtp_ctx, uint8_t use_mki, unsigned int mki, uint32_t* length);
+
+struct fuzz_srtp_func_ext {
+    fuzz_srtp_func srtp_func;
+    bool protect;
+    fuzz_srtp_get_length_func get_length;
+};
+
+const struct fuzz_srtp_func_ext srtp_funcs[] = {
+    {fuzz_srtp_protect, true, fuzz_srtp_get_protect_length},
+    {fuzz_srtp_unprotect, false, NULL},
+    {fuzz_srtp_protect_rtcp, true, fuzz_srtp_get_protect_rtcp_length},
+    {fuzz_srtp_unprotect_rtcp, false, NULL},
+    {fuzz_srtp_protect_mki, true, fuzz_srtp_get_protect_mki_length},
+    {fuzz_srtp_unprotect_mki, false, NULL},
+    {fuzz_srtp_protect_rtcp_mki, true, fuzz_srtp_get_protect_rtcp_mki_length},
+    {fuzz_srtp_unprotect_rtcp_mki, false, NULL}
+};
+
+struct fuzz_srtp_crypto_policy_func_ext {
+    fuzz_srtp_crypto_policy_func crypto_policy_func;
+    const char* name;
+};
+
+const struct fuzz_srtp_crypto_policy_func_ext fuzz_srtp_crypto_policies[] = {
+    {srtp_crypto_policy_set_rtp_default, ""},
+    {srtp_crypto_policy_set_rtcp_default, ""},
+    {srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32, "srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32"},
+    {srtp_crypto_policy_set_aes_cm_128_null_auth, "srtp_crypto_policy_set_aes_cm_128_null_auth"},
+    {srtp_crypto_policy_set_aes_cm_256_hmac_sha1_32, "srtp_crypto_policy_set_aes_cm_256_hmac_sha1_32"},
+    {srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80, "srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80"},
+    {srtp_crypto_policy_set_aes_cm_256_null_auth, "srtp_crypto_policy_set_aes_cm_256_null_auth"},
+    {srtp_crypto_policy_set_null_cipher_hmac_null, "srtp_crypto_policy_set_null_cipher_hmac_null"},
+    {srtp_crypto_policy_set_null_cipher_hmac_sha1_80, "srtp_crypto_policy_set_null_cipher_hmac_sha1_80"},
+#ifdef OPENSSL
+    {srtp_crypto_policy_set_aes_cm_192_hmac_sha1_32, "srtp_crypto_policy_set_aes_cm_192_hmac_sha1_32"},
+    {srtp_crypto_policy_set_aes_cm_192_hmac_sha1_80, "srtp_crypto_policy_set_aes_cm_192_hmac_sha1_80"},
+    {srtp_crypto_policy_set_aes_cm_192_null_auth, "srtp_crypto_policy_set_aes_cm_192_null_auth"},
+    {srtp_crypto_policy_set_aes_gcm_128_16_auth, "srtp_crypto_policy_set_aes_gcm_128_16_auth"},
+    {srtp_crypto_policy_set_aes_gcm_128_8_auth, "srtp_crypto_policy_set_aes_gcm_128_8_auth"},
+    {srtp_crypto_policy_set_aes_gcm_128_8_only_auth, "srtp_crypto_policy_set_aes_gcm_128_8_only_auth"},
+    {srtp_crypto_policy_set_aes_gcm_256_16_auth, "srtp_crypto_policy_set_aes_gcm_256_16_auth"},
+    {srtp_crypto_policy_set_aes_gcm_256_8_auth, "srtp_crypto_policy_set_aes_gcm_256_8_auth"},
+    {srtp_crypto_policy_set_aes_gcm_256_8_only_auth, "srtp_crypto_policy_set_aes_gcm_256_8_only_auth"},
+#endif
+};
+
+struct fuzz_srtp_ssrc_type_ext {
+    srtp_ssrc_type_t srtp_ssrc_type;
+    const char* name;
+};
+
+const struct fuzz_srtp_ssrc_type_ext fuzz_ssrc_type_map[] = {
+    {ssrc_undefined, "ssrc_undefined"},
+    {ssrc_specific, "ssrc_specific"},
+    {ssrc_any_inbound, "ssrc_any_inbound"},
+    {ssrc_any_outbound, "ssrc_any_outbound"},
+};
diff --git a/fuzzer/mt19937.cpp b/fuzzer/mt19937.cpp
new file mode 100644
index 0000000..984f1fb
--- /dev/null
+++ b/fuzzer/mt19937.cpp
@@ -0,0 +1,17 @@
+#include <random>
+#include <cstdint>
+
+std::mt19937* mt_rand = NULL;
+
+extern "C" void fuzz_mt19937_init(uint32_t seed) {
+    mt_rand = new std::mt19937(seed);
+}
+
+extern "C" uint32_t fuzz_mt19937_get(void) {
+    return (*mt_rand)();
+}
+
+extern "C" void fuzz_mt19937_destroy(void) {
+    delete mt_rand;
+    mt_rand = NULL;
+}
diff --git a/fuzzer/mt19937.h b/fuzzer/mt19937.h
new file mode 100644
index 0000000..cda1181
--- /dev/null
+++ b/fuzzer/mt19937.h
@@ -0,0 +1,4 @@
+#include <stdint.h>
+void fuzz_mt19937_init(uint32_t seed);
+uint32_t fuzz_mt19937_get(void);
+void fuzz_mt19937_destroy(void);
diff --git a/fuzzer/testmem.c b/fuzzer/testmem.c
new file mode 100644
index 0000000..560e0d1
--- /dev/null
+++ b/fuzzer/testmem.c
@@ -0,0 +1,23 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef FUZZ_MSAN
+#include <stdio.h>
+static void fuzz_testmem_msan(void* data, size_t size) {
+    /* This is a trick to force MemorySanitizer to evaluate the data at hand */
+    FILE* fp = fopen("/dev/null", "wb");
+    fwrite(data, size, 1, fp);
+    fclose(fp);
+}
+#endif
+
+void fuzz_testmem(void* data, size_t size) {
+#ifdef FUZZ_MSAN
+    fuzz_testmem_msan(data, size);
+#endif
+    uint8_t* copy = malloc(size);
+    memcpy(copy, data, size);
+    free(copy);
+}
diff --git a/fuzzer/testmem.h b/fuzzer/testmem.h
new file mode 100644
index 0000000..6c58b32
--- /dev/null
+++ b/fuzzer/testmem.h
@@ -0,0 +1,3 @@
+#include <stdint.h>
+#include <stddef.h>
+void fuzz_testmem(void* data, size_t size);