[regexp] Fix two more possible shape changes on fast path
This CL fixes two more cases in which a regexp could unintentionally transition to slow mode while on the fast path, leading to possible OOB accesses of lastIndex. In both cases, the fix is to re-check the shape and possibly bail to runtime. BUG=chromium:708247,v8:6210 Review-Url: https://codereview.chromium.org/2803603005 Cr-Commit-Position: refs/heads/master@{#44451}
This commit is contained in:
parent
9d7354f9f3
commit
1ccf6c0943
@ -2295,13 +2295,23 @@ TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
|
|||||||
// been changed.
|
// been changed.
|
||||||
|
|
||||||
// Convert {maybe_limit} to a uint32, capping at the maximal smi value.
|
// Convert {maybe_limit} to a uint32, capping at the maximal smi value.
|
||||||
Variable var_limit(this, MachineRepresentation::kTagged);
|
Variable var_limit(this, MachineRepresentation::kTagged, maybe_limit);
|
||||||
Label if_limitissmimax(this), limit_done(this);
|
Label if_limitissmimax(this), limit_done(this), runtime(this);
|
||||||
|
|
||||||
GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
|
GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
|
||||||
|
GotoIf(TaggedIsPositiveSmi(maybe_limit), &limit_done);
|
||||||
|
|
||||||
|
Node* const limit = ToUint32(context, maybe_limit);
|
||||||
{
|
{
|
||||||
Node* const limit = ToUint32(context, maybe_limit);
|
// ToUint32(limit) could potentially change the shape of the RegExp
|
||||||
|
// object. Recheck that we are still on the fast path and bail to runtime
|
||||||
|
// otherwise.
|
||||||
|
{
|
||||||
|
Label next(this);
|
||||||
|
BranchIfFastRegExp(context, regexp, LoadMap(regexp), &next, &runtime);
|
||||||
|
Bind(&next);
|
||||||
|
}
|
||||||
|
|
||||||
GotoIfNot(TaggedIsSmi(limit), &if_limitissmimax);
|
GotoIfNot(TaggedIsSmi(limit), &if_limitissmimax);
|
||||||
|
|
||||||
var_limit.Bind(limit);
|
var_limit.Bind(limit);
|
||||||
@ -2321,6 +2331,14 @@ TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
|
|||||||
Node* const limit = var_limit.value();
|
Node* const limit = var_limit.value();
|
||||||
RegExpPrototypeSplitBody(context, regexp, string, limit);
|
RegExpPrototypeSplitBody(context, regexp, string, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bind(&runtime);
|
||||||
|
{
|
||||||
|
// The runtime call passes in limit to ensure the second ToUint32(limit)
|
||||||
|
// call is not observable.
|
||||||
|
CSA_ASSERT(this, IsHeapNumberMap(LoadReceiverMap(limit)));
|
||||||
|
Return(CallRuntime(Runtime::kRegExpSplit, context, regexp, string, limit));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ES#sec-regexp.prototype-@@split
|
// ES#sec-regexp.prototype-@@split
|
||||||
@ -2692,6 +2710,15 @@ TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) {
|
|||||||
Node* const replace_string =
|
Node* const replace_string =
|
||||||
CallBuiltin(Builtins::kToString, context, replace_value);
|
CallBuiltin(Builtins::kToString, context, replace_value);
|
||||||
|
|
||||||
|
// ToString(replaceValue) could potentially change the shape of the RegExp
|
||||||
|
// object. Recheck that we are still on the fast path and bail to runtime
|
||||||
|
// otherwise.
|
||||||
|
{
|
||||||
|
Label next(this);
|
||||||
|
BranchIfFastRegExp(context, regexp, LoadMap(regexp), &next, &runtime);
|
||||||
|
Bind(&next);
|
||||||
|
}
|
||||||
|
|
||||||
Node* const dollar_string = HeapConstant(
|
Node* const dollar_string = HeapConstant(
|
||||||
isolate()->factory()->LookupSingleCharacterStringFromCode('$'));
|
isolate()->factory()->LookupSingleCharacterStringFromCode('$'));
|
||||||
Node* const dollar_ix =
|
Node* const dollar_ix =
|
||||||
|
34
test/mjsunit/regress/regress-6210.js
Normal file
34
test/mjsunit/regress/regress-6210.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2017 the V8 project authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// Flags: --predictable
|
||||||
|
|
||||||
|
const str = '2016-01-02';
|
||||||
|
|
||||||
|
function testToUint32InSplit() {
|
||||||
|
var re;
|
||||||
|
function toDictMode() {
|
||||||
|
re.x = 42;
|
||||||
|
delete re.x;
|
||||||
|
return "def";
|
||||||
|
}
|
||||||
|
|
||||||
|
re = /./g; // Needs to be global to trigger lastIndex accesses.
|
||||||
|
return re[Symbol.replace]("abc", { valueOf: toDictMode });
|
||||||
|
}
|
||||||
|
|
||||||
|
function testToStringInReplace() {
|
||||||
|
var re;
|
||||||
|
function toDictMode() {
|
||||||
|
re.x = 42;
|
||||||
|
delete re.x;
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
re = /./g; // Needs to be global to trigger lastIndex accesses.
|
||||||
|
return re[Symbol.split]("abc", { valueOf: toDictMode });
|
||||||
|
}
|
||||||
|
|
||||||
|
testToUint32InSplit();
|
||||||
|
testToStringInReplace();
|
Loading…
Reference in New Issue
Block a user