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 	}