Discussion:
[perl #125789] Perl segfaults while handling a syntax error, appears to be reusing an OP as an SV
Dan Collins (via RT)
2015-08-11 15:33:21 UTC
Permalink
# New Ticket Created by Dan Collins
# Please include the string: [perl #125789]
# in the subject line of all future correspondence about this issue.
# <URL: https://rt.perl.org/Ticket/Display.html?id=125789 >


Greetings,

While experimenting with the afl-gcc fuzzing utility, I located a very simple test case that causes perl to segfault without printing any errors:

$ perl ../test1.pl
Segmentation fault (core dumped)

$ perl -w ../test1.pl
Use of uninitialized value at ../test1.pl line 1.
Segmentation fault (core dumped)

The testcase reads as follows:

$ od -c ../test1.pl
0000000 B E G I N < > \n
0000010

NB: The newline is not strictly required to reproduce and is a consequence of me starting to debug this on one computer, and finishing on a different computer. I didn't realize the newline was there, but since it appears in the backtrace below, I didn't want to confuse you by removing it.

This is reproducible with perls at least as old as 5.14.4 through blead, and miniperl fails in the same manner. The segfault occurs in Perl_op_free, when attempting to free the sibling of the child of an OP, as evidenced below. Here is the stack trace for the segfault:

#0 0x000000010040c171 in Perl_op_free (o=0x600077a78) at op.c:761
#1 0x00000001005229de in Perl_leave_scope (base=39) at scope.c:986
#2 0x0000000100421331 in Perl_newATTRSUB_x (floor=39, o=0x6000777c8,
proto=0x600077a78, attrs=0x0, block=0x0, o_is_gv=false) at op.c:8828
#3 0x000000010046a20f in Perl_yyparse (gramtype=258) at perly.y:294
#4 0x0000000100404538 in S_parse_body (env=0x0, xsinit=0x10042f9e9 <xs_init>)
at perl.c:2296
#5 0x00000001004036d5 in perl_parse (my_perl=0x60003ab40,
xsinit=0x10042f9e9 <xs_init>, argc=2, argv=0x22aac0, env=0x0)
at perl.c:1626
#6 0x000000010042f93b in main (argc=2, argv=0x22aac0, env=0x6000281a0)
at miniperlmain.c:120

(gdb) l op.c:761
756 CALL_OPFREEHOOK(o);
757
758 if (o->op_flags & OPf_KIDS) {
759 OP *kid, *nextkid;
760 for (kid = cUNOPo->op_first; kid; kid = nextkid) {
761 nextkid = OpSIBLING(kid); /* Get before next freeing kid */
762 if (!kid || kid->op_type == OP_FREED)
763 /* During the forced freeing of ops after
764 compilation failure, kidops may be freed before
765 their parents. */

It appears that Perl_op_free is being called on an op which has a child that believes it has a sibling. The relevant op is created at:

#0 Perl_newUNOP (type=26, flags=0, first=0x600077ab8) at op.c:4783
#1 0x0000000100463834 in S_scan_inputsymbol (start=0x6000742c5 "<>\n")
at toke.c:9603
#2 0x000000010044f84f in Perl_yylex () at toke.c:5926
#3 0x00000001004697d2 in Perl_yyparse (gramtype=258) at perly.c:322
#4 0x0000000100404538 in S_parse_body (env=0x0, xsinit=0x10042f9e9 <xs_init>)
at perl.c:2296
#5 0x00000001004036d5 in perl_parse (my_perl=0x60003ab40,
xsinit=0x10042f9e9 <xs_init>, argc=2, argv=0x22aac0, env=0x0)
at perl.c:1626
#6 0x000000010042f93b in main (argc=2, argv=0x22aac0, env=0x6000281a0)
at miniperlmain.c:120

At the time of the crash, the following is true:

(gdb) p ((UNOP*)0x600077a78)->op_first
$32 = (OP *) 0x600077ab8
(gdb) p ((OP*)0x600077ab8)->op_sibling
$33 = (OP *) 0x300000000

The child's op_sibling becomes non-NULL at the following line:

1347 SvFLAGS(sv) |= new_type;
(gdb) p ((OP*)0x600077ab8)->op_sibling
$52 = (OP *) 0x0
(gdb) s
1352 switch (new_type) {
(gdb) p ((OP*)0x600077ab8)->op_sibling
$53 = (OP *) 0x300000000
(gdb) bt
#0 Perl_sv_upgrade (sv=0x600077ab8, new_type=SVt_PV) at sv.c:1352
#1 0x00000001004eb26d in Perl_sv_2pv_flags (sv=0x600077ab8, lp=0x22a168,
flags=34) at sv.c:3223
#2 0x000000010041f426 in Perl_newATTRSUB_x (floor=39, o=0x6000777c8,
proto=0x600077a78, attrs=0x0, block=0x0, o_is_gv=false) at op.c:8459
#3 0x000000010046a20f in Perl_yyparse (gramtype=258) at perly.y:294
#4 0x0000000100404538 in S_parse_body (env=0x0, xsinit=0x10042f9e9 <xs_init>)
at perl.c:2296
#5 0x00000001004036d5 in perl_parse (my_perl=0x60003ab40,
xsinit=0x10042f9e9 <xs_init>, argc=2, argv=0x22aac0, env=0x0)
at perl.c:1626
#6 0x000000010042f93b in main (argc=2, argv=0x22aac0, env=0x6000281a0)
at miniperlmain.c:120

