[crankshaft] Protect against deopt loops from string length overflows.
Crankshaft just unconditionally deoptimizes the code when the length of a string addition result would overflow. In order to protect against deopt loops we insert a global protector cell. We will use the same mechanism for inlining certain string additions into TurboFan as well, and protecting against overflow (we will also extend this to deal with String.prototype.concat and friends once we get there). BUG=v8:5404 R=jarin@chromium.org,hpayer@chromium.org CQ_INCLUDE_TRYBOTS=master.tryserver.v8:v8_linux64_msan_rel Committed: https://crrev.com/cb19257a926a55209a6d6858ce26d51a0447ba71 Review-Url: https://codereview.chromium.org/2348293002 Cr-Original-Commit-Position: refs/heads/master@{#39511} Cr-Commit-Position: refs/heads/master@{#39525}
This commit is contained in:
parent
209b81d8da
commit
d86038db25
@ -2368,7 +2368,7 @@ HValue* HGraphBuilder::BuildAddStringLengths(HValue* left_length,
|
||||
HValue* length = AddUncasted<HAdd>(left_length, right_length);
|
||||
// Check that length <= kMaxLength <=> length < MaxLength + 1.
|
||||
HValue* max_length = Add<HConstant>(String::kMaxLength + 1);
|
||||
if (top_info()->IsStub()) {
|
||||
if (top_info()->IsStub() || !isolate()->IsStringLengthOverflowIntact()) {
|
||||
// This is a mitigation for crbug.com/627934; the real fix
|
||||
// will be to migrate the StringAddStub to TurboFan one day.
|
||||
IfBuilder if_invalid(this);
|
||||
@ -2380,6 +2380,7 @@ HValue* HGraphBuilder::BuildAddStringLengths(HValue* left_length,
|
||||
}
|
||||
if_invalid.End();
|
||||
} else {
|
||||
graph()->MarkDependsOnStringLengthOverflow();
|
||||
Add<HBoundsCheck>(length, max_length);
|
||||
}
|
||||
return length;
|
||||
@ -3570,6 +3571,7 @@ HGraph::HGraph(CompilationInfo* info, CallInterfaceDescriptor descriptor)
|
||||
allow_code_motion_(false),
|
||||
use_optimistic_licm_(false),
|
||||
depends_on_empty_array_proto_elements_(false),
|
||||
depends_on_string_length_overflow_(false),
|
||||
type_change_checksum_(0),
|
||||
maximum_environment_size_(0),
|
||||
no_side_effects_scope_count_(0),
|
||||
|
@ -440,6 +440,13 @@ class HGraph final : public ZoneObject {
|
||||
return depends_on_empty_array_proto_elements_;
|
||||
}
|
||||
|
||||
void MarkDependsOnStringLengthOverflow() {
|
||||
if (depends_on_string_length_overflow_) return;
|
||||
info()->dependencies()->AssumePropertyCell(
|
||||
isolate()->factory()->string_length_protector());
|
||||
depends_on_string_length_overflow_ = true;
|
||||
}
|
||||
|
||||
bool has_uint32_instructions() {
|
||||
DCHECK(uint32_instructions_ == NULL || !uint32_instructions_->is_empty());
|
||||
return uint32_instructions_ != NULL;
|
||||
@ -515,6 +522,7 @@ class HGraph final : public ZoneObject {
|
||||
bool allow_code_motion_;
|
||||
bool use_optimistic_licm_;
|
||||
bool depends_on_empty_array_proto_elements_;
|
||||
bool depends_on_string_length_overflow_;
|
||||
int type_change_checksum_;
|
||||
int maximum_environment_size_;
|
||||
int no_side_effects_scope_count_;
|
||||
|
@ -1214,6 +1214,13 @@ Handle<Object> Factory::NewError(Handle<JSFunction> constructor,
|
||||
return maybe_error.ToHandleChecked();
|
||||
}
|
||||
|
||||
Handle<Object> Factory::NewInvalidStringLengthError() {
|
||||
// Invalidate the "string length" protector.
|
||||
if (isolate()->IsStringLengthOverflowIntact()) {
|
||||
isolate()->InvalidateStringLengthOverflowProtector();
|
||||
}
|
||||
return NewRangeError(MessageTemplate::kInvalidStringLength);
|
||||
}
|
||||
|
||||
#define DEFINE_ERROR(NAME, name) \
|
||||
Handle<Object> Factory::New##NAME(MessageTemplate::Template template_index, \
|
||||
|
@ -586,9 +586,7 @@ class Factory final {
|
||||
Handle<Object> NewError(Handle<JSFunction> constructor,
|
||||
Handle<String> message);
|
||||
|
||||
Handle<Object> NewInvalidStringLengthError() {
|
||||
return NewRangeError(MessageTemplate::kInvalidStringLength);
|
||||
}
|
||||
Handle<Object> NewInvalidStringLengthError();
|
||||
|
||||
Handle<Object> NewURIError() {
|
||||
return NewError(isolate()->uri_error_function(),
|
||||
|
@ -2911,6 +2911,10 @@ void Heap::CreateInitialObjects() {
|
||||
handle(Smi::FromInt(Isolate::kArrayProtectorValid), isolate()));
|
||||
set_species_protector(*species_cell);
|
||||
|
||||
cell = factory->NewPropertyCell();
|
||||
cell->set_value(Smi::FromInt(Isolate::kArrayProtectorValid));
|
||||
set_string_length_protector(*cell);
|
||||
|
||||
set_serialized_templates(empty_fixed_array());
|
||||
|
||||
set_weak_stack_trace_list(Smi::FromInt(0));
|
||||
|
@ -166,6 +166,7 @@ using v8::MemoryPressureLevel;
|
||||
V(Cell, is_concat_spreadable_protector, IsConcatSpreadableProtector) \
|
||||
V(PropertyCell, has_instance_protector, HasInstanceProtector) \
|
||||
V(Cell, species_protector, SpeciesProtector) \
|
||||
V(PropertyCell, string_length_protector, StringLengthProtector) \
|
||||
/* Special numbers */ \
|
||||
V(HeapNumber, nan_value, NanValue) \
|
||||
V(HeapNumber, hole_nan_value, HoleNanValue) \
|
||||
|
@ -147,6 +147,11 @@ bool Isolate::IsHasInstanceLookupChainIntact() {
|
||||
return has_instance_cell->value() == Smi::FromInt(kArrayProtectorValid);
|
||||
}
|
||||
|
||||
bool Isolate::IsStringLengthOverflowIntact() {
|
||||
PropertyCell* has_instance_cell = heap()->string_length_protector();
|
||||
return has_instance_cell->value() == Smi::FromInt(kArrayProtectorValid);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
|
@ -2818,6 +2818,15 @@ void Isolate::InvalidateArraySpeciesProtector() {
|
||||
DCHECK(!IsArraySpeciesLookupChainIntact());
|
||||
}
|
||||
|
||||
void Isolate::InvalidateStringLengthOverflowProtector() {
|
||||
DCHECK(factory()->string_length_protector()->value()->IsSmi());
|
||||
DCHECK(IsStringLengthOverflowIntact());
|
||||
PropertyCell::SetValueWithInvalidation(
|
||||
factory()->string_length_protector(),
|
||||
handle(Smi::FromInt(kArrayProtectorInvalid), this));
|
||||
DCHECK(!IsStringLengthOverflowIntact());
|
||||
}
|
||||
|
||||
bool Isolate::IsAnyInitialArrayPrototype(Handle<JSArray> array) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
return IsInAnyContext(*array, Context::INITIAL_ARRAY_PROTOTYPE_INDEX);
|
||||
|
@ -1010,6 +1010,7 @@ class Isolate {
|
||||
inline bool IsHasInstanceLookupChainIntact();
|
||||
bool IsIsConcatSpreadableLookupChainIntact();
|
||||
bool IsIsConcatSpreadableLookupChainIntact(JSReceiver* receiver);
|
||||
inline bool IsStringLengthOverflowIntact();
|
||||
|
||||
// On intent to set an element in object, make sure that appropriate
|
||||
// notifications occur if the set is on the elements of the array or
|
||||
@ -1028,6 +1029,7 @@ class Isolate {
|
||||
void InvalidateArraySpeciesProtector();
|
||||
void InvalidateHasInstanceProtector();
|
||||
void InvalidateIsConcatSpreadableProtector();
|
||||
void InvalidateStringLengthOverflowProtector();
|
||||
|
||||
// Returns true if array is the initial array prototype in any native context.
|
||||
bool IsAnyInitialArrayPrototype(Handle<JSArray> array);
|
||||
|
@ -234,8 +234,7 @@ RUNTIME_FUNCTION(Runtime_ThrowIncompatibleMethodReceiver) {
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ThrowInvalidStringLength) {
|
||||
HandleScope scope(isolate);
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewRangeError(MessageTemplate::kInvalidStringLength));
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ThrowIteratorResultNotAnObject) {
|
||||
|
21
test/mjsunit/regress/regress-5404.js
Normal file
21
test/mjsunit/regress/regress-5404.js
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2016 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: --allow-natives-syntax
|
||||
|
||||
function foo(a, b) {
|
||||
return a + "0123456789012";
|
||||
}
|
||||
|
||||
foo("a");
|
||||
foo("a");
|
||||
%OptimizeFunctionOnNextCall(foo);
|
||||
foo("a");
|
||||
|
||||
var a = "a".repeat(268435440);
|
||||
assertThrows(function() { foo(a); });
|
||||
|
||||
%OptimizeFunctionOnNextCall(foo);
|
||||
assertThrows(function() { foo(a); });
|
||||
assertOptimized(foo);
|
Loading…
Reference in New Issue
Block a user