Coverage Report

Created: 2026-01-10 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libconfig/fuzz/fuzz_config_read.c
Line
Count
Source
1
#include "fuzz_data.h"
2
3
#include <libconfig.h>
4
#include <stdint.h>
5
#include <stdlib.h>
6
#include <string.h>
7
8
7.03k
#define MAX_CONFIG_SIZE 4096
9
6.99k
#define MAX_PATH_SIZE 256
10
4.72k
#define MIN_BUFF_SIZE sizeof(fuzz_data_t) + 2 // Room for fixed-width data and two null terminators
11
2.35k
#define MAX_BUFF_SIZE sizeof(fuzz_data_t) + MAX_CONFIG_SIZE + MAX_PATH_SIZE + 1
12
13
// Forward-declare the libFuzzer's mutator callback.
14
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
15
16
static uint8_t dummy_input[] = {
17
    0x0, // Lookup type
18
    0x17, 0x0, 0x0, 0x0, // Content size
19
    0x3, 0x0, 0x0, 0x0, // Path size
20
    '{', '\n', '\t', 'a', ':', '\n', '{', '\n', '\t', '\t', 'b', ' ', '=', ' ', '1', ';', '\n', '\t', '}', ';', '\n', '}', ';', '\0', // config
21
    'a', '.', 'b', '\0' // Path
22
};
23
24
/**
25
 * To be called statically from the harness, this function opens /dev/null (Only
26
 * Linux compatible)
27
 * @return: FILE pointer to /dev/null
28
 */
29
FILE *open_null_file()
30
1
{
31
1
    FILE *dev_null = fopen("/dev/null", "w");
32
33
1
    if (dev_null == NULL)
34
0
    {
35
0
        abort();
36
0
    }
37
38
1
    return dev_null;
39
1
}
40
41
// Recursive function to search for a setting by name
42
config_setting_t *find_setting_recursive(
43
    config_setting_t *setting,
44
    const char *name,
45
    const size_t name_len)
46
36.1k
{
47
36.1k
    if (NULL == setting || NULL == name)
48
0
    {
49
0
        return NULL;
50
0
    }
51
52
    // Check if the current setting's name matches
53
36.1k
    const char *setting_name = config_setting_name(setting);
54
36.1k
    if (NULL != setting_name && strlen(setting_name) == name_len) {
55
2.51k
        if (strncmp(config_setting_name(setting), name, name_len) == 0) {
56
416
            return setting;
57
416
        }
58
2.51k
    }
59
60
    // If it's a group, iterate over its children recursively
61
35.7k
    if (config_setting_is_group(setting) || config_setting_is_array(setting)
62
29.3k
        || config_setting_is_list(setting))
63
8.08k
    {
64
8.08k
        int count = config_setting_length(setting);
65
41.7k
        for (int i = 0; i < count; ++i)
66
34.3k
        {
67
34.3k
            config_setting_t *child = config_setting_get_elem(setting, i);
68
34.3k
            config_setting_t *result = find_setting_recursive(child, name, name_len);
69
34.3k
            if (result != NULL)
70
719
            {
71
719
                return result; // Return the found setting
72
719
            }
73
34.3k
        }
74
8.08k
    }
75
76
    // Return NULL if not found
77
35.0k
    return NULL;
78
35.7k
}
79
80
size_t min_size(size_t a, size_t b)
81
0
{
82
0
    return (a <= b) ? a : b;
83
0
}
84
85
size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size,
86
    size_t max_size, unsigned int seed)
