Skip Menu |

From: Brian Carpenter <brian.carpenter [...] gmail.com>
To: perlbug [...] perl.org
Subject: Segfault (stack overflow?) while fuzzing Perl 5.21.8
Date: Sat, 17 Jan 2015 13:14:54 -0600
Download (untitled) / with headers
text/plain 6.5k
Download (untitled) / with headers
text/html 10.1k
Good afternoon, I'm still fuzzing Perl 5.21.8, built from git source on 14 January 2015. I used the afl-gcc compiler (http://lcamtuf.coredump.cx/afl/) to compile.

CC=/path/to/afl-gcc ./Configure

I did not use AFL_HARDEN=1 when I ran make, and I also compiled a second binary with no stack protection to try and get a little better insight into this issue. I've attached the core dump and the test case which causes this seg fault.


geeknik@deb7fuzz:~/findings/perl5/fuzzer03/crashes$ /home/geeknik/perl5-no-stack-protect/perl id\:000045\,sig\:11\,src\:009416+026249\,op\:splice\,rep\:1 

Bareword found where operator expected at id:000045,sig:11,src:009416+026249,op:splice,rep:1 line 13, near "print "into"
  (Might be a runaway multi-line "" string starting on line 12)
(Do you need to predeclare print?)
Backslash found where operator expected at id:000045,sig:11,src:009416+026249,op:splice,rep:1 line 20, near "foreachme\"
String found where operator expected at id:000045,sig:11,src:009416+026249,op:splice,rep:1 line 20, near "print ""
(Missing semicolon on previous line?)
Bareword found where operator expected at id:000045,sig:11,src:009416+026249,op:splice,rep:1 line 21, near "print "otform"
  (Might be a runaway multi-line "" string starting on line 20)
(Do you need to predeclare print?)
Backslash found where operator expected at id:000045,sig:11,src:009416+026249,op:splice,rep:1 line 21, near "<P>\"
(Missing operator before \?)
String found where operator expected at id:000045,sig:11,src:009416+026249,op:splice,rep:1 line 21, near "print ""
(Missing semicolon on previous line?)
String found where operator expected at id:000045,sig:11,src:009416+026249,op:splice,rep:1 line 21, near "print ""
(Missing semicolon on previous line?)
Backslash found where operator expected at id:000045,sig:11,src:009416+026249,op:splice,rep:1 line 23, near "is\"
  (Might be a runaway multi-line "" string starting on line 21)
String found where operator expected at id:000045,sig:11,src:009416+026249,op:splice,rep:1 line 23, near "print ""
(Missing semicolon on previous line?)
Bareword found where operator expected at id:000045,sig:11,src:009416+026249,op:splice,rep:1 line 24, near "print "necessary"
  (Might be a runaway multi-line "" string starting on line 23)
(Do you need to predeclare print?)
Backslash found where operator expected at id:000045,sig:11,src:009416+026249,op:splice,rep:1 line 26, near "m signs\"
(Do you need to predeclare m?)
String found where operator expected at id:000045,sig:11,src:009416+026249,op:splice,rep:1 line 26, near "print ""
(Missing semicolon on previous line?)
Bareword found where operator expected at id:000045,sig:11,src:009416+026249,op:splice,rep:1 line 27, near "print "into"
  (Might be a runaway multi-line "" string starting on line 26)
(Do you need to predeclare print?)
Segmentation fault (core dumped)

geeknik@deb7fuzz:~/findings/perl5/fuzzer03/crashes$ valgrind -q /home/geeknik/perl5-no-stack-protect/perl id\:000045\,sig\:11\,src\:009416+026249\,op\:splice\,rep\:1
==32809== Invalid read of size 4
==32809==    at 0x433C8A: S_aassign_common_vars (op.c:6402)
==32809==    by 0x47A776: Perl_rpeep (op.c:13955)
==32809==    by 0x475B06: Perl_newPROG (op.c:4100)
==32809==    by 0x5B7BFF: Perl_yyparse (perly.y:120)
==32809==    by 0x4E63E4: perl_parse (perl.c:2273)
==32809==    by 0x4299CB: main (perlmain.c:114)
==32809==  Address 0x18 is not stack'd, malloc'd or (recently) free'd
==32809== 
==32809== 
==32809== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==32809==  Access not within mapped region at address 0x18
==32809==    at 0x433C8A: S_aassign_common_vars (op.c:6402)
==32809==    by 0x47A776: Perl_rpeep (op.c:13955)
==32809==    by 0x475B06: Perl_newPROG (op.c:4100)
==32809==    by 0x5B7BFF: Perl_yyparse (perly.y:120)
==32809==    by 0x4E63E4: perl_parse (perl.c:2273)
==32809==    by 0x4299CB: main (perlmain.c:114)
==32809==  If you believe this happened as a result of a stack
==32809==  overflow in your program's main thread (unlikely but
==32809==  possible), you can try to increase the size of the
==32809==  main thread stack using the --main-stacksize= flag.
==32809==  The main thread stack size used in this run was 8388608.
Segmentation fault

