Perl / perl5

🐪 The Perl programming language
https://dev.perl.org/perl5/
Other
1.98k stars 559 forks source link

op.c:16388: Perl_rpeep: Assertion `oldop->op_type == OP_NEXTSTATE || oldop->op_type == OP_DBSTATE' failed. #17228

Open dur-randir opened 5 years ago

dur-randir commented 5 years ago

This is a bug report for perl from afl@dorothy, generated with the help of perlbug 1.41 running under perl 5.31.5.

[Please describe your issue here]

While fuzzing perl v5.29.10-23-g7c0d7520a3 built with afl and run under libdislocator, I found the following program

my($e,$x),(),my($c,$s);foreach({}){}

to cause an assertion failure: op.c:16388: Perl_rpeep: Assertion `oldop->op_type == OP_NEXTSTATE || oldop->op_type == OP_DBSTATE' failed. GDB stack is following:

(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007ffff7c24535 in __GI_abort () at abort.c:79
#2  0x00007ffff7c2440f in __assert_fail_base (fmt=0x7ffff7d86ee0 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n",
    assertion=0x555555934858 "oldop->op_type == OP_NEXTSTATE || oldop->op_type == OP_DBSTATE", file=0x55555592c851 "op.c", line=16388,
    function=<optimized out>) at assert.c:92
#3  0x00007ffff7c32102 in __GI___assert_fail (assertion=0x555555934858 "oldop->op_type == OP_NEXTSTATE || oldop->op_type == OP_DBSTATE",
    file=0x55555592c851 "op.c", line=16388, function=0x555555936758 <__PRETTY_FUNCTION__.23053> "Perl_rpeep") at assert.c:101
#4  0x00005555555e1a2e in Perl_rpeep (o=0x555555c0c808) at op.c:16387
#5  0x00005555555e3e2b in Perl_peep (o=0x555555c0e088) at op.c:17053
#6  0x00005555555ac62d in S_process_optree (cv=0x0, optree=0x555555c0c730, start=0x555555c0e088) at op.c:3616
#7  0x00005555555b4236 in Perl_newPROG (o=0x555555c0c730) at op.c:5723
#8  0x000055555566c212 in Perl_yyparse (gramtype=258) at perly.y:126
#9  0x00005555555ecda0 in S_parse_body (env=0x0, xsinit=0x5555555a120f <xs_init>) at perl.c:2527
#10 0x00005555555eb072 in perl_parse (my_perl=0x555555be1260, xsinit=0x5555555a120f <xs_init>, argc=3, argv=0x7fffffffe1b8, env=0x0) at perl.c:1818
#11 0x00005555555a114d in main (argc=3, argv=0x7fffffffe1b8, env=0x7fffffffe1d8) at perlmain.c:132

This is a regression between 5.20 and 5.22, bisect points to 5b807558721d4a1b9a2121285e81b695d4b5025b is the first bad commit

commit 5b807558721d4a1b9a2121285e81b695d4b5025b
Author: Father Chrysostomos <sprout@cpan.org>
Date:   Fri Oct 17 13:56:52 2014 -0700

    Allow void padrange even without nextstate

    This allows the padrange optimisation to happen with code like this:

        my ($a, $b), our $c;

[Please do not change anything below this line] Flags: category=core severity=low Site configuration information for perl 5.31.5:

Configured by root at Fri Oct 18 05:50:54 MSK 2019.

Summary of my perl5 (revision 5 version 31 subversion 5) configuration: Derived from: 859b78b Platform: osname=linux osvers=4.19.0-6-amd64 archname=x86_64-linux uname='linux dorothy 4.19.0-6-amd64 #1 smp debian 4.19.67-2+deb10u1 (2019-09-20) x86_64 gnulinux ' config_args='-des -Dusedevel -Dcc=gcc -DDEBUGGING -Doptimize=-O0 -g -ggdb3' hint=recommended useposix=true d_sigaction=define useithreads=undef usemultiplicity=undef use64bitint=define use64bitall=define uselongdouble=undef usemymalloc=n default_inc_excludes_dot=define bincompat5005=undef Compiler: cc='gcc' ccflags ='-fwrapv -DDEBUGGING -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2' optimize='-O0 -g -ggdb3' cppflags='-fwrapv -DDEBUGGING -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include' ccversion='' gccversion='8.3.0' 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='gcc' ldflags =' -fstack-protector-strong -L/usr/local/lib' libpth=/usr/local/lib /usr/lib/gcc/x86_64-linux-gnu/8/include-fixed /usr/include/x86_64-linux-gnu /usr/lib /lib/x86_64-linux-gnu /lib/../lib /usr/lib/x86_64-linux-gnu /usr/lib/../lib /lib libs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc libc=libc-2.28.so so=so useshrplib=false libperl=libperl.a gnulibc_version='2.28' Dynamic Linking: dlsrc=dl_dlopen.xs dlext=so d_dlsymun=undef ccdlflags='-Wl,-E' cccdlflags='-fPIC' lddlflags='-shared -O0 -g -ggdb3 -L/usr/local/lib -fstack-protector-strong'

