Coverage Report

Created: 2026-10-15 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython3/Modules/_xxtestfuzz/fuzzer.c
Line
Count
Source
1
/* A fuzz test for CPython.
2
3
  The only exposed function is LLVMFuzzerTestOneInput, which is called by
4
  fuzzers and by the _fuzz module for smoke tests.
5
6
  To build exactly one fuzz test, as when running in oss-fuzz etc.,
7
  build with -D _Py_FUZZ_ONE and -D _Py_FUZZ_<test_name>. e.g. to build
8
  LLVMFuzzerTestOneInput to only run "fuzz_builtin_float", build this file with
9
      -D _Py_FUZZ_ONE -D _Py_FUZZ_fuzz_builtin_float.
10
11
  See the source code for LLVMFuzzerTestOneInput for details. */
12
13
#ifndef Py_BUILD_CORE_MODULE
14
#  define Py_BUILD_CORE_MODULE 1
15
#endif
16
17
#include <Python.h>
18
#include <stdlib.h>
19
#include <inttypes.h>
20
21
/*  Fuzz PyFloat_FromString as a proxy for float(str). */
22
3.64k
static int fuzz_builtin_float(const char* data, size_t size) {
23
3.64k
    PyObject* s = PyBytes_FromStringAndSize(data, size);
24
3.64k
    if (s == NULL) return 0;
25
3.64k
    PyObject* f = PyFloat_FromString(s);
26
3.64k
    if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_ValueError)) {
27
1.94k
        PyErr_Clear();
28
1.94k
    }
29
30
3.64k
    Py_XDECREF(f);
31
3.64k
    Py_DECREF(s);
32
3.64k
    return 0;
