-
-
Notifications
You must be signed in to change notification settings - Fork 34.8k
bpo-41200: Add pickle.loads fuzz test #21289
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,3 +6,4 @@ fuzz_sre_compile | |
| fuzz_sre_match | ||
| fuzz_csv_reader | ||
| fuzz_struct_unpack | ||
| fuzz_pickle_loads |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -142,6 +142,74 @@ static int fuzz_struct_unpack(const char* data, size_t size) { | |
| } | ||
|
|
||
|
|
||
| PyObject* pickle_loads_method = NULL; | ||
| PyObject* pickle_error = NULL; | ||
| PyObject* pickling_error = NULL; | ||
| PyObject* unpickling_error = NULL; | ||
| /* Called by LLVMFuzzerTestOneInput for initialization */ | ||
| static int init_pickle_loads() { | ||
| /* Import pickle.loads */ | ||
| PyObject* pickle_module = PyImport_ImportModule("pickle"); | ||
| if (pickle_module == NULL) { | ||
| return 0; | ||
| } | ||
| pickle_error = PyObject_GetAttrString(pickle_module, "PickleError"); | ||
| if (pickle_error == NULL) { | ||
| return 0; | ||
| } | ||
| pickling_error = PyObject_GetAttrString(pickle_module, "PicklingError"); | ||
| if (pickling_error == NULL) { | ||
| return 0; | ||
| } | ||
| unpickling_error = PyObject_GetAttrString(pickle_module, "UnpicklingError"); | ||
| if (unpickling_error == NULL) { | ||
| return 0; | ||
| } | ||
| pickle_loads_method = PyObject_GetAttrString(pickle_module, "loads"); | ||
| return pickle_loads_method != NULL; | ||
| } | ||
|
|
||
| #define MAX_PICKLE_TEST_SIZE 0x10000 | ||
| /* Fuzz pickle.loads(x) */ | ||
| static int fuzz_pickle_loads(const char* data, size_t size) { | ||
| if (size > MAX_PICKLE_TEST_SIZE) { | ||
| return 0; | ||
| } | ||
| PyObject* input_bytes = PyBytes_FromStringAndSize(data, size); | ||
| if (input_bytes == NULL) { | ||
| return 0; | ||
| } | ||
| PyObject* parsed = PyObject_CallOneArg(pickle_loads_method, input_bytes); | ||
| if (parsed == NULL && | ||
| (PyErr_ExceptionMatches(PyExc_ValueError) || | ||
| PyErr_ExceptionMatches(PyExc_AttributeError) || | ||
| PyErr_ExceptionMatches(PyExc_KeyError) || | ||
| PyErr_ExceptionMatches(PyExc_TypeError) || | ||
| PyErr_ExceptionMatches(PyExc_OverflowError) || | ||
| PyErr_ExceptionMatches(PyExc_EOFError) || | ||
| PyErr_ExceptionMatches(PyExc_MemoryError) || | ||
| PyErr_ExceptionMatches(PyExc_ModuleNotFoundError) || | ||
| PyErr_ExceptionMatches(PyExc_IndexError) || | ||
| PyErr_ExceptionMatches(PyExc_RecursionError) || | ||
| PyErr_ExceptionMatches(PyExc_UnicodeDecodeError))) | ||
| { | ||
| PyErr_Clear(); | ||
| } | ||
|
|
||
| if (parsed == NULL && ( | ||
| PyErr_ExceptionMatches(pickle_error) || | ||
| PyErr_ExceptionMatches(pickling_error) || | ||
| PyErr_ExceptionMatches(unpickling_error) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not simply
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. other fuzzers in this module also work like this, but if we're going to list exceptions to ignore, we should at least add comments as to why for each or at least each class. I assume this being a pickle.loads test is really looking for process crashes. unfortunately... I expect the results of this as a fuzzer to be pretty bad, pickle is explicitly documented as not being suitable for untrusted data. it may execute arbitrary code or crash in that situation. i asked this broader question on the bug. |
||
| )) | ||
| { | ||
| PyErr_Clear(); | ||
| } | ||
| Py_DECREF(input_bytes); | ||
| Py_XDECREF(parsed); | ||
| return 0; | ||
| } | ||
|
|
||
|
|
||
| #define MAX_JSON_TEST_SIZE 0x10000 | ||
|
|
||
| PyObject* json_loads_method = NULL; | ||
|
|
@@ -452,6 +520,19 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { | |
| } | ||
| rv |= _run_fuzz(data, size, fuzz_struct_unpack); | ||
| #endif | ||
|
|
||
| #if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_pickle_loads) | ||
| static int PICKLE_LOADS_INITIALIZED = 0; | ||
| if (!PICKLE_LOADS_INITIALIZED && !init_pickle_loads()) { | ||
| PyErr_Print(); | ||
| abort(); | ||
| } else { | ||
| PICKLE_LOADS_INITIALIZED = 1; | ||
| } | ||
|
|
||
| rv |= _run_fuzz(data, size, fuzz_pickle_loads); | ||
| #endif | ||
|
|
||
| #if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_fuzz_json_loads) | ||
| static int JSON_LOADS_INITIALIZED = 0; | ||
| if (!JSON_LOADS_INITIALIZED && !init_json_loads()) { | ||
|
|
Uh oh!
There was an error while loading. Please reload this page.