Skip Menu |
Report information

To: perlbug [...] perl.org
From: Brian Carpenter <brian.carpenter [...] gmail.com>
Subject: Segmentation Fault (possible stack overflow) while fuzzing Perl 5.21.8 binary
Date: Mon, 5 Jan 2015 12:22:51 -0600
Download (untitled) / with headers
text/plain 12.1k
Download (untitled) / with headers
text/html 49.3k

Message body is not shown because it is too large.

Download core.gz
application/x-gzip 58.8k

Message body not shown because it is not plain text.

Message body not shown because it is not plain text.

Subject: 7-byte file causes Perl 5.21.8 to segfault
From: Brian Carpenter <brian.carpenter [...] gmail.com>
To: perlbug [...] perl.org
Date: Mon, 5 Jan 2015 22:23:06 -0600
Download (untitled) / with headers
text/plain 11.8k
Download (untitled) / with headers
text/html 13.4k
I'm still fuzzing a Perl binary that I built from git source on 01/02/2015
using the afl-gcc (http://lcamtuf.coredump.cx/afl/) compiler:

CC=/path/to/afl-gcc ./Configure
AFL_HARDEN=1 make

This is perl 5, version 21, subversion 8 (v5.21.8 (v5.21.7-209-g4e27940))
built for x86_64-linux

Besides the above information, this version of Perl was compiled using all
defaults (enter was pressed for every question).

While fuzzing the perl binary, I found a 7-byte test case which causes a
segfault (and possible stack overflow).

geeknik@deb7fuzz:~/findings/perl/fuzzer04/crashes$ /home/geeknik/perl5/perl id\:000291\,sig\:11\,src\:008346\,op\:havoc\,rep\:16
Segmentation fault (core dumped)

geeknik@deb7fuzz:~/findings/perl/fuzzer04/crashes$ valgrind -q /home/geeknik/perl5/perl id\:000291\,sig\:11\,src\:008346\,op\:havoc\,rep\:16
==58205== Invalid write of size 1
==58205==    at 0x4C2A7C8: memcpy (mc_replace_strmem.c:838)
==58205==    by 0x6DDF9A: Perl_repeatcpy (string3.h:52)
==58205==    by 0x84DF7B: Perl_pp_repeat (pp.c:1759)
==58205==    by 0x77B87A: Perl_runops_standard (run.c:41)
==58205==    by 0x45AC0D: S_fold_constants (op.c:4343)
==58205==    by 0x5CA050: Perl_yyparse (perly.y:769)
==58205==    by 0x4F3114: perl_parse (perl.c:2273)
==58205==    by 0x42A92B: main (perlmain.c:114)
==58205==  Address 0x5edc1a1 is 7 bytes after a block of size 10 alloc'd
==58205==    at 0x4C28BED: malloc (vg_replace_malloc.c:263)
==58205==    by 0x6CEFBC: Perl_safesysmalloc (util.c:144)
==58205==    by 0x7EDB8F: Perl_sv_grow (sv.c:1623)
==58205==    by 0x7FCCBF: Perl_sv_2pv_flags (sv.c:3095)
==58205==    by 0x81754D: Perl_sv_pvn_force_flags (sv.c:9861)
==58205==    by 0x84E526: Perl_pp_repeat (pp.c:1748)
==58205==    by 0x77B87A: Perl_runops_standard (run.c:41)
==58205==    by 0x45AC0D: S_fold_constants (op.c:4343)
==58205==    by 0x5CA050: Perl_yyparse (perly.y:769)
==58205==    by 0x4F3114: perl_parse (perl.c:2273)
==58205==    by 0x42A92B: main (perlmain.c:114)
==58205==
==58205== Invalid write of size 2
==58205==    at 0x4C2A846: memcpy (mc_replace_strmem.c:838)
==58205==    by 0x6DDF9A: Perl_repeatcpy (string3.h:52)
==58205==    by 0x84DF7B: Perl_pp_repeat (pp.c:1759)
==58205==    by 0x77B87A: Perl_runops_standard (run.c:41)
==58205==    by 0x45AC0D: S_fold_constants (op.c:4343)
==58205==    by 0x5CA050: Perl_yyparse (perly.y:769)
==58205==    by 0x4F3114: perl_parse (perl.c:2273)
==58205==    by 0x42A92B: main (perlmain.c:114)
==58205==  Address 0x5edc19e is 4 bytes after a block of size 10 alloc'd
==58205==    at 0x4C28BED: malloc (vg_replace_malloc.c:263)
==58205==    by 0x6CEFBC: Perl_safesysmalloc (util.c:144)
==58205==    by 0x7EDB8F: Perl_sv_grow (sv.c:1623)
==58205==    by 0x7FCCBF: Perl_sv_2pv_flags (sv.c:3095)
==58205==    by 0x81754D: Perl_sv_pvn_force_flags (sv.c:9861)
==58205==    by 0x84E526: Perl_pp_repeat (pp.c:1748)
==58205==    by 0x77B87A: Perl_runops_standard (run.c:41)
==58205==    by 0x45AC0D: S_fold_constants (op.c:4343)
==58205==    by 0x5CA050: Perl_yyparse (perly.y:769)
==58205==    by 0x4F3114: perl_parse (perl.c:2273)
==58205==    by 0x42A92B: main (perlmain.c:114)
==58205==
==58205== Invalid read of size 1
==58205==    at 0x4C2A7C1: memcpy (mc_replace_strmem.c:838)
==58205==    by 0x6DDF9A: Perl_repeatcpy (string3.h:52)
==58205==    by 0x84DF7B: Perl_pp_repeat (pp.c:1759)
==58205==    by 0x77B87A: Perl_runops_standard (run.c:41)
==58205==    by 0x45AC0D: S_fold_constants (op.c:4343)
==58205==    by 0x5CA050: Perl_yyparse (perly.y:769)
==58205==    by 0x4F3114: perl_parse (perl.c:2273)
==58205==    by 0x42A92B: main (perlmain.c:114)
==58205==  Address 0x5edc1a1 is 7 bytes after a block of size 10 alloc'd
==58205==    at 0x4C28BED: malloc (vg_replace_malloc.c:263)
==58205==    by 0x6CEFBC: Perl_safesysmalloc (util.c:144)
==58205==    by 0x7EDB8F: Perl_sv_grow (sv.c:1623)
==58205==    by 0x7FCCBF: Perl_sv_2pv_flags (sv.c:3095)
==58205==    by 0x81754D: Perl_sv_pvn_force_flags (sv.c:9861)
==58205==    by 0x84E526: Perl_pp_repeat (pp.c:1748)
==58205==    by 0x77B87A: Perl_runops_standard (run.c:41)
==58205==    by 0x45AC0D: S_fold_constants (op.c:4343)
==58205==    by 0x5CA050: Perl_yyparse (perly.y:769)
==58205==    by 0x4F3114: perl_parse (perl.c:2273)
==58205==    by 0x42A92B: main (perlmain.c:114)
==58205==
==58205== Invalid read of size 8
==58205==    at 0x4C2A7E8: memcpy (mc_replace_strmem.c:838)
==58205==    by 0x6DDF9A: Perl_repeatcpy (string3.h:52)
==58205==    by 0x84DF7B: Perl_pp_repeat (pp.c:1759)
==58205==    by 0x77B87A: Perl_runops_standard (run.c:41)
==58205==    by 0x45AC0D: S_fold_constants (op.c:4343)
==58205==    by 0x5CA050: Perl_yyparse (perly.y:769)
==58205==    by 0x4F3114: perl_parse (perl.c:2273)
==58205==    by 0x42A92B: main (perlmain.c:114)
==58205==  Address 0x5edc198 is 8 bytes inside a block of size 10 alloc'd
==58205==    at 0x4C28BED: malloc (vg_replace_malloc.c:263)
==58205==    by 0x6CEFBC: Perl_safesysmalloc (util.c:144)
==58205==    by 0x7EDB8F: Perl_sv_grow (sv.c:1623)
==58205==    by 0x7FCCBF: Perl_sv_2pv_flags (sv.c:3095)
==58205==    by 0x81754D: Perl_sv_pvn_force_flags (sv.c:9861)
==58205==    by 0x84E526: Perl_pp_repeat (pp.c:1748)
==58205==    by 0x77B87A: Perl_runops_standard (run.c:41)
==58205==    by 0x45AC0D: S_fold_constants (op.c:4343)
==58205==    by 0x5CA050: Perl_yyparse (perly.y:769)
==58205==    by 0x4F3114: perl_parse (perl.c:2273)
==58205==    by 0x42A92B: main (perlmain.c:114)
==58205==
==58205== Invalid write of size 8
==58205==    at 0x4C2A7ED: memcpy (mc_replace_strmem.c:838)
==58205==    by 0x6DDF9A: Perl_repeatcpy (string3.h:52)
==58205==    by 0x84DF7B: Perl_pp_repeat (pp.c:1759)
==58205==    by 0x77B87A: Perl_runops_standard (run.c:41)
==58205==    by 0x45AC0D: S_fold_constants (op.c:4343)
==58205==    by 0x5CA050: Perl_yyparse (perly.y:769)
==58205==    by 0x4F3114: perl_parse (perl.c:2273)
==58205==    by 0x42A92B: main (perlmain.c:114)
==58205==  Address 0x5edc1a8 is 14 bytes after a block of size 10 alloc'd
==58205==    at 0x4C28BED: malloc (vg_replace_malloc.c:263)
==58205==    by 0x6CEFBC: Perl_safesysmalloc (util.c:144)
==58205==    by 0x7EDB8F: Perl_sv_grow (sv.c:1623)
==58205==    by 0x7FCCBF: Perl_sv_2pv_flags (sv.c:3095)
==58205==    by 0x81754D: Perl_sv_pvn_force_flags (sv.c:9861)
==58205==    by 0x84E526: Perl_pp_repeat (pp.c:1748)
==58205==    by 0x77B87A: Perl_runops_standard (run.c:41)
==58205==    by 0x45AC0D: S_fold_constants (op.c:4343)
==58205==    by 0x5CA050: Perl_yyparse (perly.y:769)
==58205==    by 0x4F3114: perl_parse (perl.c:2273)
==58205==    by 0x42A92B: main (perlmain.c:114)
==58205==
==58205== Invalid read of size 8
==58205==    at 0x4C2A7FA: memcpy (mc_replace_strmem.c:838)
==58205==    by 0x6DDF9A: Perl_repeatcpy (string3.h:52)
==58205==    by 0x84DF7B: Perl_pp_repeat (pp.c:1759)
==58205==    by 0x77B87A: Perl_runops_standard (run.c:41)
==58205==    by 0x45AC0D: S_fold_constants (op.c:4343)
==58205==    by 0x5CA050: Perl_yyparse (perly.y:769)
==58205==    by 0x4F3114: perl_parse (perl.c:2273)
==58205==    by 0x42A92B: main (perlmain.c:114)
==58205==  Address 0x5edc198 is 8 bytes inside a block of size 10 alloc'd
==58205==    at 0x4C28BED: malloc (vg_replace_malloc.c:263)
==58205==    by 0x6CEFBC: Perl_safesysmalloc (util.c:144)
==58205==    by 0x7EDB8F: Perl_sv_grow (sv.c:1623)
==58205==    by 0x7FCCBF: Perl_sv_2pv_flags (sv.c:3095)
==58205==    by 0x81754D: Perl_sv_pvn_force_flags (sv.c:9861)
==58205==    by 0x84E526: Perl_pp_repeat (pp.c:1748)
==58205==    by 0x77B87A: Perl_runops_standard (run.c:41)
==58205==    by 0x45AC0D: S_fold_constants (op.c:4343)
==58205==    by 0x5CA050: Perl_yyparse (perly.y:769)
==58205==    by 0x4F3114: perl_parse (perl.c:2273)
==58205==    by 0x42A92B: main (perlmain.c:114)
==58205==
==58205==
==58205== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==58205==  Access not within mapped region at address 0x62DC191
==58205==    at 0x4C2A7C8: memcpy (mc_replace_strmem.c:838)
==58205==    by 0x6DDF9A: Perl_repeatcpy (string3.h:52)
==58205==    by 0x84DF7B: Perl_pp_repeat (pp.c:1759)
==58205==    by 0x77B87A: Perl_runops_standard (run.c:41)
==58205==    by 0x45AC0D: S_fold_constants (op.c:4343)
==58205==    by 0x5CA050: Perl_yyparse (perly.y:769)
==58205==    by 0x4F3114: perl_parse (perl.c:2273)
==58205==    by 0x42A92B: main (perlmain.c:114)
==58205==  If you believe this happened as a result of a stack
==58205==  overflow in your program's main thread (unlikely but
==58205==  possible), you can try to increase the size of the
==58205==  main thread stack using the --main-stacksize= flag.
==58205==  The main thread stack size used in this run was 8388608.
Segmentation fault