33
3.64k
}
34
35
3.30k
#define MAX_INT_TEST_SIZE 0x10000
36
37
/* Fuzz PyLong_FromUnicodeObject as a proxy for int(str). */
38
3.30k
static int fuzz_builtin_int(const char* data, size_t size) {
39
    /* Ignore test cases with very long ints to avoid timeouts
40
       int("9" * 1000000) is not a very interesting test caase */
41
3.30k
    if (size < 1 || size > MAX_INT_TEST_SIZE) {
42
5
        return 0;
43
5
    }
44
    // Use the first byte to pick a base
45
3.30k
    int base = ((unsigned char) data[0]) % 37;
46
3.30k
    if (base == 1) {
47
        // 1 is the only number between 0 and 36 that is not a valid base.
48
251
        base = 0;
49
251
    }
50
51
3.30k
    data += 1;
52
3.30k
    size -= 1;
53
54
3.30k
    PyObject* s = PyUnicode_FromStringAndSize(data, size);
55
3.30k
    if (s == NULL) {
56
698
        if (PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) {
57
698
            PyErr_Clear();
58
698
        }
59
698
        return 0;
60
698
    }
61
2.60k
    PyObject* l = PyLong_FromUnicodeObject(s, base);
62
2.60k
    if (l == NULL && PyErr_ExceptionMatches(PyExc_ValueError)) {
63
2.17k
        PyErr_Clear();
64
2.17k
    }
65
2.60k
    PyErr_Clear();
66
2.60k
    Py_XDECREF(l);
67
2.60k
    Py_DECREF(s);
68
2.60k
    return 0;
69
3.30k
}
70
71
/* Fuzz PyUnicode_FromStringAndSize as a proxy for unicode(str). */
72
1.48k
static int fuzz_builtin_unicode(const char* data, size_t size) {
73
1.48k
    PyObject* s = PyUnicode_FromStringAndSize(data, size);
74
1.48k
    if (s == NULL && PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) {
75
893
        PyErr_Clear();
76
893
    }
77
1.48k
    Py_XDECREF(s);
78
1.48k
    return 0;
79
1.48k
}
80
81
82
PyObject* struct_unpack_method = NULL;
83
PyObject* struct_error = NULL;
84
/* Called by LLVMFuzzerTestOneInput for initialization */
85
1
static int init_struct_unpack(void) {
86
    /* Import struct.unpack */
87
1
    PyObject* struct_module = PyImport_ImportModule("struct");
88
1
    if (struct_module == NULL) {
89
0
        return 0;
90
0
    }
91
1
    struct_error = PyObject_GetAttrString(struct_module, "error");
92
1
    if (struct_error == NULL) {
93
0
        return 0;
94
0
    }
95
1
    struct_unpack_method = PyObject_GetAttrString(struct_module, "unpack");
96
1
    return struct_unpack_method != NULL;
97
1
}
98
/* Fuzz struct.unpack(x, y) */
99
2.99k
static int fuzz_struct_unpack(const char* data, size_t size) {
100
    /* Everything up to the first null byte is considered the
101
       format. Everything after is the buffer */
102
2.99k
    const char* first_null = memchr(data, '\0', size);
103
2.99k
    if (first_null == NULL) {
104
11
        return 0;
105
11
    }
106
107
2.98k
    size_t format_length = first_null - data;
108
2.98k
    size_t buffer_length = size - format_length - 1;
109
110
2.98k
    PyObject* pattern = PyBytes_FromStringAndSize(data, format_length);
111
2.98k
    if (pattern == NULL) {
112
0
        return 0;
113
0
    }
114
2.98k
    PyObject* buffer = PyBytes_FromStringAndSize(first_null + 1, buffer_length);
115
2.98k
    if (buffer == NULL) {
116
0
        Py_DECREF(pattern);
117
0
        return 0;
118
0
    }
119
120
2.98k
    PyObject* unpacked = PyObject_CallFunctionObjArgs(
121
2.98k
        struct_unpack_method, pattern, buffer, NULL);
122
    /* Ignore any overflow errors, these are easily triggered accidentally */
123
2.98k
    if (unpacked == NULL && PyErr_ExceptionMatches(PyExc_OverflowError)) {
124
0
        PyErr_Clear();
125
0
    }
126
    /* The pascal format string will throw a negative size when passing 0
127
       like: struct.unpack('0p', b'') */
128
2.98k
    if (unpacked == NULL && PyErr_ExceptionMatches(PyExc_SystemError)) {
129
0
        PyErr_Clear();
130
0
    }
131
    /* Ignore any ValueError, these are triggered by non-ASCII format. */
132
2.98k
    if (unpacked == NULL && PyErr_ExceptionMatches(PyExc_ValueError)) {
133
181
        PyErr_Clear();
134
181
    }
135
    /* Ignore any struct.error exceptions, these can be caused by invalid
136
       formats or incomplete buffers both of which are common. */
137
2.98k
    if (unpacked == NULL && PyErr_ExceptionMatches(struct_error)) {
138
674
        PyErr_Clear();
139
674
    }
140
141
2.98k
    Py_XDECREF(unpacked);
142
2.98k
    Py_DECREF(pattern);
143
2.98k
    Py_DECREF(buffer);
144
2.98k
    return 0;
145
2.98k
}
Unexecuted instantiation: fuzzer.c:fuzz_struct_unpack
fuzzer.c:fuzz_struct_unpack
Line
Count
Source
99
2.99k
static int fuzz_struct_unpack(const char* data, size_t size) {
100
    /* Everything up to the first null byte is considered the
101
       format. Everything after is the buffer */
102
2.99k
    const char* first_null = memchr(data, '\0', size);
103
2.99k
    if (first_null == NULL) {
104
11
        return 0;
105
11
    }
106
107
2.98k
    size_t format_length = first_null - data;
108
2.98k
    size_t buffer_length = size - format_length - 1;
109
110
2.98k
    PyObject* pattern = PyBytes_FromStringAndSize(data, format_length);
111
2.98k
    if (pattern == NULL) {
112
0
        return 0;
113
0
    }
114
2.98k
    PyObject* buffer = PyBytes_FromStringAndSize(first_null + 1, buffer_length);
115
2.98k
    if (buffer == NULL) {
116
0
        Py_DECREF(pattern);
117
0
        return 0;
118
0
    }
119
120
2.98k
    PyObject* unpacked = PyObject_CallFunctionObjArgs(
121
2.98k
        struct_unpack_method, pattern, buffer, NULL);
122
    /* Ignore any overflow errors, these are easily triggered accidentally */
123
2.98k
    if (unpacked == NULL && PyErr_ExceptionMatches(PyExc_OverflowError)) {
124
0
        PyErr_Clear();
125
0
    }
126
    /* The pascal format string will throw a negative size when passing 0
127
       like: struct.unpack('0p', b'') */
128
2.98k
    if (unpacked == NULL && PyErr_ExceptionMatches(PyExc_SystemError)) {
129
0
        PyErr_Clear();
130
0
    }
131
    /* Ignore any ValueError, these are triggered by non-ASCII format. */
132
2.98k
    if (unpacked == NULL && PyErr_ExceptionMatches(PyExc_ValueError)) {
133
181
        PyErr_Clear();
134
181
    }
135
    /* Ignore any struct.error exceptions, these can be caused by invalid
136
       formats or incomplete buffers both of which are common. */
137
2.98k
    if (unpacked == NULL && PyErr_ExceptionMatches(struct_error)) {
138
674
        PyErr_Clear();
139
674
    }
140
141
2.98k
    Py_XDECREF(unpacked);
142
2.98k
    Py_DECREF(pattern);
143
2.98k
    Py_DECREF(buffer);
144
2.98k
    return 0;
145
2.98k
}
146
147
148
8.25k
#define MAX_JSON_TEST_SIZE 0x100000
149
150
PyObject* json_loads_method = NULL;
151
/* Called by LLVMFuzzerTestOneInput for initialization */
152
1
static int init_json_loads(void) {
153
    /* Import json.loads */
154
1
    PyObject* json_module = PyImport_ImportModule("json");
155
1
    if (json_module == NULL) {
156
0
        return 0;
157
0
    }
158
1
    json_loads_method = PyObject_GetAttrString(json_module, "loads");
159
1
    return json_loads_method != NULL;
160
1
}
161
/* Fuzz json.loads(x) */
162
8.25k
static int fuzz_json_loads(const char* data, size_t size) {
163
    /* Since python supports arbitrarily large ints in JSON,
164
       long inputs can lead to timeouts on boring inputs like
165
       `json.loads("9" * 100000)` */
166
8.25k
    if (size > MAX_JSON_TEST_SIZE) {
167
0
        return 0;
168
0
    }
169
8.25k
    PyObject* input_bytes = PyBytes_FromStringAndSize(data, size);
170
8.25k
    if (input_bytes == NULL) {
171
0
        return 0;
172
0
    }
173
8.25k
    PyObject* parsed = PyObject_CallOneArg(json_loads_method, input_bytes);
174
8.25k
    if (parsed == NULL) {
175
        /* Ignore ValueError as the fuzzer will more than likely
176
           generate some invalid json and values */
177
7.06k
        if (PyErr_ExceptionMatches(PyExc_ValueError) ||
178
        /* Ignore RecursionError as the fuzzer generates long sequences of
179
           arrays such as `[[[...` */
180
103
            PyErr_ExceptionMatches(PyExc_RecursionError) ||
181
        /* Ignore unicode errors, invalid byte sequences are common */
182
0
            PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)
183
7.06k
        ) {
184
7.06k
            PyErr_Clear();
185
7.06k
        }
186
7.06k
    }
