Coverage Report

Created: 2026-10-17 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}