/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 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 | } |