This memory appears to be being used as both an OP and an SV. This appears to be problematic. See for example:
(gdb) p sv
$66 = (SV * const) 0x600077ab8
(gdb) p ((UNOP*)0x600077a78)->op_first # Some time in the future, this address will be passed to Perl_op_free
$67 = (OP *) 0x600077ab8
(gdb) p *sv
$68 = {sv_any = 0x600077ab8, sv_refcnt = 0, sv_flags = 3, sv_u = {
svu_pv = 0x1004d8257 <Perl_pp_gv> "UH\211\345H\203\354\060H\213\005\212%\022", svu_iv = 4300046935, svu_uv = 4300046935, svu_nv = 2.1245054660884477e-314,
svu_rv = 0x1004d8257 <Perl_pp_gv>, svu_rx = 0x1004d8257 <Perl_pp_gv>,
svu_array = 0x1004d8257 <Perl_pp_gv>, svu_hash = 0x1004d8257 <Perl_pp_gv>,
svu_gp = 0x1004d8257 <Perl_pp_gv>, svu_fp = 0x1004d8257 <Perl_pp_gv>}}
(gdb) p *(OP*)0x600077ab8
$69 = {op_next = 0x600077ab8, op_sibling = 0x300000000,
op_ppaddr = 0x1004d8257 <Perl_pp_gv>, op_targ = 0, op_type = 7, op_opt = 0,
op_slabbed = 1, op_savefree = 0, op_static = 0, op_folded = 0,
op_moresib = 0, op_spare = 0, op_flags = 2 '\002', op_private = 0 '\000'}

I'm not sure if this memory was freed somewhere and reused, and we have failed to null out ((UNOP*)0x600077a78)->op_first before using the memory it points to to store an SV, or if some other witchcraft has allowed this to occur, so I'll hand it off to you.


$ perl -V
Summary of my perl5 (revision 5 version 14 subversion 4) configuration:

Platform:
osname=cygwin, osvers=1.7.18(0.26353), archname=cygwin-thread-multi
uname='cygwin_nt-6.1 yaakov04 1.7.18(0.26353) 2013-03-07 19:25 x86_64 cygwin '
config_args='-d -e -Dprefix=/usr -Dmksymlinks -Dusethreads -Darchname=x86_64-cygwin-threads -Dlibperl=cygperl5_14.dll -Dcc=gcc -Dld=g++'
hint=recommended, useposix=true, d_sigaction=define
useithreads=define, usemultiplicity=define
useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
use64bitint=define, use64bitall=define, uselongdouble=undef
usemymalloc=n, bincompat5005=undef
Compiler:
cc='gcc', ccflags ='-DPERL_USE_SAFE_PUTENV -U__STRICT_ANSI__ -fno-strict-aliasing -pipe -fstack-protector',
optimize='-O3',
cppflags='-DPERL_USE_SAFE_PUTENV -U__STRICT_ANSI__ -fno-strict-aliasing -pipe -fstack-protector'
ccversion='', gccversion='4.8.0 20130307 (experimental)', gccosandvers=''
intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678
d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
alignbytes=8, prototype=define
Linker and Libraries:
ld='g++', ldflags =' -Wl,--enable-auto-import -Wl,--export-all-symbols -Wl,--enable-auto-image-base -fstack-protector'
libpth=/usr/lib /lib
libs=-lgdbm -ldb -ldl -lcrypt -lgdbm_compat
perllibs=-ldl -lcrypt
libc=/usr/lib/libc.a, so=dll, useshrplib=true, libperl=cygperl5_14.dll
gnulibc_version=''
Dynamic Linking:
dlsrc=dl_dlopen.xs, dlext=dll, d_dlsymun=undef, ccdlflags=' '
cccdlflags=' ', lddlflags=' --shared -Wl,--enable-auto-import -Wl,--export-all-symbols -Wl,--enable-auto-image-base -fstack-protector'


Characteristics of this binary (from libperl):
Compile-time options: MULTIPLICITY PERL_DONT_CREATE_GVSV
PERL_IMPLICIT_CONTEXT PERL_PRESERVE_IVUV
PERL_USE_SAFE_PUTENV USE_64_BIT_ALL USE_64_BIT_INT
USE_ITHREADS USE_LARGE_FILES USE_PERLIO USE_PERL_ATOF
USE_REENTRANT_API
Locally applied patches:
Bug#55162 File::Spec::case_tolerant performance
CYG07 $vendorarch/auto/.rebase
CYG15 static Win32CORE
CYG17 cyg-1.7 paths-utf8
0c612ce82 Fix building static extensions on cygwin, -UUSEIMPORTLIB
1bac5ecc1 Fix 64-bit threading sv.c: S_anonymise_cv_maybe
Cygwin::sync_winenv added
Built under cygwin
Compiled at Mar 11 2013 18:25:23
@INC:
/usr/lib/perl5/site_perl/5.14/x86_64-cygwin-threads
/usr/lib/perl5/site_perl/5.14
/usr/lib/perl5/vendor_perl/5.14/x86_64-cygwin-threads
/usr/lib/perl5/vendor_perl/5.14
/usr/lib/perl5/5.14/x86_64-cygwin-threads
/usr/lib/perl5/5.14
.

