Commit e28f7fc9 authored by Peter Marshall's avatar Peter Marshall Committed by Commit Bot

[builtins] Don't clear buffer memory that will be overwritten.

Currently we initialize the allocated buffer to be full of 0s, which
adds significant overhead.

TypedArrayConstructByArrayLike will always either fully initialize the
buffer, or throw an exception, in which case the buffer will not be
leaked to user code.

The length of the new TypedArray (and thus the buffer) is derived from
the length of the source Array/TypedArray, so we know that we will
always set every byte of the new buffer, or throw trying.

Bug:v8:5977

Change-Id: I8ceaa883cfad85f8708a5bdaada3ce463d97e007
Reviewed-on: https://chromium-review.googlesource.com/469348Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#44447}
parent 95c5c76f
......@@ -2691,6 +2691,13 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
InstallWithIntrinsicDefaultProto(isolate, array_buffer_fun,
Context::ARRAY_BUFFER_FUN_INDEX);
InstallSpeciesGetter(array_buffer_fun);
Handle<JSFunction> array_buffer_noinit_fun = SimpleCreateFunction(
isolate,
factory->NewStringFromAsciiChecked(
"arrayBufferConstructor_DoNotInitialize"),
Builtins::kArrayBufferConstructor_DoNotInitialize, 1, false);
native_context()->set_array_buffer_noinit_fun(*array_buffer_noinit_fun);
}
{ // -- T y p e d A r r a y
......
......@@ -34,26 +34,16 @@ BUILTIN(ArrayBufferConstructor) {
handle(target->shared()->name(), isolate)));
}
// ES6 section 24.1.2.1 ArrayBuffer ( length ) for the [[Construct]] case.
BUILTIN(ArrayBufferConstructor_ConstructStub) {
HandleScope scope(isolate);
Handle<JSFunction> target = args.target();
Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
Handle<Object> length = args.atOrUndefined(isolate, 1);
DCHECK(*target == target->native_context()->array_buffer_fun() ||
*target == target->native_context()->shared_array_buffer_fun());
Handle<Object> number_length;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_length,
Object::ToInteger(isolate, length));
if (number_length->Number() < 0.0) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
}
namespace {
Object* ConstructBuffer(Isolate* isolate, Handle<JSFunction> target,
Handle<JSReceiver> new_target, Handle<Object> length,
bool initialize) {
Handle<JSObject> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
JSObject::New(target, new_target));
size_t byte_length;
if (!TryNumberToSize(*number_length, &byte_length)) {
if (!TryNumberToSize(*length, &byte_length)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
}
......@@ -62,7 +52,7 @@ BUILTIN(ArrayBufferConstructor_ConstructStub) {
? SharedFlag::kNotShared
: SharedFlag::kShared;
if (!JSArrayBuffer::SetupAllocatingData(Handle<JSArrayBuffer>::cast(result),
isolate, byte_length, true,
isolate, byte_length, initialize,
shared_flag)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kArrayBufferAllocationFailed));
......@@ -70,6 +60,38 @@ BUILTIN(ArrayBufferConstructor_ConstructStub) {
return *result;
}
} // namespace
// ES6 section 24.1.2.1 ArrayBuffer ( length ) for the [[Construct]] case.
BUILTIN(ArrayBufferConstructor_ConstructStub) {
HandleScope scope(isolate);
Handle<JSFunction> target = args.target();
Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
Handle<Object> length = args.atOrUndefined(isolate, 1);
DCHECK(*target == target->native_context()->array_buffer_fun() ||
*target == target->native_context()->shared_array_buffer_fun());
Handle<Object> number_length;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_length,
Object::ToInteger(isolate, length));
if (number_length->Number() < 0.0) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
}
return ConstructBuffer(isolate, target, new_target, number_length, true);
}
// This is a helper to construct an ArrayBuffer with uinitialized memory.
// This means the caller must ensure the buffer is totally initialized in
// all cases, or we will expose uinitialized memory to user code.
BUILTIN(ArrayBufferConstructor_DoNotInitialize) {
HandleScope scope(isolate);
Handle<JSFunction> target(isolate->native_context()->array_buffer_fun());
Handle<Object> length = args.atOrUndefined(isolate, 1);
return ConstructBuffer(isolate, target, target, length, false);
}
// ES6 section 24.1.4.1 get ArrayBuffer.prototype.byteLength
BUILTIN(ArrayBufferPrototypeGetByteLength) {
const char* const kMethodName = "get ArrayBuffer.prototype.byteLength";
......
......@@ -301,6 +301,7 @@ namespace internal {
/* ArrayBuffer */ \
CPP(ArrayBufferConstructor) \
CPP(ArrayBufferConstructor_ConstructStub) \
CPP(ArrayBufferConstructor_DoNotInitialize) \
CPP(ArrayBufferPrototypeGetByteLength) \
CPP(ArrayBufferIsView) \
CPP(ArrayBufferPrototypeSlice) \
......
......@@ -289,16 +289,17 @@ TF_BUILTIN(TypedArrayInitialize, TypedArrayBuiltinsAssembler) {
void TypedArrayBuiltinsAssembler::InitializeBasedOnLength(
Node* const holder, Node* const length, Node* const element_size,
Node* const byte_offset, Node* const initialize, Node* const context) {
Label allocate_buffer(this), do_init(this);
Label allocate_buffer(this), allocate_buffer_noinit(this), do_init(this);
Variable maybe_buffer(this, MachineRepresentation::kTagged, NullConstant());
// SmiMul returns a heap number in case of Smi overflow.
Node* byte_length = SmiMul(length, element_size);
GotoIf(TaggedIsNotSmi(byte_length), &allocate_buffer);
Branch(SmiLessThanOrEqual(byte_length,
GotoIf(SmiLessThanOrEqual(byte_length,
SmiConstant(FLAG_typed_array_max_size_in_heap)),
&do_init, &allocate_buffer);
&do_init);
Branch(IsTrue(initialize), &allocate_buffer, &allocate_buffer_noinit);
BIND(&allocate_buffer);
{
......@@ -309,7 +310,17 @@ void TypedArrayBuiltinsAssembler::InitializeBasedOnLength(
Goto(&do_init);
}
BIND(&do_init);
Bind(&allocate_buffer_noinit);
{
Node* const buffer_constructor_noinit = LoadContextElement(
LoadNativeContext(context), Context::ARRAY_BUFFER_NOINIT_FUN_INDEX);
maybe_buffer.Bind(CallJS(CodeFactory::Call(isolate()), context,
buffer_constructor_noinit, UndefinedConstant(),
byte_length));
Goto(&do_init);
}
Bind(&do_init);
{
DoInitialize(holder, length, maybe_buffer.value(), byte_offset, byte_length,
initialize, context);
......
......@@ -211,6 +211,7 @@ enum ContextLookupFlags {
V(ALLOW_CODE_GEN_FROM_STRINGS_INDEX, Object, allow_code_gen_from_strings) \
V(ARRAY_BUFFER_FUN_INDEX, JSFunction, array_buffer_fun) \
V(ARRAY_BUFFER_MAP_INDEX, Map, array_buffer_map) \
V(ARRAY_BUFFER_NOINIT_FUN_INDEX, JSFunction, array_buffer_noinit_fun) \
V(ARRAY_FUNCTION_INDEX, JSFunction, array_function) \
V(ASYNC_FROM_SYNC_ITERATOR_MAP_INDEX, Map, async_from_sync_iterator_map) \
V(ASYNC_FUNCTION_AWAIT_REJECT_SHARED_FUN, SharedFunctionInfo, \
......
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