187
8.25k
    Py_DECREF(input_bytes);
188
8.25k
    Py_XDECREF(parsed);
189
8.25k
    return 0;
190
8.25k
}
Unexecuted instantiation: fuzzer.c:fuzz_json_loads
fuzzer.c:fuzz_json_loads
Line
Count
Source
162
8.25k
static int fuzz_json_loads(const char* data, size_t size) {
163
    /* Since python supports arbitrarily large ints in JSON,
164
       long inputs can lead to timeouts on boring inputs like
165
       `json.loads("9" * 100000)` */
166
8.25k
    if (size > MAX_JSON_TEST_SIZE) {
167
0
        return 0;
168
0
    }
169
8.25k
    PyObject* input_bytes = PyBytes_FromStringAndSize(data, size);
170
8.25k
    if (input_bytes == NULL) {
171
0
        return 0;
172
0
    }
173
8.25k
    PyObject* parsed = PyObject_CallOneArg(json_loads_method, input_bytes);
174
8.25k
    if (parsed == NULL) {
175
        /* Ignore ValueError as the fuzzer will more than likely
176
           generate some invalid json and values */
177
7.06k
        if (PyErr_ExceptionMatches(PyExc_ValueError) ||
178
        /* Ignore RecursionError as the fuzzer generates long sequences of
179
           arrays such as `[[[...` */
180
103
            PyErr_ExceptionMatches(PyExc_RecursionError) ||
181
        /* Ignore unicode errors, invalid byte sequences are common */
182
0
            PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)
183
7.06k
        ) {
184
7.06k
            PyErr_Clear();
185
7.06k
        }
186
7.06k
    }
187
8.25k
    Py_DECREF(input_bytes);
188
8.25k
    Py_XDECREF(parsed);
189
8.25k
    return 0;