87
0
{
88
0
    size_t remaining_size = max_size;
89
0
    config_t cfg = {0};
90
0
    uint8_t* config_data = NULL;
91
0
    uint8_t* path_data = NULL;
92
93
0
    srand(seed);
94
0
    config_init(&cfg);
95
96
0
    if (size < MIN_BUFF_SIZE || max_size < MIN_BUFF_SIZE || max_size > MAX_BUFF_SIZE) {
97
0
        return MIN_BUFF_SIZE;
98
0
    }
99
100
0
    fuzz_data_t *fuzz_data = (fuzz_data_t *) data;
101
0
    remaining_size -= sizeof(fuzz_data_t);
102
103
    // Limit sizes to avoid overflow
104
0
    size_t max_content = min_size(remaining_size, MAX_CONFIG_SIZE - 1);
105
0
    fuzz_data->content_size = rand() % max_content;
106
107
0
    size_t max_path = min_size(remaining_size - fuzz_data->content_size, MAX_PATH_SIZE - 1);
108
0
    fuzz_data->path_size = rand() % max_path;
109
110
0
    if (fuzz_data->content_size + fuzz_data->path_size + sizeof(fuzz_data_t) >= max_size) {
111
0
        fuzz_data->content_size = max_content / 2;
112
0
        fuzz_data->path_size = max_path / 2;
113
0
    }
114
115
0
    fuzz_data_content(fuzz_data, &config_data);
116
0
    fuzz_data_path(fuzz_data, &path_data);
117
118
    // Mutate content and path safely
119
0
    fuzz_data->content_size = LLVMFuzzerMutate(config_data, fuzz_data->content_size, max_content);
120
0
    config_data[fuzz_data->content_size < max_content ? fuzz_data->content_size : max_content - 1] = '\0';
121
122
0
    fuzz_data->path_size = LLVMFuzzerMutate(path_data, fuzz_data->path_size, max_path);
123
0
    path_data[fuzz_data->path_size < max_path ? fuzz_data->path_size : max_path - 1] = '\0';
124
125
0
    return sizeof(fuzz_data_t) + fuzz_data->content_size + fuzz_data->path_size + 2;
126
0
}
127
128
int LLVMFuzzerTestOneInput(const uint8_t *data, const size_t size)
129
2.36k
{
130
2.36k
    static FILE *dev_null;
131
2.36k
    uint8_t data_buff[MAX_BUFF_SIZE] = {0};
132
2.36k
    uint8_t scratch_mem[sizeof(uint64_t)] = {0};
133
2.36k
    config_t cfg = {0};
134
2.36k
    uint8_t *config_ptr;
135
2.36k
    uint8_t *path_ptr;
136
2.36k
    config_setting_t *root;
137
2.36k
    int rc = -1;
138
139
2.36k
    fuzz_data_t *fuzz_data = (fuzz_data_t*) data_buff;
140
141
2.36k
    if (NULL == dev_null)
142
1
    {
143
        // Only called once per process
144
1
        dev_null = open_null_file();
145
1
    }
146
147
2.36k
    if (size < MIN_BUFF_SIZE || size > MAX_BUFF_SIZE)
148
24
    {
149
        // Not enough bytes to be a fuzz_data_t
150
24
        rc = 0;
151
24
        goto end;
152
24
    }
153
154
    // Copy the data to a buffer that can be mutated
155
2.33k
    memcpy(data_buff, data, size);
156
157
2.33k
    config_init(&cfg);
158
159
2.33k
    if (fuzz_data->content_size > MAX_CONFIG_SIZE || 
160
2.30k
        fuzz_data->path_size > MAX_PATH_SIZE || 
161
2.24k
        sizeof(fuzz_data_t) + fuzz_data->content_size + fuzz_data->path_size > size) { 
162
107
        goto end; 
163
107
    } 
164
2.23k
    fuzz_data_content(fuzz_data, &config_ptr);
165
2.23k
    fuzz_data_path(fuzz_data, &path_ptr);
166
2.23k
    const char *config_data = (const char *) config_ptr;
167
2.23k
    const char *path_data = (const char *) path_ptr;
168
169
2.23k
    if (CONFIG_TRUE != config_read_string(&cfg, config_data)
170
2.23k
        || NULL == (root = config_root_setting(&cfg)))
171
400
    {
172
        // Parsing failed
173
400
        goto end;
174
400
    }
175
176
1.83k
    config_setting_t *setting = find_setting_recursive(
177
1.83k
        root,
178
1.83k
        (const char*) path_ptr,
179
1.83k
        fuzz_data->path_size
180
1.83k
    );
181
182
1.83k
    if (NULL != setting)
183
416
    {
184
416
        config_setting_get_elem(setting, config_setting_length(setting) - 1);
185
186
416
        switch (fuzz_data->lookup_type % (CONFIG_TYPE_LIST + 1)) {
187
49
            case CONFIG_TYPE_FLOAT:
188
49
                config_setting_lookup_float(setting, path_data,
189
49
                    (double*) scratch_mem);
190
49
                break;
191
45
            case CONFIG_TYPE_BOOL:
192
45
                config_setting_lookup_bool(setting, path_data,
193
45
                    (int*) scratch_mem);
194
45
                break;
195
195
            case CONFIG_TYPE_INT:
196
195
                config_setting_lookup_int(setting, path_data,
197
195
                    (int*) scratch_mem);
198
195
                break;
199
50
            case CONFIG_TYPE_INT64:
200
50
                config_setting_lookup_int64(setting, path_data,
201
50
                    (long long *) scratch_mem);
202
50
                break;
203
51
            case CONFIG_TYPE_STRING: {
204
51
                const char *string_ptr = NULL;
205
51
                config_setting_lookup_string(setting, path_data, &string_ptr);
206
51
                break;
207
0
            }
208
26
            default:
209
26
                config_setting_lookup_const(setting, path_data);
210
416
        }
211
416
    }
212
213
1.83k
    if (NULL != (setting = config_setting_get_member(root, path_data)))
214
401
    {
215
        // This setting exists, let's overwrite it
216
401
        config_setting_set_float(setting, 1.234);
217
401
    }
218
1.43k
    else {
219
        // This setting does not exist, create it
220
1.43k
        setting = config_setting_add(root, path_data, CONFIG_TYPE_FLOAT);
221
222
1.43k
        if (setting == NULL)
223
431
        {
224
431
            rc = -1;
225
431
            goto end;
226
431
        }
227
999
        config_setting_set_float(setting, 1.234);
228
999
    }
229
230
1.40k
    config_write(&cfg, dev_null);
231
1.40k
    config_setting_remove(root, path_data);
232
233
1.40k
    rc = 0;
234
235
2.36k
    end:
236
2.36k
    config_destroy(&cfg);
237
2.36k
    return rc;
238
1.40k
}