/src/yyjson/fuzz/patch_fuzzer.c
Line | Count | Source |
1 | | #include <stdlib.h> |
2 | | #include <string.h> |
3 | | #include <yyjson.h> |
4 | | |
5 | 25.4k | #define YYJSON_PATCH_MAX_INPUT_SIZE 8192 |
6 | | |
7 | | /* Fuzzer-only allocator cap (not a library limit). |
8 | | OSS-Fuzz kills processes above ~2560MB RSS. Valid JSON Patch "copy" from |
9 | | the root can grow exponentially; return NULL before the process is killed. */ |
10 | | #ifndef YYJSON_PATCH_FUZZ_ALLOC_LIMIT |
11 | 160k | # define YYJSON_PATCH_FUZZ_ALLOC_LIMIT (128u * 1024u * 1024u) |
12 | | #endif |
13 | | |
14 | 146k | static void *fuzz_malloc(void *ctx, size_t size) { |
15 | 146k | (void)ctx; |
16 | 146k | if (size > YYJSON_PATCH_FUZZ_ALLOC_LIMIT) return NULL; |
17 | 145k | return malloc(size); |
18 | 146k | } |
19 | | |
20 | 14.1k | static void *fuzz_realloc(void *ctx, void *ptr, size_t old_size, size_t size) { |
21 | 14.1k | (void)ctx; |
22 | 14.1k | (void)old_size; |
23 | 14.1k | if (size > YYJSON_PATCH_FUZZ_ALLOC_LIMIT) return NULL; |
24 | 14.0k | return realloc(ptr, size); |
25 | 14.1k | } |
26 | | |
27 | 145k | static void fuzz_free(void *ctx, void *ptr) { |
28 | 145k | (void)ctx; |
29 | 145k | free(ptr); |
30 | 145k | } |
31 | | |
32 | | static const yyjson_alc FUZZ_ALC = { |
33 | | fuzz_malloc, fuzz_realloc, fuzz_free, NULL |
34 | | }; |
35 | | |
36 | | /*----------------------------------------------------------------------------*/ |
37 | | |
38 | 12.5k | static void write_and_free_mut_val(yyjson_mut_val *val) { |
39 | 12.5k | char *json; |
40 | 12.5k | if (!val) return; |
41 | 9.10k | json = yyjson_mut_val_write_opts(val, YYJSON_WRITE_NOFLAG, &FUZZ_ALC, |
42 | 9.10k | NULL, NULL); |
43 | 9.10k | if (json) FUZZ_ALC.free(FUZZ_ALC.ctx, json); |
44 | 9.10k | } |
45 | | |
46 | 6.28k | static void run_json_patch(yyjson_doc *base_doc, yyjson_doc *patch_doc) { |
47 | 6.28k | yyjson_val *base_root = yyjson_doc_get_root(base_doc); |
48 | 6.28k | yyjson_val *patch_root = yyjson_doc_get_root(patch_doc); |
49 | | |
50 | 6.28k | if (base_root == NULL || patch_root == NULL) { |
51 | 0 | return; |
52 | 0 | } |
53 | | |
54 | 6.28k | yyjson_mut_doc *result_doc = yyjson_mut_doc_new(&FUZZ_ALC); |
55 | 6.28k | if (result_doc != NULL) { |
56 | 6.28k | yyjson_patch_err err; |
57 | 6.28k | memset(&err, 0, sizeof(err)); |
58 | | |
59 | 6.28k | yyjson_mut_val *result = |
60 | 6.28k | yyjson_patch(result_doc, base_root, patch_root, &err); |
61 | 6.28k | write_and_free_mut_val(result); |
62 | | |
63 | 6.28k | yyjson_mut_doc_free(result_doc); |
64 | 6.28k | } |
65 | | |
66 | 6.28k | yyjson_mut_doc *mut_doc = yyjson_mut_doc_new(&FUZZ_ALC); |
67 | 6.28k | if (mut_doc != NULL) { |
68 | 6.28k | yyjson_mut_val *base_mut = yyjson_val_mut_copy(mut_doc, base_root); |
69 | 6.28k | yyjson_mut_val *patch_mut = yyjson_val_mut_copy(mut_doc, patch_root); |
70 | | |
71 | 6.28k | if (base_mut != NULL && patch_mut != NULL) { |
72 | 6.28k | yyjson_patch_err err; |
73 | 6.28k | memset(&err, 0, sizeof(err)); |
74 | | |
75 | 6.28k | yyjson_mut_val *result = |
76 | 6.28k | yyjson_mut_patch(mut_doc, base_mut, patch_mut, &err); |
77 | 6.28k | write_and_free_mut_val(result); |
78 | 6.28k | } |
79 | | |
80 | 6.28k | yyjson_mut_doc_free(mut_doc); |
81 | 6.28k | } |
82 | 6.28k | } |
83 | | |
84 | 25.4k | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
85 | 25.4k | if (data == NULL || |
86 | 25.4k | size < sizeof(uint16_t) + 1 || |
87 | 25.4k | size > YYJSON_PATCH_MAX_INPUT_SIZE) { |
88 | 12 | return 0; |
89 | 12 | } |
90 | | |
91 | 25.4k | uint16_t split_len_raw; |
92 | 25.4k | memcpy(&split_len_raw, data, sizeof(split_len_raw)); |
93 | | |
94 | 25.4k | data += sizeof(split_len_raw); |
95 | 25.4k | size -= sizeof(split_len_raw); |
96 | | |
97 | 25.4k | size_t base_size = split_len_raw % (size + 1); |
98 | 25.4k | size_t patch_size = size - base_size; |
99 | | |
100 | 25.4k | const uint8_t *base_data = data; |
101 | 25.4k | const uint8_t *patch_data = data + base_size; |
102 | | |
103 | 25.4k | yyjson_read_flag flags = |
104 | 25.4k | YYJSON_READ_ALLOW_INVALID_UNICODE | |
105 | 25.4k | YYJSON_READ_ALLOW_COMMENTS; |
106 | | |
107 | 25.4k | yyjson_doc *base_doc = |
108 | 25.4k | yyjson_read_opts((char *)(void *)base_data, base_size, flags, |
109 | 25.4k | &FUZZ_ALC, NULL); |
110 | 25.4k | yyjson_doc *patch_doc = |
111 | 25.4k | yyjson_read_opts((char *)(void *)patch_data, patch_size, flags, |
112 | 25.4k | &FUZZ_ALC, NULL); |
113 | | |
114 | 25.4k | if (base_doc != NULL && patch_doc != NULL) { |
115 | 6.28k | run_json_patch(base_doc, patch_doc); |
116 | 6.28k | } |
117 | | |
118 | 25.4k | yyjson_doc_free(base_doc); |
119 | 25.4k | yyjson_doc_free(patch_doc); |
120 | | |
121 | 25.4k | return 0; |
122 | 25.4k | } |