Skip to content

Commit

Permalink
Make map and grep preserve their args lists.
Browse files Browse the repository at this point in the history
Issue Perl#19340: Previously if you mapped/grepped over an array and mutated
the array within the map/grep, a segfault would happen. This fixes that
by bumping the map/grep args' reference count at the start of the map/grep,
then enqueueing those args for a refcount decrement at the end.
  • Loading branch information
FGasper committed Feb 9, 2022
1 parent b0a34aa commit 2ad6fe1
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 2 deletions.
1 change: 1 addition & 0 deletions MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -6166,6 +6166,7 @@ t/re/uniprops09.t Test unicode \p{} regex constructs
t/re/uniprops10.t Test unicode \p{} regex constructs
t/re/user_prop_race_thr.t Test races in user-defined \p{} under threads
t/README Instructions for regression tests
t/run/argv_free.t Ensure no conflicts between @ARGV and <>.
t/run/cloexec.t Test close-on-exec.
t/run/dtrace.pl For dtrace.t
t/run/dtrace.t Test for DTrace probes
Expand Down
14 changes: 13 additions & 1 deletion pp_ctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -972,17 +972,29 @@ PP(pp_grepstart)
dSP;
SV *src;

if (PL_stack_base + TOPMARK == SP) {
int count = SP - (PL_stack_base + TOPMARK);

if (count == 0) {
(void)POPMARK;
if (GIMME_V == G_SCALAR)
XPUSHs(&PL_sv_zero);
RETURNOP(PL_op->op_next->op_next);
}

PL_stack_sp = PL_stack_base + TOPMARK + 1;
Perl_pp_pushmark(aTHX); /* push dst */
Perl_pp_pushmark(aTHX); /* push src */
ENTER_with_name("grep"); /* enter outer scope */

/* This prevents the map/grep arguments from disappearing midstream.
(e.g., map { @foo = () } @foo)
*/
for (int i=0; i<count; i++) {
SV* sv = PL_stack_base[TOPMARK + i];
SvREFCNT_inc(sv);
SAVEFREESV(sv);
}

SAVETMPS;
SAVE_DEFSV;
ENTER_with_name("grep_item"); /* enter inner scope */
Expand Down
9 changes: 8 additions & 1 deletion t/op/grep.t
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ BEGIN {
set_up_inc( qw(. ../lib) );
}

plan( tests => 67 );
plan( tests => 68 );

{
my @lol = ([qw(a b c)], [], [qw(1 2 3)]);
Expand Down Expand Up @@ -238,3 +238,10 @@ pass 'no double frees with grep/map { undef *_ }';
my @a = map { 1; "$_" } 1,2;
is("@a", "1 2", "PADTMP");
}

{
# Ensure that the map args list doesn't disappear midstream:
my @foo = qw(1 2 3 4 5 6 7);
my @foo2 = map { @foo = (); $_ } @foo;
is("@foo2", "1 2 3 4 5 6 7", 'map preserves args list');
}
13 changes: 13 additions & 0 deletions t/run/argv_free.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!./perl

BEGIN {
chdir 't' if -d 't';
@INC = '../lib';
require './test.pl';
skip_all_without_config('d_fcntl');
}

system $^X, '-e', 'close STDIN; map <>, @ARGV', 1, 2;
is($?, 0, '@ARGV does not conflict with <>');

done_testing;

0 comments on commit 2ad6fe1

Please sign in to comment.