$ ./miniperl -Ilib -V
Summary of my perl5 (revision 5 version 22 subversion 0) configuration:

Platform:
osname=cygwin, osvers=1.7.28(0.27153), archname=cygwin
uname='cygwin_nt-6.1 wdlkhr204823 1.7.28(0.27153) 2014-02-09 21:06 x86_64 cygwin '
config_args=''
hint=recommended, useposix=true, d_sigaction=define
useithreads=undef, usemultiplicity=undef
use64bitint=define, use64bitall=define, uselongdouble=undef
usemymalloc=n, bincompat5005=undef
Compiler:
cc='gcc', ccflags ='-DPERL_USE_SAFE_PUTENV -U__STRICT_ANSI__ -fwrapv -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include',
optimize='-g',
cppflags='-DPERL_USE_SAFE_PUTENV -U__STRICT_ANSI__ -fwrapv -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include'
ccversion='', gccversion='4.8.2', gccosandvers=''
intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678, doublekind=3
d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16, longdblkind=3
ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
alignbytes=8, prototype=define
Linker and Libraries:
ld='g++', ldflags =' -Wl,--enable-auto-import -Wl,--export-all-symbols -Wl,--enable-auto-image-base -fstack-protector -L/usr/local/lib'
libpth=/usr/local/lib /usr/lib/gcc/x86_64-pc-cygwin/4.8.2/include-fixed /usr/lib /usr/lib/gcc/x86_64-pc-cygwin/4.8.2/../../../../lib/../include/w32api /lib
libs=-lpthread -ldb -ldl -lcrypt
perllibs=-lpthread -ldl -lcrypt
libc=/usr/lib/libc.a, so=dll, useshrplib=true, libperl=cygperl5_22_0.dll
gnulibc_version=''
Dynamic Linking:
dlsrc=dl_dlopen.xs, dlext=dll, d_dlsymun=undef, ccdlflags=' '
cccdlflags=' ', lddlflags=' --shared -Wl,--enable-auto-import -Wl,--export-all-symbols -Wl,--enable-auto-image-base -L/usr/local/lib -fstack-protector'


Characteristics of this binary (from libperl):
Compile-time options: HAS_TIMES PERLIO_LAYERS PERL_DONT_CREATE_GVSV
PERL_EXTERNAL_GLOB PERL_HASH_FUNC_ONE_AT_A_TIME_HARD
PERL_IS_MINIPERL PERL_NEW_COPY_ON_WRITE
PERL_PRESERVE_IVUV PERL_USE_SAFE_PUTENV
USE_64_BIT_ALL USE_64_BIT_INT USE_LARGE_FILES
USE_LOCALE USE_LOCALE_COLLATE USE_LOCALE_CTYPE
USE_LOCALE_NUMERIC USE_LOCALE_TIME USE_PERLIO
USE_PERL_ATOF USE_SITECUSTOMIZE
Built under cygwin
Compiled at Aug 11 2015 10:01:48
@INC:
/home/daniel.r.collins1/perl-5.22.0/cpan/AutoLoader/lib
/home/daniel.r.collins1/perl-5.22.0/dist/Carp/lib
/home/daniel.r.collins1/perl-5.22.0/dist/PathTools
/home/daniel.r.collins1/perl-5.22.0/dist/PathTools/lib
/home/daniel.r.collins1/perl-5.22.0/cpan/ExtUtils-Command/lib
/home/daniel.r.collins1/perl-5.22.0/cpan/ExtUtils-Install/lib
/home/daniel.r.collins1/perl-5.22.0/cpan/ExtUtils-MakeMaker/lib
/home/daniel.r.collins1/perl-5.22.0/cpan/ExtUtils-Manifest/lib
/home/daniel.r.collins1/perl-5.22.0/cpan/File-Path/lib
/home/daniel.r.collins1/perl-5.22.0/ext/re
/home/daniel.r.collins1/perl-5.22.0/dist/Term-ReadLine/lib
/home/daniel.r.collins1/perl-5.22.0/dist/Exporter/lib
/home/daniel.r.collins1/perl-5.22.0/ext/File-Find/lib
/home/daniel.r.collins1/perl-5.22.0/cpan/Text-Tabs/lib
/home/daniel.r.collins1/perl-5.22.0/dist/constant/lib
/home/daniel.r.collins1/perl-5.22.0/lib
.
Zefram
2015-08-11 22:34:11 UTC
Permalink
Post by Dan Collins (via RT)
$ od -c ../test1.pl
0000000 B E G I N < > \n
This is a duplicate of [perl #121048]. (Except that the test code in
that ticket has braces as well.)

-zefram

Loading...