Skip to content

Commit

Permalink
[regexp] Fix two more possible shape changes on fast path
Browse files Browse the repository at this point in the history
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}
  • Loading branch information
schuay authored and Commit bot committed Apr 6, 2017
1 parent 9d7354f commit 1ccf6c0
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 3 deletions.
33 changes: 30 additions & 3 deletions src/builtins/builtins-regexp-gen.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2295,13 +2295,23 @@ TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
// been changed.

// Convert {maybe_limit} to a uint32, capping at the maximal smi value.
Variable var_limit(this, MachineRepresentation::kTagged);
Label if_limitissmimax(this), limit_done(this);
Variable var_limit(this, MachineRepresentation::kTagged, maybe_limit);
Label if_limitissmimax(this), limit_done(this), runtime(this);

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);

var_limit.Bind(limit);
Expand All @@ -2321,6 +2331,14 @@ TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
Node* const limit = var_limit.value();
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
Expand Down Expand Up @@ -2692,6 +2710,15 @@ TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) {
Node* const replace_string =
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(
isolate()->factory()->LookupSingleCharacterStringFromCode('$'));
Node* const dollar_ix =
Expand Down
34 changes: 34 additions & 0 deletions test/mjsunit/regress/regress-6210.js
Original file line number Diff line number Diff line change
@@ -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();

0 comments on commit 1ccf6c0

Please sign in to comment.