Skip to content

Segmentation fault with use re 'eval' #19390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
florian-pe opened this issue Feb 4, 2022 · 12 comments
Closed

Segmentation fault with use re 'eval' #19390

florian-pe opened this issue Feb 4, 2022 · 12 comments

Comments

@florian-pe
Copy link

I'm not sure this is really a problem with use re 'eval' but there is a Segmentation fault.

This happens when I tested the perl version of 99 bottles.
http://www.99-bottles-of-beer.net/language-perl-737.html

this is my exact script

#!/usr/bin/perl

use strict;
use warnings;
use re 'eval';

    ''=~(        '(?{'        .('`'        |'%')        .('['        ^'-')
    .('`'        |'!')        .('`'        |',')        .'"'.        '\\$'
    .'=='        .('['        ^'+')        .('`'        |'/')        .('['
    ^'+')        .'||'        .(';'        &'=')        .(';'        &'=')
    .';-'        .'-'.        '\\$'        .'=;'        .('['        ^'(')
    .('['        ^'.')        .('`'        |'"')        .('!'        ^'+')
   .'_\\{'      .'(\\$'      .';=('.      '\\$=|'      ."\|".(      '`'^'.'
  ).(('`')|    '/').').'    .'\\"'.+(    '{'^'[').    ('`'|'"')    .('`'|'/'
 ).('['^'/')  .('['^'/').  ('`'|',').(  '`'|('%')).  '\\".\\"'.(  '['^('(')).
 '\\"'.('['^  '#').'!!--'  .'\\$=.\\"'  .('{'^'[').  ('`'|'/').(  '`'|"\&").(
 '{'^"\[").(  '`'|"\"").(  '`'|"\%").(  '`'|"\%").(  '['^(')')).  '\\").\\"'.
 ('{'^'[').(  '`'|"\/").(  '`'|"\.").(  '{'^"\[").(  '['^"\/").(  '`'|"\(").(
 '`'|"\%").(  '{'^"\[").(  '['^"\,").(  '`'|"\!").(  '`'|"\,").(  '`'|(',')).
 '\\"\\}'.+(  '['^"\+").(  '['^"\)").(  '`'|"\)").(  '`'|"\.").(  '['^('/')).
 '+_,\\",'.(  '{'^('[')).  ('\\$;!').(  '!'^"\+").(  '{'^"\/").(  '`'|"\!").(
 '`'|"\+").(  '`'|"\%").(  '{'^"\[").(  '`'|"\/").(  '`'|"\.").(  '`'|"\%").(
 '{'^"\[").(  '`'|"\$").(  '`'|"\/").(  '['^"\,").(  '`'|('.')).  ','.(('{')^
 '[').("\["^  '+').("\`"|  '!').("\["^  '(').("\["^  '(').("\{"^  '[').("\`"|
 ')').("\["^  '/').("\{"^  '[').("\`"|  '!').("\["^  ')').("\`"|  '/').("\["^
 '.').("\`"|  '.').("\`"|  '$')."\,".(  '!'^('+')).  '\\",_,\\"'  .'!'.("\!"^
 '+').("\!"^  '+').'\\"'.  ('['^',').(  '`'|"\(").(  '`'|"\)").(  '`'|"\,").(
 '`'|('%')).  '++\\$="})'  );$:=('.')^  '~';$~='@'|  '(';$^=')'^  '[';$/='`';

this is my perl version :

Summary of my perl5 (revision 5 version 34 subversion 0) configuration:
   
  Platform:
    osname=linux
    osvers=5.12.15-arch1-1
    archname=x86_64-linux-thread-multi
    uname='archlinux'
    config_args='-des -Dusethreads -Duseshrplib -Doptimize=-march=x86-64 -mtune=generic -O2 -pipe -fno-plt -Dprefix=/usr -Dvendorprefix=/usr -Dprivlib=/usr/share/perl5/core_perl -Darchlib=/usr/lib/perl5/5.34/core_perl -Dsitelib=/usr/share/perl5/site_perl -Dsitearch=/usr/lib/perl5/5.34/site_perl -Dvendorlib=/usr/share/perl5/vendor_perl -Dvendorarch=/usr/lib/perl5/5.34/vendor_perl -Dscriptdir=/usr/bin/core_perl -Dsitescript=/usr/bin/site_perl -Dvendorscript=/usr/bin/vendor_perl -Dinc_version_list=none -Dman1ext=1perl -Dman3ext=3perl -Dcccdlflags='-fPIC' -Dlddlflags=-shared -Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now -Dldflags=-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now'
    hint=recommended
    useposix=true
    d_sigaction=define
    useithreads=define
    usemultiplicity=define
    use64bitint=define
    use64bitall=define
    uselongdouble=undef
    usemymalloc=n
    default_inc_excludes_dot=define
  Compiler:
    cc='cc'
    ccflags ='-D_REENTRANT -D_GNU_SOURCE -fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2'
    optimize='-march=x86-64 -mtune=generic -O2 -pipe -fno-plt'
    cppflags='-D_REENTRANT -D_GNU_SOURCE -fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include'
    ccversion=''
    gccversion='11.1.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='cc'
    ldflags ='-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now -fstack-protector-strong -L/usr/local/lib'
    libpth=/usr/local/lib /usr/lib
    libs=-lpthread -lgdbm -ldb -ldl -lm -lcrypt -lutil -lc -lgdbm_compat
    perllibs=-lpthread -ldl -lm -lcrypt -lutil -lc
    libc=libc-2.33.so
    so=so
    useshrplib=true
    libperl=libperl.so
    gnulibc_version='2.33'
  Dynamic Linking:
    dlsrc=dl_dlopen.xs
    dlext=so
    d_dlsymun=undef
    ccdlflags='-Wl,-E -Wl,-rpath,/usr/lib/perl5/5.34/core_perl/CORE'
    cccdlflags='-fPIC'
    lddlflags='-shared -Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now -L/usr/local/lib -fstack-protector-strong'


Characteristics of this binary (from libperl): 
  Compile-time options:
    HAS_TIMES
    MULTIPLICITY
    PERLIO_LAYERS
    PERL_COPY_ON_WRITE
    PERL_DONT_CREATE_GVSV
    PERL_IMPLICIT_CONTEXT
    PERL_MALLOC_WRAP
    PERL_OP_PARENT
    PERL_PRESERVE_IVUV
    USE_64_BIT_ALL
    USE_64_BIT_INT
    USE_ITHREADS
    USE_LARGE_FILES
    USE_LOCALE
    USE_LOCALE_COLLATE
    USE_LOCALE_CTYPE
    USE_LOCALE_NUMERIC
    USE_LOCALE_TIME
    USE_PERLIO
    USE_PERL_ATOF
    USE_REENTRANT_API
    USE_THREAD_SAFE_LOCALE
  Built under linux
  Compiled at Nov 13 2021 20:22:42
  %ENV:
    PERL5LIB="/home/london/.my_configurations/perl_modules"
  @INC:
    /home/london/.my_configurations/perl_modules
    /usr/lib/perl5/5.34/site_perl
    /usr/share/perl5/site_perl
    /usr/lib/perl5/5.34/vendor_perl
    /usr/share/perl5/vendor_perl
    /usr/lib/perl5/5.34/core_perl
    /usr/share/perl5/core_perl
@florian-pe
Copy link
Author

I've just discovered that the segmentation fault doesn't happen when we remove use strict. This might help.

@hvds
Copy link
Contributor

hvds commented Feb 4, 2022

Do you have a deobfuscated version of it? I'd be happy to look into the core dump, but am disinclined to spend my time untangling it.

Building blead with debugging it stops instead with NULL OP IN RUN at (eval 1) line 1, which is probably a clue.

@hvds
Copy link
Contributor

hvds commented Feb 4, 2022

I dug a bit more:

% perl -wle 'use strict; use re "eval"; our $x; "" =~ m{(?{ eval q{ $x = 1 } })}; print "ok"'
ok
% perl -wle 'use strict; use re "eval"; "" =~ m{(?{ eval q{ $x = 1 } })}; print "ok"'
Segmentation fault (core dumped)
% 

So my first guess is that we try to raise a strict error, but something in the string-eval inside re-eval context trips up.

@hvds
Copy link
Contributor

hvds commented Feb 4, 2022

Stack trace:

