Coverage Report

Created: 2026-09-03 06:39

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
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
}