Perl / perl5

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

panic: attempt to copy freed scalar via @ARGV on stack, Getopt::Long + Carp::longmess #15959

Open p5pRT opened 7 years ago

p5pRT commented 7 years ago

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

Searchable as RT131192$

p5pRT commented 7 years ago

From @nthykier

Created by @nthykier

Hi\,

I discovered that after a change to a project of mine\, perl started throwing Bizarre Copy errors. I eventually reproduced it to the following test case (included below). As I reduced the test case\, the error eventually turned into "panic​: attempt to copy freed scalar" (although\, tweaks to the test case sometimes flip it back to a Bizarre copy).

Some observations​:

* @​ARGV must be passed to a sub and @​ARGV must be non-empty * That sub (or something it calls) must call Getopt​::Long​::GetOptions * Carp​::longmess() is required to trigger the bug * Originally discovered on Debian stretch's perl 5.24\, but is also   reproducible in blead.

Running the test case​:

  """   ./perl -Ilib run.pl --state-dir foo   panic​: attempt to copy freed scalar 55a956c38168 to 55a956c59510 at lib/Carp.pm line 229.   """

The definition of run.pl​:

""" #!/usr/bin/perl -w

use strict; use warnings; use Carp qw(longmess);

use Getopt​::Long(); use Errno qw(ENOENT);

my %OPT; # If the %OPT_HASH is changed\, I some times see my %OPT_HASH= (   'state-dir=s' => \$OPT{'state-dir'}\,   'mirror-path=s' => \$OPT{'mirror-path'}\,   # Comma-separated lists   'distributions=s@​' =>   sub { push(@​{$OPT{'distributions'}}\, split(m/\,/\, $_[1])); }\,   'mirror-areas=s@​' =>   sub { push(@​{$OPT{'mirror-areas'}}\, split(m/\,/\, $_[1])); }\,   'architectures=s@​' =>   sub { push(@​{$OPT{'architectures'}}\, split(m/\,/\, $_[1])); }\,   'desired-version=s' => \$OPT{'desired-version'}\,   'lintian-lab=s' => \$OPT{'lintian-lab'}\,   'reschedule-all' => \$OPT{'reschedule-all'}\,   'help|h' => \&usage\,   'debug|d' => \$OPT{'debug'}\,   'dry-run' => \$OPT{'dry-run'}\, );

sub tool_main {   # We have to call GetOptions to reproduce the issue - plus we need   # at least one argument consumed.   die("Usage​: $0 --state-dir does-not-matter\n") if not @​ARGV;   Getopt​::Long​::config('bundling'\, 'no_getopt_compat'\, 'no_auto_abbrev');   Getopt​::Long​::GetOptions(%OPT_HASH) or die("error parsing options\n");   load_state_cache();   exit(0); }

sub load_state_cache {   my ($state_dir) = @​_;   my $state_file = "./file-that-does-not-exist";   my $fd;   if (not open($fd\, '\<​:raw'\, $state_file)) {   my $err = $!;   # Originally reproduced with autodie\, but it was reproducible by   # simply calling longmess (which autodie does to collect a stack   # trace for its exception)   Carp​::longmess(); # \<-- Triggers a "attempt to copy freed scalar"   # OR a Bizarre copy of X   die("open ($state_file)​: $err") if $err != ENOENT;   # Not present; presume empty   return;   }   # We wanted to get here because of ENOENT   ...;   close($fd);   return; }

sub usage {   print \<\<EOF; Random usage message EOF

  exit(0); }

tool_main(@​ARGV); # Has to be here to trigger the error

# Local Variables​: # indent-tabs-mode​: nil # cperl-indent-level​: 4 # End​: # vim​: syntax=perl sw=4 sts=4 sr et

"""

Perl Info ``` Flags: category=core severity=low Site configuration information for perl 5.26.0: Configured by moon at Sat Apr 22 10:20:49 CEST 2017. Summary of my perl5 (revision 5 version 26 subversion 0) configuration: Commit id: 78d5fac03632a8c05a3e3e954b0cb9dfa0be4f36 Platform: osname=linux osvers=4.9.0-2-amd64 archname=x86_64-linux uname='linux hostname 4.9.0-2-amd64 #1 smp debian 4.9.18-1 (2017-03-30) x86_64 gnulinux ' config_args='-des -DDEBUG' 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='cc' ccflags ='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64' optimize='-O2' cppflags='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include' ccversion='' gccversion='6.3.0 20170406' 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/6/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.24.so so=so useshrplib=false libperl=libperl.a gnulibc_version='2.24' Dynamic Linking: dlsrc=dl_dlopen.xs dlext=so d_dlsymun=undef ccdlflags='-Wl,-E' cccdlflags='-fPIC' lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector-strong' @INC for perl 5.26.0: lib /usr/local/lib/perl5/site_perl/5.26.0/x86_64-linux /usr/local/lib/perl5/site_perl/5.26.0 /usr/local/lib/perl5/5.26.0/x86_64-linux /usr/local/lib/perl5/5.26.0 Environment for perl 5.26.0: HOME=/home/user LANG=en_DK.UTF-8 LANGUAGE=en_GB:en LC_NUMERIC=C.UTF-8 LD_LIBRARY_PATH (unset) LOGDIR (unset) PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games PERL_BADLANG (unset) SHELL=/bin/bash ```
p5pRT commented 7 years ago

From zefram@fysh.org

niels@​thykier.net wrote​:

* @​ARGV must be passed to a sub and @​ARGV must be non-empty * That sub (or something it calls) must call Getopt​::Long​::GetOptions

It's another stack-not-refcounted bug. GetOptions() mutates @​ARGV\, causing deletion of some of its elements that are on the stack.

-zefram

p5pRT commented 7 years ago

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

p5pRT commented 7 years ago

From slade@openmailbox.org

Workaround​: Replace `tool_main(@​ARGV)` with `tool_main(@​ARGV = @​ARGV)`. I'm not quite sure why this works (or if indeed it works in all cases where one passes @​ARGV to a sub that mutates it)\, but it's the most concise fix I've found.