Skip to content
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

"local *Package::" leads to segfaults and bizarre errors #15059

Open
p5pRT opened this issue Nov 22, 2015 · 3 comments
Open

"local *Package::" leads to segfaults and bizarre errors #15059

p5pRT opened this issue Nov 22, 2015 · 3 comments

Comments

@p5pRT
Copy link

p5pRT commented Nov 22, 2015

Migrated from rt.perl.org#126709 (status was 'open')

Searchable as RT126709$

@p5pRT
Copy link
Author

p5pRT commented Nov 22, 2015

From amon@cpan.org

#!/usr/bin/env perl
use strict;
use warnings;

BEGIN {
  package Callback;
  sub new { my ($class, $code) = @​_; return bless $code => $class }
}

sub compile {
  my ($source) = @​_;
  my $namespace = 'TheNamespace';

  no strict 'refs';
  local *{ $namespace . '​::' };

  my $compiled = eval "package $namespace; sub { $source; return }";
  die $@​ if $@​;

  return Callback->new($compiled);
}

compile('my $c = q(NoSuchClass); $c->new')->();

__END__

The above program will segfault or produce bizarre errors.

Usually, the script dies with​:

Attempt to access disallowed key 'NoSuchClass' in a restricted hash at (eval 1) line 1.

Clearly, that is wrong.

But with some probability (estimated 5% to 100%) we get a segfault instead, which is even worse. This case can be forced by setting the environment variable "PERL_HASH_SEED=8". I can reproduce this behaviour on a variety of perls​:

- v5.18.2 built for i686-linux-gnu-thread-multi-64int (Ubuntu 14.04 system perl)
- v5.18.2 built for i686-linux-thread-multi-64int (perlbrewed)
- v5.20.1 built for i686-linux-thread-multi-64int (perlbrewed)

- v5.20.2 built for x86_64-linux-gnu-thread-multi (Ubuntu 15.10 system perl)
- v5.22.0 built for x86_64-linux (perlbrewed)
- v5.23.6 (v5.23.5-20-g96b6870) built for x86_64-linux (perlbrewed)

I cannot reproduce on v5.10.1 built for i686-linux-thread-multi-64int (perlbrewed).

That v5.23.6 perl was a debug build. Running under GDB yielded the following stack trace​:

  (gdb) run testcase
  Starting program​: /home/atkinson-lukas/perl5/perlbrew/perls/perl-blead/bin/perl testcase
  [Thread debugging using libthread_db enabled]
  Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

  Program received signal SIGSEGV, Segmentation fault.
  0x00000000004a6040 in Perl_hv_common (hv=0x7d6cd8, keysv=keysv@​entry=0x0, key=key@​entry=0x7f0340 "NoSuchClass", klen=11, flags=0, action=<optimized out>, val=0x0, hash=2292890956) at hv.c​:671
  671 if (HeHASH(entry) != hash) /* strings can't be equal */
  (gdb) backtrace
  #0 0x00000000004a6040 in Perl_hv_common (hv=0x7d6cd8, keysv=keysv@​entry=0x0, key=key@​entry=0x7f0340 "NoSuchClass", klen=11, flags=0, action=<optimized out>, val=0x0, hash=2292890956) at hv.c​:671
  #1 0x00000000004a74b4 in Perl_hv_common_key_len (hv=hv@​entry=0x7d6cd8, key=key@​entry=0x7f0340 "NoSuchClass", klen_i32=klen_i32@​entry=11, action=<optimized out>, val=val@​entry=0x0, hash=hash@​entry=0) at hv.c​:333
  #2 0x000000000044572a in Perl_gv_fetchpvn_flags (nambeg=nambeg@​entry=0x7f0340 "NoSuchClass", full_len=11, flags=flags@​entry=0, sv_type=sv_type@​entry=SVt_PVIO) at gv.c​:2273
  #3 0x00000000004b4088 in S_opmethod_stash (meth=0x7ed8f8) at pp_hot.c​:3770
  #4 Perl_pp_method_named () at pp_hot.c​:3847
  #5 0x00000000004aa9f3 in Perl_runops_standard () at run.c​:41
  #6 0x0000000000441ec6 in S_run_body (oldscope=1) at perl.c​:2464
  #7 perl_run (my_perl=<optimized out>) at perl.c​:2387
  #8 0x000000000041fd55 in main (argc=2, argv=0x7fffffffdc18, env=0x7fffffffdc30) at perlmain.c​:116

If certain details of the script are changed, it behaves as expected and dies with this message​:

Can't locate object method "new" via package "NoSuchClass" (perhaps you forgot to load "NoSuchClass"?) at (eval 1) line 1.

Why did I write such code in the first place? I was trying to produce code refs by eval'ing their source in individual packages, and making sure the package is cleaned up after the code ref's refcount drops to zero, as to avoid memory leaks. Hence the `local *TheNamespace​::` which would restore the previous (empty) package after the code ref has been compiled. The code did previously work without problems, but I didn't track the execution environment closely enough to pinpoint the breaking change.

Subtle modifications to the script will produce the expected behaviour (unknown method new) rather than the restricted hash error or the segfault​:

- returning a bare code ref rather than a blessed object from `compile()`.
- not localizing the package stash
- loading or not loading additional packages
- referencing GVs inside the sub being compiled (e.g. compiling `our $foo; sub { $foo; NoSuchClass->new }`)

