Coverage Report

Created: 2026-09-28 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/yyjson/fuzz/patch_fuzzer.c
Line
Count
Source
1
#include <stdlib.h>
2
#include <string.h>
3
#include <yyjson.h>
4
5
51.6k
#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.7k
static void *fuzz_realloc(void *ctx, void *ptr, size_t old_size, size_t size) {
21
14.7k
    (void)ctx;
22
14.7k
    (void)old_size;
23
14.7k
    if (size > YYJSON_PATCH_FUZZ_ALLOC_LIMIT) return NULL;
24
14.6k
    return realloc(ptr, size);
25
14.7k
}
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
13.0k
static void write_and_free_mut_val(yyjson_mut_val *val) {
39
13.0k
    char *json;
40
13.0k
    if (!val) return;
41
9.51k
    json = yyjson_mut_val_write_opts(val, YYJSON_WRITE_NOFLAG, &FUZZ_ALC,
42
9.51k
                                     NULL, NULL);
43
9.51k
    if (json) FUZZ_ALC.free(FUZZ_ALC.ctx, json);
44
9.51k
}
45
46
6.52k
static void run_json_patch(yyjson_doc *base_doc, yyjson_doc *patch_doc) {
47
6.52k
    yyjson_val *base_root = yyjson_doc_get_root(base_doc);
48
6.52k
    yyjson_val *patch_root = yyjson_doc_get_root(patch_doc);
49
50
6.52k
    if (base_root == NULL || patch_root == NULL) {
51
0
        return;
52
0
    }
53
54
6.52k
    yyjson_mut_doc *result_doc = yyjson_mut_doc_new(&FUZZ_ALC);
55
6.52k
    if (result_doc != NULL) {
56
6.52k
        yyjson_patch_err err;
57
6.52k
        memset(&err, 0, sizeof(err));
58
59
6.52k
        yyjson_mut_val *result =
60
6.52k
            yyjson_patch(result_doc, base_root, patch_root, &err);
61
6.52k
        write_and_free_mut_val(result);
62
63
6.52k
        yyjson_mut_doc_free(result_doc);
64
6.52k
    }
65
66
6.52k
    yyjson_mut_doc *mut_doc = yyjson_mut_doc_new(&FUZZ_ALC);
67
6.52k
    if (mut_doc != NULL) {
68
6.52k
        yyjson_mut_val *base_mut = yyjson_val_mut_copy(mut_doc, base_root);
69
6.52k
        yyjson_mut_val *patch_mut = yyjson_val_mut_copy(mut_doc, patch_root);
70
71
6.52k
        if (base_mut != NULL && patch_mut != NULL) {
72
6.52k
            yyjson_patch_err err;
73
6.52k
            memset(&err, 0, sizeof(err));
74
75
6.52k
            yyjson_mut_val *result =
76
6.52k
                yyjson_mut_patch(mut_doc, base_mut, patch_mut, &err);
77
6.52k
            write_and_free_mut_val(result);
78
6.52k
        }
79
80
6.52k
        yyjson_mut_doc_free(mut_doc);
81
6.52k
    }
82
6.52k
}
83
84
51.6k
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
85
51.6k
    if (data == NULL ||
86
51.6k
        size < sizeof(uint16_t) + 1 ||
87
51.6k
        size > YYJSON_PATCH_MAX_INPUT_SIZE) {
88
16
        return 0;
89
16
    }
90
91
51.6k
    uint16_t split_len_raw;
92
51.6k
    memcpy(&split_len_raw, data, sizeof(split_len_raw));
93
94
51.6k
    data += sizeof(split_len_raw);
95
51.6k
    size -= sizeof(split_len_raw);
96
97
51.6k
    size_t base_size = split_len_raw % (size + 1);
98
51.6k
    size_t patch_size = size - base_size;
99
100
51.6k
    const uint8_t *base_data = data;
101
51.6k
    const uint8_t *patch_data = data + base_size;
102
103
51.6k
    yyjson_read_flag flags =
104
51.6k
        YYJSON_READ_ALLOW_INVALID_UNICODE |
105
51.6k
        YYJSON_READ_ALLOW_COMMENTS;
106
107
51.6k
    yyjson_doc *base_doc =
108
51.6k
        yyjson_read_opts((char *)(void *)base_data, base_size, flags,
109
51.6k
                         &FUZZ_ALC, NULL);
110
51.6k
    yyjson_doc *patch_doc =
111
51.6k
        yyjson_read_opts((char *)(void *)patch_data, patch_size, flags,
112
51.6k
                         &FUZZ_ALC, NULL);
113
114
51.6k
    if (base_doc != NULL && patch_doc != NULL) {
115
11.6k
        run_json_patch(base_doc, patch_doc);
116
11.6k
    }
117
118
51.6k
    yyjson_doc_free(base_doc);
119
51.6k
    yyjson_doc_free(patch_doc);
120
121
51.6k
    return 0;
122
51.6k
}