geeknik@deb7fuzz:~/findings/perl/fuzzer04/crashes$ gdb /home/geeknik/perl5/perl core
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/geeknik/perl5/perl...done.
[New LWP 52892]

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/perl id:000291,sig:11,src:008346,op:havoc,rep:16'.
Program terminated with signal 11, Segmentation fault.
#0  __memcpy_ssse3_back ()
    at ../sysdeps/x86_64/multiarch/memcpy-ssse3-back.S:1664
1664 ../sysdeps/x86_64/multiarch/memcpy-ssse3-back.S: No such file or directory.
(gdb) bt
#0  __memcpy_ssse3_back ()
    at ../sysdeps/x86_64/multiarch/memcpy-ssse3-back.S:1664
#1  0x00000000006ddf9b in memcpy (__len=131072, __src=0x2304502,
    __dest=<optimized out>) at /usr/include/x86_64-linux-gnu/bits/string3.h:52
#2  Perl_repeatcpy (to=0x2304502 '3' <repeats 200 times>...,
    from=0x2304500 '3' <repeats 200 times>..., len=2,
    count=9223372036854775806) at util.c:3107
#3  0x000000000084df7c in Perl_pp_repeat () at pp.c:1759
#4  0x000000000077b87b in Perl_runops_standard () at run.c:41
#5  0x000000000045ac0e in S_fold_constants (o=0x23043f0) at op.c:4343
#6  0x00000000005ca051 in Perl_yyparse (gramtype=<optimized out>)
    at perly.y:769
