Skip to content

Commit c57cd9b

Browse files
ofrobotsgibfahn
authored andcommitted
deps: V8: cherry-pick cfc3404f from upstream
Original commit message: [string] Fix regexp fast path in MaybeCallFunctionAtSymbol The regexp fast path in MaybeCallFunctionAtSymbol had an issue in which we'd call ToString after checking that the given {object} was a fast regexp and deciding to take the fast path. This is invalid since ToString() can call into user-controlled JS and may mutate {object}. There's no way to place the ToString call correctly in this instance: 1 before BranchIfFastRegExp, it's a spec violation if we end up on the slow regexp path; 2 the problem with the current location is already described above; 3 and we can't place it into the fast-path regexp builtin (e.g. RegExpReplace) either due to the same reasons as 1. The solution in this CL is to restrict the fast path to string arguments only, i.e. cases where ToString would be a nop and can safely be skipped. Bug: chromium:782145 Change-Id: Ifd35b3a9a6cf2e77c96cb860a8ec98eaec35aa85 Reviewed-on: https://chromium-review.googlesource.com/758257 Commit-Queue: Jakob Gruber <[email protected]> Reviewed-by: Yang Guo <[email protected]> Cr-Commit-Position: refs/heads/master@{#49213} Refs: v8/v8@cfc3404 Refs: v8/v8@55a9807 PR-URL: #17354
1 parent f34ee5c commit c57cd9b

File tree

4 files changed

+40
-14
lines changed

4 files changed

+40
-14
lines changed

deps/v8/include/v8-version.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#define V8_MAJOR_VERSION 6
1212
#define V8_MINOR_VERSION 1
1313
#define V8_BUILD_NUMBER 534
14-
#define V8_PATCH_LEVEL 49
14+
#define V8_PATCH_LEVEL 50
1515

1616
// Use 1 for candidates and 0 otherwise.
1717
// (Boolean macro values are not supported by all preprocessors.)

deps/v8/src/builtins/builtins-string-gen.cc

+16-13
Original file line numberDiff line numberDiff line change
@@ -1004,9 +1004,9 @@ void StringBuiltinsAssembler::RequireObjectCoercible(Node* const context,
10041004
}
10051005

10061006
void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
1007-
Node* const context, Node* const object, Handle<Symbol> symbol,
1008-
const NodeFunction0& regexp_call, const NodeFunction1& generic_call,
1009-
CodeStubArguments* args) {
1007+
Node* const context, Node* const object, Node* const maybe_string,
1008+
Handle<Symbol> symbol, const NodeFunction0& regexp_call,
1009+
const NodeFunction1& generic_call, CodeStubArguments* args) {
10101010
Label out(this);
10111011

10121012
// Smis definitely don't have an attached symbol.
@@ -1036,14 +1036,21 @@ void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
10361036
}
10371037

10381038
// Take the fast path for RegExps.
1039+
// There's two conditions: {object} needs to be a fast regexp, and
1040+
// {maybe_string} must be a string (we can't call ToString on the fast path
1041+
// since it may mutate {object}).
10391042
{
10401043
Label stub_call(this), slow_lookup(this);
10411044

1045+
GotoIf(TaggedIsSmi(maybe_string), &slow_lookup);
1046+
GotoIfNot(IsString(maybe_string), &slow_lookup);
1047+
10421048
RegExpBuiltinsAssembler regexp_asm(state());
10431049
regexp_asm.BranchIfFastRegExp(context, object, object_map, &stub_call,
10441050
&slow_lookup);
10451051

10461052
BIND(&stub_call);
1053+
// TODO(jgruber): Add a no-JS scope once it exists.
10471054
Node* const result = regexp_call();
10481055
if (args == nullptr) {
10491056
Return(result);
@@ -1149,12 +1156,10 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
11491156
// Redirect to replacer method if {search[@@replace]} is not undefined.
11501157

11511158
MaybeCallFunctionAtSymbol(
1152-
context, search, isolate()->factory()->replace_symbol(),
1159+
context, search, receiver, isolate()->factory()->replace_symbol(),
11531160
[=]() {
1154-
Node* const subject_string = ToString_Inline(context, receiver);
1155-
1156-
return CallBuiltin(Builtins::kRegExpReplace, context, search,
1157-
subject_string, replace);
1161+
return CallBuiltin(Builtins::kRegExpReplace, context, search, receiver,
1162+
replace);
11581163
},
11591164
[=](Node* fn) {
11601165
Callable call_callable = CodeFactory::Call(isolate());
@@ -1392,12 +1397,10 @@ TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
13921397
// Redirect to splitter method if {separator[@@split]} is not undefined.
13931398

13941399
MaybeCallFunctionAtSymbol(
1395-
context, separator, isolate()->factory()->split_symbol(),
1400+
context, separator, receiver, isolate()->factory()->split_symbol(),
13961401
[=]() {
1397-
Node* const subject_string = ToString_Inline(context, receiver);
1398-
1399-
return CallBuiltin(Builtins::kRegExpSplit, context, separator,
1400-
subject_string, limit);
1402+
return CallBuiltin(Builtins::kRegExpSplit, context, separator, receiver,
1403+
limit);
14011404
},
14021405
[=](Node* fn) {
14031406
Callable call_callable = CodeFactory::Call(isolate());

deps/v8/src/builtins/builtins-string-gen.h

+2
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,11 @@ class StringBuiltinsAssembler : public CodeStubAssembler {
8989
// }
9090
//
9191
// Contains fast paths for Smi and RegExp objects.
92+
// Important: {regexp_call} may not contain any code that can call into JS.
9293
typedef std::function<Node*()> NodeFunction0;
9394
typedef std::function<Node*(Node* fn)> NodeFunction1;
9495
void MaybeCallFunctionAtSymbol(Node* const context, Node* const object,
96+
Node* const maybe_string,
9597
Handle<Symbol> symbol,
9698
const NodeFunction0& regexp_call,
9799
const NodeFunction1& generic_call,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2017 the V8 project authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
function newFastRegExp() { return new RegExp('.'); }
6+
function toSlowRegExp(re) { re.exec = 42; }
7+
8+
let re = newFastRegExp();
9+
const evil_nonstring = { [Symbol.toPrimitive]: () => toSlowRegExp(re) };
10+
const empty_string = "";
11+
12+
String.prototype.replace.call(evil_nonstring, re, empty_string);
13+
14+
re = newFastRegExp();
15+
String.prototype.match.call(evil_nonstring, re, empty_string);
16+
17+
re = newFastRegExp();
18+
String.prototype.search.call(evil_nonstring, re, empty_string);
19+
20+
re = newFastRegExp();
21+
String.prototype.split.call(evil_nonstring, re, empty_string);

0 commit comments

Comments
 (0)