Skip to content

Commit 2ad6fe1

Browse files
committed
Make map and grep preserve their args lists.
Issue #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.
1 parent b0a34aa commit 2ad6fe1

File tree

4 files changed

+35
-2
lines changed

4 files changed

+35
-2
lines changed

MANIFEST

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6166,6 +6166,7 @@ t/re/uniprops09.t Test unicode \p{} regex constructs
61666166
t/re/uniprops10.t Test unicode \p{} regex constructs
61676167
t/re/user_prop_race_thr.t Test races in user-defined \p{} under threads
61686168
t/README Instructions for regression tests
6169+
t/run/argv_free.t Ensure no conflicts between @ARGV and <>.
61696170
t/run/cloexec.t Test close-on-exec.
61706171
t/run/dtrace.pl For dtrace.t
61716172
t/run/dtrace.t Test for DTrace probes

pp_ctl.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -972,17 +972,29 @@ PP(pp_grepstart)
972972
dSP;
973973
SV *src;
974974

975-
if (PL_stack_base + TOPMARK == SP) {
975+
int count = SP - (PL_stack_base + TOPMARK);
976+
977+
if (count == 0) {
976978
(void)POPMARK;
977979
if (GIMME_V == G_SCALAR)
978980
XPUSHs(&PL_sv_zero);
979981
RETURNOP(PL_op->op_next->op_next);
980982
}
983+
981984
PL_stack_sp = PL_stack_base + TOPMARK + 1;
982985
Perl_pp_pushmark(aTHX); /* push dst */
983986
Perl_pp_pushmark(aTHX); /* push src */
984987
ENTER_with_name("grep"); /* enter outer scope */
985988

989+
/* This prevents the map/grep arguments from disappearing midstream.
990+
(e.g., map { @foo = () } @foo)
991+
*/
992+
for (int i=0; i<count; i++) {
993+
SV* sv = PL_stack_base[TOPMARK + i];
994+
SvREFCNT_inc(sv);
995+
SAVEFREESV(sv);
996+
}
997+
986998
SAVETMPS;
987999
SAVE_DEFSV;
9881000
ENTER_with_name("grep_item"); /* enter inner scope */

t/op/grep.t

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ BEGIN {
1010
set_up_inc( qw(. ../lib) );
1111
}
1212

13-
plan( tests => 67 );
13+
plan( tests => 68 );
1414

1515
{
1616
my @lol = ([qw(a b c)], [], [qw(1 2 3)]);
@@ -238,3 +238,10 @@ pass 'no double frees with grep/map { undef *_ }';
238238
my @a = map { 1; "$_" } 1,2;
239239
is("@a", "1 2", "PADTMP");
240240
}
241+
242+
{
243+
# Ensure that the map args list doesn't disappear midstream:
244+
my @foo = qw(1 2 3 4 5 6 7);
245+
my @foo2 = map { @foo = (); $_ } @foo;
246+
is("@foo2", "1 2 3 4 5 6 7", 'map preserves args list');
247+
}

t/run/argv_free.t

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!./perl
2+
3+
BEGIN {
4+
chdir 't' if -d 't';
5+
@INC = '../lib';
6+
require './test.pl';
7+
skip_all_without_config('d_fcntl');
8+
}
9+
10+
system $^X, '-e', 'close STDIN; map <>, @ARGV', 1, 2;
11+
is($?, 0, '@ARGV does not conflict with <>');
12+
13+
done_testing;

0 commit comments

Comments
 (0)