Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

TypedArrays: Fix size/index overflow on 32-bit. #4550

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
157 changes: 73 additions & 84 deletions src/v8_typed_array.cc
Expand Up @@ -131,7 +131,7 @@ class ArrayBuffer {

static v8::Handle<v8::Value> slice(const v8::Arguments& args) {
if (args.Length() < 1)
return ThrowError("Wrong number of arguments.");
return ThrowTypeError("Not enough arguments");

unsigned int length =
args.This()->Get(v8::String::New("byteLength"))->Uint32Value();
Expand Down Expand Up @@ -217,7 +217,6 @@ class TypedArray {
v8::Local<v8::Signature> default_signature = v8::Signature::New(ft_cache);

static BatchedMethods methods[] = {
{ "get", &TypedArray<TBytes, TEAType>::get },
{ "set", &TypedArray<TBytes, TEAType>::set },
{ "slice", &TypedArray<TBytes, TEAType>::subarray },
{ "subarray", &TypedArray<TBytes, TEAType>::subarray },
Expand Down Expand Up @@ -350,77 +349,63 @@ class TypedArray {
return args.This();
}

static v8::Handle<v8::Value> get(const v8::Arguments& args) {
if (args.Length() < 1)
return ThrowError("Wrong number of arguments.");

if (args[0]->IsNumber())
return args.This()->Get(args[0]->Uint32Value());

return v8::Undefined();
}

static v8::Handle<v8::Value> set(const v8::Arguments& args) {
if (args.Length() < 1)
return ThrowError("Wrong number of arguments.");

//if (!args[0]->IsObject())
// return ThrowTypeError("Type error.");

if (args[0]->IsNumber()) { // index, <type> value
args.This()->Set(args[0]->Uint32Value(), args[1]);
} else if (args[0]->IsObject()) {
v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(args[0]);

if (TypedArray<TBytes, TEAType>::HasInstance(obj)) { // ArrayBufferView.
if (args[1]->Int32Value() < 0)
return ThrowRangeError("Offset may not be negative.");

unsigned int offset = args[1]->Uint32Value();
unsigned int src_length =
obj->Get(v8::String::New("length"))->Uint32Value();
unsigned int dst_length =
args.This()->Get(v8::String::New("length"))->Uint32Value();
if (offset > dst_length)
return ThrowRangeError("Offset out of range.");

if (src_length > dst_length - offset)
return ThrowRangeError("Offset/length out of range.");

// We don't want to get the buffer pointer, because that means we'll have
// to just do the calculations for byteOffset / byteLength again.
// Instead just use the pointer on the external array data.
void* src_ptr = obj->GetIndexedPropertiesExternalArrayData();
void* dst_ptr = args.This()->GetIndexedPropertiesExternalArrayData();

// From the spec:
// If the input array is a TypedArray, the two arrays may use the same
// underlying ArrayBuffer. In this situation, setting the values takes
// place as if all the data is first copied into a temporary buffer that
// does not overlap either of the arrays, and then the data from the
// temporary buffer is copied into the current array.
memmove(reinterpret_cast<char*>(dst_ptr) + offset * TBytes, src_ptr,
src_length * TBytes);
} else { // type[]
if (args[1]->Int32Value() < 0)
return ThrowRangeError("Offset may not be negative.");

unsigned int src_length =
obj->Get(v8::String::New("length"))->Uint32Value();
unsigned int dst_length =
args.This()->Get(v8::String::New("length"))->Uint32Value();
unsigned int offset = args[1]->Uint32Value();

if (offset > dst_length)
return ThrowRangeError("Offset out of range.");

if (src_length > dst_length - offset)
return ThrowRangeError("Offset/length out of range.");

for (uint32_t i = 0; i < src_length; ++i) {
// Use the v8 setter to deal with typing. Maybe slow?
args.This()->Set(i + offset, obj->Get(i));
}
return ThrowTypeError("Not enough arguments");

if (!args[0]->IsObject())
return ThrowTypeError("Invalid argument");

v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(args[0]);

if (TypedArray<TBytes, TEAType>::HasInstance(obj)) { // ArrayBufferView.
if (args[1]->Int32Value() < 0)
return ThrowRangeError("Offset may not be negative.");

unsigned int offset = args[1]->Uint32Value();
unsigned int src_length =
obj->Get(v8::String::New("length"))->Uint32Value();
unsigned int dst_length =
args.This()->Get(v8::String::New("length"))->Uint32Value();
if (offset > dst_length)
return ThrowRangeError("Offset out of range.");

if (src_length > dst_length - offset)
return ThrowRangeError("Index is out of range.");

// We don't want to get the buffer pointer, because that means we'll have
// to just do the calculations for byteOffset / byteLength again.
// Instead just use the pointer on the external array data.
void* src_ptr = obj->GetIndexedPropertiesExternalArrayData();
void* dst_ptr = args.This()->GetIndexedPropertiesExternalArrayData();

// From the spec:
// If the input array is a TypedArray, the two arrays may use the same
// underlying ArrayBuffer. In this situation, setting the values takes
// place as if all the data is first copied into a temporary buffer that
// does not overlap either of the arrays, and then the data from the
// temporary buffer is copied into the current array.
memmove(reinterpret_cast<char*>(dst_ptr) + offset * TBytes, src_ptr,
src_length * TBytes);
} else { // type[]
if (args[1]->Int32Value() < 0)
return ThrowRangeError("Offset may not be negative.");

unsigned int src_length =
obj->Get(v8::String::New("length"))->Uint32Value();
unsigned int dst_length =
args.This()->Get(v8::String::New("length"))->Uint32Value();
unsigned int offset = args[1]->Uint32Value();

if (offset > dst_length)
return ThrowRangeError("Offset out of range.");

if (src_length > dst_length - offset)
return ThrowRangeError("Index is out of range.");

for (uint32_t i = 0; i < src_length; ++i) {
// Use the v8 setter to deal with typing. Maybe slow?
args.This()->Set(i + offset, obj->Get(i));
}
}

Expand All @@ -439,11 +424,11 @@ class TypedArray {

if (begin < 0) begin = length + begin;
if (begin < 0) begin = 0;
if ((unsigned)begin > length) begin = length;
if (static_cast<unsigned int>(begin) > length) begin = length;

if (end < 0) end = length + end;
if (end < 0) end = 0;
if ((unsigned)end > length) end = length;
if (static_cast<unsigned int>(end) > length) end = length;

if (begin > end) begin = end;

Expand Down Expand Up @@ -617,7 +602,7 @@ class DataView {
return ThrowTypeError("Constructor cannot be called as a function.");

if (args.Length() < 1)
return ThrowError("Wrong number of arguments.");
return ThrowTypeError("Not enough arguments");

if (!args[0]->IsObject())
return ThrowError("Object must be an ArrayBuffer.");
Expand All @@ -631,16 +616,16 @@ class DataView {
unsigned int byte_offset = args[1]->Uint32Value();

if (args[1]->Int32Value() < 0 || byte_offset >= byte_length)
return ThrowRangeError("byteOffset out of range.");
return ThrowRangeError("Size is too large (or is negative).");

if (!args[2]->IsUndefined()) {
if (args[2]->Int32Value() < 0)
return ThrowRangeError("byteLength out of range.");
return ThrowRangeError("Size is too large (or is negative).");
unsigned int new_byte_length = args[2]->Uint32Value();
if (new_byte_length > byte_length)
return ThrowRangeError("byteLength out of range.");
return ThrowRangeError("Size is too large (or is negative).");
if (byte_offset + new_byte_length > byte_length)
return ThrowRangeError("byteOffset/byteLength out of range.");
return ThrowRangeError("Size is too large (or is negative).");
byte_length = new_byte_length;
} else {
// Adjust the original byte_length from total length to length to end.
Expand Down Expand Up @@ -669,7 +654,7 @@ class DataView {
template <typename T>
static v8::Handle<v8::Value> getGeneric(const v8::Arguments& args) {
if (args.Length() < 1)
return ThrowError("Wrong number of arguments.");
return ThrowTypeError("Not enough arguments");

unsigned int index = args[0]->Uint32Value();
bool little_endian = args[1]->BooleanValue();
Expand All @@ -679,8 +664,10 @@ class DataView {
int size = args.This()->GetIndexedPropertiesExternalArrayDataLength() *
element_size;

if (index + sizeof(T) > (unsigned)size) // TODO(deanm): integer overflow.
return ThrowError("Index out of range.");
if (size <= 0 || static_cast<unsigned int>(size) < sizeof(T) ||
index > static_cast<unsigned int>(size) - sizeof(T)) {
return ThrowError("IndexSizeError: DOM Exception 1");
}

void* ptr = reinterpret_cast<char*>(
args.This()->GetIndexedPropertiesExternalArrayData()) + index;
Expand All @@ -701,7 +688,7 @@ class DataView {
template <typename T>
static v8::Handle<v8::Value> setGeneric(const v8::Arguments& args) {
if (args.Length() < 2)
return ThrowError("Wrong number of arguments.");
return ThrowTypeError("Not enough arguments");

unsigned int index = args[0]->Int32Value();
bool little_endian = args[2]->BooleanValue();
Expand All @@ -711,8 +698,10 @@ class DataView {
int size = args.This()->GetIndexedPropertiesExternalArrayDataLength() *
element_size;

if (index + sizeof(T) > (unsigned)size) // TODO(deanm): integer overflow.
return ThrowError("Index out of range.");
if (size <= 0 || static_cast<unsigned int>(size) < sizeof(T) ||
index > static_cast<unsigned int>(size) - sizeof(T)) {
return ThrowError("IndexSizeError: DOM Exception 1");
}

void* ptr = reinterpret_cast<char*>(
args.This()->GetIndexedPropertiesExternalArrayData()) + index;
Expand Down
14 changes: 7 additions & 7 deletions test/simple/test-typed-arrays.js
Expand Up @@ -153,13 +153,13 @@ sub[1] = 0x34;
assert.equal(uint8[2], 0x12);
assert.equal(uint8[3], 0x34);

// test .set(index, value), .set(arr, offset) and .get(index)
uint8.set(1, 0x09);
// test .set(index, value), .set(arr, offset)
uint8[1] = 0x09;
uint8.set([0x0a, 0x0b], 2);

assert.equal(uint8.get(1), 0x09);
assert.equal(uint8.get(2), 0x0a);
assert.equal(uint8.get(3), 0x0b);
assert.equal(uint8[1], 0x09);
assert.equal(uint8[2], 0x0a);
assert.equal(uint8[3], 0x0b);

// test clamped array
var uint8c = new Uint8ClampedArray(buffer);
Expand All @@ -169,8 +169,8 @@ uint8c[1] = 257;
assert.equal(uint8c[0], 0);
assert.equal(uint8c[1], 255);

uint8c.set(0, -10);
uint8c.set(1, 260);
uint8c[0] = -10;
uint8c[1] = 260;

assert.equal(uint8c[0], 0);
assert.equal(uint8c[1], 255);
Expand Down