190
8.25k
}
191
192
5.27k
#define MAX_RE_TEST_SIZE 0x10000
193
194
PyObject* re_compile_method = NULL;
195
PyObject* re_error_exception = NULL;
196
int RE_FLAG_DEBUG = 0;
197
/* Called by LLVMFuzzerTestOneInput for initialization */
198
1
static int init_sre_compile(void) {
199
    /* Import sre_compile.compile and sre.error */
200
1
    PyObject* re_module = PyImport_ImportModule("re");
201
1
    if (re_module == NULL) {
202
0
        return 0;
203
0
    }
204
1
    re_compile_method = PyObject_GetAttrString(re_module, "compile");
205
1
    if (re_compile_method == NULL) {
206
0
        return 0;
207
0
    }
208
209
1
    re_error_exception = PyObject_GetAttrString(re_module, "error");
210
1
    if (re_error_exception == NULL) {
211
0
        return 0;
212
0
    }
213
1
    PyObject* debug_flag = PyObject_GetAttrString(re_module, "DEBUG");
214
1
    if (debug_flag == NULL) {
215
0
        return 0;
216
0
    }
217
1
    RE_FLAG_DEBUG = PyLong_AsLong(debug_flag);
218
1
    return 1;
219
1
}
220
/* Fuzz re.compile(x) */
221
4.85k
static int fuzz_sre_compile(const char* data, size_t size) {
222
    /* Ignore really long regex patterns that will timeout the fuzzer */
223
4.85k
    if (size > MAX_RE_TEST_SIZE) {
224
1
        return 0;
225
1
    }
226
    /* We treat the first 2 bytes of the input as a number for the flags */
227
4.84k
    if (size < 2) {
228
2
        return 0;
229
2
    }
230
4.84k
    uint16_t flags = ((uint16_t*) data)[0];
231
    /* We remove the SRE_FLAG_DEBUG if present. This is because it
232
       prints to stdout which greatly decreases fuzzing speed */
233
4.84k
    flags &= ~RE_FLAG_DEBUG;
234
235
    /* Pull the pattern from the remaining bytes */
236
4.84k
    PyObject* pattern_bytes = PyBytes_FromStringAndSize(data + 2, size - 2);
237
4.84k
    if (pattern_bytes == NULL) {
238
0
        return 0;
239
0
    }
240
4.84k
    PyObject* flags_obj = PyLong_FromUnsignedLong(flags);
241
4.84k
    if (flags_obj == NULL) {
242
0
        Py_DECREF(pattern_bytes);
243
0
        return 0;
244
0
    }
245
246
    /* compiled = re.compile(data[2:], data[0:2] */
247
4.84k
    PyObject* compiled = PyObject_CallFunctionObjArgs(
248
4.84k
        re_compile_method, pattern_bytes, flags_obj, NULL);
249
    /* Ignore ValueError as the fuzzer will more than likely
250
       generate some invalid combination of flags */
251
4.84k
    if (compiled == NULL && PyErr_ExceptionMatches(PyExc_ValueError)) {
252
175
        PyErr_Clear();
253
175
    }
254
    /* Ignore some common errors thrown by sre_parse:
255
       Overflow, Assertion, Recursion and Index */
256
4.84k
    if (compiled == NULL && (PyErr_ExceptionMatches(PyExc_OverflowError) ||
257
2.36k
                             PyErr_ExceptionMatches(PyExc_AssertionError) ||
258
2.36k
                             PyErr_ExceptionMatches(PyExc_RecursionError) ||
259
2.29k
                             PyErr_ExceptionMatches(PyExc_IndexError))
260
4.84k
    ) {
261
173
        PyErr_Clear();
262
173
    }
263
    /* Ignore re.error */
264
4.84k
    if (compiled == NULL && PyErr_ExceptionMatches(re_error_exception)) {
265
2.12k
        PyErr_Clear();
266
2.12k
    }
267
268
4.84k
    Py_DECREF(pattern_bytes);
269
4.84k
    Py_DECREF(flags_obj);
270
4.84k
    Py_XDECREF(compiled);
271
4.84k
    return 0;
272
4.84k
}
273
274
/* Some random patterns used to test re.match.
275
   Be careful not to add catostraphically slow regexes here, we want to
276
   exercise the matching code without causing timeouts.*/