The attached perlbug information refers to the v5.23.6 build, though other data can be posted on request.


Flags​:
  category=core
  severity=low


Site configuration information for perl 5.23.6​:

Configured by atkinson-lukas at Sun Nov 22 22​:02​:40 CET 2015.

Summary of my perl5 (revision 5 version 23 subversion 6) configuration​:
  Snapshot of​: 96b6870
  Platform​:
  osname=linux, osvers=4.2.0-18-generic, archname=x86_64-linux
  uname='linux mccd 4.2.0-18-generic #22-ubuntu smp fri nov 6 18​:25​:50 utc 2015 x86_64 x86_64 x86_64 gnulinux '
  config_args='-de -Dprefix=/home/atkinson-lukas/perl5/perlbrew/perls/perl-blead -Doptimize=-O2 -g -Duse64bitall -Dusedevel -Aeval​:scriptdir=/home/atkinson-lukas/perl5/perlbrew/perls/perl-blead/bin'
  hint=recommended, useposix=true, d_sigaction=define
  useithreads=undef, usemultiplicity=undef
  use64bitint=define, use64bitall=define, uselongdouble=undef
  usemymalloc=n, bincompat5005=undef
  Compiler​:
  cc='cc', ccflags ='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
  optimize='-O2 -g',
  cppflags='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include'
  ccversion='', gccversion='5.2.1 20151010', 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 =' -fstack-protector-strong -L/usr/local/lib'
  libpth=/usr/local/lib /usr/lib/gcc/x86_64-linux-gnu/5/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.21.so, so=so, useshrplib=false, libperl=libperl.a
  gnulibc_version='2.21'
  Dynamic Linking​:
  dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E'
  cccdlflags='-fPIC', lddlflags='-shared -O2 -g -L/usr/local/lib -fstack-protector-strong'

Locally applied patches​:
  Devel​::PatchPerl 1.38


@​INC for perl 5.23.6​:
  /home/atkinson-lukas/perl5/perlbrew/perls/perl-blead/lib/site_perl/5.23.6/x86_64-linux
  /home/atkinson-lukas/perl5/perlbrew/perls/perl-blead/lib/site_perl/5.23.6
  /home/atkinson-lukas/perl5/perlbrew/perls/perl-blead/lib/5.23.6/x86_64-linux
  /home/atkinson-lukas/perl5/perlbrew/perls/perl-blead/lib/5.23.6
  .


Environment for perl 5.23.6​:
  HOME=/home/atkinson-lukas
  LANG=de_DE.UTF-8
  LANGUAGE=
  LD_LIBRARY_PATH (unset)
  LOGDIR (unset)
  PATH=/home/atkinson-lukas/perl5/perlbrew/bin​:/home/atkinson-lukas/perl5/perlbrew/perls/perl-blead/bin​:/usr/local/sbin​:/usr/local/bin​:/usr/sbin​:/usr/bin​:/sbin​:/bin​:/usr/games​:/usr/local/games​:/home/atkinson-lukas/.cabal/bin
  PERLBREW_BASHRC_VERSION=0.73
  PERLBREW_HOME=/home/atkinson-lukas/.perlbrew
  PERLBREW_MANPATH=/home/atkinson-lukas/perl5/perlbrew/perls/perl-blead/man
  PERLBREW_PATH=/home/atkinson-lukas/perl5/perlbrew/bin​:/home/atkinson-lukas/perl5/perlbrew/perls/perl-blead/bin
  PERLBREW_PERL=perl-blead
  PERLBREW_ROOT=/home/atkinson-lukas/perl5/perlbrew
  PERLBREW_VERSION=0.73
  PERL_BADLANG (unset)
  SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented Nov 23, 2015

From @iabyn

On Sun, Nov 22, 2015 at 02​:40​:52PM -0800, Lukas Atkinson wrote​:

The above program will segfault or produce bizarre errors.

It can be reduced to the following​:

  sub BAR​::new { }

  my $cb;
  {
  local *{FOO​::};

  $cb = eval q{
  package FOO;
  sub FOO​::foo { 'NoSuchClass'->foo; };
  \&FOO​::foo;
  };
  die $@​ if $@​;

  bless $cb, 'BAR';
  study;
  }
  $cb->();

which on a debugging blead seems to consistently trigger this assert​:

  perl​: hv.c​:355​: Perl_hv_common​: Assertion `((svtype)((hv)->sv_flags & 0xff)) == SVt_PVHV' failed.

This is because the method call 'NoSuchClass'->foo tries to find the
current stash while determining the class to use.

I'm not sure whether that represents a bug - i.e. I'm not sure whether
it should need to know the current stash to resolve that class method
call; but in any case, it ends up looking at the current cop to find the
current package, and since that cop was compiled in a stash that's
subsequently been freed by the scope exit, the 'stash' is an SV that's
since been reallocated and happens to no longer be an HV (hence the
assertion failure).

I'm not sure what to do about this. Arguably *every* COP's CopSTASH()
pointer is a weak ref to the stash, and so the stash should have a backref
magic entry for every COP that references it. This seems a bit expensive
though.

--
Dave's first rule of Opera​:
If something needs saying, say it​: don't warble it.

@p5pRT
Copy link
Author

p5pRT commented Nov 23, 2015

The RT System itself - Status changed from 'new' to 'open'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants