On Tue, Sep 17, 2019 at 11:56:26PM +0800, Michael Mikonos wrote:
> 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;
When I remembered to check if upstream flex had fixed the sf_pop()
issue I found their fix was a little different:
https://github.com/westes/flex/commit/9ba6e5283efd2fe454d3bc92eca960b3ebd91294
Notably, FLEX_EXIT() is not called so flexscan() won't immediately
terminate. This allows other syntax errors to be reported.
The following patch applies what they have.
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 21 Sep 2019 12:08:36 -0000
@@ -741,7 +741,13 @@ nmstr[yyleng - 2 - end_is_ws] = '\0'; /
return '(';
}
"(" sf_push(); return '(';
- ")" sf_pop(); return ')';
+ ")" {
+ if (_sf_top_ix > 0) {
+ sf_pop();
+ return ')';
+ } else
+ synerr(_("unbalanced parenthesis"));
+ }
[/|*+?.(){}] return (unsigned char) yytext[0];
. RETURNCHAR;