aboutsummaryrefslogtreecommitdiffstats
path: root/tests/fuzztest
diff options
context:
space:
mode:
Diffstat (limited to 'tests/fuzztest')
-rw-r--r--tests/fuzztest/SConscript4
-rw-r--r--tests/fuzztest/generate_message.c101
2 files changed, 105 insertions, 0 deletions
diff --git a/tests/fuzztest/SConscript b/tests/fuzztest/SConscript
index 973148c2..d2fb689c 100644
--- a/tests/fuzztest/SConscript
+++ b/tests/fuzztest/SConscript
@@ -36,4 +36,8 @@ fuzzstub = malloc_env.Program(["fuzzstub.c",
"$COMMON/pb_common_with_malloc.o",
"$COMMON/malloc_wrappers.o"])
+generate_message = malloc_env.Program(["generate_message.c",
+ "alltypes_static.pb.c",
+ "$COMMON/pb_encode.o",
+ "$COMMON/pb_common.o"])
diff --git a/tests/fuzztest/generate_message.c b/tests/fuzztest/generate_message.c
new file mode 100644
index 00000000..6e492990
--- /dev/null
+++ b/tests/fuzztest/generate_message.c
@@ -0,0 +1,101 @@
+/* Generates a random, valid protobuf message. Useful to seed
+ * external fuzzers such as afl-fuzz.
+ */
+
+#include <pb_encode.h>
+#include <pb_common.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include "alltypes_static.pb.h"
+
+static uint64_t random_seed;
+
+/* Uses xorshift64 here instead of rand() for both speed and
+ * reproducibility across platforms. */
+static uint32_t rand_word()
+{
+ random_seed ^= random_seed >> 12;
+ random_seed ^= random_seed << 25;
+ random_seed ^= random_seed >> 27;
+ return random_seed * 2685821657736338717ULL;
+}
+
+/* Fills a buffer with random data. */
+static void rand_fill(uint8_t *buf, size_t count)
+{
+ while (count--)
+ {
+ *buf++ = rand_word() & 0xff;
+ }
+}
+
+/* Check that size/count fields do not exceed their max size.
+ * Otherwise we would have to loop pretty long in generate_message().
+ * Note that there may still be a few encoding errors from submessages.
+ */
+static void limit_sizes(alltypes_static_AllTypes *msg)
+{
+ pb_field_iter_t iter;
+ pb_field_iter_begin(&iter, alltypes_static_AllTypes_fields, msg);
+ while (pb_field_iter_next(&iter))
+ {
+ if (PB_LTYPE(iter.pos->type) == PB_LTYPE_BYTES)
+ {
+ ((pb_bytes_array_t*)iter.pData)->size %= iter.pos->data_size - PB_BYTES_ARRAY_T_ALLOCSIZE(0);
+ }
+
+ if (PB_HTYPE(iter.pos->type) == PB_HTYPE_REPEATED)
+ {
+ *((pb_size_t*)iter.pSize) %= iter.pos->array_size;
+ }
+
+ if (PB_HTYPE(iter.pos->type) == PB_HTYPE_ONEOF)
+ {
+ /* Set the oneof to this message type with 50% chance. */
+ if (rand_word() & 1)
+ {
+ *((pb_size_t*)iter.pSize) = iter.pos->tag;
+ }
+ }
+ }
+}
+
+static void generate_message()
+{
+ alltypes_static_AllTypes msg;
+ uint8_t buf[8192];
+ pb_ostream_t stream = {0};
+
+ do {
+ if (stream.errmsg)
+ fprintf(stderr, "Encoder error: %s\n", stream.errmsg);
+
+ stream = pb_ostream_from_buffer(buf, sizeof(buf));
+ rand_fill((void*)&msg, sizeof(msg));
+ limit_sizes(&msg);
+ } while (!pb_encode(&stream, alltypes_static_AllTypes_fields, &msg));
+
+ fwrite(buf, 1, stream.bytes_written, stdout);
+}
+
+int main(int argc, char **argv)
+{
+ if (argc > 1)
+ {
+ random_seed = atol(argv[1]);
+ }
+ else
+ {
+ random_seed = time(NULL);
+ }
+
+ fprintf(stderr, "Random seed: %llu\n", (long long unsigned)random_seed);
+
+ generate_message();
+
+ return 0;
+}
+