277
static const char* regex_patterns[] = {
278
    ".", "^", "abc", "abc|def", "^xxx$", "\\b", "()", "[a-zA-Z0-9]",
279
    "abc+", "[^A-Z]", "[x]", "(?=)", "a{z}", "a+b", "a*?", "a??", "a+?",
280
    "{}", "a{,}", "{", "}", "^\\(*\\d{3}\\)*( |-)*\\d{3}( |-)*\\d{4}$",
281
    "(?:a*)*", "a{1,2}?"
282
};
283
const size_t NUM_PATTERNS = sizeof(regex_patterns) / sizeof(regex_patterns[0]);
284
PyObject** compiled_patterns = NULL;
285
/* Called by LLVMFuzzerTestOneInput for initialization */
286
0
static int init_sre_match(void) {
287
0
    PyObject* re_module = PyImport_ImportModule("re");
288
0
    if (re_module == NULL) {
289
0
        return 0;
290
0
    }
291
0
    compiled_patterns = (PyObject**) PyMem_RawMalloc(
292
0
        sizeof(PyObject*) * NUM_PATTERNS);
293
0
    if (compiled_patterns == NULL) {
294
0
        PyErr_NoMemory();
295
0
        return 0;
296
0
    }
297
0
298
0
    /* Precompile all the regex patterns on the first run for faster fuzzing */
299
0
    for (size_t i = 0; i < NUM_PATTERNS; i++) {
300
0
        PyObject* compiled = PyObject_CallMethod(
301
0
            re_module, "compile", "y", regex_patterns[i]);
302
0
        /* Bail if any of the patterns fail to compile */
303
0
        if (compiled == NULL) {
304
0
            return 0;
305
0
        }
306
0
        compiled_patterns[i] = compiled;
307
0
    }
308
0
    return 1;
309
0
}
310
/* Fuzz re.match(x) */
311
424
static int fuzz_sre_match(const char* data, size_t size) {
312
424
    if (size < 1 || size > MAX_RE_TEST_SIZE) {
313
6
        return 0;
314
6
    }
315
    /* Use the first byte as a uint8_t specifying the index of the
316
       regex to use */
317
418
    unsigned char idx = (unsigned char) data[0];
318
418
    idx = idx % NUM_PATTERNS;
319
320
    /* Pull the string to match from the remaining bytes */
321
418
    PyObject* to_match = PyBytes_FromStringAndSize(data + 1, size - 1);
322
418
    if (to_match == NULL) {
323
0
        return 0;
324
0
    }
325
326
418
    PyObject* pattern = compiled_patterns[idx];
327
418
    PyObject* match_callable = PyObject_GetAttrString(pattern, "match");
328
329
418
    PyObject* matches = PyObject_CallOneArg(match_callable, to_match);
330
331
418
    Py_XDECREF(matches);
332
418
    Py_DECREF(match_callable);
333
418
    Py_DECREF(to_match);
334
418
    return 0;
335
418
}
336
337
2.09k
#define MAX_CSV_TEST_SIZE 0x100000
338
PyObject* csv_module = NULL;
339
PyObject* csv_error = NULL;
340
/* Called by LLVMFuzzerTestOneInput for initialization */
341
1
static int init_csv_reader(void) {
342
    /* Import csv and csv.Error */
343
1
    csv_module = PyImport_ImportModule("csv");
344
1
    if (csv_module == NULL) {
345
0
        return 0;
346
0
    }
347
1
    csv_error = PyObject_GetAttrString(csv_module, "Error");
348
1
    return csv_error != NULL;
349
1
}
350
/* Fuzz csv.reader([x]) */
351
2.09k
static int fuzz_csv_reader(const char* data, size_t size) {
352
2.09k
    if (size < 1 || size > MAX_CSV_TEST_SIZE) {
353
0
        return 0;
354
0
    }
355
    /* Ignore non null-terminated strings since _csv can't handle
356
       embedded nulls */
357
2.09k
    if (memchr(data, '\0', size) == NULL) {
358
3
        return 0;
359
3
    }
360
361
2.09k
    PyObject* s = PyUnicode_FromString(data);
362
    /* Ignore exceptions until we have a valid string */
363
2.09k
    if (s == NULL) {
364
455
        PyErr_Clear();
365
455
        return 0;
366
455
    }
367
368
    /* Split on \n so we can test multiple lines */
369
1.64k
    PyObject* lines = PyObject_CallMethod(s, "split", "s", "\n");
370
1.64k
    if (lines == NULL) {
371
0
        Py_DECREF(s);
372
0
        return 0;
373
0
    }
374
375
1.64k
    PyObject* reader = PyObject_CallMethod(csv_module, "reader", "N", lines);
376
1.64k
    if (reader) {
377
        /* Consume all of the reader as an iterator */
378
1.64k
        PyObject* parsed_line;
379
30.1M
        while ((parsed_line = PyIter_Next(reader))) {
380
30.1M
            Py_DECREF(parsed_line);
381
30.1M
        }
382
1.64k
    }
383
384
    /* Ignore csv.Error because we're probably going to generate
385
       some bad files (embedded new-lines, unterminated quotes etc) */
386
1.64k
    if (PyErr_ExceptionMatches(csv_error)) {
387
147
        PyErr_Clear();
388
147
    }
389
390
1.64k
    Py_XDECREF(reader);
391
1.64k
    Py_DECREF(s);
392
1.64k
    return 0;
393
1.64k
}
394
395
897
#define MAX_AST_LITERAL_EVAL_TEST_SIZE 0x100000
396
PyObject* ast_literal_eval_method = NULL;
397
/* Called by LLVMFuzzerTestOneInput for initialization */
398
1
static int init_ast_literal_eval(void) {
399
1
    PyObject* ast_module = PyImport_ImportModule("ast");
400
1
    if (ast_module == NULL) {
401
0
        return 0;
402
0
    }
403
1
    ast_literal_eval_method = PyObject_GetAttrString(ast_module, "literal_eval");
404
1
    return ast_literal_eval_method != NULL;
405
1
}
406
/* Fuzz ast.literal_eval(x) */
407
897
static int fuzz_ast_literal_eval(const char* data, size_t size) {
408
897
    if (size > MAX_AST_LITERAL_EVAL_TEST_SIZE) {
409
0
        return 0;
410
0
    }
411
    /* Ignore non null-terminated strings since ast can't handle
412
       embedded nulls */
413
897
    if (memchr(data, '\0', size) == NULL) {
414
0
        return 0;
415
0
    }
416
417
897
    PyObject* s = PyUnicode_FromString(data);
418
    /* Ignore exceptions until we have a valid string */
419
897
    if (s == NULL) {
420
2
        PyErr_Clear();
421
2
        return 0;
422
2
    }
423
424
895
    PyObject* literal = PyObject_CallOneArg(ast_literal_eval_method, s);
425
    /* Ignore some common errors thrown by ast.literal_eval */
426
895
    if (literal == NULL && (PyErr_ExceptionMatches(PyExc_ValueError) ||
427
522
                            PyErr_ExceptionMatches(PyExc_TypeError) ||
428
522
                            PyErr_ExceptionMatches(PyExc_SyntaxError) ||
429
8
                            PyErr_ExceptionMatches(PyExc_MemoryError) ||
430
0
                            PyErr_ExceptionMatches(PyExc_OverflowError) ||
431
0
                            PyErr_ExceptionMatches(PyExc_RecursionError))
432
895
    ) {
433
880
        PyErr_Clear();
434
880
    }
435
436
895
    Py_XDECREF(literal);
437
895
    Py_DECREF(s);
438
895
    return 0;
439
897
}
440
441
18.5k
#define MAX_ELEMENTTREE_PARSEWHOLE_TEST_SIZE 0x100000
442
PyObject* xmlparser_type = NULL;
443
PyObject* bytesio_type = NULL;
444
/* Called by LLVMFuzzerTestOneInput for initialization */
445
1
static int init_elementtree_parsewhole(void) {
446
1
    PyObject* elementtree_module = PyImport_ImportModule("_elementtree");
447
1
    if (elementtree_module == NULL) {
448
0
        return 0;
449
0
    }
450
1
    xmlparser_type = PyObject_GetAttrString(elementtree_module, "XMLParser");
451
1
    Py_DECREF(elementtree_module);
452
1
    if (xmlparser_type == NULL) {
453
0
        return 0;
454
0
    }
455
456
457
1
    PyObject* io_module = PyImport_ImportModule("io");
458
1
    if (io_module == NULL) {
459
0
        return 0;
460
0
    }
461
1
    bytesio_type = PyObject_GetAttrString(io_module, "BytesIO");
462
1
    Py_DECREF(io_module);
463
1
    if (bytesio_type == NULL) {
464
0
        return 0;
465
0
    }
466
467
1
    return 1;
468
1
}
469
/* Fuzz _elementtree.XMLParser._parse_whole(x) */
470
18.5k
static int fuzz_elementtree_parsewhole(const char* data, size_t size) {
471
18.5k
    if (size > MAX_ELEMENTTREE_PARSEWHOLE_TEST_SIZE) {
472
0
        return 0;
473
0
    }
474
475
18.5k
    PyObject *input = PyObject_CallFunction(bytesio_type, "y#", data, (Py_ssize_t)size);
476
18.5k
    if (input == NULL) {
477
0
        assert(PyErr_Occurred());
478
0
        PyErr_Print();
479
0
        abort();
480
0
    }
481
482
18.5k
    PyObject *xmlparser_instance = PyObject_CallObject(xmlparser_type, NULL);
483
18.5k
    if (xmlparser_instance == NULL) {
484
0
        assert(PyErr_Occurred());
485
0
        PyErr_Print();
486
0
        abort();
487
0
    }
488
489
18.5k
    PyObject *result = PyObject_CallMethod(xmlparser_instance, "_parse_whole", "O", input);
490
18.5k
    if (result == NULL) {
491
        /* Ignore exception here, which can be caused by invalid XML input */
492
18.4k
        PyErr_Clear();
493
18.4k
    } else {
494
83
        Py_DECREF(result);
495
83
    }
496
497
18.5k
    Py_DECREF(xmlparser_instance);
498
18.5k
    Py_DECREF(input);
499
500
18.5k
    return 0;
501
18.5k
}
Unexecuted instantiation: fuzzer.c:fuzz_elementtree_parsewhole
fuzzer.c:fuzz_elementtree_parsewhole
Line
Count
Source
470
18.5k
static int fuzz_elementtree_parsewhole(const char* data, size_t size) {
471
18.5k
    if (size > MAX_ELEMENTTREE_PARSEWHOLE_TEST_SIZE) {
472
0
        return 0;
473
0
    }
474
475
18.5k
    PyObject *input = PyObject_CallFunction(bytesio_type, "y#", data, (Py_ssize_t)size);
476
18.5k
    if (input == NULL) {
477
0
        assert(PyErr_Occurred());
478
0
        PyErr_Print();
479
0
        abort();
480
0
    }
481
482
18.5k
    PyObject *xmlparser_instance = PyObject_CallObject(xmlparser_type, NULL);
483
18.5k
    if (xmlparser_instance == NULL) {
484
0
        assert(PyErr_Occurred());
485
0
        PyErr_Print();
486
0
        abort();
487
0
    }
488
489
18.5k
    PyObject *result = PyObject_CallMethod(xmlparser_instance, "_parse_whole", "O", input);
490
18.5k
    if (result == NULL) {
491
        /* Ignore exception here, which can be caused by invalid XML input */
492
18.4k
        PyErr_Clear();
493
18.4k
    } else {
494
83
        Py_DECREF(result);
495
83
    }
496
497
18.5k
    Py_DECREF(xmlparser_instance);
498
18.5k
    Py_DECREF(input);
499
500
18.5k
    return 0;
501
18.5k
}
502
503
32.0k
#define MAX_PYCOMPILE_TEST_SIZE 16384
504
505
static const int start_vals[] = {Py_eval_input, Py_single_input, Py_file_input};
506
const size_t NUM_START_VALS = sizeof(start_vals) / sizeof(start_vals[0]);
507
508
static const int optimize_vals[] = {-1, 0, 1, 2};
509
const size_t NUM_OPTIMIZE_VALS = sizeof(optimize_vals) / sizeof(optimize_vals[0]);
510
511
/* Fuzz `PyCompileStringExFlags` using a variety of input parameters.
512
 * That function is essentially behind the `compile` builtin */
513
32.0k
static int fuzz_pycompile(const char* data, size_t size) {
514
    // Ignore overly-large inputs, and account for a NUL terminator
515
32.0k
    if (size > MAX_PYCOMPILE_TEST_SIZE - 1) {
516
1
        return 0;
517
1
    }
518
519
    // Need 3 bytes for parameter selection
520
32.0k
    if (size < 3) {
521
2
        return 0;
522
2
    }
523
524
    // Use first byte to determine element of `start_vals` to use
525
32.0k
    unsigned char start_idx = (unsigned char) data[0];
526
32.0k
    int start = start_vals[start_idx % NUM_START_VALS];
527
528
    // Use second byte to determine element of `optimize_vals` to use
529
32.0k
    unsigned char optimize_idx = (unsigned char) data[1];
530
32.0k
    int optimize = optimize_vals[optimize_idx % NUM_OPTIMIZE_VALS];
531
532
    // Use third byte to determine compiler flags to use.
533
32.0k
    unsigned char flags_byte = (unsigned char) data[2];
534
32.0k
    PyCompilerFlags flags = _PyCompilerFlags_INIT;
535
32.0k
    if (flags_byte & 0x01) {
536
14.9k
        flags.cf_flags |= PyCF_DONT_IMPLY_DEDENT;
537
14.9k
    }
538
32.0k
    if (flags_byte & 0x02) {
539
8.11k
        flags.cf_flags |= PyCF_ONLY_AST;
540
8.11k
    }
541
32.0k
    if (flags_byte & 0x04) {
542
14.6k
        flags.cf_flags |= PyCF_IGNORE_COOKIE;
543
14.6k
    }
544
32.0k
    if (flags_byte & 0x08) {
545
14.2k
        flags.cf_flags |= PyCF_TYPE_COMMENTS;
546
14.2k
    }
547
32.0k
    if (flags_byte & 0x10) {
548
9.47k
        flags.cf_flags |= PyCF_ALLOW_TOP_LEVEL_AWAIT;
549
9.47k
    }
550
32.0k
    if (flags_byte & 0x20) {
551
23.4k
        flags.cf_flags |= PyCF_ALLOW_INCOMPLETE_INPUT;
552
23.4k
    }
553
32.0k
    if (flags_byte & 0x40) {
554
6.24k
        flags.cf_flags |= PyCF_OPTIMIZED_AST;
555
6.24k
    }
556
557
32.0k
    char pycompile_scratch[MAX_PYCOMPILE_TEST_SIZE];
558
559
    // Create a NUL-terminated C string from the remaining input
560
32.0k
    memcpy(pycompile_scratch, data + 3, size - 3);
561
    // Put a NUL terminator just after the copied data. (Space was reserved already.)
562
32.0k
    pycompile_scratch[size - 3] = '\0';
563
564
32.0k
    PyObject *result = Py_CompileStringExFlags(pycompile_scratch, "<fuzz input>", start, &flags, optimize);
565
32.0k
    if (result == NULL) {
566
        /* Compilation failed, most likely from a syntax error. If it was a
567
           SystemError we abort. There's no non-bug reason to raise a
568
           SystemError. */
569
15.2k
        if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SystemError)) {
570
0
            PyErr_Print();
571
0
            abort();
572
0
        }
573
15.2k
        PyErr_Clear();
574
16.7k
    } else {
575
16.7k
        Py_DECREF(result);
576
16.7k
    }
577
578
32.0k
    return 0;
579
32.0k
}
580
581
/* Run fuzzer and abort on failure. */
582
78.5k
static int _run_fuzz(const uint8_t *data, size_t size, int(*fuzzer)(const char* , size_t)) {
583
78.5k
    int rv = fuzzer((const char*) data, size);
584
78.5k
    if (PyErr_Occurred()) {
585
        /* Fuzz tests should handle expected errors for themselves.
586
           This is last-ditch check in case they didn't. */
587
0
        PyErr_Print();
588
0
        abort();
589
0
    }
590
    /* Someday the return value might mean something, propagate it. */
591
78.5k
    return rv;
592
78.5k
}
593
594
/* CPython generates a lot of leak warnings for whatever reason. */
595
0
int __lsan_is_turned_off(void) { return 1; }
596
597
598
22
int LLVMFuzzerInitialize(int *argc, char ***argv) {
599
22
    PyConfig config;
600
22
    PyConfig_InitPythonConfig(&config);
601
22
    config.install_signal_handlers = 0;
602
    /* Raise the limit above the default allows exercising larger things
603
     * now that we fall back to the _pylong module for large values. */
604
22
    config.int_max_str_digits = 8086;
605
22
    PyStatus status;
606
22
    status = PyConfig_SetBytesString(&config, &config.program_name, *argv[0]);
607
22
    if (PyStatus_Exception(status)) {
608
0
        goto fail;
609
0
    }
610
611
22
    status = Py_InitializeFromConfig(&config);
612
22
    if (PyStatus_Exception(status)) {
613
0
        goto fail;
614
0
    }
615
22
    PyConfig_Clear(&config);
616
617
22
    return 0;
618
619
0
fail:
620
0
    PyConfig_Clear(&config);
621
0
    Py_ExitStatusException(status);
622
22
}
623
624
/* Fuzz test interface.
625
   This returns the bitwise or of all fuzz test's return values.
626
627
   All fuzz tests must return 0, as all nonzero return codes are reserved for
628
   future use -- we propagate the return values for that future case.
629
   (And we bitwise or when running multiple tests to verify that normally we
630
   only return 0.) */
631
40.4k
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
632
40.4k
    assert(Py_IsInitialized());
633
634
40.4k
    int rv = 0;
635
636
#if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_builtin_float)
637
    rv |= _run_fuzz(data, size, fuzz_builtin_float);
638
#endif
639
#if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_builtin_int)
640
    rv |= _run_fuzz(data, size, fuzz_builtin_int);
641
#endif
642
40.4k
#if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_builtin_unicode)
643
40.4k
    rv |= _run_fuzz(data, size, fuzz_builtin_unicode);
644
40.4k
#endif
645
#if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_struct_unpack)
646
    static int STRUCT_UNPACK_INITIALIZED = 0;
647
    if (!STRUCT_UNPACK_INITIALIZED && !init_struct_unpack()) {
648
        PyErr_Print();
649
        abort();
650
    } else {
651
        STRUCT_UNPACK_INITIALIZED = 1;
652
    }
653
    rv |= _run_fuzz(data, size, fuzz_struct_unpack);
654
#endif
655
#if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_json_loads)
656
    static int JSON_LOADS_INITIALIZED = 0;
657
    if (!JSON_LOADS_INITIALIZED && !init_json_loads()) {
658
        PyErr_Print();
659
        abort();
660
    } else {
661
        JSON_LOADS_INITIALIZED = 1;
662
    }
663
664
    rv |= _run_fuzz(data, size, fuzz_json_loads);
665
#endif
666
#if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_sre_compile)
667
    static int SRE_COMPILE_INITIALIZED = 0;
668
    if (!SRE_COMPILE_INITIALIZED && !init_sre_compile()) {
669
        PyErr_Print();
670
        abort();
671
    } else {
672
        SRE_COMPILE_INITIALIZED = 1;
673
    }
674
675
    if (SRE_COMPILE_INITIALIZED) {
676
        rv |= _run_fuzz(data, size, fuzz_sre_compile);
677
    }
678
#endif
679
#if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_sre_match)
680
    static int SRE_MATCH_INITIALIZED = 0;
681
    if (!SRE_MATCH_INITIALIZED && !init_sre_match()) {
682
        PyErr_Print();
683
        abort();
684
    } else {
685
        SRE_MATCH_INITIALIZED = 1;
686
    }
687
688
    rv |= _run_fuzz(data, size, fuzz_sre_match);
689
#endif
690
#if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_csv_reader)
691
    static int CSV_READER_INITIALIZED = 0;
692
    if (!CSV_READER_INITIALIZED && !init_csv_reader()) {
693
        PyErr_Print();
694
        abort();
695
    } else {
696
        CSV_READER_INITIALIZED = 1;
697
    }
698
699
    rv |= _run_fuzz(data, size, fuzz_csv_reader);
700
#endif
701
#if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_ast_literal_eval)
702
    static int AST_LITERAL_EVAL_INITIALIZED = 0;
703
    if (!AST_LITERAL_EVAL_INITIALIZED && !init_ast_literal_eval()) {
704
        PyErr_Print();
705
        abort();
706
    } else {
707
        AST_LITERAL_EVAL_INITIALIZED = 1;
708
    }
709
710
    rv |= _run_fuzz(data, size, fuzz_ast_literal_eval);
711
#endif
712
#if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_elementtree_parsewhole)
713
    static int ELEMENTTREE_PARSEWHOLE_INITIALIZED = 0;
714
    if (!ELEMENTTREE_PARSEWHOLE_INITIALIZED && !init_elementtree_parsewhole()) {
715
        PyErr_Print();
716
        abort();
717
    } else {
718
        ELEMENTTREE_PARSEWHOLE_INITIALIZED = 1;
719
    }
720
721
    rv |= _run_fuzz(data, size, fuzz_elementtree_parsewhole);
722
#endif
723
#if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_pycompile)
724
    rv |= _run_fuzz(data, size, fuzz_pycompile);
725
#endif
726
40.4k
  return rv;
727
40.4k
}