Skip to content

Commit 953cf3f

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 84fa78b commit 953cf3f

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
@@ -4871,6 +4871,39 @@ S_fold_constants(pTHX_ OP *const o)
48714871
break;
48724872
case OP_REPEAT:
48734873
if (o->op_private & OPpREPEAT_DOLIST) goto nope;
4874+
/* Croak if the string is going to be unrealistically
4875+
* large. (GH#13324) Otherwise, don't constant fold
4876+
* above a certain threshold. (GH#13793 & GH#20586)
4877+
*
4878+
* Implementation note: pp_pow returns powers of 2 as an NV
4879+
* e.g. my $x = "A" x (2**3);
4880+
*/
4881+
if (OP_TYPE_IS(cBINOPo->op_last, OP_CONST)) {
4882+
SV *constsv = cSVOPx_sv(cBINOPo->op_last);
4883+
UV arbitrary = 1024 * 1024;
4884+
4885+
if (SvIOKp(constsv)) {
4886+
if (SvIOK_UV(constsv)) {
4887+
if (SvUVX(constsv) > SIZE_MAX >> 2)
4888+
goto repetition_die;
4889+
if (SvUVX(constsv) > arbitrary)
4890+
goto nope;
4891+
} else {
4892+
if (SvIVX(constsv) > (IV)(SIZE_MAX >> 2))
4893+
goto repetition_die;
4894+
if (SvIVX(constsv) > (IV)arbitrary)
4895+
goto nope;
4896+
}
4897+
} else {
4898+
NV rhs = SvNV_nomg(constsv);
4899+
if (rhs > (NV)(SIZE_MAX >> 2)) {
4900+
repetition_die:
4901+
DIE(aTHX_ "Unrealistically large string repetition value");
4902+
}
4903+
if (rhs > (NV)arbitrary)
4904+
goto nope;
4905+
}
4906+
}
48744907
break;
48754908
case OP_SREFGEN:
48764909
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
@@ -6971,6 +6971,18 @@ reserved word. It's best to put such a word in quotes, or capitalize it
69716971
somehow, or insert an underbar into it. You might also declare it as a
69726972
subroutine.
69736973

6974+
=item Unrealistically large string repetition value
6975+
6976+
The value of the right operand in the string repetition operator is
6977+
likely close to or will exceed the maximum memory allocation that
6978+
your system can provide.
6979+
6980+
Even if an allocation of this size does succeed, subsequent string
6981+
copies may still result in an out-of-memory condition.
6982+
6983+
Note that a smaller memory constraint might be imposed on your
6984+
application under C<ulimit>, if containerized, or other local configuration.
6985+
69746986
=item Unrecognized character %s; marked by S<<-- HERE> after %s near column
69756987
%d
69766988

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
@@ -1012,4 +1012,11 @@ test_opcount(0, "Empty anonhash ref and direct lexical assignment",
10121012
srefgen => 1,
10131013
});
10141014

1015+
# GH #13793, GH #20586
1016+
test_opcount(0, "Don't fold string repetition once deeemed too large",
1017+
sub { my $x = "A" x (2**22) },
1018+
{
1019+
repeat => 1,
1020+
});
1021+
10151022
done_testing();

0 commit comments

Comments
 (0)