Skip to content

bpo-29505: Fuzz ast.parse() / compile() with PyCF_ONLY_AST #3437

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Modules/_xxtestfuzz/fuzz_tests.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
fuzz_ast_parse
fuzz_builtin_float
fuzz_builtin_int
fuzz_builtin_unicode
39 changes: 39 additions & 0 deletions Modules/_xxtestfuzz/fuzzer.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,42 @@
#include <stdlib.h>
#include <inttypes.h>

/* Get a null-terminated copy of some data. The caller owns the return value. */
static char* copy_as_string(const char* data, size_t size) {
char* new_s = malloc(size + 1);
memcpy(new_s, data, size);
new_s[size] = 0;
return new_s;
}

/* Fuzz Py_CompileStringExFlags as a proxy for ast.parse(str) */
static int fuzz_ast_parse(const char* data, size_t size) {
char* null_terminated_data = copy_as_string(data, size);
PyCompilerFlags flags;
flags.cf_flags = PyCF_ONLY_AST;
PyObject* ast = Py_CompileStringExFlags(
null_terminated_data,
"<imaginary_file>",
Py_file_input,
&flags,
0);
free(null_terminated_data);

if (ast == NULL) {
/* SyntaxError (from gibberish) and MemoryError (from deeply nested
expressions) are to be expected. */
if (PyErr_ExceptionMatches(PyExc_SyntaxError) || PyErr_ExceptionMatches(PyExc_MemoryError)) {
PyErr_Clear();
return 0;
} else {
PyErr_Print();
abort();
}
}
Py_DECREF(ast);
return 0;
}

/* Fuzz PyFloat_FromString as a proxy for float(str). */
static int fuzz_builtin_float(const char* data, size_t size) {
PyObject* s = PyBytes_FromStringAndSize(data, size);
Expand Down Expand Up @@ -105,6 +141,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {

int rv = 0;

#if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_ast_parse)
rv |= _run_fuzz(data, size, fuzz_ast_parse);
#endif
#if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_builtin_float)
rv |= _run_fuzz(data, size, fuzz_builtin_float);
#endif
Expand Down