geeknik@deb7fuzz:~/findings/perl5/fuzzer03/crashes$ gdb /home/geeknik/perl5-no-stack-protect/perl core
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
Reading symbols from /home/geeknik/perl5-no-stack-protect/perl...done.
[New LWP 46646]

warning: Can't read pathname for load map: Input/output error.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `/home/geeknik/perl5-no-stack-protect/perl id:000045,sig:11,src:009416+026249,op'.
Program terminated with signal 11, Segmentation fault.
#0  0x0000000000433c8a in S_aassign_common_vars (o=0xdfcc18) at op.c:6402
6402 || (int)GvASSIGN_GENERATION(gv) == PL_generation)
(gdb) bt
#0  0x0000000000433c8a in S_aassign_common_vars (o=0xdfcc18) at op.c:6402
#1  S_aassign_common_vars (o=0xdfca88) at op.c:6420
#2  S_aassign_common_vars (o=<optimized out>) at op.c:6420
#3  0x000000000047a777 in Perl_rpeep (o=0xdfca40) at op.c:13955
#4  0x0000000000475b07 in Perl_newPROG (o=0xdfbb20) at op.c:4100
#5  0x00000000005b7c00 in Perl_yyparse (gramtype=<optimized out>) at perly.y:120
#6  0x00000000004e63e5 in S_parse_body (xsinit=0x429dc0 <xs_init>, env=0x0) at perl.c:2273
#7  perl_parse (my_perl=<optimized out>, xsinit=0x429dc0 <xs_init>, argc=<optimized out>, argv=<optimized out>, env=0x0)
    at perl.c:1607
#8  0x00000000004299cc in main (argc=2, argv=0x7fffffffe338, env=0x7fffffffe350) at perlmain.c:114
#9  0x00007ffff6d7bead in __libc_start_main (main=<optimized out>, argc=<optimized out>, ubp_av=<optimized out>, 
    init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe328) at libc-start.c:244
#10 0x0000000000429ce5 in _start ()
(gdb) i r
rax            0xd 13
rbx            0xdfc6b0 14665392
rcx            0xd 13
rdx            0x0 0
rsi            0x540 1344
rdi            0x7 7
rbp            0xdfca88 0xdfca88
rsp            0x7fffffffde40 0x7fffffffde40
r8             0x4 4
r9             0x0 0
r10            0x65 101
r11            0x0 0
r12            0xdfcc18 14666776
r13            0xdfc1a0 14664096
r14            0x40 64
r15            0xffffffff 4294967295
rip            0x433c8a 0x433c8a <S_aassign_common_vars+4066>
eflags         0x10206 [ PF IF RF ]
cs             0x33 51
ss             0x2b 43
ds             0x0 0
es             0x0 0
fs             0x0 0
gs             0x0 0
(gdb) 

Download perl5-crash.tar.gz
application/x-gzip 66.6k

Message body not shown because it is not plain text.

