Hello, I was wondering why fuzz input was sometimes crashing flex(1). Now that assert() is enabled in the build I am able to trigger it with the following input.
$ cat test.l %{ hello %} %% ))) world $ /usr/bin/lex -d - < test.l assertion "_sf_top_ix > 0" failed: file "scanflags.c", line 55, function "sf_pop" Abort trap (core dumped) $ gdb -c lex.core -e /usr/bin/lex GNU gdb 6.3 Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-unknown-openbsd6.6". Core was generated by `lex'. Program terminated with signal 6, Aborted. Loaded symbols for /usr/bin/lex.assert.debug Reading symbols from /usr/lib/libm.so.10.1...done. Loaded symbols for /usr/lib/libm.so.10.1 Reading symbols from /usr/lib/libc.so.95.1...done. Loaded symbols for /usr/lib/libc.so.95.1 Reading symbols from /usr/libexec/ld.so...done. Loaded symbols for /usr/libexec/ld.so #0 thrkill () at -:3 3 -: No such file or directory. in - (gdb) bt #0 thrkill () at -:3 #1 0x0d0fce40 in _libc_raise (s=6) at /usr/src/lib/libc/gen/raise.c:37 #2 0x0d104e3b in _libc_abort () at /usr/src/lib/libc/stdlib/abort.c:51 #3 0x0d0c3b30 in _libc___assert2 (file=0x1a14d90e "scanflags.c", line=55, func=0x1a1526a2 "sf_pop", failedexpr=0x1a158462 "_sf_top_ix > 0") at /usr/src/lib/libc/gen/assert.c:52 #4 0x1a189024 in sf_pop () at scanflags.c:55 #5 0x1a186eec in flexscan () at scan.l:744 #6 0x1a18cfa6 in yylex () at yylex.c:52 #7 0x1a17ffc5 in yyparse () at parse.c:687 #8 0x1a179a97 in readin () at main.c:1445 #9 0x1a178d81 in flex_main (argc=3, argv=0xcf7e8d74) at main.c:175 #10 0x1a17b535 in main (argc=3, argv=0xcf7e8d74) at main.c:223 Current language: auto; currently asm Stack trace from gdb shows flexscan() calls sf_pop() but the stack _sf_stk is empty and has nothing to pop. flexscan() calls sf_push() when it sees "(", but fails to check the size of _sf_stk before calling sf_pop() when ")" is seen. The diff below prints a syntax error message instead of aborting in this case. It looks like this for the test file: $ lex -d - < test.l <stdin>:7: ')' with no matching '(' Before assert() was enabled, calling sf_pop() would wrap the array index _sf_top_ix from 0 to 0xffffffff and a SEGV could happen later. Does this look OK? - Michael Index: scan.l =================================================================== RCS file: /cvs/src/usr.bin/lex/scan.l,v retrieving revision 1.12 diff -u -p -u -r1.12 scan.l --- scan.l 19 Nov 2015 23:34:56 -0000 1.12 +++ scan.l 17 Sep 2019 15:35:27 -0000 @@ -741,7 +741,14 @@ nmstr[yyleng - 2 - end_is_ws] = '\0'; / return '('; } "(" sf_push(); return '('; - ")" sf_pop(); return ')'; + ")" { + if (_sf_top_ix == 0) { + synerr( _("')' with no matching '('\n")); + FLEX_EXIT(EXIT_FAILURE); + } + sf_pop(); + return ')'; + } [/|*+?.(){}] return (unsigned char) yytext[0]; . RETURNCHAR;