Skip to content

Commit 3725af9

Browse files
committed
Add limits to the size of the string repetition multiplier
Historically, given a statement like `my $x = "A" x SOMECONSTANT;`, no examination of the size of the multiplier (`SOMECONSTANT` in this example) was done at compile time. Depending upon the constant folding behaviour, this might mean: * The buffer allocation needed at runtime could be clearly bigger than the system can support, but Perl would happily compile the statement and let the author find this out at runtime. * Constants resulting from folding could be very large and the memory taken up undesirable, especially in cases where the constant resides in cold code. This commit adds some compile time checking such that: * A string size beyond or close to the likely limit of support triggers a fatal error. * Strings above a certain static size do not get constant folded.
1 parent da662ca commit 3725af9

File tree

4 files changed

+61
-1
lines changed

4 files changed

+61
-1
lines changed

op.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5011,6 +5011,39 @@ S_fold_constants(pTHX_ OP *const o)
50115011
break;
50125012
case OP_REPEAT:
50135013
if (o->op_private & OPpREPEAT_DOLIST) goto nope;
5014+
/* Croak if the string is going to be unrealistically
5015+
* large. (GH#13324) Otherwise, don't constant fold
5016+
* above a certain threshold. (GH#13793 & GH#20586)
5017+
*
5018+
* Implementation note: pp_pow returns powers of 2 as an NV
5019+
* e.g. my $x = "A" x (2**3);
5020+
*/
5021+
if (OP_TYPE_IS(cBINOPo->op_last, OP_CONST)) {
5022+
SV *constsv = cSVOPx_sv(cBINOPo->op_last);
5023+
UV arbitrary = 1024 * 1024;
5024+
5025+
if (SvIOKp(constsv)) {
5026+
if (SvIOK_UV(constsv)) {
5027+
if (SvUVX(constsv) > SIZE_MAX >> 2)
5028+
goto repetition_die;
5029+
if (SvUVX(constsv) > arbitrary)
5030+
goto nope;
5031+
} else {
5032+
if (SvIVX(constsv) > (IV)(SIZE_MAX >> 2))
5033+
goto repetition_die;
5034+
if (SvIVX(constsv) > (IV)arbitrary)
5035+
goto nope;
5036+
}
5037+
} else {
5038+
NV rhs = SvNV_nomg(constsv);
5039+
if (rhs > (NV)(SIZE_MAX >> 2)) {
5040+
repetition_die:
5041+
DIE(aTHX_ "Unrealistically large string repetition value");
5042+
}
5043+
if (rhs > (NV)arbitrary)
5044+
goto nope;
5045+
}
5046+
}
50145047
break;
50155048
case OP_SREFGEN:
50165049
if (cUNOPx(cUNOPo->op_first)->op_first->op_type != OP_CONST

pod/perldiag.pod

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7385,6 +7385,18 @@ reserved word. It's best to put such a word in quotes, or capitalize it
73857385
somehow, or insert an underbar into it. You might also declare it as a
73867386
subroutine.
73877387

7388+
=item Unrealistically large string repetition value
7389+
7390+
The value of the right operand in the string repetition operator is
7391+
likely close to or will exceed the maximum memory allocation that
7392+
your system can provide.
7393+
7394+
Even if an allocation of this size does succeed, subsequent string
7395+
copies may still result in an out-of-memory condition.
7396+
7397+
Note that a smaller memory constraint might be imposed on your
7398+
application under C<ulimit>, if containerized, or other local configuration.
7399+
73887400
=item Unrecognized character %s; marked by S<<-- HERE> after %s near column
73897401
%d
73907402

t/op/repeat.t

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ BEGIN {
66
set_up_inc( '../lib' );
77
}
88

9-
plan(tests => 50);
9+
plan(tests => 51);
1010

1111
# compile time
1212

@@ -193,6 +193,14 @@ fresh_perl_like(
193193
eval q{() = (() or ((0) x 0)); 1};
194194
is($@, "", "RT #130247");
195195

196+
# [GH #13324] Perl croaks if a string repetition seems unsupportable
197+
fresh_perl_like(
198+
'my $x = "A" x (2**99)',
199+
qr/Unrealistically large string repetition/,
200+
{ },
201+
'Croak on unrealistically large string repetition',
202+
);
203+
196204
# yes, the newlines matter
197205
fresh_perl_is(<<'PERL', "", { stderr => 1 }, "(perl #133778) MARK mishandling");
198206
map{s[][];eval;0}<DATA>__END__

t/perf/opcount.t

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,4 +1245,11 @@ test_opcount(0, "Empty else{} blocks are optimised away",
12451245
stub => 0
12461246
});
12471247

1248+
# GH #13793, GH #20586
1249+
test_opcount(0, "Don't fold string repetition once deeemed too large",
1250+
sub { my $x = "A" x (2**22) },
1251+
{
1252+
repeat => 1,
1253+
});
1254+
12481255
done_testing();

0 commit comments

Comments
 (0)