Commit d86038db authored by bmeurer's avatar bmeurer Committed by Commit bot

[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}
parent 209b81d8
......@@ -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) {
......
// 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);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment