Coverage Report

Created: 2026-08-13 06:33

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.72k
static int fuzz_builtin_float(const char* data, size_t size) {
23
3.72k
    PyObject* s = PyBytes_FromStringAndSize(data, size);
24
3.72k
    if (s == NULL) return 0;
25
3.72k
    PyObject* f = PyFloat_FromString(s);
26
3.72k
    if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_ValueError)) {
27
2.00k
        PyErr_Clear();
28
2.00k
    }
29
30
3.72k
    Py_XDECREF(f);
31
3.72k
    Py_DECREF(s);
32
3.72k
    return 0;
33
3.72k
}
34
35
3.45k
#define MAX_INT_TEST_SIZE 0x10000
36
37
/* Fuzz PyLong_FromUnicodeObject as a proxy for int(str). */
38
3.45k
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.45k
    if (size < 1 || size > MAX_INT_TEST_SIZE) {
42
4
        return 0;
43
4
    }
44
    // Use the first byte to pick a base
45
3.45k
    int base = ((unsigned char) data[0]) % 37;
46
3.45k
    if (base == 1) {
47
        // 1 is the only number between 0 and 36 that is not a valid base.
48
354
        base = 0;
49
354
    }
50
51
3.45k
    data += 1;
52
3.45k
    size -= 1;
53
54
3.45k
    PyObject* s = PyUnicode_FromStringAndSize(data, size);
55
3.45k
    if (s == NULL) {
56
710
        if (PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) {
57
710
            PyErr_Clear();
58
710
        }
59
710
        return 0;
60
710
    }
61
2.74k
    PyObject* l = PyLong_FromUnicodeObject(s, base);
62
2.74k
    if (l == NULL && PyErr_ExceptionMatches(PyExc_ValueError)) {
63
2.30k
        PyErr_Clear();
64
2.30k
    }
65
2.74k
    PyErr_Clear();
66
2.74k
    Py_XDECREF(l);
67
2.74k
    Py_DECREF(s);
68
2.74k
    return 0;
69
3.45k
}
70
71
/* Fuzz PyUnicode_FromStringAndSize as a proxy for unicode(str). */
72
1.53k
static int fuzz_builtin_unicode(const char* data, size_t size) {
73
1.53k
    PyObject* s = PyUnicode_FromStringAndSize(data, size);
74
1.53k
    if (s == NULL && PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) {
75
915
        PyErr_Clear();
76
915
    }
77
1.53k
    Py_XDECREF(s);
78
1.53k
    return 0;
79
1.53k
}
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
3.05k
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
3.05k
    const char* first_null = memchr(data, '\0', size);
103
3.05k
    if (first_null == NULL) {
104
10
        return 0;
105
10
    }
106
107
3.04k
    size_t format_length = first_null - data;
108
3.04k
    size_t buffer_length = size - format_length - 1;
109
110
3.04k
    PyObject* pattern = PyBytes_FromStringAndSize(data, format_length);
111
3.04k
    if (pattern == NULL) {
112
0
        return 0;
113
0
    }
114
3.04k
    PyObject* buffer = PyBytes_FromStringAndSize(first_null + 1, buffer_length);
115
3.04k
    if (buffer == NULL) {
116
0
        Py_DECREF(pattern);
117
0
        return 0;
118
0
    }
119
120
3.04k
    PyObject* unpacked = PyObject_CallFunctionObjArgs(
121
3.04k
        struct_unpack_method, pattern, buffer, NULL);
122
    /* Ignore any overflow errors, these are easily triggered accidentally */
123
3.04k
    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
3.04k
    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
3.04k
    if (unpacked == NULL && PyErr_ExceptionMatches(PyExc_ValueError)) {
133
177
        PyErr_Clear();
134
177
    }
135
    /* Ignore any struct.error exceptions, these can be caused by invalid
136
       formats or incomplete buffers both of which are common. */
137
3.04k
    if (unpacked == NULL && PyErr_ExceptionMatches(struct_error)) {
138
677
        PyErr_Clear();
139
677
    }
140
141
3.04k
    Py_XDECREF(unpacked);
142
3.04k
    Py_DECREF(pattern);
143
3.04k
    Py_DECREF(buffer);
144
3.04k
    return 0;
145
3.04k
}
Unexecuted instantiation: fuzzer.c:fuzz_struct_unpack
fuzzer.c:fuzz_struct_unpack
Line
Count
Source
99
3.05k
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
3.05k
    const char* first_null = memchr(data, '\0', size);
103
3.05k
    if (first_null == NULL) {
104
10
        return 0;
105
10
    }
106
107
3.04k
    size_t format_length = first_null - data;
108
3.04k
    size_t buffer_length = size - format_length - 1;
109
110
3.04k
    PyObject* pattern = PyBytes_FromStringAndSize(data, format_length);
111
3.04k
    if (pattern == NULL) {
112
0
        return 0;
113
0
    }
114
3.04k
    PyObject* buffer = PyBytes_FromStringAndSize(first_null + 1, buffer_length);
115
3.04k
    if (buffer == NULL) {
116
0
        Py_DECREF(pattern);
117
0
        return 0;
118
0
    }
119
120
3.04k
    PyObject* unpacked = PyObject_CallFunctionObjArgs(
121
3.04k
        struct_unpack_method, pattern, buffer, NULL);
122
    /* Ignore any overflow errors, these are easily triggered accidentally */
123
3.04k
    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
3.04k
    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
3.04k
    if (unpacked == NULL && PyErr_ExceptionMatches(PyExc_ValueError)) {
133
177
        PyErr_Clear();
134
177
    }
135
    /* Ignore any struct.error exceptions, these can be caused by invalid
136
       formats or incomplete buffers both of which are common. */
137
3.04k
    if (unpacked == NULL && PyErr_ExceptionMatches(struct_error)) {
138
677
        PyErr_Clear();
139
677
    }
140
141
3.04k
    Py_XDECREF(unpacked);
142
3.04k
    Py_DECREF(pattern);
143
3.04k
    Py_DECREF(buffer);
144
3.04k
    return 0;
145
3.04k
}
146
147
148
8.82k
#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.82k
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.82k
    if (size > MAX_JSON_TEST_SIZE) {
167
0
        return 0;
168
0
    }
169
8.82k
    PyObject* input_bytes = PyBytes_FromStringAndSize(data, size);
170
8.82k
    if (input_bytes == NULL) {
171
0
        return 0;
172
0
    }
173
8.82k
    PyObject* parsed = PyObject_CallOneArg(json_loads_method, input_bytes);
174
8.82k
    if (parsed == NULL) {
175
        /* Ignore ValueError as the fuzzer will more than likely
176
           generate some invalid json and values */
177
7.53k
        if (PyErr_ExceptionMatches(PyExc_ValueError) ||
178
        /* Ignore RecursionError as the fuzzer generates long sequences of
179
           arrays such as `[[[...` */
180
130
            PyErr_ExceptionMatches(PyExc_RecursionError) ||
181
        /* Ignore unicode errors, invalid byte sequences are common */
182
0
            PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)
183
7.53k
        ) {
184
7.53k
            PyErr_Clear();
185
7.53k
        }
186
7.53k
    }
187
8.82k
    Py_DECREF(input_bytes);
188
8.82k
    Py_XDECREF(parsed);
189
8.82k
    return 0;
190
8.82k
}
Unexecuted instantiation: fuzzer.c:fuzz_json_loads
fuzzer.c:fuzz_json_loads
Line
Count
Source
162
8.82k
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.82k
    if (size > MAX_JSON_TEST_SIZE) {
167
0
        return 0;
168
0
    }
169
8.82k
    PyObject* input_bytes = PyBytes_FromStringAndSize(data, size);
170
8.82k
    if (input_bytes == NULL) {
171
0
        return 0;
172
0
    }
173
8.82k
    PyObject* parsed = PyObject_CallOneArg(json_loads_method, input_bytes);
174
8.82k
    if (parsed == NULL) {
175
        /* Ignore ValueError as the fuzzer will more than likely
176
           generate some invalid json and values */
177
7.53k
        if (PyErr_ExceptionMatches(PyExc_ValueError) ||
178
        /* Ignore RecursionError as the fuzzer generates long sequences of
179
           arrays such as `[[[...` */
180
130
            PyErr_ExceptionMatches(PyExc_RecursionError) ||
181
        /* Ignore unicode errors, invalid byte sequences are common */
182
0
            PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)
183
7.53k
        ) {
184
7.53k
            PyErr_Clear();
185
7.53k
        }
186
7.53k
    }
187
8.82k
    Py_DECREF(input_bytes);
188
8.82k
    Py_XDECREF(parsed);
189
8.82k
    return 0;
190
8.82k
}
191
192
5.17k
#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.75k
static int fuzz_sre_compile(const char* data, size_t size) {
222
    /* Ignore really long regex patterns that will timeout the fuzzer */
223
4.75k
    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.74k
    if (size < 2) {
228
2
        return 0;
229
2
    }
230
4.74k
    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.74k
    flags &= ~RE_FLAG_DEBUG;
234
235
    /* Pull the pattern from the remaining bytes */
236
4.74k
    PyObject* pattern_bytes = PyBytes_FromStringAndSize(data + 2, size - 2);
237
4.74k
    if (pattern_bytes == NULL) {
238
0
        return 0;
239
0
    }
240
4.74k
    PyObject* flags_obj = PyLong_FromUnsignedLong(flags);
241
4.74k
    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.74k
    PyObject* compiled = PyObject_CallFunctionObjArgs(
248
4.74k
        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.74k
    if (compiled == NULL && PyErr_ExceptionMatches(PyExc_ValueError)) {
252
176
        PyErr_Clear();
253
176
    }
254
    /* Ignore some common errors thrown by sre_parse:
255
       Overflow, Assertion, Recursion and Index */
256
4.74k
    if (compiled == NULL && (PyErr_ExceptionMatches(PyExc_OverflowError) ||
257
2.39k
                             PyErr_ExceptionMatches(PyExc_AssertionError) ||
258
2.39k
                             PyErr_ExceptionMatches(PyExc_RecursionError) ||
259
2.32k
                             PyErr_ExceptionMatches(PyExc_IndexError))
260
4.74k
    ) {
261
151
        PyErr_Clear();
262
151
    }
263
    /* Ignore re.error */
264
4.74k
    if (compiled == NULL && PyErr_ExceptionMatches(re_error_exception)) {
265
2.15k
        PyErr_Clear();
266
2.15k
    }
267
268
4.74k
    Py_DECREF(pattern_bytes);
269
4.74k
    Py_DECREF(flags_obj);
270
4.74k
    Py_XDECREF(compiled);
271
4.74k
    return 0;
272
4.74k
}
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
426
static int fuzz_sre_match(const char* data, size_t size) {
312
426
    if (size < 1 || size > MAX_RE_TEST_SIZE) {
313
10
        return 0;
314
10
    }
315
    /* Use the first byte as a uint8_t specifying the index of the
316
       regex to use */
317
416
    unsigned char idx = (unsigned char) data[0];
318
416
    idx = idx % NUM_PATTERNS;
319
320
    /* Pull the string to match from the remaining bytes */
321
416
    PyObject* to_match = PyBytes_FromStringAndSize(data + 1, size - 1);
322
416
    if (to_match == NULL) {
323
0
        return 0;
324
0
    }
325
326
416
    PyObject* pattern = compiled_patterns[idx];
327
416
    PyObject* match_callable = PyObject_GetAttrString(pattern, "match");
328
329
416
    PyObject* matches = PyObject_CallOneArg(match_callable, to_match);
330
331
416
    Py_XDECREF(matches);
332
416
    Py_DECREF(match_callable);
333
416
    Py_DECREF(to_match);
334
416
    return 0;
335
416
}
336
337
2.19k
#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.19k
static int fuzz_csv_reader(const char* data, size_t size) {
352
2.19k
    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.19k
    if (memchr(data, '\0', size) == NULL) {
358
5
        return 0;
359
5
    }
360
361
2.18k
    PyObject* s = PyUnicode_FromString(data);
362
    /* Ignore exceptions until we have a valid string */
363
2.18k
    if (s == NULL) {
364
461
        PyErr_Clear();
365
461
        return 0;
366
461
    }
367
368
    /* Split on \n so we can test multiple lines */
369
1.72k
    PyObject* lines = PyObject_CallMethod(s, "split", "s", "\n");
370
1.72k
    if (lines == NULL) {
371
0
        Py_DECREF(s);
372
0
        return 0;
373
0
    }
374
375
1.72k
    PyObject* reader = PyObject_CallMethod(csv_module, "reader", "N", lines);
376
1.72k
    if (reader) {
377
        /* Consume all of the reader as an iterator */
378
1.72k
        PyObject* parsed_line;
379
29.6M
        while ((parsed_line = PyIter_Next(reader))) {
380
29.6M
            Py_DECREF(parsed_line);
381
29.6M
        }
382
1.72k
    }
383
384
    /* Ignore csv.Error because we're probably going to generate
385
       some bad files (embedded new-lines, unterminated quotes etc) */
386
1.72k
    if (PyErr_ExceptionMatches(csv_error)) {
387
160
        PyErr_Clear();
388
160
    }
389
390
1.72k
    Py_XDECREF(reader);
391
1.72k
    Py_DECREF(s);
392
1.72k
    return 0;
393
1.72k
}
394
395
39
#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
39
static int fuzz_ast_literal_eval(const char* data, size_t size) {
408
39
    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
39
    if (memchr(data, '\0', size) == NULL) {
414
0
        return 0;
415
0
    }
416
417
39
    PyObject* s = PyUnicode_FromString(data);
418
    /* Ignore exceptions until we have a valid string */
419
39
    if (s == NULL) {
420
2
        PyErr_Clear();
421
2
        return 0;
422
2
    }
423
424
37
    PyObject* literal = PyObject_CallOneArg(ast_literal_eval_method, s);
425
    /* Ignore some common errors thrown by ast.literal_eval */
426
37
    if (literal == NULL && (PyErr_ExceptionMatches(PyExc_ValueError) ||
427
21
                            PyErr_ExceptionMatches(PyExc_TypeError) ||
428
21
                            PyErr_ExceptionMatches(PyExc_SyntaxError) ||
429
0
                            PyErr_ExceptionMatches(PyExc_MemoryError) ||
430
0
                            PyErr_ExceptionMatches(PyExc_OverflowError) ||
431
0
                            PyErr_ExceptionMatches(PyExc_RecursionError))
432
37
    ) {
433
37
        PyErr_Clear();
434
37
    }
435
436
37
    Py_XDECREF(literal);
437
37
    Py_DECREF(s);
438
37
    return 0;
439
39
}
440
441
0
#define MAX_ELEMENTTREE_PARSEWHOLE_TEST_SIZE 0x100000
442
PyObject* xmlparser_type = NULL;
443
PyObject* bytesio_type = NULL;
444
/* Called by LLVMFuzzerTestOneInput for initialization */
445
0
static int init_elementtree_parsewhole(void) {
446
0
    PyObject* elementtree_module = PyImport_ImportModule("_elementtree");
447
0
    if (elementtree_module == NULL) {
448
0
        return 0;
449
0
    }
450
0
    xmlparser_type = PyObject_GetAttrString(elementtree_module, "XMLParser");
451
0
    Py_DECREF(elementtree_module);
452
0
    if (xmlparser_type == NULL) {
453
0
        return 0;
454
0
    }
455
456
457
0
    PyObject* io_module = PyImport_ImportModule("io");
458
0
    if (io_module == NULL) {
459
0
        return 0;
460
0
    }
461
0
    bytesio_type = PyObject_GetAttrString(io_module, "BytesIO");
462
0
    Py_DECREF(io_module);
463
0
    if (bytesio_type == NULL) {
464
0
        return 0;
465
0
    }
466
467
0
    return 1;
468
0
}
Unexecuted instantiation: fuzzer.c:init_elementtree_parsewhole
Unexecuted instantiation: fuzzer.c:init_elementtree_parsewhole
469
/* Fuzz _elementtree.XMLParser._parse_whole(x) */
470
0
static int fuzz_elementtree_parsewhole(const char* data, size_t size) {
471
0
    if (size > MAX_ELEMENTTREE_PARSEWHOLE_TEST_SIZE) {
472
0
        return 0;
473
0
    }
474
475
0
    PyObject *input = PyObject_CallFunction(bytesio_type, "y#", data, (Py_ssize_t)size);
476
0
    if (input == NULL) {
477
0
        assert(PyErr_Occurred());
478
0
        PyErr_Print();
479
0
        abort();
480
0
    }
481
482
0
    PyObject *xmlparser_instance = PyObject_CallObject(xmlparser_type, NULL);
483
0
    if (xmlparser_instance == NULL) {
484
0
        assert(PyErr_Occurred());
485
0
        PyErr_Print();
486
0
        abort();
487
0
    }
488
489
0
    PyObject *result = PyObject_CallMethod(xmlparser_instance, "_parse_whole", "O", input);
490
0
    if (result == NULL) {
491
        /* Ignore exception here, which can be caused by invalid XML input */
492
0
        PyErr_Clear();
493
0
    } else {
494
0
        Py_DECREF(result);
495
0
    }
496
497
0
    Py_DECREF(xmlparser_instance);
498
0
    Py_DECREF(input);
499
500
0
    return 0;
501
0
}
Unexecuted instantiation: fuzzer.c:fuzz_elementtree_parsewhole
Unexecuted instantiation: fuzzer.c:fuzz_elementtree_parsewhole
502
503
24.5k
#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
24.5k
static int fuzz_pycompile(const char* data, size_t size) {
514
    // Ignore overly-large inputs, and account for a NUL terminator
515
24.5k
    if (size > MAX_PYCOMPILE_TEST_SIZE - 1) {
516
1
        return 0;
517
1
    }
518
519
    // Need 3 bytes for parameter selection
520
24.5k
    if (size < 3) {
521
2
        return 0;
522
2
    }
523
524
    // Use first byte to determine element of `start_vals` to use
525
24.5k
    unsigned char start_idx = (unsigned char) data[0];
526
24.5k
    int start = start_vals[start_idx % NUM_START_VALS];
527
528
    // Use second byte to determine element of `optimize_vals` to use
529
24.5k
    unsigned char optimize_idx = (unsigned char) data[1];
530
24.5k
    int optimize = optimize_vals[optimize_idx % NUM_OPTIMIZE_VALS];
531
532
    // Use third byte to determine compiler flags to use.
533
24.5k
    unsigned char flags_byte = (unsigned char) data[2];
534
24.5k
    PyCompilerFlags flags = _PyCompilerFlags_INIT;
535
24.5k
    if (flags_byte & 0x01) {
536
11.7k
        flags.cf_flags |= PyCF_DONT_IMPLY_DEDENT;
537
11.7k
    }
538
24.5k
    if (flags_byte & 0x02) {
539
6.84k
        flags.cf_flags |= PyCF_ONLY_AST;
540
6.84k
    }
541
24.5k
    if (flags_byte & 0x04) {
542
11.0k
        flags.cf_flags |= PyCF_IGNORE_COOKIE;
543
11.0k
    }
544
24.5k
    if (flags_byte & 0x08) {
545
10.4k
        flags.cf_flags |= PyCF_TYPE_COMMENTS;
546
10.4k
    }
547
24.5k
    if (flags_byte & 0x10) {
548
7.31k
        flags.cf_flags |= PyCF_ALLOW_TOP_LEVEL_AWAIT;
549
7.31k
    }
550
24.5k
    if (flags_byte & 0x20) {
551
18.5k
        flags.cf_flags |= PyCF_ALLOW_INCOMPLETE_INPUT;
552
18.5k
    }
553
24.5k
    if (flags_byte & 0x40) {
554
5.35k
        flags.cf_flags |= PyCF_OPTIMIZED_AST;
555
5.35k
    }
556
557
24.5k
    char pycompile_scratch[MAX_PYCOMPILE_TEST_SIZE];
558
559
    // Create a NUL-terminated C string from the remaining input
560
24.5k
    memcpy(pycompile_scratch, data + 3, size - 3);
561
    // Put a NUL terminator just after the copied data. (Space was reserved already.)
562
24.5k
    pycompile_scratch[size - 3] = '\0';
563
564
24.5k
    PyObject *result = Py_CompileStringExFlags(pycompile_scratch, "<fuzz input>", start, &flags, optimize);
565
24.5k
    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
12.6k
        if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SystemError)) {
570
0
            PyErr_Print();
571
0
            abort();
572
0
        }
573
12.6k
        PyErr_Clear();
574
12.6k
    } else {
575
11.9k
        Py_DECREF(result);
576
11.9k
    }
577
578
24.5k
    return 0;
579
24.5k
}
580
581
/* Run fuzzer and abort on failure. */
582
52.5k
static int _run_fuzz(const uint8_t *data, size_t size, int(*fuzzer)(const char* , size_t)) {
583
52.5k
    int rv = fuzzer((const char*) data, size);
584
52.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
52.5k
    return rv;
592
52.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
21
int LLVMFuzzerInitialize(int *argc, char ***argv) {
599
21
    PyConfig config;
600
21
    PyConfig_InitPythonConfig(&config);
601
21
    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
21
    config.int_max_str_digits = 8086;
605
21
    PyStatus status;
606
21
    status = PyConfig_SetBytesString(&config, &config.program_name, *argv[0]);
607
21
    if (PyStatus_Exception(status)) {
608
0
        goto fail;
609
0
    }
610
611
21
    status = Py_InitializeFromConfig(&config);
612
21
    if (PyStatus_Exception(status)) {
613
0
        goto fail;
614
0
    }
615
21
    PyConfig_Clear(&config);
616
617
21
    return 0;
618
619
0
fail:
620
0
    PyConfig_Clear(&config);
621
0
    Py_ExitStatusException(status);
622
21
}
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
33.2k
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
632
33.2k
    assert(Py_IsInitialized());
633
634
33.2k
    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
33.2k
#if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_builtin_unicode)
643
33.2k
    rv |= _run_fuzz(data, size, fuzz_builtin_unicode);
644
33.2k
#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
33.2k
  return rv;
727
33.2k
}