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