gdb --args ./perl -Ilib -wle 'use strict; use re "eval"; "" =~ m{(?{ eval q{ $x = 1 } })}; print "ok"'
...
Program received signal SIGSEGV, Segmentation fault.
0x00005555556c7cee in Perl_runops_standard () at run.c:41
41          while ((PL_op = op = op->op_ppaddr(aTHX))) {
(gdb) where
#0  0x00005555556c7cee in Perl_runops_standard () at run.c:41
#1  0x000055555572ab95 in S_docatch (
    firstpp=0x55555572eb1f <Perl_pp_entereval>) at pp_ctl.c:3335
#2  0x000055555572eb65 in Perl_pp_entereval () at pp_ctl.c:4466
#3  0x00005555556c7cf4 in Perl_runops_standard () at run.c:41
#4  0x0000555555773dbb in S_regmatch (reginfo=0x7fffffffe2f0, 
    startpos=0x555555ba4440 "", prog=0x555555bb4808) at regexec.c:8101
#5  0x0000555555764b42 in S_regtry (reginfo=0x7fffffffe2f0, 
    startposp=0x7fffffffe228) at regexec.c:4282
#6  0x0000555555764896 in Perl_regexec_flags (rx=0x555555b9c378, 
    stringarg=0x555555ba4440 "", strend=0x555555ba4440 "", 
    strbeg=0x555555ba4440 "", minend=0, sv=0x555555b9c300, data=0x0, flags=97)
    at regexec.c:4145
#7  0x00005555556d109f in Perl_pp_match () at pp_hot.c:3072
#8  0x00005555556c7cf4 in Perl_runops_standard () at run.c:41
#9  0x00005555555d256c in S_run_body (oldscope=1) at perl.c:2725
#10 0x00005555555d2114 in perl_run (my_perl=0x555555b77260) at perl.c:2648
#11 0x00005555555a06e7 in main (argc=4, argv=0x7fffffffe708, 
    env=0x7fffffffe730) at perlmain.c:110

In S_docatch we get NULL back from firstpp(aTHX):

    case 0:
        PL_op = firstpp(aTHX);
 redo_body:
        CALLRUNOPS(aTHX);
        break;

@tonycoz do you know how this stuff should fit together?

@florian-pe
Copy link
Author

@hvds No I don't have the original source code sadly.

I was thinking, could you recommend it some ressources to getting started with debugging perl with GDB ?
I know some C, have looked fairly at perl's source code and I'm learning how to use gdb. I've also experimented a little with the C api.

I also want to modify the source code but I'm having trouble to set up a developement environnement. First to Configure with the right options so that @INC is correctly set to the developpement build. And also to have a lightweight build when I run make, for just recompiling perl's core when I modify it and not recompile "everything" each time.

@florian-pe
Copy link
Author

florian-pe commented Feb 5, 2022

@hvds This is the original source code used to generate Acme, which was in the comments of the original page http://www.99-bottles-of-beer.net/language-perl-737.html. I re-generated it and verified that it was the same as the bottles I posted above, removing extra lines, removing whitespace s/\s+//g and using diff.

use Acme::EyeDrops qw(sightly get_eye_string hjoin_shapes);
my $ninety_nine = <<'BURP';
$==pop||99;--$=;sub
_{($;=($=||No)." bottle"."s"x!!--$=." of beer")." on the wall"}
print+_,", $;!
Take one down, pass it around,
",_,"!

"while++$=
BURP
chop($ninety_nine); $ninety_nine =~ s/\nprint/print/;
print sightly( { Regex         => 1,
                 Compact       => 1,
                 ShapeString   => hjoin_shapes(2,
                                  (get_eye_string('bottle2'))x6),
                  SourceString  => $ninety_nine } );

And running the original code on its own (without strict) produce the correct 99 bottles output and doesn't segfault.
So the segfault does come from the generated regex eval.

@hvds
Copy link
Contributor

hvds commented Feb 17, 2022

@hvds No I don't have the original source code sadly.

I was thinking, could you recommend it some ressources to getting started with debugging perl with GDB ? I know some C, have looked fairly at perl's source code and I'm learning how to use gdb. I've also experimented a little with the C api.

I also want to modify the source code but I'm having trouble to set up a developement environnement. First to Configure with the right options so that @INC is correctly set to the developpement build. And also to have a lightweight build when I run make, for just recompiling perl's core when I modify it and not recompile "everything" each time.

