diff --git a/runtime/lib/string.cc b/runtime/lib/string.cc index 201fe2c6758..947ba4f9640 100644 --- a/runtime/lib/string.cc +++ b/runtime/lib/string.cc @@ -287,8 +287,17 @@ DEFINE_NATIVE_ENTRY(OneByteString_splitWithCharCode, 0, 2) { } DEFINE_NATIVE_ENTRY(OneByteString_allocate, 0, 1) { - GET_NON_NULL_NATIVE_ARGUMENT(Smi, length_obj, arguments->NativeArgAt(0)); - return OneByteString::New(length_obj.Value(), Heap::kNew); + GET_NON_NULL_NATIVE_ARGUMENT(Integer, length_obj, arguments->NativeArgAt(0)); + const int64_t length = length_obj.AsInt64Value(); + if ((length < 0) || (length > OneByteString::kMaxElements)) { + // Assume that negative lengths are the result of wrapping in code in + // string_patch.dart. + const Instance& exception = + Instance::Handle(thread->isolate()->object_store()->out_of_memory()); + Exceptions::Throw(thread, exception); + UNREACHABLE(); + } + return OneByteString::New(static_cast(length), Heap::kNew); } DEFINE_NATIVE_ENTRY(OneByteString_allocateFromOneByteList, 0, 3) { diff --git a/runtime/lib/typed_data.cc b/runtime/lib/typed_data.cc index af97a0f4e69..49805167a90 100644 --- a/runtime/lib/typed_data.cc +++ b/runtime/lib/typed_data.cc @@ -9,6 +9,7 @@ #include "vm/exceptions.h" #include "vm/native_entry.h" #include "vm/object.h" +#include "vm/object_store.h" namespace dart { @@ -189,12 +190,18 @@ DEFINE_NATIVE_ENTRY(TypedData_setRange, 0, 7) { // Argument 0 is type arguments and is ignored. #define TYPED_DATA_NEW(name) \ DEFINE_NATIVE_ENTRY(TypedData_##name##_new, 0, 2) { \ - GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(1)); \ + GET_NON_NULL_NATIVE_ARGUMENT(Integer, length, arguments->NativeArgAt(1)); \ const intptr_t cid = kTypedData##name##Cid; \ - const intptr_t len = length.Value(); \ const intptr_t max = TypedData::MaxElements(cid); \ - LengthCheck(len, max); \ - return TypedData::New(cid, len); \ + const int64_t len = length.AsInt64Value(); \ + if (len < 0) { \ + Exceptions::ThrowRangeError("length", length, 0, max); \ + } else if (len > max) { \ + const Instance& exception = Instance::Handle( \ + zone, thread->isolate()->object_store()->out_of_memory()); \ + Exceptions::Throw(thread, exception); \ + } \ + return TypedData::New(cid, static_cast(len)); \ } #define TYPED_DATA_NEW_NATIVE(name) TYPED_DATA_NEW(name) diff --git a/runtime/vm/compiler/asm_intrinsifier_arm.cc b/runtime/vm/compiler/asm_intrinsifier_arm.cc index ae216fa9a1f..b83a7723ae1 100644 --- a/runtime/vm/compiler/asm_intrinsifier_arm.cc +++ b/runtime/vm/compiler/asm_intrinsifier_arm.cc @@ -1936,19 +1936,22 @@ void AsmIntrinsifier::OneByteString_getHashCode(Assembler* assembler, __ Ret(); } -// Allocates one-byte string of length 'end - start'. The content is not -// initialized. -// 'length-reg' (R2) contains tagged length. +// Allocates a _OneByteString. The content is not initialized. +// 'length-reg' (R2) contains the desired length as a _Smi or _Mint. // Returns new string as tagged pointer in R0. -static void TryAllocateOnebyteString(Assembler* assembler, +static void TryAllocateOneByteString(Assembler* assembler, Label* ok, Label* failure) { const Register length_reg = R2; - Label fail; + // _Mint length: call to runtime to produce error. + __ BranchIfNotSmi(length_reg, failure); + // Negative length: call to runtime to produce error. + __ cmp(length_reg, Operand(0)); + __ b(failure, LT); + NOT_IN_PRODUCT(__ LoadAllocationStatsAddress(R0, kOneByteStringCid)); NOT_IN_PRODUCT(__ MaybeTraceAllocation(R0, failure)); __ mov(R8, Operand(length_reg)); // Save the length register. - // TODO(koda): Protect against negative length and overflow here. __ SmiUntag(length_reg); const intptr_t fixed_size_plus_alignment_padding = target::String::InstanceSize() + @@ -1962,7 +1965,7 @@ static void TryAllocateOnebyteString(Assembler* assembler, // length_reg: allocation size. __ adds(R1, R0, Operand(length_reg)); - __ b(&fail, CS); // Fail on unsigned overflow. + __ b(failure, CS); // Fail on unsigned overflow. // Check if the allocation fits into the remaining space. // R0: potential new object start. @@ -1970,7 +1973,7 @@ static void TryAllocateOnebyteString(Assembler* assembler, // R2: allocation size. __ ldr(NOTFP, Address(THR, target::Thread::end_offset())); __ cmp(R1, Operand(NOTFP)); - __ b(&fail, CS); + __ b(failure, CS); // Successfully allocated the object(s), now update top to point to // next object start and initialize the object. @@ -2010,9 +2013,6 @@ static void TryAllocateOnebyteString(Assembler* assembler, NOT_IN_PRODUCT(__ IncrementAllocationStatsWithSize(R4, R2)); __ b(ok); - - __ Bind(&fail); - __ b(failure); } // Arg0: OneByteString (receiver). @@ -2033,7 +2033,7 @@ void AsmIntrinsifier::OneByteString_substringUnchecked(Assembler* assembler, __ b(normal_ir_body, NE); // 'start', 'end' not Smi. __ sub(R2, R2, Operand(TMP)); - TryAllocateOnebyteString(assembler, &ok, normal_ir_body); + TryAllocateOneByteString(assembler, &ok, normal_ir_body); __ Bind(&ok); // R0: new string as tagged pointer. // Copy string. @@ -2092,7 +2092,7 @@ void AsmIntrinsifier::OneByteString_allocate(Assembler* assembler, Label* normal_ir_body) { __ ldr(R2, Address(SP, 0 * target::kWordSize)); // Length. Label ok; - TryAllocateOnebyteString(assembler, &ok, normal_ir_body); + TryAllocateOneByteString(assembler, &ok, normal_ir_body); __ Bind(&ok); __ Ret(); diff --git a/runtime/vm/compiler/asm_intrinsifier_arm64.cc b/runtime/vm/compiler/asm_intrinsifier_arm64.cc index d96d9fcdca5..2ae8fb34268 100644 --- a/runtime/vm/compiler/asm_intrinsifier_arm64.cc +++ b/runtime/vm/compiler/asm_intrinsifier_arm64.cc @@ -1999,15 +1999,18 @@ void AsmIntrinsifier::OneByteString_getHashCode(Assembler* assembler, __ ret(); } -// Allocates one-byte string of length 'end - start'. The content is not -// initialized. -// 'length-reg' (R2) contains tagged length. +// Allocates a _OneByteString. The content is not initialized. +// 'length-reg' (R2) contains the desired length as a _Smi or _Mint. // Returns new string as tagged pointer in R0. -static void TryAllocateOnebyteString(Assembler* assembler, +static void TryAllocateOneByteString(Assembler* assembler, Label* ok, Label* failure) { const Register length_reg = R2; - Label fail; + // _Mint length: call to runtime to produce error. + __ BranchIfNotSmi(length_reg, failure); + // negative length: call to runtime to produce error. + __ tbnz(failure, length_reg, compiler::target::kBitsPerWord - 1); + NOT_IN_PRODUCT(__ MaybeTraceAllocation(kOneByteStringCid, R0, failure)); __ mov(R6, length_reg); // Save the length register. // TODO(koda): Protect against negative length and overflow here. @@ -2030,7 +2033,7 @@ static void TryAllocateOnebyteString(Assembler* assembler, // length_reg: allocation size. __ adds(R1, R0, Operand(length_reg)); - __ b(&fail, CS); // Fail on unsigned overflow. + __ b(failure, CS); // Fail on unsigned overflow. // Check if the allocation fits into the remaining space. // R0: potential new object start. @@ -2038,7 +2041,7 @@ static void TryAllocateOnebyteString(Assembler* assembler, // R2: allocation size. __ ldr(R7, Address(THR, target::Thread::end_offset())); __ cmp(R1, Operand(R7)); - __ b(&fail, CS); + __ b(failure, CS); // Successfully allocated the object(s), now update top to point to // next object start and initialize the object. @@ -2072,9 +2075,6 @@ static void TryAllocateOnebyteString(Assembler* assembler, __ StoreIntoObjectNoBarrier( R0, FieldAddress(R0, target::String::length_offset()), R6); __ b(ok); - - __ Bind(&fail); - __ b(failure); } // Arg0: OneByteString (receiver). @@ -2094,7 +2094,7 @@ void AsmIntrinsifier::OneByteString_substringUnchecked(Assembler* assembler, __ BranchIfNotSmi(R3, normal_ir_body); // 'start', 'end' not Smi. __ sub(R2, R2, Operand(TMP)); - TryAllocateOnebyteString(assembler, &ok, normal_ir_body); + TryAllocateOneByteString(assembler, &ok, normal_ir_body); __ Bind(&ok); // R0: new string as tagged pointer. // Copy string. @@ -2155,7 +2155,7 @@ void AsmIntrinsifier::OneByteString_allocate(Assembler* assembler, Label ok; __ ldr(R2, Address(SP, 0 * target::kWordSize)); // Length. - TryAllocateOnebyteString(assembler, &ok, normal_ir_body); + TryAllocateOneByteString(assembler, &ok, normal_ir_body); __ Bind(&ok); __ ret(); diff --git a/runtime/vm/compiler/asm_intrinsifier_ia32.cc b/runtime/vm/compiler/asm_intrinsifier_ia32.cc index 690df695a59..d53462a731c 100644 --- a/runtime/vm/compiler/asm_intrinsifier_ia32.cc +++ b/runtime/vm/compiler/asm_intrinsifier_ia32.cc @@ -1951,13 +1951,19 @@ void AsmIntrinsifier::OneByteString_getHashCode(Assembler* assembler, __ ret(); } -// Allocates one-byte string of length 'end - start'. The content is not -// initialized. 'length-reg' contains tagged length. +// Allocates a _OneByteString. The content is not initialized. +// 'length-reg' contains the desired length as a _Smi or _Mint. // Returns new string as tagged pointer in EAX. -static void TryAllocateOnebyteString(Assembler* assembler, +static void TryAllocateOneByteString(Assembler* assembler, Label* ok, Label* failure, Register length_reg) { + // _Mint length: call to runtime to produce error. + __ BranchIfNotSmi(length_reg, failure); + // negative length: call to runtime to produce error. + __ cmpl(length_reg, Immediate(0)); + __ j(LESS, failure); + NOT_IN_PRODUCT( __ MaybeTraceAllocation(kOneByteStringCid, EAX, failure, false)); if (length_reg != EDI) { @@ -2048,7 +2054,7 @@ void AsmIntrinsifier::OneByteString_substringUnchecked(Assembler* assembler, __ j(NOT_ZERO, normal_ir_body); // 'start', 'end' not Smi. __ subl(EDI, Address(ESP, +kStartIndexOffset)); - TryAllocateOnebyteString(assembler, &ok, normal_ir_body, EDI); + TryAllocateOneByteString(assembler, &ok, normal_ir_body, EDI); __ Bind(&ok); // EAX: new string as tagged pointer. // Copy string. @@ -2098,7 +2104,7 @@ void AsmIntrinsifier::OneByteString_allocate(Assembler* assembler, Label* normal_ir_body) { __ movl(EDI, Address(ESP, +1 * target::kWordSize)); // Length. Label ok; - TryAllocateOnebyteString(assembler, &ok, normal_ir_body, EDI); + TryAllocateOneByteString(assembler, &ok, normal_ir_body, EDI); // EDI: Start address to copy from (untagged). __ Bind(&ok); diff --git a/runtime/vm/compiler/asm_intrinsifier_x64.cc b/runtime/vm/compiler/asm_intrinsifier_x64.cc index d210d676e7f..02ec2c2d4bb 100644 --- a/runtime/vm/compiler/asm_intrinsifier_x64.cc +++ b/runtime/vm/compiler/asm_intrinsifier_x64.cc @@ -1977,13 +1977,19 @@ void AsmIntrinsifier::OneByteString_getHashCode(Assembler* assembler, __ ret(); } -// Allocates one-byte string of length 'end - start'. The content is not -// initialized. 'length-reg' contains tagged length. +// Allocates a _OneByteString. The content is not initialized. +// 'length-reg' contains the desired length as a _Smi or _Mint. // Returns new string as tagged pointer in RAX. -static void TryAllocateOnebyteString(Assembler* assembler, +static void TryAllocateOneByteString(Assembler* assembler, Label* ok, Label* failure, Register length_reg) { + // _Mint length: call to runtime to produce error. + __ BranchIfNotSmi(length_reg, failure); + // negative length: call to runtime to produce error. + __ cmpq(length_reg, Immediate(0)); + __ j(LESS, failure); + NOT_IN_PRODUCT(__ MaybeTraceAllocation(kOneByteStringCid, failure, false)); if (length_reg != RDI) { __ movq(RDI, length_reg); @@ -2076,7 +2082,7 @@ void AsmIntrinsifier::OneByteString_substringUnchecked(Assembler* assembler, __ j(NOT_ZERO, normal_ir_body); // 'start', 'end' not Smi. __ subq(RDI, Address(RSP, +kStartIndexOffset)); - TryAllocateOnebyteString(assembler, &ok, normal_ir_body, RDI); + TryAllocateOneByteString(assembler, &ok, normal_ir_body, RDI); __ Bind(&ok); // RAX: new string as tagged pointer. // Copy string. @@ -2126,7 +2132,7 @@ void AsmIntrinsifier::OneByteString_allocate(Assembler* assembler, Label* normal_ir_body) { __ movq(RDI, Address(RSP, +1 * target::kWordSize)); // Length.v= Label ok; - TryAllocateOnebyteString(assembler, &ok, normal_ir_body, RDI); + TryAllocateOneByteString(assembler, &ok, normal_ir_body, RDI); // RDI: Start address to copy from (untagged). __ Bind(&ok); diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc index aa3fd63d29d..9051461c6a7 100644 --- a/runtime/vm/exceptions.cc +++ b/runtime/vm/exceptions.cc @@ -1005,12 +1005,6 @@ void Exceptions::ThrowUnsupportedError(const char* msg) { Exceptions::ThrowByType(Exceptions::kUnsupported, args); } -void Exceptions::ThrowRangeErrorMsg(const char* msg) { - const Array& args = Array::Handle(Array::New(1)); - args.SetAt(0, String::Handle(String::New(msg))); - Exceptions::ThrowByType(Exceptions::kRangeMsg, args); -} - void Exceptions::ThrowCompileTimeError(const LanguageError& error) { const Array& args = Array::Handle(Array::New(1)); args.SetAt(0, String::Handle(error.FormatMessage())); diff --git a/runtime/vm/exceptions.h b/runtime/vm/exceptions.h index 50d1a8f4bcf..d0a44d78e99 100644 --- a/runtime/vm/exceptions.h +++ b/runtime/vm/exceptions.h @@ -83,7 +83,6 @@ class Exceptions : AllStatic { const Integer& argument_value, intptr_t expected_from, intptr_t expected_to); - DART_NORETURN static void ThrowRangeErrorMsg(const char* msg); DART_NORETURN static void ThrowUnsupportedError(const char* msg); DART_NORETURN static void ThrowCompileTimeError(const LanguageError& error); diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc index a46aa0f3249..9371fc6cc4c 100644 --- a/runtime/vm/interpreter.cc +++ b/runtime/vm/interpreter.cc @@ -262,6 +262,9 @@ DART_FORCE_INLINE static bool TryAllocate(Thread* thread, intptr_t class_id, intptr_t instance_size, RawObject** result) { + ASSERT(instance_size > 0); + ASSERT(Utils::IsAligned(instance_size, kObjectAlignment)); + const uword start = thread->top(); #ifndef PRODUCT auto table = thread->isolate()->shared_class_table(); @@ -269,7 +272,8 @@ DART_FORCE_INLINE static bool TryAllocate(Thread* thread, return false; } #endif - if (LIKELY((start + instance_size) < thread->end())) { + const intptr_t remaining = thread->end() - start; + if (LIKELY(remaining >= instance_size)) { thread->set_top(start + instance_size); #ifndef PRODUCT table->UpdateAllocatedNew(class_id, instance_size); diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc index 8592a76a5aa..f748ed2d8e3 100644 --- a/runtime/vm/object_test.cc +++ b/runtime/vm/object_test.cc @@ -1835,10 +1835,22 @@ TEST_CASE(ArrayLengthNegativeOne) { TEST_CASE(ArrayLengthSmiMin) { TestIllegalArrayLength(kSmiMin); } + TEST_CASE(ArrayLengthOneTooMany) { const intptr_t kOneTooMany = Array::kMaxElements + 1; ASSERT(kOneTooMany >= 0); - TestIllegalArrayLength(kOneTooMany); + + char buffer[1024]; + Utils::SNPrint(buffer, sizeof(buffer), + "main() {\n" + " return new List(%" Pd + ");\n" + "}\n", + kOneTooMany); + Dart_Handle lib = TestCase::LoadTestScript(buffer, NULL); + EXPECT_VALID(lib); + Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); + EXPECT_ERROR(result, "Out of Memory"); } TEST_CASE(ArrayLengthMaxElements) { @@ -1876,7 +1888,7 @@ static void TestIllegalTypedDataLength(const char* class_name, EXPECT_VALID(lib); Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); Utils::SNPrint(buffer, sizeof(buffer), "%" Pd, length); - EXPECT_ERROR(result, "Invalid argument(s)"); + EXPECT_ERROR(result, "RangeError (length): Invalid value"); EXPECT_ERROR(result, buffer); } @@ -1890,7 +1902,19 @@ TEST_CASE(Int8ListLengthOneTooMany) { const intptr_t kOneTooMany = TypedData::MaxElements(kTypedDataInt8ArrayCid) + 1; ASSERT(kOneTooMany >= 0); - TestIllegalTypedDataLength("Int8List", kOneTooMany); + + char buffer[1024]; + Utils::SNPrint(buffer, sizeof(buffer), + "import 'dart:typed_data';\n" + "main() {\n" + " return new Int8List(%" Pd + ");\n" + "}\n", + kOneTooMany); + Dart_Handle lib = TestCase::LoadTestScript(buffer, NULL); + EXPECT_VALID(lib); + Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); + EXPECT_ERROR(result, "Out of Memory"); } TEST_CASE(Int8ListLengthMaxElements) { diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc index 3641e8a3439..7cb1ebb0772 100644 --- a/runtime/vm/runtime_entry.cc +++ b/runtime/vm/runtime_entry.cc @@ -248,29 +248,29 @@ DEFINE_RUNTIME_ENTRY(AllocateArray, 2) { args.SetAt(2, String::Handle(zone, String::New("is not an integer"))); Exceptions::ThrowByType(Exceptions::kArgumentValue, args); } - if (length.IsSmi()) { - const intptr_t len = Smi::Cast(length).Value(); - if (Array::IsValidLength(len)) { - const Array& array = Array::Handle(zone, Array::New(len, Heap::kNew)); - arguments.SetReturn(array); - TypeArguments& element_type = - TypeArguments::CheckedHandle(zone, arguments.ArgAt(1)); - // An Array is raw or takes one type argument. However, its type argument - // vector may be longer than 1 due to a type optimization reusing the type - // argument vector of the instantiator. - ASSERT(element_type.IsNull() || - (element_type.Length() >= 1 && element_type.IsInstantiated())); - array.SetTypeArguments(element_type); // May be null. - return; - } + const int64_t len = Integer::Cast(length).AsInt64Value(); + if (len < 0) { + // Throw: new RangeError.range(length, 0, Array::kMaxElements, "length"); + Exceptions::ThrowRangeError("length", Integer::Cast(length), 0, + Array::kMaxElements); } - // Throw: new RangeError.range(length, 0, Array::kMaxElements, "length"); - const Array& args = Array::Handle(zone, Array::New(4)); - args.SetAt(0, length); - args.SetAt(1, Integer::Handle(zone, Integer::New(0))); - args.SetAt(2, Integer::Handle(zone, Integer::New(Array::kMaxElements))); - args.SetAt(3, Symbols::Length()); - Exceptions::ThrowByType(Exceptions::kRange, args); + if (len > Array::kMaxElements) { + const Instance& exception = Instance::Handle( + zone, thread->isolate()->object_store()->out_of_memory()); + Exceptions::Throw(thread, exception); + } + + const Array& array = + Array::Handle(zone, Array::New(static_cast(len), Heap::kNew)); + arguments.SetReturn(array); + TypeArguments& element_type = + TypeArguments::CheckedHandle(zone, arguments.ArgAt(1)); + // An Array is raw or takes one type argument. However, its type argument + // vector may be longer than 1 due to a type optimization reusing the type + // argument vector of the instantiator. + ASSERT(element_type.IsNull() || + (element_type.Length() >= 1 && element_type.IsInstantiated())); + array.SetTypeArguments(element_type); // May be null. } // Helper returning the token position of the Dart caller. diff --git a/tests/corelib_2/growable_list_test.dart b/tests/corelib_2/growable_list_test.dart index 2172710089b..cea1446e523 100644 --- a/tests/corelib_2/growable_list_test.dart +++ b/tests/corelib_2/growable_list_test.dart @@ -91,7 +91,8 @@ void testConstructor() { testGrowable(new List.filled(5, null, growable: true)); Expect.throwsArgumentError(() => new List(-1), "-1"); // There must be limits. Fix this test if we ever allow 2^63 elements. - Expect.throwsArgumentError(() => new List(0x7ffffffffffff000), "bignum"); + Expect.throws(() => new List(0x7ffffffffffff000), + (e) => e is OutOfMemoryError || e is ArgumentError, "bignum"); Expect.throwsArgumentError(() => new List(null), "null"); testThrowsOrTypeError( () => new List([] as Object), // Cast to avoid warning. diff --git a/tests/language_2/vm/allocate_overflow_array_test.dart b/tests/language_2/vm/allocate_overflow_array_test.dart new file mode 100644 index 00000000000..39d00d3a02b --- /dev/null +++ b/tests/language_2/vm/allocate_overflow_array_test.dart @@ -0,0 +1,33 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:expect/expect.dart'; + +const interestingLengths = [ + 0x3FFFFFFF00000000, + 0x3FFFFFFFFFFFF000, + 0x3FFFFFFFFFFFFF00, + 0x3FFFFFFFFFFFFFF0, + 0x3FFFFFFFFFFFFFFE, + 0x3FFFFFFFFFFFFFFF, + 0x7FFFFFFF00000000, + 0x7FFFFFFFFFFFF000, + 0x7FFFFFFFFFFFFF00, + 0x7FFFFFFFFFFFFFF0, + 0x7FFFFFFFFFFFFFFE, + 0x7FFFFFFFFFFFFFFF, +]; + +main() { + for (int interestingLength in interestingLengths) { + for (int elementLength in [1, 2, 3, 4, 5, 6, 7, 8, 9]) { + print(interestingLength ~/ elementLength); + + Expect.throws(() { + var array = new List(interestingLength ~/ elementLength); + print(array.first); + }, (e) => e is OutOfMemoryError); + } + } +} diff --git a/tests/language_2/vm/allocate_overflow_bytearray_test.dart b/tests/language_2/vm/allocate_overflow_bytearray_test.dart new file mode 100644 index 00000000000..9242494224d --- /dev/null +++ b/tests/language_2/vm/allocate_overflow_bytearray_test.dart @@ -0,0 +1,44 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:typed_data'; + +import 'package:expect/expect.dart'; + +const interestingLengths = [ + 0x3FFFFFFF00000000, + 0x3FFFFFFFFFFFFFF0, + 0x3FFFFFFFFFFFFFFE, + 0x3FFFFFFFFFFFFFFF, + 0x7FFFFFFF00000000, + 0x7FFFFFFFFFFFFFF0, + 0x7FFFFFFFFFFFFFFE, + 0x7FFFFFFFFFFFFFFF, +]; + +main() { + for (int interestingLength in interestingLengths) { + print(interestingLength); + + Expect.throws(() { + var bytearray = new Uint8List(interestingLength); + print(bytearray.first); + }, (e) => e is OutOfMemoryError); + + Expect.throws(() { + var bytearray = new Uint8ClampedList(interestingLength); + print(bytearray.first); + }, (e) => e is OutOfMemoryError); + + Expect.throws(() { + var bytearray = new Int8List(interestingLength); + print(bytearray.first); + }, (e) => e is OutOfMemoryError); + + Expect.throws(() { + var bytearray = new ByteData(interestingLength); + print(bytearray.getUint8(0)); + }, (e) => e is OutOfMemoryError); + } +} diff --git a/tests/language_2/vm/allocate_overflow_string_test.dart b/tests/language_2/vm/allocate_overflow_string_test.dart new file mode 100644 index 00000000000..f4488a1da54 --- /dev/null +++ b/tests/language_2/vm/allocate_overflow_string_test.dart @@ -0,0 +1,37 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:expect/expect.dart'; + +const interestingLengths = [ + 0x3FFFFFFF00000000, + 0x3FFFFFFFFFFFFFF0, + 0x3FFFFFFFFFFFFFFE, + 0x3FFFFFFFFFFFFFFF, + 0x7FFFFFFF00000000, + 0x7FFFFFFFFFFFFFF0, + 0x7FFFFFFFFFFFFFFE, + 0x7FFFFFFFFFFFFFFF, +]; + +main() { + for (int interestingLength in interestingLengths) { + print(interestingLength); + + Expect.throws(() { + var oneByteString = "v"; + oneByteString *= interestingLength; + }, (e) => e is OutOfMemoryError); + + Expect.throws(() { + var oneByteString = "v"; + oneByteString = oneByteString.padLeft(interestingLength); + }, (e) => e is OutOfMemoryError); + + Expect.throws(() { + var oneByteString = "v"; + oneByteString = oneByteString.padRight(interestingLength); + }, (e) => e is OutOfMemoryError); + } +}