1 /** 2 * License: MIT 3 */ 4 module ossfuzz.json_load_dump_fuzzer; 5 6 7 private static import core.memory; 8 private static import core.stdc.stdio; 9 private static import core.stdc.stdlib; 10 private static import jansson_d.jansson; 11 private static import jansson_d.jansson_private; 12 13 private __gshared bool enable_diags; 14 15 pragma(inline, true) 16 nothrow @nogc @live 17 void FUZZ_DEBUG(F ...)(immutable char* fmt, F f) 18 19 do 20 { 21 if (.enable_diags) { 22 static if (f.length != 0) { 23 core.stdc.stdio.fprintf(core.stdc.stdio.stderr, fmt, f[0 .. $]); 24 } else { 25 core.stdc.stdio.fprintf(core.stdc.stdio.stderr, fmt, f[0]); 26 } 27 28 core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "\n"); 29 } 30 } 31 32 extern (C) 33 pure nothrow @trusted @nogc @live 34 private int json_dump_counter(scope const char* buffer, size_t size, scope void* data) 35 36 do 37 { 38 ulong* counter = cast(ulong*)(data); 39 *counter += size; 40 41 return 0; 42 } 43 44 private enum NUM_COMMAND_BYTES = size_t.sizeof + size_t.sizeof + 1; 45 46 private enum FUZZ_DUMP_CALLBACK = 0x00; 47 private enum FUZZ_DUMP_STRING = 0x01; 48 49 extern (C) 50 int LLVMFuzzerTestOneInput(scope const ubyte* data, size_t size) 51 52 do 53 { 54 // Enable or disable diagnostics based on the FUZZ_VERBOSE environment flag. 55 .enable_diags = core.stdc.stdlib.getenv("FUZZ_VERBOSE") != null; 56 57 .FUZZ_DEBUG("Input data length: %zd", size); 58 59 if (size < .NUM_COMMAND_BYTES) { 60 return 0; 61 } 62 63 const (ubyte)* data_temp = cast(const (ubyte)*)(data); 64 65 // Use the first size_t.sizeof bytes as load flags. 66 size_t load_flags = *(cast(const size_t*)(data_temp)); 67 data_temp += size_t.sizeof; 68 69 .FUZZ_DEBUG("load_flags: 0x%zx\n& JSON_REJECT_DUPLICATES = 0x%zx\n& JSON_DECODE_ANY = 0x%zx\n& JSON_DISABLE_EOF_CHECK = 0x%zx\n& JSON_DECODE_INT_AS_REAL = 0x%zx\n& JSON_ALLOW_NUL = 0x%zx\n", load_flags, load_flags & jansson_d.jansson.JSON_REJECT_DUPLICATES, load_flags & jansson_d.jansson.JSON_DECODE_ANY, load_flags & jansson_d.jansson.JSON_DISABLE_EOF_CHECK, load_flags & jansson_d.jansson.JSON_DECODE_INT_AS_REAL, load_flags & jansson_d.jansson.JSON_ALLOW_NUL); 70 71 // Use the next size_t.sizeof bytes as dump flags. 72 size_t dump_flags = *(cast(const size_t*)(data_temp)); 73 data_temp += size_t.sizeof; 74 75 .FUZZ_DEBUG("dump_flags: 0x%zx\n& JSON_MAX_INDENT = 0x%zx\n& JSON_COMPACT = 0x%zx\n& JSON_ENSURE_ASCII = 0x%zx\n& JSON_SORT_KEYS = 0x%zx\n& JSON_PRESERVE_ORDER = 0x%zx\n& JSON_ENCODE_ANY = 0x%zx\n& JSON_ESCAPE_SLASH = 0x%zx\n& JSON_REAL_PRECISION = 0x%zx\n& JSON_EMBED = 0x%zx\n", dump_flags, dump_flags & jansson_d.jansson.JSON_MAX_INDENT, dump_flags & jansson_d.jansson.JSON_COMPACT, dump_flags & jansson_d.jansson.JSON_ENSURE_ASCII, dump_flags & jansson_d.jansson.JSON_SORT_KEYS, dump_flags & jansson_d.jansson.JSON_PRESERVE_ORDER, dump_flags & jansson_d.jansson.JSON_ENCODE_ANY, dump_flags & jansson_d.jansson.JSON_ESCAPE_SLASH, ((dump_flags >> 11) & 0x1F) << 11, dump_flags & jansson_d.jansson.JSON_EMBED); 76 77 // Use the next byte as the dump mode. 78 ubyte dump_mode = data_temp[0]; 79 data_temp++; 80 81 .FUZZ_DEBUG("dump_mode: 0x%x", cast(uint)(dump_mode)); 82 83 // Remove the command bytes from the size total. 84 size -= .NUM_COMMAND_BYTES; 85 86 // Attempt to load the remainder of the data with the given load flags. 87 const char* text = cast(const char*)(data_temp); 88 jansson_d.jansson.json_error_t error = void; 89 jansson_d.jansson.json_t* jobj = jansson_d.jansson.json_loadb(text, size, load_flags, &error); 90 91 if (jobj == null) { 92 return 0; 93 } 94 95 if (dump_mode & .FUZZ_DUMP_STRING) { 96 // Dump as a string. Remove indents so that we don't run out of memory. 97 char* out_ =jansson_d.jansson.json_dumps(jobj, dump_flags & ~jansson_d.jansson.JSON_MAX_INDENT); 98 99 if (out_ != null) { 100 core.memory.pureFree(out_); 101 } 102 } else { 103 // Default is callback mode. 104 // 105 // Attempt to dump the loaded json object with the given dump flags. 106 ulong counter = 0; 107 108 jansson_d.jansson.json_dump_callback(jobj, &.json_dump_counter, &counter, dump_flags); 109 .FUZZ_DEBUG("Counter function counted %llu bytes.", counter); 110 } 111 112 if (jobj != null) { 113 jansson_d.jansson.json_decref(jobj); 114 } 115 116 return 0; 117 }