I build and use miniperl when I can, for speed of recompiling. I tend not to rely on @INC - ./perl -Ilib ... is usually enough to do the right thing within the build environment, so for a specific issue I'll tend to use something like gdb --args ./perl -Ilib -e ....

I'm not sure what you're looking for with respect to "resources to getting started" debugging perl with gdb - if you have more specific questions, feel free to ask me directly or send your questions to p5p (preferably without intentional obfuscation:).

@florian-pe
Copy link
Author

florian-pe commented Feb 20, 2022

@hvds That's already good tips so thank you.

When asking for ressources to getting started with debugging perl with gdb, I meant reproduce what you got in gdb, for a start. I managed to do it, after compiling perl with debug symbols and with the core dump.

My question was a little weird because I thought there was something perl-specific to do. Searching for debugging examples of perl with gdb, I found this reponse of Reini Urban here https://stackoverflow.com/questions/8836711/how-can-i-attach-a-debugger-to-a-running-perl-process and was really confused by what he wrote. In is answer he said to do this (gdb) p Perl_op_dump(PL_op) and I had no idea why.

make miniperl was exacty what I was looking for for a fast compilation involving only the main C files.

My other question, about hacking on the perl's core was essentially about the workflow. When working on the interpreter, say you want to modify regcomp.c, do you guys p5p developpers just rebuild perl with make miniperl or is there more to it than that ?

@demerphq
Copy link
Collaborator

demerphq commented Feb 20, 2022 via email

@hvds
Copy link
Contributor

hvds commented Jul 9, 2022

@iabyn I thought the work you did on #19680 should probably have fixed this problem too, but while the reduced testcase no longer SEGVs it now emits a new diagnostic:

% ./perl -Ilib -wle 'use strict; use re "eval"; "" =~ m{(?{ eval q{ $x = 1 } })}; print "ok"'
NULL OP IN RUN at -e line 1.
ok

However it looks clean if the eval is not the last thing in the block:

% ./perl -Ilib -wle 'use strict; use re "eval"; "" =~ m{(?{ eval q{ $x = 1 }; warn $@ })}; print "ok"'
Global symbol "$x" requires explicit package name (did you forget to declare "my $x"?) at (eval 1) line 1.
ok

Is this now expected behaviour?

iabyn added a commit that referenced this issue Jul 9, 2022
GH #19390

This issue is similar to GH #19680 (fixed by v5.37.0-272-g5fd637ce8c),
but exiting the eval via a syntax error rather than via raising an
exception.

In this case, the NULL op_next of the OP_ENTEREVAL was passed to a new
RUNOPS loop, which would normally SEGV (or on debugging builds, warn
"NULL OP IN RUN").
@iabyn
Copy link
Contributor

iabyn commented Jul 9, 2022 via email

@hvds
Copy link
Contributor

hvds commented Jul 9, 2022

Thanks @iabyn.

Whether it SEGVs or warns is down to whether it's a debugging build or not.

Ugh, one day I'll actually remember to consider that possibility.

@florian-pe I confirm that your original example no longer segfaults after this fix. Inserting a warn $@ at the appropriate place shows why adding use strict makes the difference:

Bareword "No" not allowed while "strict subs" in use at (eval 2) line 2.

Closing this ticket.

@hvds hvds closed this as completed Jul 9, 2022
scottchiefbaker pushed a commit to scottchiefbaker/perl5 that referenced this issue Nov 3, 2022
GH Perl#19390

This issue is similar to GH Perl#19680 (fixed by v5.37.0-272-g5fd637ce8c),
but exiting the eval via a syntax error rather than via raising an
exception.

In this case, the NULL op_next of the OP_ENTEREVAL was passed to a new
RUNOPS loop, which would normally SEGV (or on debugging builds, warn
"NULL OP IN RUN").
steve-m-hay pushed a commit that referenced this issue Apr 10, 2023
GH #19390

This issue is similar to GH #19680 (fixed by v5.37.0-272-g5fd637ce8c),
but exiting the eval via a syntax error rather than via raising an
exception.

In this case, the NULL op_next of the OP_ENTEREVAL was passed to a new
RUNOPS loop, which would normally SEGV (or on debugging builds, warn
"NULL OP IN RUN").

(cherry picked from commit 16e43ef)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants