@@ -441,6 +441,22 @@ static int fuzz_ast_literal_eval(const char* data, size_t size) {
441441#define MAX_ELEMENTTREE_PARSEWHOLE_TEST_SIZE 0x100000
442442PyObject * xmlparser_type = NULL ;
443443PyObject * bytesio_type = NULL ;
444+
445+ static int
446+ init_bytesio (void )
447+ {
448+ if (bytesio_type != NULL ) {
449+ return 1 ;
450+ }
451+ PyObject * io_module = PyImport_ImportModule ("_io" );
452+ if (io_module == NULL ) {
453+ return 0 ;
454+ }
455+ bytesio_type = PyObject_GetAttrString (io_module , "BytesIO" );
456+ Py_DECREF (io_module );
457+ return bytesio_type != NULL ;
458+ }
459+
444460/* Called by LLVMFuzzerTestOneInput for initialization */
445461static int init_elementtree_parsewhole (void ) {
446462 PyObject * elementtree_module = PyImport_ImportModule ("_elementtree" );
@@ -452,15 +468,7 @@ static int init_elementtree_parsewhole(void) {
452468 if (xmlparser_type == NULL ) {
453469 return 0 ;
454470 }
455-
456-
457- PyObject * io_module = PyImport_ImportModule ("io" );
458- if (io_module == NULL ) {
459- return 0 ;
460- }
461- bytesio_type = PyObject_GetAttrString (io_module , "BytesIO" );
462- Py_DECREF (io_module );
463- if (bytesio_type == NULL ) {
471+ if (!init_bytesio ()) {
464472 return 0 ;
465473 }
466474
@@ -578,6 +586,136 @@ static int fuzz_pycompile(const char* data, size_t size) {
578586 return 0 ;
579587}
580588
589+ #define MAX_TOKENIZER_TEST_SIZE 16384
590+
591+ static PyObject * tokenizer_iter_type ;
592+ static PyObject * stringio_type ;
593+
594+ static int
595+ init_tokenizer (void )
596+ {
597+ PyObject * tokenize_module = PyImport_ImportModule ("_tokenize" );
598+ if (tokenize_module == NULL ) {
599+ return 0 ;
600+ }
601+ tokenizer_iter_type = PyObject_GetAttrString (
602+ tokenize_module , "TokenizerIter" );
603+ Py_DECREF (tokenize_module );
604+ if (tokenizer_iter_type == NULL ) {
605+ return 0 ;
606+ }
607+
608+ PyObject * io_module = PyImport_ImportModule ("_io" );
609+ if (io_module == NULL ) {
610+ return 0 ;
611+ }
612+ stringio_type = PyObject_GetAttrString (io_module , "StringIO" );
613+ Py_DECREF (io_module );
614+ if (stringio_type == NULL ) {
615+ return 0 ;
616+ }
617+ return init_bytesio ();
618+ }
619+
620+ static void
621+ clear_expected_tokenizer_error (void )
622+ {
623+ if (PyErr_ExceptionMatches (PyExc_SyntaxError ) ||
624+ PyErr_ExceptionMatches (PyExc_UnicodeError ) ||
625+ PyErr_ExceptionMatches (PyExc_LookupError ) ||
626+ PyErr_ExceptionMatches (PyExc_MemoryError ) ||
627+ PyErr_ExceptionMatches (PyExc_Warning )) {
628+ PyErr_Clear ();
629+ return ;
630+ }
631+ PyErr_Print ();
632+ abort ();
633+ }
634+
635+ static int
636+ fuzz_tokenizer (const char * data , size_t size )
637+ {
638+ if (size < 1 || size > MAX_TOKENIZER_TEST_SIZE ) {
639+ return 0 ;
640+ }
641+
642+ unsigned char options = (unsigned char )data [0 ];
643+ data ++ ;
644+ size -- ;
645+
646+ int bytes_mode = options & 0x01 ;
647+ PyObject * source ;
648+ if (bytes_mode ) {
649+ source = PyBytes_FromStringAndSize (data , size );
650+ }
651+ else {
652+ source = PyUnicode_DecodeUTF8 (data , size , "replace" );
653+ }
654+ if (source == NULL ) {
655+ clear_expected_tokenizer_error ();
656+ return 0 ;
657+ }
658+
659+ PyObject * stream_type = bytes_mode ? bytesio_type : stringio_type ;
660+ PyObject * stream = PyObject_CallOneArg (stream_type , source );
661+ Py_DECREF (source );
662+ if (stream == NULL ) {
663+ clear_expected_tokenizer_error ();
664+ return 0 ;
665+ }
666+ PyObject * readline = PyObject_GetAttrString (stream , "readline" );
667+ Py_DECREF (stream );
668+ if (readline == NULL ) {
669+ clear_expected_tokenizer_error ();
670+ return 0 ;
671+ }
672+
673+ PyObject * args = PyTuple_Pack (1 , readline );
674+ Py_DECREF (readline );
675+ if (args == NULL ) {
676+ clear_expected_tokenizer_error ();
677+ return 0 ;
678+ }
679+ PyObject * extra_tokens = options & 0x02 ? Py_True : Py_False ;
680+ PyObject * kwargs = bytes_mode
681+ ? Py_BuildValue (
682+ "{s:O,s:s}" , "extra_tokens" , extra_tokens ,
683+ "encoding" , "utf-8" )
684+ : Py_BuildValue ("{s:O}" , "extra_tokens" , extra_tokens );
685+ if (kwargs == NULL ) {
686+ Py_DECREF (args );
687+ clear_expected_tokenizer_error ();
688+ return 0 ;
689+ }
690+
691+ PyObject * iterator = PyObject_Call (tokenizer_iter_type , args , kwargs );
692+ Py_DECREF (args );
693+ Py_DECREF (kwargs );
694+ if (iterator == NULL ) {
695+ clear_expected_tokenizer_error ();
696+ return 0 ;
697+ }
698+
699+ int terminated = 0 ;
700+ size_t limit = size * 4 + 32 ;
701+ for (size_t i = 0 ; i < limit ; i ++ ) {
702+ PyObject * token = PyIter_Next (iterator );
703+ if (token == NULL ) {
704+ terminated = 1 ;
705+ if (PyErr_Occurred ()) {
706+ clear_expected_tokenizer_error ();
707+ }
708+ break ;
709+ }
710+ Py_DECREF (token );
711+ }
712+ Py_DECREF (iterator );
713+ if (!terminated ) {
714+ abort ();
715+ }
716+ return 0 ;
717+ }
718+
581719/* Run fuzzer and abort on failure. */
582720static int _run_fuzz (const uint8_t * data , size_t size , int (* fuzzer )(const char * , size_t )) {
583721 int rv = fuzzer ((const char * ) data , size );
@@ -722,6 +860,15 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
722860#endif
723861#if !defined(_Py_FUZZ_ONE ) || defined(_Py_FUZZ_fuzz_pycompile )
724862 rv |= _run_fuzz (data , size , fuzz_pycompile );
863+ #endif
864+ #if !defined(_Py_FUZZ_ONE ) || defined(_Py_FUZZ_fuzz_tokenizer )
865+ static int TOKENIZER_INITIALIZED = 0 ;
866+ if (!TOKENIZER_INITIALIZED && !init_tokenizer ()) {
867+ PyErr_Print ();
868+ abort ();
869+ }
870+ TOKENIZER_INITIALIZED = 1 ;
871+ rv |= _run_fuzz (data , size , fuzz_tokenizer );
725872#endif
726873 return rv ;
727874}
0 commit comments