From 6c01c3fd56977fa72940132991a69731dea15846 Mon Sep 17 00:00:00 2001 From: "dslomov@chromium.org" Date: Tue, 18 Mar 2014 10:55:29 +0000 Subject: [PATCH] Apply numeric casts correctly in typed arrays and related code. R=jkummerow@chromium.org BUG=353004 LOG=Y Committed: https://code.google.com/p/v8/source/detail?r=20020 Review URL: https://codereview.chromium.org/201873005 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20022 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/api.cc | 15 +------ src/arraybuffer.js | 5 ++- src/runtime.cc | 42 ++++++++++++++++++- src/runtime.h | 3 ++ src/typedarray.js | 38 +++++++++++++---- test/mjsunit/regress/regress-353004.js | 74 ++++++++++++++++++++++++++++++++++ 6 files changed, 152 insertions(+), 25 deletions(-) create mode 100644 test/mjsunit/regress/regress-353004.js diff --git a/src/api.cc b/src/api.cc index c5241ec..5dd392f 100644 --- a/src/api.cc +++ b/src/api.cc @@ -5936,20 +5936,7 @@ void v8::ArrayBuffer::Neuter() { "Only externalized ArrayBuffers can be neutered"); LOG_API(obj->GetIsolate(), "v8::ArrayBuffer::Neuter()"); ENTER_V8(isolate); - - for (i::Handle view_obj(obj->weak_first_view(), isolate); - !view_obj->IsUndefined();) { - i::Handle view(i::JSArrayBufferView::cast(*view_obj)); - if (view->IsJSTypedArray()) { - i::JSTypedArray::cast(*view)->Neuter(); - } else if (view->IsJSDataView()) { - i::JSDataView::cast(*view)->Neuter(); - } else { - UNREACHABLE(); - } - view_obj = i::handle(view->weak_next(), isolate); - } - obj->Neuter(); + i::Runtime::NeuterArrayBuffer(obj); } diff --git a/src/arraybuffer.js b/src/arraybuffer.js index cfaa8d7..d5fd9ad 100644 --- a/src/arraybuffer.js +++ b/src/arraybuffer.js @@ -56,6 +56,9 @@ function ArrayBufferSlice(start, end) { } var relativeStart = TO_INTEGER(start); + if (!IS_UNDEFINED(end)) { + end = TO_INTEGER(end); + } var first; var byte_length = %ArrayBufferGetByteLength(this); if (relativeStart < 0) { @@ -63,7 +66,7 @@ function ArrayBufferSlice(start, end) { } else { first = MathMin(relativeStart, byte_length); } - var relativeEnd = IS_UNDEFINED(end) ? byte_length : TO_INTEGER(end); + var relativeEnd = IS_UNDEFINED(end) ? byte_length : end; var fin; if (relativeEnd < 0) { fin = MathMax(byte_length + relativeEnd, 0); diff --git a/src/runtime.cc b/src/runtime.cc index 75b9f2f..6c5ec1c 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -791,6 +791,24 @@ bool Runtime::SetupArrayBufferAllocatingData( } +void Runtime::NeuterArrayBuffer(Handle array_buffer) { + Isolate* isolate = array_buffer->GetIsolate(); + for (Handle view_obj(array_buffer->weak_first_view(), isolate); + !view_obj->IsUndefined();) { + Handle view(JSArrayBufferView::cast(*view_obj)); + if (view->IsJSTypedArray()) { + JSTypedArray::cast(*view)->Neuter(); + } else if (view->IsJSDataView()) { + JSDataView::cast(*view)->Neuter(); + } else { + UNREACHABLE(); + } + view_obj = handle(view->weak_next(), isolate); + } + array_buffer->Neuter(); +} + + RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayBufferInitialize) { HandleScope scope(isolate); ASSERT(args.length() == 2); @@ -844,7 +862,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayBufferSliceImpl) { if (target_length == 0) return isolate->heap()->undefined_value(); - ASSERT(NumberToSize(isolate, source->byte_length()) - target_length >= start); + size_t source_byte_length = NumberToSize(isolate, source->byte_length()); + CHECK(start <= source_byte_length); + CHECK(source_byte_length - start >= target_length); uint8_t* source_data = reinterpret_cast(source->backing_store()); uint8_t* target_data = reinterpret_cast(target->backing_store()); CopyBytes(target_data, source_data + start, target_length); @@ -862,6 +882,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayBufferIsView) { } +RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayBufferNeuter) { + HandleScope scope(isolate); + CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, array_buffer, 0); + ASSERT(!array_buffer->is_external()); + void* backing_store = array_buffer->backing_store(); + size_t byte_length = NumberToSize(isolate, array_buffer->byte_length()); + array_buffer->set_is_external(true); + Runtime::NeuterArrayBuffer(array_buffer); + V8::ArrayBufferAllocator()->Free(backing_store, byte_length); + return isolate->heap()->undefined_value(); +} + + void Runtime::ArrayIdToTypeAndSize( int arrayId, ExternalArrayType* array_type, size_t* element_size) { switch (arrayId) { @@ -905,7 +938,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitialize) { size_t byte_offset = NumberToSize(isolate, *byte_offset_object); size_t byte_length = NumberToSize(isolate, *byte_length_object); - ASSERT(byte_length % element_size == 0); + size_t array_buffer_byte_length = + NumberToSize(isolate, buffer->byte_length()); + CHECK(byte_offset <= array_buffer_byte_length); + CHECK(array_buffer_byte_length - byte_offset >= byte_length); + + CHECK_EQ(0, static_cast(byte_length % element_size)); size_t length = byte_length / element_size; if (length > static_cast(Smi::kMaxValue)) { diff --git a/src/runtime.h b/src/runtime.h index 99c575f..23d6d95 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -365,6 +365,7 @@ namespace internal { F(ArrayBufferGetByteLength, 1, 1)\ F(ArrayBufferSliceImpl, 3, 1) \ F(ArrayBufferIsView, 1, 1) \ + F(ArrayBufferNeuter, 1, 1) \ \ F(TypedArrayInitialize, 5, 1) \ F(TypedArrayInitializeFromArrayLike, 4, 1) \ @@ -828,6 +829,8 @@ class Runtime : public AllStatic { size_t allocated_length, bool initialize = true); + static void NeuterArrayBuffer(Handle array_buffer); + static void FreeArrayBuffer( Isolate* isolate, JSArrayBuffer* phantom_array_buffer); diff --git a/src/typedarray.js b/src/typedarray.js index 88fbb34..cba8993 100644 --- a/src/typedarray.js +++ b/src/typedarray.js @@ -49,12 +49,20 @@ endmacro macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE) function NAMEConstructByArrayBuffer(obj, buffer, byteOffset, length) { + if (!IS_UNDEFINED(byteOffset)) { + byteOffset = + ToPositiveInteger(byteOffset, "invalid_typed_array_length"); + } + if (!IS_UNDEFINED(length)) { + length = ToPositiveInteger(length, "invalid_typed_array_length"); + } + var bufferByteLength = %ArrayBufferGetByteLength(buffer); var offset; if (IS_UNDEFINED(byteOffset)) { offset = 0; } else { - offset = ToPositiveInteger(byteOffset, "invalid_typed_array_length"); + offset = byteOffset; if (offset % ELEMENT_SIZE !== 0) { throw MakeRangeError("invalid_typed_array_alignment", @@ -75,7 +83,7 @@ macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE) newByteLength = bufferByteLength - offset; newLength = newByteLength / ELEMENT_SIZE; } else { - var newLength = ToPositiveInteger(length, "invalid_typed_array_length"); + var newLength = length; newByteLength = newLength * ELEMENT_SIZE; } if ((offset + newByteLength > bufferByteLength) @@ -99,6 +107,7 @@ macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE) function NAMEConstructByArrayLike(obj, arrayLike) { var length = arrayLike.length; var l = ToPositiveInteger(length, "invalid_typed_array_length"); + if (l > %MaxSmi()) { throw MakeRangeError("invalid_typed_array_length"); } @@ -148,15 +157,19 @@ function TypedArrayGetLength() { function CreateSubArray(elementSize, constructor) { return function(begin, end) { - var srcLength = %TypedArrayGetLength(this); var beginInt = TO_INTEGER(begin); + if (!IS_UNDEFINED(end)) { + end = TO_INTEGER(end); + } + + var srcLength = %TypedArrayGetLength(this); if (beginInt < 0) { beginInt = MathMax(0, srcLength + beginInt); } else { beginInt = MathMin(srcLength, beginInt); } - var endInt = IS_UNDEFINED(end) ? srcLength : TO_INTEGER(end); + var endInt = IS_UNDEFINED(end) ? srcLength : end; if (endInt < 0) { endInt = MathMax(0, srcLength + endInt); } else { @@ -317,14 +330,23 @@ function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3 if (!IS_ARRAYBUFFER(buffer)) { throw MakeTypeError('data_view_not_array_buffer', []); } + if (!IS_UNDEFINED(byteOffset)) { + byteOffset = ToPositiveInteger(byteOffset, 'invalid_data_view_offset'); + } + if (!IS_UNDEFINED(byteLength)) { + byteLength = TO_INTEGER(byteLength); + } + var bufferByteLength = %ArrayBufferGetByteLength(buffer); - var offset = IS_UNDEFINED(byteOffset) ? - 0 : ToPositiveInteger(byteOffset, 'invalid_data_view_offset'); + + var offset = IS_UNDEFINED(byteOffset) ? 0 : byteOffset; if (offset > bufferByteLength) { throw MakeRangeError('invalid_data_view_offset'); } - var length = IS_UNDEFINED(byteLength) ? - bufferByteLength - offset : TO_INTEGER(byteLength); + + var length = IS_UNDEFINED(byteLength) + ? bufferByteLength - offset + : byteLength; if (length < 0 || offset + length > bufferByteLength) { throw new MakeRangeError('invalid_data_view_length'); } diff --git a/test/mjsunit/regress/regress-353004.js b/test/mjsunit/regress/regress-353004.js new file mode 100644 index 0000000..658fd6d --- /dev/null +++ b/test/mjsunit/regress/regress-353004.js @@ -0,0 +1,74 @@ +// Copyright 2014 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 + +var buffer1 = new ArrayBuffer(100 * 1024); + +var array1 = new Uint8Array(buffer1, {valueOf : function() { + %ArrayBufferNeuter(buffer1); + return 0; +}}); + +assertEquals(0, array1.length); + +var buffer2 = new ArrayBuffer(100 * 1024); + +assertThrows(function() { + var array2 = new Uint8Array(buffer2, 0, {valueOf : function() { + %ArrayBufferNeuter(buffer2); + return 100 * 1024; + }}); +}, RangeError); + + +var buffer3 = new ArrayBuffer(100 * 1024 * 1024); +var dataView1 = new DataView(buffer3, {valueOf : function() { + %ArrayBufferNeuter(buffer3); + return 0; +}}); + +assertEquals(0, dataView1.byteLength); + +var buffer4 = new ArrayBuffer(100 * 1024); +assertThrows(function() { + var dataView2 = new DataView(buffer4, 0, {valueOf : function() { + %ArrayBufferNeuter(buffer4); + return 100 * 1024 * 1024; + }}); +}, RangeError); + + +var buffer5 = new ArrayBuffer(100 * 1024); +var buffer6 = buffer5.slice({valueOf : function() { + %ArrayBufferNeuter(buffer5); + return 0; +}}, 100 * 1024 * 1024); +assertEquals(0, buffer6.byteLength); + + +var buffer7 = new ArrayBuffer(100 * 1024 * 1024); +var buffer8 = buffer7.slice(0, {valueOf : function() { + %ArrayBufferNeuter(buffer7); + return 100 * 1024 * 1024; +}}); +assertEquals(0, buffer8.byteLength); + +var buffer9 = new ArrayBuffer(1024); +var array9 = new Uint8Array(buffer9); +var array10 = array9.subarray({valueOf : function() { + %ArrayBufferNeuter(buffer9); + return 0; + }}, 1024); +assertEquals(0, array9.length); +assertEquals(0, array10.length); + +var buffer11 = new ArrayBuffer(1024); +var array11 = new Uint8Array(buffer11); +var array12 = array11.subarray(0, {valueOf : function() { + %ArrayBufferNeuter(buffer11); + return 1024; + }}); +assertEquals(0, array11.length); +assertEquals(0, array12.length); -- 2.7.4