#7  0x00000000004f3115 in S_parse_body (xsinit=0x42ad20 <xs_init>, env=0x0)
    at perl.c:2273
#8  perl_parse (my_perl=<optimized out>, xsinit=0x42ad20 <xs_init>,
    argc=<optimized out>, argv=<optimized out>, env=0x0) at perl.c:1607
#9  0x000000000042a92c in main (argc=2, argv=0x7fffa43513e8,
    env=0x7fffa4351400) at perlmain.c:114
#10 0x00007fef77441ead 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=0x7fffa43513d8)
    at libc-start.c:244
#11 0x000000000042ac45 in _start ()
(gdb) i r
rax            0x2324502 36848898
rbx            0x2344502 36979970
rcx            0x1fffe 131070
rdx            0x1fefe 130814
rsi            0x2324500 36848896
rdi            0x2344500 36979968
rbp            0x2304502 0x2304502
rsp            0x7fffa4350cf8 0x7fffa4350cf8
r8             0x23444f2 36979954
r9             0x2 2
r10            0x2304508 36717832
r11            0x7fef775793f0 140666476139504
r12            0x2 2
r13            0x20000 131072
r14            0x3fffffffffffffff 4611686018427387903
r15            0x20000 131072
rip            0x7fef7754e5f8 0x7fef7754e5f8 <__memcpy_ssse3_back+7016>
eflags         0x10202 [ IF RF ]
cs             0x33 51
ss             0x2b 43
ds             0x0 0
es             0x0 0
fs             0x0 0
gs             0x0 0
(gdb)

hexdump output of the test case file:
0000000 3333 7e78 3b33 000a                    
0000007

similar test cases that also cause a segfault:
0000000 3333 7e78 3b33 0a0a                    
0000008

0000000 3333 7e78 3b33                        
0000006
Download 7bytecrash.gz
application/x-gzip 41b

Message body not shown because it is not plain text.

Download core.gz
application/x-gzip 56.1k

Message body not shown because it is not plain text.