Subject: Re: [perl #123617] Segfault (stack overflow?) while fuzzing Perl 5.21.8
To: perl5-porters [...] perl.org
From: hv [...] crypt.org
Date: Sun, 18 Jan 2015 01:09:39 +0000
Download (untitled) / with headers
text/plain 415b
A perl built with debugging gives: % ./perl test.pl perl: toke.c:2430: S_sublex_done: Assertion `(PL_parser->lex_inwhat) == OP_SUBST || (PL_parser->lex_inwhat) == OP_TRANS' failed. Aborted (core dumped) % Here's a shorter testcase (which doesn't clarify much): % cat test.pl "$a{m/""$b / m ss ";@c = split /x/ % We seem to have a PL_lex_repl with PL_lex_inwhat == OP_MATCH, which clearly can't happen. Hugo
RT-Send-CC: perl5-porters [...] perl.org
Download (untitled) / with headers
text/plain 876b
On Sat Jan 17 17:09:43 2015, hv wrote: Show quoted text
> A perl built with debugging gives: > > % ./perl test.pl > perl: toke.c:2430: S_sublex_done: Assertion `(PL_parser->lex_inwhat) > == OP_SUBST || (PL_parser->lex_inwhat) == OP_TRANS' failed. > Aborted (core dumped) > % > > Here's a shorter testcase (which doesn't clarify much): > > % cat test.pl > "$a{m/""$b > / m ss > ";@c = split /x/ > % > > We seem to have a PL_lex_repl with PL_lex_inwhat == OP_MATCH, which > clearly > can't happen.
This is no doubt related to my two favourite japhs: s||${s/.*/|; /s}Just another Perl hacker, print "${;s/.*/Just an"; other Perl hacker, /s} die or return; print I don’t remember exactly how they work. At the time I wrote them I based them on the interesting implementation details I saw in toke.c, which had something to do with PL_sublex_info.sub_inwhat. -- Father Chrysostomos
RT-Send-CC: perl5-porters [...] perl.org
On Sat Jan 24 22:08:15 2015, sprout wrote: Show quoted text
> On Sat Jan 17 17:09:43 2015, hv wrote:
> > A perl built with debugging gives: > > > > % ./perl test.pl > > perl: toke.c:2430: S_sublex_done: Assertion `(PL_parser->lex_inwhat) > > == OP_SUBST || (PL_parser->lex_inwhat) == OP_TRANS' failed. > > Aborted (core dumped) > > % > > > > Here's a shorter testcase (which doesn't clarify much): > > > > % cat test.pl > > "$a{m/""$b > > / m ss > > ";@c = split /x/ > > % > > > > We seem to have a PL_lex_repl with PL_lex_inwhat == OP_MATCH, which > > clearly > > can't happen.
> > This is no doubt related to my two favourite japhs: > > s||${s/.*/|; > /s}Just another Perl hacker, > print > > "${;s/.*/Just an"; > other Perl hacker, > /s} die or return; > print > > I don’t remember exactly how they work. At the time I wrote them I > based them on the interesting implementation details I saw in toke.c, > which had something to do with PL_sublex_info.sub_inwhat.
skipspace() in toke.c, which is usually used to skip whitespace between tokens, reads more input from the file if it needs to. But it checks first to see whether we are in a multiline construct and does not read more input if we are. scan_str, which looks for the final delimiter of the string, does not check whether we are in a multiline construct, but just reads straight ahead. So in this japh: s||${s/.*/|; /s}Just another Perl hacker, print When the replacement part of the outer s||| is parsed, ‘${s/.*/’ is the current ‘line’ of input, and scan_str, trying to find the final delimiter for the s///, reads the next line of input from the stream, which is ‘/s}Just another Perl hacker,’. It finds its delimiter (and the /s flag to boot) immediately. Then the inner s/// construct is exited via S_sublex_done, which sets PL_sublex_info.sub_inwhat to 0 indiscriminately, even though we are still inside the outer string-like construct. PL_sublex_info.sub_inwhat is what skipspace checks before reading more input. That’s as far as my investigation has reached. I may not continue for another week. -- Father Chrysostomos
RT-Send-CC: perl5-porters [...] perl.org
Download (untitled) / with headers
text/plain 3.1k
On Sat Jan 24 22:44:36 2015, sprout wrote: Show quoted text
> On Sat Jan 24 22:08:15 2015, sprout wrote:
> > On Sat Jan 17 17:09:43 2015, hv wrote:
> > > A perl built with debugging gives: > > > > > > % ./perl test.pl > > > perl: toke.c:2430: S_sublex_done: Assertion `(PL_parser-
> > > >lex_inwhat)
> > > == OP_SUBST || (PL_parser->lex_inwhat) == OP_TRANS' failed. > > > Aborted (core dumped) > > > % > > > > > > Here's a shorter testcase (which doesn't clarify much): > > > > > > % cat test.pl > > > "$a{m/""$b > > > / m ss > > > ";@c = split /x/ > > > % > > > > > > We seem to have a PL_lex_repl with PL_lex_inwhat == OP_MATCH, which > > > clearly > > > can't happen.
> > > > This is no doubt related to my two favourite japhs: > > > > s||${s/.*/|; > > /s}Just another Perl hacker, > > print > > > > "${;s/.*/Just an"; > > other Perl hacker, > > /s} die or return; > > print > > > > I don’t remember exactly how they work. At the time I wrote them I > > based them on the interesting implementation details I saw in toke.c, > > which had something to do with PL_sublex_info.sub_inwhat.
> > skipspace() in toke.c, which is usually used to skip whitespace > between tokens, reads more input from the file if it needs to. But it > checks first to see whether we are in a multiline construct and does > not read more input if we are. > > scan_str, which looks for the final delimiter of the string, does not > check whether we are in a multiline construct, but just reads straight > ahead. > > So in this japh: > > s||${s/.*/|; > /s}Just another Perl hacker, > print > > When the replacement part of the outer s||| is parsed, ‘${s/.*/’ is > the current ‘line’ of input, and scan_str, trying to find the final > delimiter for the s///, reads the next line of input from the stream, > which is ‘/s}Just another Perl hacker,’. It finds its delimiter (and > the /s flag to boot) immediately. Then the inner s/// construct is > exited via S_sublex_done, which sets PL_sublex_info.sub_inwhat to 0 > indiscriminately, even though we are still inside the outer string- > like construct. > > PL_sublex_info.sub_inwhat is what skipspace checks before reading more > input.
The setting of PL_sublex_info.sub_inwhat to 0 prematurely does not appear to be involved in this particular case. The current line of input, which is assumed to be the replacement part of the substitution, now contains ‘Just another Perl hacker,’, which is parsed as part of the s||...| and then parsing continues after that. So s||${s/.*/|; /s}Just another Perl hacker, print ends up equivalent to s||${s/.*/ /s}Just another Perl hacker,|; print (The purpose of that discourse was to refresh my memory of how this stuff works.) Now, for the reduced crashing case: "$a{m/""$b / m ss ";@c = split /x/ When parsing the initial "$a{m/" string, we find m/.../ inside it, and, again, scan_str reads the next line of input (‘/ m ss’), which is assumed to be inside the first string. Notice that we have no } on the second line, so I assume the setting of sub_inwhat to 0 has some role here, but I do not yet see how PL_lex_repl gets set. (The assertion fails because PL_lex_repl is set when we are inside a match op.) -- Father Chrysostomos
RT-Send-CC: perl5-porters [...] perl.org
Download (untitled) / with headers
text/plain 2.2k
On Sun Feb 01 07:54:28 2015, sprout wrote: Show quoted text
> Now, for the reduced crashing case: > > "$a{m/""$b > / m ss > ";@c = split /x/ > > When parsing the initial "$a{m/" string, we find m/.../ inside it, > and, again, scan_str reads the next line of input (‘/ m ss’), which is > assumed to be inside the first string.
At this point, m ss is read as another match op. So the parser sees stringify ( $a{ PMFUNC ( "" ) PMFUNC and immediately reports this as a syntax error, before we even enter the scope for parsing the inside of m ss. The content of m ss (the empty string) is still inside PL_lex_stuff. The parser, on encountering the syntax error, pops the savestack in the process of popping tokens, thereby exiting the quote-parsing scope. So we end up back at line 1 with ‘"$b’ as the next thing to be read. However, PL_lex_stuff is not localised, so it does not get cleared in this process, but remains set. (Usually PL_lex_stuff does not need to be localised, since it is a temporary value that is set when the string is scanned and then read after the next token has been emitted.) So here we have a string to parse, but PL_lex_stuff is already set, so the string inside "$b<newline>" gets stuffed into PL_sublex_info.repl. The '"'-handling code in yylex then checks PL_lex_stuff to see whether it needs to do double-quote handling. Since PL_lex_stuff contains the empty string from m ss it concludes that this is simply a "" constant, which gets emitted. We continue parsing line 3, till we get to the /x/, which requires an inner quote-handling scope. ‘x’ is written to PL_lex_stuff. PL_sublex_info.repl is still set from before ("$b\n"), which is placed in PL_lex_repl, so when we finish handling the pattern, S_sublex_done sees that there is a replacement still, which fails an assertion because a match cannot have a replacement part. Knowing this much, I can reduce it further, and we can trigger the bug even without the "$a{/" hack, which means we have (at least) two separate bugs here: $ ./miniperl -e '"$a{ 1 m// }"; //' Assertion failed: (PL_lex_inwhat == OP_SUBST || PL_lex_inwhat == OP_TRANS), function S_sublex_done, file toke.c, line 2430. Abort trap: 6 Localising PL_lex_stuff will probably fix this last example. -- Father Chrysostomos
RT-Send-CC: perl5-porters [...] perl.org
Download (untitled) / with headers
text/plain 210b
I have fixed the crash in eabab8bcc. The oddity with nested quote-like operators overlapping I have left for now, since it is necessary to track down another bug, and it is harmless. -- Father Chrysostomos
Subject: Your ticket against Perl 5 has been resolved
Download (untitled) / with headers
text/plain 263b
Thanks for submitting this ticket The issue should be resolved with the release today of Perl v5.22, available at http://www.perl.org/get.html If you find that the problem persists, feel free to reopen this ticket -- Karl Williamson for the Perl 5 porters team


This service is sponsored and maintained by Best Practical Solutions and runs on Perl.org infrastructure.

For issues related to this RT instance (aka "perlbug"), please contact perlbug-admin at perl.org