Open p5pRT opened 7 years ago
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
"""
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
The RT System itself - Status changed from 'new' to 'open'
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.
Migrated from rt.perl.org#131192 (status was 'open')
Searchable as RT131192$