Coverage Report

Created: 2025-07-12 06:05

/src/libconfig/fuzz/fuzz_config_read.c
Line
Count
Source (jump to first uncovered line)
1
#include "fuzz_data.h"
2
3
#include <libconfig.h>
4
#include <stdint.h>
5
#include <stdlib.h>
6
#include <string.h>
7
8
6.89k
#define MAX_CONFIG_SIZE 4096
9
6.87k
#define MAX_PATH_SIZE 256
10
4.62k
#define MIN_BUFF_SIZE sizeof(fuzz_data_t) + 2 // Room for fixed-width data and two null terminators
11
2.30k
#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
38.4k
{
47
38.4k
    if (NULL == setting || NULL == name)
48
0
    {
49
0
        return NULL;
50
0
    }
51
52
    // Check if the current setting's name matches
53
38.4k
    const char *setting_name = config_setting_name(setting);
54
38.4k
    if (NULL != setting_name && strlen(setting_name) == name_len) {
55
2.90k
        if (strncmp(config_setting_name(setting), name, name_len) == 0) {
56
504
            return setting;
57
504
        }
58
2.90k
    }
59
60
    // If it's a group, iterate over its children recursively
61
37.8k
    if (config_setting_is_group(setting) || config_setting_is_array(setting)
62
37.8k
        || config_setting_is_list(setting))
63
7.75k
    {
64
7.75k
        int count = config_setting_length(setting);
65
43.5k
        for (int i = 0; i < count; ++i)
66
36.5k
        {
67
36.5k
            config_setting_t *child = config_setting_get_elem(setting, i);
68
36.5k
            config_setting_t *result = find_setting_recursive(child, name, name_len);
69
36.5k
            if (result != NULL)
70
781
            {
71
781
                return result; // Return the found setting
72
781
            }
73
36.5k
        }
74
7.75k
    }
75
76
    // Return NULL if not found
77
37.1k
    return NULL;
78
37.8k
}
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.31k
{
130
2.31k
    static FILE *dev_null;
131
2.31k
    uint8_t data_buff[MAX_BUFF_SIZE] = {0};
132
2.31k
    uint8_t scratch_mem[sizeof(uint64_t)] = {0};
133
2.31k
    config_t cfg = {0};
134
2.31k
    uint8_t *config_ptr;
135
2.31k
    uint8_t *path_ptr;
136
2.31k
    config_setting_t *root;
137
2.31k
    int rc = -1;
138
139
2.31k
    fuzz_data_t *fuzz_data = (fuzz_data_t*) data_buff;
140
141
2.31k
    if (NULL == dev_null)
142
1
    {
143
        // Only called once per process
144
1
        dev_null = open_null_file();
145
1
    }
146
147
2.31k
    if (size < MIN_BUFF_SIZE || size > MAX_BUFF_SIZE)
148
19
    {
149
        // Not enough bytes to be a fuzz_data_t
150
19
        rc = 0;
151
19
        goto end;
152
19
    }
153
154
    // Copy the data to a buffer that can be mutated
155
2.29k
    memcpy(data_buff, data, size);
156
157
2.29k
    config_init(&cfg);
158
159
2.29k
    if (fuzz_data->content_size > MAX_CONFIG_SIZE || 
160
2.29k
        fuzz_data->path_size > MAX_PATH_SIZE || 
161
2.29k
        sizeof(fuzz_data_t) + fuzz_data->content_size + fuzz_data->path_size > size) { 
162
88
        goto end; 
163
88
    } 
164
2.20k
    fuzz_data_content(fuzz_data, &config_ptr);
165
2.20k
    fuzz_data_path(fuzz_data, &path_ptr);
166
2.20k
    const char *config_data = (const char *) config_ptr;
167
2.20k
    const char *path_data = (const char *) path_ptr;
168
169
2.20k
    if (CONFIG_TRUE != config_read_string(&cfg, config_data)
170
2.20k
        || NULL == (root = config_root_setting(&cfg)))
171
357
    {
172
        // Parsing failed
173
357
        goto end;
174
357
    }
175
176
1.84k
    config_setting_t *setting = find_setting_recursive(
177
1.84k
        root,
178
1.84k
        (const char*) path_ptr,
179
1.84k
        fuzz_data->path_size
180
1.84k
    );
181
182
1.84k
    if (NULL != setting)
183
504
    {
184
504
        config_setting_get_elem(setting, config_setting_length(setting) - 1);
185
186
504
        switch (fuzz_data->lookup_type % (CONFIG_TYPE_LIST + 1)) {
187
67
            case CONFIG_TYPE_FLOAT:
188
67
                config_setting_lookup_float(setting, path_data,
189
67
                    (double*) scratch_mem);
190
67
                break;
191
55
            case CONFIG_TYPE_BOOL:
192
55
                config_setting_lookup_bool(setting, path_data,
193
55
                    (int*) scratch_mem);
194
55
                break;
195
208
            case CONFIG_TYPE_INT:
196
208
                config_setting_lookup_int(setting, path_data,
197
208
                    (int*) scratch_mem);
198
208
                break;
199
57
            case CONFIG_TYPE_INT64:
200
57
                config_setting_lookup_int64(setting, path_data,
201
57
                    (long long *) scratch_mem);
202
57
                break;
203
88
            case CONFIG_TYPE_STRING: {
204
88
                const char *string_ptr = NULL;
205
88
                config_setting_lookup_string(setting, path_data, &string_ptr);
206
88
                break;
207
0
            }
208
29
            default:
209
29
                config_setting_lookup_const(setting, path_data);
210
504
        }
211
504
    }
212
213
1.84k
    if (NULL != (setting = config_setting_get_member(root, path_data)))
214
476
    {
215
        // This setting exists, let's overwrite it
216
476
        config_setting_set_float(setting, 1.234);
217
476
    }
218
1.37k
    else {
219
        // This setting does not exist, create it
220
1.37k
        setting = config_setting_add(root, path_data, CONFIG_TYPE_FLOAT);
221
222
1.37k
        if (setting == NULL)
223
433
        {
224
433
            rc = -1;
225
433
            goto end;
226
433
        }
227
939
        config_setting_set_float(setting, 1.234);
228
939
    }
229
230
1.41k
    config_write(&cfg, dev_null);
231
1.41k
    config_setting_remove(root, path_data);
232
233
1.41k
    rc = 0;
234
235
2.31k
    end:
236
2.31k
    config_destroy(&cfg);
237
2.31k
    return rc;
238
1.41k
}