/src/jansson/test/ossfuzz/json_pack_unpack_fuzzer.cc
Line | Count | Source |
1 | | /* |
2 | | * json_pack_unpack_fuzzer.cc |
3 | | * |
4 | | * Fuzz harness for jansson json_unpack() and json_deep_copy() paths. |
5 | | * json_unpack() processes a format string + JSON value and extracts |
6 | | * typed fields — it involves non-trivial string/type dispatch that |
7 | | * is not exercised by the load/dump fuzzer. |
8 | | * |
9 | | * OSS-Fuzz build: compiled via test/ossfuzz/ossfuzz.sh. |
10 | | */ |
11 | | #include <stdint.h> |
12 | | #include <stddef.h> |
13 | | #include <string.h> |
14 | | #include <stdlib.h> |
15 | | |
16 | | #include "jansson.h" |
17 | | |
18 | | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) |
19 | 5.91k | { |
20 | 5.91k | if (size < 2) |
21 | 1 | return 0; |
22 | | |
23 | | /* Use first byte to select format variant, rest as JSON input */ |
24 | 5.91k | uint8_t variant = data[0] % 8; |
25 | 5.91k | const char *json_str = (const char *)(data + 1); |
26 | 5.91k | size_t json_len = size - 1; |
27 | | |
28 | 5.91k | json_error_t error; |
29 | 5.91k | json_t *root = json_loadb(json_str, json_len, |
30 | 5.91k | JSON_DECODE_ANY | JSON_ALLOW_NUL, &error); |
31 | 5.91k | if (root == NULL) |
32 | 1.28k | return 0; |
33 | | |
34 | | /* Exercise json_deep_copy on whatever we parsed */ |
35 | 4.63k | json_t *copy = json_deep_copy(root); |
36 | 4.63k | if (copy != NULL) |
37 | 4.63k | json_decref(copy); |
38 | | |
39 | | /* Exercise json_dumps with various flags */ |
40 | 4.63k | const int flag_sets[] = { |
41 | 4.63k | 0, |
42 | 4.63k | JSON_COMPACT, |
43 | 4.63k | JSON_ENSURE_ASCII, |
44 | 4.63k | JSON_SORT_KEYS, |
45 | 4.63k | JSON_COMPACT | JSON_ENSURE_ASCII, |
46 | 4.63k | }; |
47 | 4.63k | int flags = flag_sets[variant % 5]; |
48 | 4.63k | char *dumped = json_dumps(root, flags); |
49 | 4.63k | if (dumped != NULL) { |
50 | | /* Re-load the dumped output for round-trip check */ |
51 | 4.52k | json_t *reparsed = json_loads(dumped, 0, NULL); |
52 | 4.52k | if (reparsed != NULL) |
53 | 4.51k | json_decref(reparsed); |
54 | 4.52k | free(dumped); |
55 | 4.52k | } |
56 | | |
57 | | /* Exercise json_unpack with simple format strings if root is an object */ |
58 | 4.63k | if (json_is_object(root)) { |
59 | 442 | json_t *val = NULL; |
60 | 442 | const char *key = NULL; |
61 | | /* Unpack first key-value pair */ |
62 | 442 | void *iter = json_object_iter(root); |
63 | 442 | if (iter != NULL) { |
64 | 440 | key = json_object_iter_key(iter); |
65 | 440 | val = json_object_iter_value(iter); |
66 | 440 | (void)key; |
67 | 440 | (void)val; |
68 | 440 | } |
69 | | |
70 | | /* json_unpack with "o" format — extract object */ |
71 | 442 | json_t *out_obj = NULL; |
72 | 442 | json_unpack(root, "o", &out_obj); |
73 | | |
74 | | /* json_unpack with "{s?o}" — optional key lookup */ |
75 | 442 | json_t *field = NULL; |
76 | 442 | json_unpack(root, "{s?o}", "data", &field); |
77 | 442 | } |
78 | | |
79 | | /* Exercise json_unpack on array */ |
80 | 4.63k | if (json_is_array(root) && json_array_size(root) > 0) { |
81 | 4.08k | json_t *first = NULL; |
82 | 4.08k | json_unpack(root, "[o!]", &first); |
83 | 4.08k | } |
84 | | |
85 | 4.63k | json_decref(root); |
86 | 4.63k | return 0; |
87 | 5.91k | } |