RT-Send-CC: perl5-porters [...] perl.org
Download (untitled) / with headers
text/plain 892b
On Mon Jan 05 20:24:11 2015, brian.carpenter@gmail.com wrote: Show quoted text
> I'm still fuzzing a Perl binary that I built from git source on 01/02/2015 > using the afl-gcc (http://lcamtuf.coredump.cx/afl/) compiler: > > CC=/path/to/afl-gcc ./Configure > AFL_HARDEN=1 make > > This is perl 5, version 21, subversion 8 (v5.21.8 (v5.21.7-209-g4e27940)) > built for x86_64-linux > > Besides the above information, this version of Perl was compiled using all > defaults (enter was pressed for every question). > > While fuzzing the perl binary, I found a 7-byte test case which causes a > segfault (and possible stack overflow).
Nice bug! $ perl5.12 -e '33x~3' Out of memory! $ perl5.14.4 -e '33x~3' panic: memory wrap at -e line 1. $ perl5.18.3 -e '33x~3' Out of memory! panic: fold_constants JMPENV_PUSH returned 2 at -e line 1. $ perl5.20.1 -e '33x~3' Segmentation fault: 11 -- Father Chrysostomos
RT-Send-CC: perl5-porters [...] perl.org
On Mon Jan 05 20:24:11 2015, brian.carpenter@gmail.com wrote: Show quoted text
> I'm still fuzzing a Perl binary that I built from git source on 01/02/2015 > using the afl-gcc (http://lcamtuf.coredump.cx/afl/) compiler: > > CC=/path/to/afl-gcc ./Configure > AFL_HARDEN=1 make
This doesn't need afl-gcc to reproduce: $ ./perl -e '33x~3' Segmentation fault This is caused by pp_repeat passing ((size_t)~0) to SvGROW() and then Perl_sv_grow() bumps that up by 1 (per cbcb2a1) to allocate a byte for a COW count. Unfortunately ((size_t)~0)+1 is 0, so Perl_sv_grow() does nothing, and the string is repeated into the original buffer. The following prevents the segfault and produces the expected panic instead: diff --git a/sv.c b/sv.c index fe092c4..1a28368 100644 --- a/sv.c +++ b/sv.c @@ -1596,7 +1596,8 @@ Perl_sv_grow(pTHX_ SV *const sv, STRLEN newlen) * caller wanted a nice 2^N sized block and will be annoyed at getting * 2^N+1 */ if (newlen & 0xff) - newlen++; + if (!++newlen) + --newlen; #endif #if defined(PERL_USE_MALLOC_SIZE) && defined(Perl_safesysmalloc_size) Tony
RT-Send-CC: perl5-porters [...] perl.org
Download (untitled) / with headers
text/plain 875b
On Mon Jan 05 21:58:35 2015, tonyc wrote: Show quoted text
> On Mon Jan 05 20:24:11 2015, brian.carpenter@gmail.com wrote:
> > I'm still fuzzing a Perl binary that I built from git source on > > 01/02/2015 > > using the afl-gcc (http://lcamtuf.coredump.cx/afl/) compiler: > > > > CC=/path/to/afl-gcc ./Configure > > AFL_HARDEN=1 make
> > This doesn't need afl-gcc to reproduce: > > $ ./perl -e '33x~3' > Segmentation fault > > This is caused by pp_repeat passing ((size_t)~0) to SvGROW() and then > Perl_sv_grow() bumps that up by 1 (per cbcb2a1) to allocate a byte for > a COW count. Unfortunately ((size_t)~0)+1 is 0, so Perl_sv_grow() > does nothing, and the string is repeated into the original buffer. > > The following prevents the segfault and produces the expected panic > instead:
Is a panic really expected? 5.12 just died with ‘Out of memory!’ -- Father Chrysostomos
Date: Tue, 6 Jan 2015 06:39:02 +0000 (UTC)
To: perl5-porters [...] perl.org
From: Petr Pisar <ppisar [...] redhat.com>
Subject: Re: [perl #123554] 7-byte file causes Perl 5.21.8 to segfault
Download (untitled) / with headers
text/plain 1.4k
On 2015-01-06, Tony Cook via RT <perlbug-followup@perl.org> wrote: Show quoted text
> This is caused by pp_repeat passing ((size_t)~0) to SvGROW() and then > Perl_sv_grow() bumps that up by 1 (per cbcb2a1) to allocate a byte for > a COW count. Unfortunately ((size_t)~0)+1 is 0, so Perl_sv_grow() > does nothing, and the string is repeated into the original buffer. > > The following prevents the segfault and produces the expected panic > instead: > > diff --git a/sv.c b/sv.c > index fe092c4..1a28368 100644 > --- a/sv.c > +++ b/sv.c > @@ -1596,7 +1596,8 @@ Perl_sv_grow(pTHX_ SV *const sv, STRLEN newlen) > * caller wanted a nice 2^N sized block and will be annoyed at getting > * 2^N+1 */ > if (newlen & 0xff) > - newlen++; > + if (!++newlen) > + --newlen; > #endif > > #if defined(PERL_USE_MALLOC_SIZE) && defined(Perl_safesysmalloc_size) >
newlen's type is STRLEN. STRLEN is defined as MEM_SIZE and MEM_SIZE is defines as Size_t. My config.h says: /* Size_t: * This symbol holds the type used to declare length parameters * for string functions. It is usually size_t, but may be * unsigned long, int, etc. It may be necessary to include * <sys/types.h> to get any typedef'ed information. */ So it can be a signed integer. And overflow of signed variables is undefined in C language. Therefore instead of the undefined behavior, (!++newlen), you should compare against maximal value (MEM_SIZE_MAX). -- Petr
Date: Tue, 6 Jan 2015 17:57:00 +1100
CC: perl5-porters [...] perl.org
Subject: Re: [perl #123554] 7-byte file causes Perl 5.21.8 to segfault
From: Tony Cook <tony [...] develop-help.com>
To: Petr Pisar <ppisar [...] redhat.com>
Download (untitled) / with headers
text/plain 1.8k
On Tue, Jan 06, 2015 at 06:39:02AM +0000, Petr Pisar wrote: Show quoted text
> On 2015-01-06, Tony Cook via RT <perlbug-followup@perl.org> wrote:
> > This is caused by pp_repeat passing ((size_t)~0) to SvGROW() and then > > Perl_sv_grow() bumps that up by 1 (per cbcb2a1) to allocate a byte for > > a COW count. Unfortunately ((size_t)~0)+1 is 0, so Perl_sv_grow() > > does nothing, and the string is repeated into the original buffer. > > > > The following prevents the segfault and produces the expected panic > > instead: > > > > diff --git a/sv.c b/sv.c > > index fe092c4..1a28368 100644 > > --- a/sv.c > > +++ b/sv.c > > @@ -1596,7 +1596,8 @@ Perl_sv_grow(pTHX_ SV *const sv, STRLEN newlen) > > * caller wanted a nice 2^N sized block and will be annoyed at getting > > * 2^N+1 */ > > if (newlen & 0xff) > > - newlen++; > > + if (!++newlen) > > + --newlen; > > #endif > > > > #if defined(PERL_USE_MALLOC_SIZE) && defined(Perl_safesysmalloc_size) > >
> newlen's type is STRLEN. STRLEN is defined as MEM_SIZE and MEM_SIZE is > defines as Size_t. My config.h says: > > /* Size_t: > * This symbol holds the type used to declare length parameters > * for string functions. It is usually size_t, but may be > * unsigned long, int, etc. It may be necessary to include > * <sys/types.h> to get any typedef'ed information. > */ > > So it can be a signed integer. And overflow of signed variables is > undefined in C language. Therefore instead of the undefined behavior, > (!++newlen), you should compare against maximal value (MEM_SIZE_MAX).
If available, Size_t is ANSI C89 size_t which is required to be unsigned. You may be reading the (unlikely) alternatives incorrectly, they are unsigned long, unsigned int, etc. MEM_SIZE_MAX is defined as ((MEM_SIZE)~0) (aka ((Size_t)~0) and doesn't attempt to account for a signed Size_t. Tony
CC: perl5-porters [...] perl.org
Date: Tue, 6 Jan 2015 18:03:48 +1100
To: Father Chrysostomos via RT <perlbug-followup [...] perl.org>
From: Tony Cook <tony [...] develop-help.com>
Subject: Re: [perl #123554] 7-byte file causes Perl 5.21.8 to segfault
Download (untitled) / with headers
text/plain 1.3k
On Mon, Jan 05, 2015 at 10:32:34PM -0800, Father Chrysostomos via RT wrote: Show quoted text
> On Mon Jan 05 21:58:35 2015, tonyc wrote:
> > On Mon Jan 05 20:24:11 2015, brian.carpenter@gmail.com wrote:
> > > I'm still fuzzing a Perl binary that I built from git source on > > > 01/02/2015 > > > using the afl-gcc (http://lcamtuf.coredump.cx/afl/) compiler: > > > > > > CC=/path/to/afl-gcc ./Configure > > > AFL_HARDEN=1 make
> > > > This doesn't need afl-gcc to reproduce: > > > > $ ./perl -e '33x~3' > > Segmentation fault > > > > This is caused by pp_repeat passing ((size_t)~0) to SvGROW() and then > > Perl_sv_grow() bumps that up by 1 (per cbcb2a1) to allocate a byte for > > a COW count. Unfortunately ((size_t)~0)+1 is 0, so Perl_sv_grow() > > does nothing, and the string is repeated into the original buffer. > > > > The following prevents the segfault and produces the expected panic > > instead:
> > Is a panic really expected? 5.12 just died with ‘Out of memory!’
5.14 does the panic: $ perl -v | grep subversion This is perl 5, version 14, subversion 2 (v5.14.2) built for x86_64-linux-gnu-thread-multi $ perl -e '33x~3' panic: memory wrap at -e line 1. While it says it's a panic, it's a croak, so it can be captured by eval. The "Out of memory!" message is simple write() to stderr and a hard exit(1), so can't be captured. Tony
RT-Send-CC: perl5-porters [...] perl.org
Download (untitled) / with headers
text/plain 1.3k
On Mon Jan 05 21:58:35 2015, tonyc wrote: Show quoted text
> On Mon Jan 05 20:24:11 2015, brian.carpenter@gmail.com wrote:
> > I'm still fuzzing a Perl binary that I built from git source on > > 01/02/2015 > > using the afl-gcc (http://lcamtuf.coredump.cx/afl/) compiler: > > > > CC=/path/to/afl-gcc ./Configure > > AFL_HARDEN=1 make
> > This doesn't need afl-gcc to reproduce: > > $ ./perl -e '33x~3' > Segmentation fault > > This is caused by pp_repeat passing ((size_t)~0) to SvGROW() and then > Perl_sv_grow() bumps that up by 1 (per cbcb2a1) to allocate a byte for > a COW count. Unfortunately ((size_t)~0)+1 is 0, so Perl_sv_grow() > does nothing, and the string is repeated into the original buffer. > > The following prevents the segfault and produces the expected panic > instead...
I've attached a format-patch patch instead, which directly checks against MEM_SIZE_MAX instead, rather than checking for the overflow. I considered making the comparison C<< newlen < MEM_SIZE_MAX >> instead, which produced the same code, in case (somehow!) MEM_SIZE/STRLEN/Size_t ended up signed, but since that would break MEM_SIZE_MAX anyway I though it would be slightly more confusing. As to the test, since we're allocating (address_space_size-1) bytes, the result creation should fail before any memory is allocated, so this isn't going to cause smokers (or other perl test runs) to attempt to allocate stupidly large amounts of space. Tony
Subject: 0001-perl-123554-avoid-a-crash-from-SvGROW-MEM_SIZE_MAX.patch
From 9a747d06187928e4f40916f7ecf1328aef95f84c Mon Sep 17 00:00:00 2001 From: Tony Cook <tony@develop-help.com> Date: Wed, 7 Jan 2015 11:09:15 +1100 Subject: [PATCH] [perl #123554] avoid a crash from SvGROW(MEM_SIZE_MAX) --- sv.c | 7 +++++-- t/op/repeat.t | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/sv.c b/sv.c index fe092c4..11f8499 100644 --- a/sv.c +++ b/sv.c @@ -1594,8 +1594,11 @@ Perl_sv_grow(pTHX_ SV *const sv, STRLEN newlen) * make more strings COW-able. * If the new size is a big power of two, don't bother: we assume the * caller wanted a nice 2^N sized block and will be annoyed at getting - * 2^N+1 */ - if (newlen & 0xff) + * 2^N+1. + * Only increment if the allocation isn't MEM_SIZE_MAX, + * otherwise it will wrap to 0. + */ + if (newlen & 0xff && newlen != MEM_SIZE_MAX) newlen++; #endif diff --git a/t/op/repeat.t b/t/op/repeat.t index 8df5241..421b705 100644 --- a/t/op/repeat.t +++ b/t/op/repeat.t @@ -6,7 +6,7 @@ BEGIN { } require './test.pl'; -plan(tests => 47); +plan(tests => 49); # compile time @@ -173,3 +173,7 @@ for(($#that_array)x2) { $_ *= 2; } is($#that_array, 28, 'list repetition propagates lvalue cx to its lhs'); + +# see [perl #123554] +ok(!eval '33x~3', "eval 33x~3 should panic, not crash perl"); +like($@, qr/^panic: memory wrap/, "check it's a panic"); -- 1.7.10.4
RT-Send-CC: perl5-porters [...] perl.org
Download (untitled) / with headers
text/plain 340b
On Mon Jan 05 10:23:49 2015, brian.carpenter@gmail.com wrote: Show quoted text
> I'm still fuzzing a Perl binary that I built from git source on 01/02/2015 > using the afl-gcc compiler: > > CC=/path/to/afl-gcc ./Configure > AFL_HARDEN=1 make
This particular test case: 3333333333333333333333333333333333333;33x~3333333333epl`t(/&/"oˇˇer web encodˇˇÄ
RT-Send-CC: perl5-porters [...] perl.org
Download (untitled) / with headers
text/plain 712b
On Wed Jan 07 03:50:46 2015, sprout wrote: Show quoted text
> On Mon Jan 05 10:23:49 2015, brian.carpenter@gmail.com wrote:
> > I'm still fuzzing a Perl binary that I built from git source on > > 01/02/2015 > > using the afl-gcc compiler: > > > > CC=/path/to/afl-gcc ./Configure > > AFL_HARDEN=1 make
> > This particular test case: > > 3333333333333333333333333333333333333;33x~3333333333epl`t(/&/"oˇˇer > web encodˇˇÄ
Something didn’t like the nulls that I unwittingly copied and pasted into the browser. So the message got cut off. This particular test case fails for the same reason as #123554. The syntax error makes no difference, because the 33x~3333333... is folded at compile time. -- Father Chrysostomos
RT-Send-CC: perl5-porters [...] perl.org
Download (untitled) / with headers
text/plain 1.5k
On Tue Jan 06 16:23:36 2015, tonyc wrote: Show quoted text
> On Mon Jan 05 21:58:35 2015, tonyc wrote:
> > On Mon Jan 05 20:24:11 2015, brian.carpenter@gmail.com wrote:
> > > I'm still fuzzing a Perl binary that I built from git source on > > > 01/02/2015 > > > using the afl-gcc (http://lcamtuf.coredump.cx/afl/) compiler: > > > > > > CC=/path/to/afl-gcc ./Configure > > > AFL_HARDEN=1 make
> > > > This doesn't need afl-gcc to reproduce: > > > > $ ./perl -e '33x~3' > > Segmentation fault > > > > This is caused by pp_repeat passing ((size_t)~0) to SvGROW() and then > > Perl_sv_grow() bumps that up by 1 (per cbcb2a1) to allocate a byte > > for > > a COW count. Unfortunately ((size_t)~0)+1 is 0, so Perl_sv_grow() > > does nothing, and the string is repeated into the original buffer. > > > > The following prevents the segfault and produces the expected panic > > instead...
> > I've attached a format-patch patch instead, which directly checks > against MEM_SIZE_MAX instead, rather than checking for the overflow. > > I considered making the comparison C<< newlen < MEM_SIZE_MAX >> > instead, which produced the same code, in case (somehow!) > MEM_SIZE/STRLEN/Size_t ended up signed, but since that would break > MEM_SIZE_MAX anyway I though it would be slightly more confusing. > > As to the test, since we're allocating (address_space_size-1) bytes, > the result creation should fail before any memory is allocated, so > this isn't going to cause smokers (or other perl test runs) to attempt > to allocate stupidly large amounts of space.
Applied as fa8f4f85ec35b0f047603e5b90a788571f7141bd. Tony
Date: Mon, 2 Feb 2015 10:24:57 -0600
From: Brian Carpenter <brian.carpenter [...] gmail.com>
To: perlbug [...] perl.org
Subject: Segfault in multiple versions of Perl5
Download (untitled) / with headers
text/plain 4.9k
I'm still attacking Perl5 with american fuzzy lop (http://lcamtuf.coredump.cx/afl/). 

CC=/path/to/afl-gcc ./Configure
AFL_HARDEN=1 make

This is perl 5, version 21, subversion 9 (v5.21.9 (v5.21.8-79-g4932eec)) built for x86_64-linux

Perl was compiled using all defaults (except adding -g to the CFLAGS).

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x2 
RCX: 0x1 
RDX: 0x2 
RSI: 0x12 
RDI: 0x0 
RBP: 0x2 
RSP: 0x7fffffffdcb0 --> 0x100 
RIP: 0x6e5a1c (<Perl_repeatcpy+2180>: movzx  edx,BYTE PTR [r9])
R8 : 0x0 
R9 : 0x0 
R10: 0x2 
R11: 0x0 
R12: 0x2 
R13: 0x101 
R14: 0x0 
R15: 0x4
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x6e5a0b <Perl_repeatcpy+2163>: mov    rax,QWORD PTR [rsp+0x10]
   0x6e5a10 <Perl_repeatcpy+2168>: lea    rsp,[rsp+0x98]
   0x6e5a18 <Perl_repeatcpy+2176>: lea    rcx,[rbp-0x1]
=> 0x6e5a1c <Perl_repeatcpy+2180>: movzx  edx,BYTE PTR [r9]
   0x6e5a20 <Perl_repeatcpy+2184>: lea    rax,[r9+0x1]
   0x6e5a24 <Perl_repeatcpy+2188>: mov    rsi,rcx
   0x6e5a27 <Perl_repeatcpy+2191>: and    esi,0x7
   0x6e5a2a <Perl_repeatcpy+2194>: test   rcx,rcx
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdcb0 --> 0x100 
0008| 0x7fffffffdcb8 --> 0x7ffffffffffffffe 
0016| 0x7fffffffdcc0 --> 0x0 
0024| 0x7fffffffdcc8 --> 0x2 
0032| 0x7fffffffdcd0 --> 0x10 
0040| 0x7fffffffdcd8 --> 0x2 
0048| 0x7fffffffdce0 --> 0xe4cfe8 --> 0xe4cfd8 --> 0x440300000001 
0056| 0x7fffffffdce8 --> 0x603d63bf155a1200 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00000000006e5a1c in Perl_repeatcpy (to=0x2 <Address 0x2 out of bounds>, from=0x0, len=0x2, count=0x7ffffffffffffffe) at util.c:3086
3086 *p++ = *q++;

gdb-peda$ bt
#0  0x00000000006e5a1c in Perl_repeatcpy (to=0x2 <Address 0x2 out of bounds>, from=0x0, len=0x2, count=0x7ffffffffffffffe) at util.c:3086
#1  0x0000000000854eac in Perl_pp_repeat () at pp.c:1768
#2  0x0000000000782f7b in Perl_runops_standard () at run.c:41
#3  0x000000000046012e in S_fold_constants (o=0xe58420) at op.c:4332
#4  0x00000000005c94f9 in Perl_yyparse (gramtype=<optimized out>) at perly.y:797
#5  0x00000000004f2615 in S_parse_body (xsinit=0x42a850 <xs_init>, env=0x0) at perl.c:2273
#6  perl_parse (my_perl=<optimized out>, xsinit=0x42a850 <xs_init>, argc=<optimized out>, argv=<optimized out>, env=0x0) at perl.c:1607
#7  0x000000000042a45c in main (argc=0x2, argv=0x7fffffffe398, env=0x7fffffffe3b0) at perlmain.c:114
#8  0x00007ffff6f97ead 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=0x7fffffffe388) at libc-start.c:244
#9  0x000000000042a775 in _start ()

gdb-peda$ exploit
Description: Access violation near NULL on source operand
Short description: SourceAvNearNull (16/22)
Hash: 000c219cf66df9b6d330542118bb3e1e.8e1055cedb39ef6cf4cbc49a4c29dd29
Exploitability Classification: PROBABLY_NOT_EXPLOITABLE
Explanation: The target crashed on an access violation at an address matching the source operand of the current instruction. This likely indicates a read access violation, which may mean the application crashed on a simple NULL dereference to data structure that has no immediate effect on control of the processor.
Other tags: AccessViolation (21/22)

I noticed that this same test case crashes Perl 5.21.7 as well (v5.21.6-602-ge9d2bd8) but with a little different backtrace:

#0  __memcpy_ssse3_back () at ../sysdeps/x86_64/multiarch/memcpy-ssse3-back.S:1664
#1  0x00000000006d8d0b in memcpy (__len=0x20000, __src=0xe46432, __dest=<optimized out>) at /usr/include/x86_64-linux-gnu/bits/string3.h:52
#2  Perl_repeatcpy (to=0xe46432 '6' <repeats 200 times>..., from=0xe46430 '6' <repeats 200 times>..., len=0x2, count=0x7ffffffffffffffe) at util.c:3092
#3  0x000000000084742c in Perl_pp_repeat () at pp.c:1749
#4  0x0000000000775a6b in Perl_runops_standard () at run.c:41
#5  0x000000000045a3ee in S_fold_constants (o=0xe46320) at op.c:4337
#6  0x00000000005c7321 in Perl_yyparse (gramtype=<optimized out>) at perly.y:769
#7  0x00000000004f0875 in S_parse_body (xsinit=0x42ac70 <xs_init>, env=0x0) at perl.c:2271
#8  perl_parse (my_perl=<optimized out>, xsinit=0x42ac70 <xs_init>, argc=<optimized out>, argv=<optimized out>, env=0x0) at perl.c:1605
#9  0x000000000042a87c in main (argc=0x2, argv=0x7fffffffe398, env=0x7fffffffe3b0) at perlmain.c:114
#10 0x00007ffff6f97ead 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=0x7fffffffe388) at libc-start.c:244
#11 0x000000000042ab95 in _start ()

The test case is a bit similar to the one in #123551, but the gdb output is a bit different.
Download perl5-crash.gz
application/x-gzip 82b

Message body not shown because it is not plain text.

RT-Send-CC: perl5-porters [...] perl.org
Download (untitled) / with headers
text/plain 806b
On Mon Feb 02 08:25:53 2015, brian.carpenter@gmail.com wrote: Show quoted text
> I'm still attacking Perl5 with american fuzzy lop ( > http://lcamtuf.coredump.cx/afl/). > > CC=/path/to/afl-gcc ./Configure > AFL_HARDEN=1 make
I can't reproduce this in blead @ v5.21.8-186-g533686c (using afl-gcc): ./Configure -des -Dusedevel -DDEBUGGING -Dcc=afl-gcc -Doptimize=-O2\ -g && AFL_HARDEN=1 make -j6 test-prep $ AFL_HARDEN=1 valgrind -q ./perl ../123717.pl Number found where operator expected at ../123717.pl line 1, near "--031" (Missing operator before 031?) Can't modify constant item in postdecrement (--) at ../123717.pl line 1, near " Z6xx--" syntax error at ../123717.pl line 1, near "--031" Unrecognized character \xB1; marked by <-- HERE after Z6xx--031<-- HERE near column 18 at ../123717.pl line 1. Tony
Download (untitled) / with headers
text/plain 162b
Well that is what I get for using an outdated github repo. I've got this box pointed at the right repo now. We can probably close this if nobody can reproduce it.
RT-Send-CC: perl5-porters [...] perl.org
Download (untitled) / with headers
text/plain 812b
On Mon Feb 02 16:13:02 2015, brian.carpenter@gmail.com wrote: Show quoted text
> Well that is what I get for using an outdated github repo. I've got > this box pointed at the right repo now. We can probably close this if > nobody can reproduce it.
This appears to have been fixed in passing by: commit fa8f4f85ec35b0f047603e5b90a788571f7141bd Author: Tony Cook <tony@develop-help.com> Date: Mon Jan 19 13:31:26 2015 +1100 [perl #123554] avoid a crash from SvGROW(MEM_SIZE_MAX) (Perhaps these tickets should be linked?) This appears to have been broken only recently - in perl-5.19.1, from: commit cbcb2a16c528e483a6c8804d2d2daefecc1a0d3e Author: David Mitchell <davem@iabyn.com> Date: Sat Jun 8 18:24:51 2013 +0100 add 1 to SvGROW under COW (and fix svleak.t) [...] -- Matthew Horsfall (alh)
RT-Send-CC: perl5-porters [...] perl.org
Download (untitled) / with headers
text/plain 1.9k
On Sun Jan 18 20:48:57 2015, tonyc wrote: Show quoted text
> On Tue Jan 06 16:23:36 2015, tonyc wrote:
> > On Mon Jan 05 21:58:35 2015, tonyc wrote:
> > > On Mon Jan 05 20:24:11 2015, brian.carpenter@gmail.com wrote:
> > > > I'm still fuzzing a Perl binary that I built from git source on > > > > 01/02/2015 > > > > using the afl-gcc (http://lcamtuf.coredump.cx/afl/) compiler: > > > > > > > > CC=/path/to/afl-gcc ./Configure > > > > AFL_HARDEN=1 make
> > > > > > This doesn't need afl-gcc to reproduce: > > > > > > $ ./perl -e '33x~3' > > > Segmentation fault > > > > > > This is caused by pp_repeat passing ((size_t)~0) to SvGROW() and then > > > Perl_sv_grow() bumps that up by 1 (per cbcb2a1) to allocate a byte > > > for > > > a COW count. Unfortunately ((size_t)~0)+1 is 0, so Perl_sv_grow() > > > does nothing, and the string is repeated into the original buffer. > > > > > > The following prevents the segfault and produces the expected panic > > > instead...
> > > > I've attached a format-patch patch instead, which directly checks > > against MEM_SIZE_MAX instead, rather than checking for the overflow. > > > > I considered making the comparison C<< newlen < MEM_SIZE_MAX >> > > instead, which produced the same code, in case (somehow!) > > MEM_SIZE/STRLEN/Size_t ended up signed, but since that would break > > MEM_SIZE_MAX anyway I though it would be slightly more confusing. > > > > As to the test, since we're allocating (address_space_size-1) bytes, > > the result creation should fail before any memory is allocated, so > > this isn't going to cause smokers (or other perl test runs) to attempt > > to allocate stupidly large amounts of space.
> > Applied as fa8f4f85ec35b0f047603e5b90a788571f7141bd.
With jhi's help, fixed a couple of other size overflows in 9efda33a86bb90e4838144d230a4fc3ae4d63d7d. Unfortunately, which perl isn't segfaulting, in some builds this falls back to the out of memory handler, which can't be caught by eval, so remove the test. Tony
Merge into [perl #123554], it's the same issue.
I think this is identical to [perl #123554], since the test case starts with '66x~6'. I'll merge.


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