Characteristics of this binary (from libperl): Compile-time options: DEBUGGING HAS_TIMES PERLIO_LAYERS PERL_COPY_ON_WRITE PERL_DONT_CREATE_GVSV PERL_MALLOC_WRAP PERL_OP_PARENT PERL_PRESERVE_IVUV PERL_USE_DEVEL 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 Locally applied patches: uncommitted-changes Built under linux Compiled at Oct 18 2019 06:02:50 %ENV: PERLBREW_BASHRC_VERSION="0.78" PERLBREW_HOME="/home/afl/.perlbrew" PERLBREW_MANPATH="/home/afl/perlbrew/perls/perl-5.20.2/man" PERLBREW_PATH="/home/afl/perlbrew/bin:/home/afl/perlbrew/perls/perl-5.20.2/bin" PERLBREW_PERL="perl-5.20.2" PERLBREW_ROOT="/home/afl/perlbrew" PERLBREW_VERSION="0.78" @inc: lib /usr/local/lib/perl5/site_perl/5.31.5/x86_64-linux /usr/local/lib/perl5/site_perl/5.31.5 /usr/local/lib/perl5/5.31.5/x86_64-linux /usr/local/lib/perl5/5.31.5

tonycoz commented 5 years ago

This doesn't need the foreach, as long as the original list is in compile-time void context, eg.

$ ./perl -e 'my($e,$x),(),my($c,$s);1'
perl: op.c:16392: Perl_rpeep: Assertion `oldop->op_type == OP_NEXTSTATE || oldop->op_type == OP_DBSTATE' failed.
Aborted

oldop at the point of the assertion points at the stub op in the tree like:

11   |   +--list LISTOP(0x555555e00868) ===> 12 [stub 0x555555e00838]
     |   |   FLAGS = (VOID,KIDS,PARENS,SLABBED,MORESIB)
     |   |   |   
7    |   |   +--padrange OP(0x555555e008a8) ===> 12 [stub 0x555555e00838]
     |   |   |   TARG = 1
     |   |   |   FLAGS = (VOID,MOD,SLABBED,MORESIB)
     |   |   |   PRIVATE = (LVINTRO,range=0x2)
     |   |   |   
13   |   |   +--padsv OP(0x555555e00908) ===> 14 [padsv 0x555555e008d8]
     |   |   |   TARG = 1
     |   |   |   FLAGS = (VOID,MOD,SLABBED,MORESIB)
     |   |   |   PRIVATE = (LVINTRO)
     |   |   |   
14   |   |   +--padsv OP(0x555555e008d8) ===> 11 [list 0x555555e00868]
     |   |       TARG = 2
     |   |       FLAGS = (VOID,MOD,SLABBED)
     |   |       PRIVATE = (LVINTRO)
     |   |   
12   |   +--stub OP(0x555555e00838) ===> 1 [pushmark 0x555555e00e48]
     |   |   FLAGS = (VOID,PARENS,SLABBED,MORESIB)
     |   |   
3    |   +--list LISTOP(0x555555e00e08) ===> 8 [null 0x555555e007c8]
     |       FLAGS = (VOID,KIDS,PARENS,SLABBED)
     |       |   
1    |       +--pushmark OP(0x555555e00e48) ===> 2 [padsv 0x555555e00798]
     |       |   FLAGS = (VOID,MOD,SLABBED,MORESIB)
     |       |   PRIVATE = (LVINTRO)
     |       |   
2    |       +--padsv OP(0x555555e00798) ===> 15 [padsv 0x555555e00768]
     |       |   TARG = 3
     |       |   FLAGS = (VOID,MOD,SLABBED,MORESIB)
     |       |   PRIVATE = (LVINTRO)
     |       |   
15   |       +--padsv OP(0x555555e00768) ===> 3 [list 0x555555e00e08]
     |           TARG = 4
     |           FLAGS = (VOID,MOD,SLABBED)
     |           PRIVATE = (LVINTRO)

I suspect the fix is to allow OP_STUB here and handle it (somehow) in the while loop below.