@@ -79,6 +79,69 @@ static int fuzz_builtin_unicode(const char* data, size_t size) {
7979 return 0 ;
8080}
8181
82+
83+ PyObject * struct_unpack_method = NULL ;
84+ PyObject * struct_error = NULL ;
85+ /* Called by LLVMFuzzerTestOneInput for initialization */
86+ static int init_struct_unpack () {
87+ /* Import struct.unpack */
88+ PyObject * struct_module = PyImport_ImportModule ("struct" );
89+ if (struct_module == NULL ) {
90+ return 0 ;
91+ }
92+ struct_error = PyObject_GetAttrString (struct_module , "error" );
93+ if (struct_error == NULL ) {
94+ return 0 ;
95+ }
96+ struct_unpack_method = PyObject_GetAttrString (struct_module , "unpack" );
97+ return struct_unpack_method != NULL ;
98+ }
99+ /* Fuzz struct.unpack(x, y) */
100+ static int fuzz_struct_unpack (const char * data , size_t size ) {
101+ /* Everything up to the first null byte is considered the
102+ format. Everything after is the buffer */
103+ const char * first_null = memchr (data , '\0' , size );
104+ if (first_null == NULL ) {
105+ return 0 ;
106+ }
107+
108+ size_t format_length = first_null - data ;
109+ size_t buffer_length = size - format_length - 1 ;
110+
111+ PyObject * pattern = PyBytes_FromStringAndSize (data , format_length );
112+ if (pattern == NULL ) {
113+ return 0 ;
114+ }
115+ PyObject * buffer = PyBytes_FromStringAndSize (first_null + 1 , buffer_length );
116+ if (buffer == NULL ) {
117+ Py_DECREF (pattern );
118+ return 0 ;
119+ }
120+
121+ PyObject * unpacked = PyObject_CallFunctionObjArgs (
122+ struct_unpack_method , pattern , buffer , NULL );
123+ /* Ignore any overflow errors, these are easily triggered accidentally */
124+ if (unpacked == NULL && PyErr_ExceptionMatches (PyExc_OverflowError )) {
125+ PyErr_Clear ();
126+ }
127+ /* The pascal format string will throw a negative size when passing 0
128+ like: struct.unpack('0p', b'') */
129+ if (unpacked == NULL && PyErr_ExceptionMatches (PyExc_SystemError )) {
130+ PyErr_Clear ();
131+ }
132+ /* Ignore any struct.error exceptions, these can be caused by invalid
133+ formats or incomplete buffers both of which are common. */
134+ if (unpacked == NULL && PyErr_ExceptionMatches (struct_error )) {
135+ PyErr_Clear ();
136+ }
137+
138+ Py_XDECREF (unpacked );
139+ Py_DECREF (pattern );
140+ Py_DECREF (buffer );
141+ return 0 ;
142+ }
143+
144+
82145#define MAX_JSON_TEST_SIZE 0x10000
83146
84147PyObject * json_loads_method = NULL ;
@@ -190,9 +253,10 @@ static int fuzz_sre_compile(const char* data, size_t size) {
190253 PyErr_Clear ();
191254 }
192255 /* Ignore some common errors thrown by sre_parse:
193- Overflow, Assertion and Index */
256+ Overflow, Assertion, Recursion and Index */
194257 if (compiled == NULL && (PyErr_ExceptionMatches (PyExc_OverflowError ) ||
195258 PyErr_ExceptionMatches (PyExc_AssertionError ) ||
259+ PyErr_ExceptionMatches (PyExc_RecursionError ) ||
196260 PyErr_ExceptionMatches (PyExc_IndexError ))
197261 ) {
198262 PyErr_Clear ();
@@ -378,6 +442,16 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
378442#if !defined(_Py_FUZZ_ONE ) || defined(_Py_FUZZ_fuzz_builtin_unicode )
379443 rv |= _run_fuzz (data , size , fuzz_builtin_unicode );
380444#endif
445+ #if !defined(_Py_FUZZ_ONE ) || defined(_Py_FUZZ_fuzz_struct_unpack )
446+ static int STRUCT_UNPACK_INITIALIZED = 0 ;
447+ if (!STRUCT_UNPACK_INITIALIZED && !init_struct_unpack ()) {
448+ PyErr_Print ();
449+ abort ();
450+ } else {
451+ STRUCT_UNPACK_INITIALIZED = 1 ;
452+ }
453+ rv |= _run_fuzz (data , size , fuzz_struct_unpack );
454+ #endif
381455#if !defined(_Py_FUZZ_ONE ) || defined(_Py_FUZZ_fuzz_json_loads )
382456 static int JSON_LOADS_INITIALIZED = 0 ;
383457 if (!JSON_LOADS_INITIALIZED && !init_json_loads ()) {
0 commit comments