Add a fuzz testing stub for ability to use external generators also
authorPetteri Aimonen <jpa@git.mail.kapsi.fi>
Thu, 11 Sep 2014 14:58:53 +0000 (17:58 +0300)
committerPetteri Aimonen <jpa@git.mail.kapsi.fi>
Thu, 11 Sep 2014 16:22:57 +0000 (19:22 +0300)
tests/fuzztest/SConscript
tests/fuzztest/fuzzstub.c [new file with mode: 0644]
tests/fuzztest/run_radamsa.sh [new file with mode: 0755]
tests/fuzztest/sample_data/sample1.pb [new file with mode: 0644]
tests/fuzztest/sample_data/sample2.pb [new file with mode: 0644]

index 36b62c5..6499714 100644 (file)
@@ -47,4 +47,12 @@ Depends([p1, p2, fuzz], ["fuzz_syshdr.h", "malloc_wrappers.h"])
 
 env.RunTest(fuzz)
 
+fuzzstub = env.Program(["fuzzstub.c",
+                    "alltypes_pointer.pb.c",
+                    "alltypes_static.pb.c",
+                    "pb_encode_with_malloc.o",
+                    "pb_decode_with_malloc.o",
+                    "pb_common_with_malloc.o",
+                    "malloc_wrappers.c"])
+
 
diff --git a/tests/fuzztest/fuzzstub.c b/tests/fuzztest/fuzzstub.c
new file mode 100644 (file)
index 0000000..5099841
--- /dev/null
@@ -0,0 +1,189 @@
+/* Fuzz testing for the nanopb core.
+ * This can be used with external fuzzers, e.g. radamsa.
+ * It performs most of the same checks as fuzztest, but does not feature data generation.
+ */
+
+#include <pb_decode.h>
+#include <pb_encode.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include "malloc_wrappers.h"
+#include "alltypes_static.pb.h"
+#include "alltypes_pointer.pb.h"
+
+#define BUFSIZE 4096
+
+static bool do_static_decode(uint8_t *buffer, size_t msglen, bool assert_success)
+{
+    pb_istream_t stream;
+    bool status;
+    
+    alltypes_static_AllTypes *msg = malloc_with_check(sizeof(alltypes_static_AllTypes));
+    stream = pb_istream_from_buffer(buffer, msglen);
+    status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg);
+    
+    if (!status && assert_success)
+    {
+        /* Anything that was successfully encoded, should be decodeable.
+         * One exception: strings without null terminator are encoded up
+         * to end of buffer, but refused on decode because the terminator
+         * would not fit. */
+        if (strcmp(stream.errmsg, "string overflow") != 0)
+            assert(status);
+    }
+    
+    free_with_check(msg);
+    return status;
+}
+
+static bool do_pointer_decode(uint8_t *buffer, size_t msglen, bool assert_success)
+{
+    pb_istream_t stream;
+    bool status;
+    alltypes_pointer_AllTypes *msg;
+    
+    msg = malloc_with_check(sizeof(alltypes_pointer_AllTypes));
+    memset(msg, 0, sizeof(alltypes_pointer_AllTypes));
+    stream = pb_istream_from_buffer(buffer, msglen);
+
+    assert(get_alloc_count() == 0);
+    status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg);
+    
+    if (assert_success)
+        assert(status);
+    
+    pb_release(alltypes_pointer_AllTypes_fields, msg);    
+    assert(get_alloc_count() == 0);
+    
+    free_with_check(msg);
+
+    return status;
+}
+
+/* Do a decode -> encode -> decode -> encode roundtrip */
+static void do_static_roundtrip(uint8_t *buffer, size_t msglen)
+{
+    bool status;
+    uint8_t *buf2 = malloc_with_check(BUFSIZE);
+    uint8_t *buf3 = malloc_with_check(BUFSIZE);
+    size_t msglen2, msglen3;
+    alltypes_static_AllTypes *msg1 = malloc_with_check(sizeof(alltypes_static_AllTypes));
+    alltypes_static_AllTypes *msg2 = malloc_with_check(sizeof(alltypes_static_AllTypes));
+    memset(msg1, 0, sizeof(alltypes_static_AllTypes));
+    memset(msg2, 0, sizeof(alltypes_static_AllTypes));
+    
+    {
+        pb_istream_t stream = pb_istream_from_buffer(buffer, msglen);
+        status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg1);
+        assert(status);
+    }
+    
+    {
+        pb_ostream_t stream = pb_ostream_from_buffer(buf2, BUFSIZE);
+        status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg1);
+        assert(status);
+        msglen2 = stream.bytes_written;
+    }
+    
+    {
+        pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2);
+        status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg2);
+        assert(status);
+    }
+    
+    {
+        pb_ostream_t stream = pb_ostream_from_buffer(buf3, BUFSIZE);
+        status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg2);
+        assert(status);
+        msglen3 = stream.bytes_written;
+    }
+    
+    assert(msglen2 == msglen3);
+    assert(memcmp(buf2, buf3, msglen2) == 0);
+    
+    free_with_check(msg1);
+    free_with_check(msg2);
+    free_with_check(buf2);
+    free_with_check(buf3);
+}
+
+/* Do decode -> encode -> decode -> encode roundtrip */
+static void do_pointer_roundtrip(uint8_t *buffer, size_t msglen)
+{
+    bool status;
+    uint8_t *buf2 = malloc_with_check(BUFSIZE);
+    uint8_t *buf3 = malloc_with_check(BUFSIZE);
+    size_t msglen2, msglen3;
+    alltypes_pointer_AllTypes *msg1 = malloc_with_check(sizeof(alltypes_pointer_AllTypes));
+    alltypes_pointer_AllTypes *msg2 = malloc_with_check(sizeof(alltypes_pointer_AllTypes));
+    memset(msg1, 0, sizeof(alltypes_pointer_AllTypes));
+    memset(msg2, 0, sizeof(alltypes_pointer_AllTypes));
+    
+    {
+        pb_istream_t stream = pb_istream_from_buffer(buffer, msglen);
+        status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg1);
+        assert(status);
+    }
+    
+    {
+        pb_ostream_t stream = pb_ostream_from_buffer(buf2, BUFSIZE);
+        status = pb_encode(&stream, alltypes_pointer_AllTypes_fields, msg1);
+        assert(status);
+        msglen2 = stream.bytes_written;
+    }
+    
+    {
+        pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2);
+        status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg2);
+        assert(status);
+    }
+    
+    {
+        pb_ostream_t stream = pb_ostream_from_buffer(buf3, BUFSIZE);
+        status = pb_encode(&stream, alltypes_pointer_AllTypes_fields, msg2);
+        assert(status);
+        msglen3 = stream.bytes_written;
+    }
+    
+    assert(msglen2 == msglen3);
+    assert(memcmp(buf2, buf3, msglen2) == 0);
+    
+    pb_release(alltypes_pointer_AllTypes_fields, msg1);
+    pb_release(alltypes_pointer_AllTypes_fields, msg2);
+    free_with_check(msg1);
+    free_with_check(msg2);
+    free_with_check(buf2);
+    free_with_check(buf3);
+}
+
+static void run_iteration()
+{
+    uint8_t *buffer = malloc_with_check(BUFSIZE);
+    size_t msglen;
+    bool status;
+    
+    msglen = fread(buffer, BUFSIZE, 1, stdin);
+
+    status = do_static_decode(buffer, msglen, false);
+    
+    if (status)
+        do_static_roundtrip(buffer, msglen);
+    
+    status = do_pointer_decode(buffer, msglen, false);
+    
+    if (status)
+        do_pointer_roundtrip(buffer, msglen);
+    
+    free_with_check(buffer);
+}
+
+int main(int argc, char **argv)
+{
+    run_iteration();
+    
+    return 0;
+}
+
diff --git a/tests/fuzztest/run_radamsa.sh b/tests/fuzztest/run_radamsa.sh
new file mode 100755 (executable)
index 0000000..52cd40a
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+TMP=`tempfile`
+
+echo $TMP
+while true
+do
+   radamsa sample_data/* > $TMP
+   $1 < $TMP
+   test $? -gt 127 && break
+done
diff --git a/tests/fuzztest/sample_data/sample1.pb b/tests/fuzztest/sample_data/sample1.pb
new file mode 100644 (file)
index 0000000..0752788
Binary files /dev/null and b/tests/fuzztest/sample_data/sample1.pb differ
diff --git a/tests/fuzztest/sample_data/sample2.pb b/tests/fuzztest/sample_data/sample2.pb
new file mode 100644 (file)
index 0000000..cc89f91
Binary files /dev/null and b/tests/fuzztest/sample_data/sample2.pb differ