Fixed potential length miscalculations by limiting max size of arrays and strings.
Review URL: http://codereview.chromium.org/525064 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3560 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
c4416d628d
commit
dddadf9f7e
40
src/heap.cc
40
src/heap.cc
@ -806,7 +806,8 @@ void Heap::ScavengeExternalStringTable() {
|
||||
}
|
||||
}
|
||||
|
||||
ExternalStringTable::ShrinkNewStrings(last - start);
|
||||
ASSERT(last <= end);
|
||||
ExternalStringTable::ShrinkNewStrings(static_cast<int>(last - start));
|
||||
}
|
||||
|
||||
|
||||
@ -2038,6 +2039,9 @@ Object* Heap::LookupSingleCharacterStringFromCode(uint16_t code) {
|
||||
|
||||
|
||||
Object* Heap::AllocateByteArray(int length, PretenureFlag pretenure) {
|
||||
if (length < 0 || length > ByteArray::kMaxLength) {
|
||||
return Failure::OutOfMemoryException();
|
||||
}
|
||||
if (pretenure == NOT_TENURED) {
|
||||
return AllocateByteArray(length);
|
||||
}
|
||||
@ -2054,6 +2058,9 @@ Object* Heap::AllocateByteArray(int length, PretenureFlag pretenure) {
|
||||
|
||||
|
||||
Object* Heap::AllocateByteArray(int length) {
|
||||
if (length < 0 || length > ByteArray::kMaxLength) {
|
||||
return Failure::OutOfMemoryException();
|
||||
}
|
||||
int size = ByteArray::SizeFor(length);
|
||||
AllocationSpace space =
|
||||
(size > MaxObjectSizeInPagedSpace()) ? LO_SPACE : NEW_SPACE;
|
||||
@ -2656,12 +2663,16 @@ Map* Heap::SymbolMapForString(String* string) {
|
||||
Object* Heap::AllocateInternalSymbol(unibrow::CharacterStream* buffer,
|
||||
int chars,
|
||||
uint32_t hash_field) {
|
||||
ASSERT(chars >= 0);
|
||||
// Ensure the chars matches the number of characters in the buffer.
|
||||
ASSERT(static_cast<unsigned>(chars) == buffer->Length());
|
||||
// Determine whether the string is ascii.
|
||||
bool is_ascii = true;
|
||||
while (buffer->has_more() && is_ascii) {
|
||||
if (buffer->GetNext() > unibrow::Utf8::kMaxOneByteChar) is_ascii = false;
|
||||
while (buffer->has_more()) {
|
||||
if (buffer->GetNext() > unibrow::Utf8::kMaxOneByteChar) {
|
||||
is_ascii = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
buffer->Rewind();
|
||||
|
||||
@ -2670,9 +2681,15 @@ Object* Heap::AllocateInternalSymbol(unibrow::CharacterStream* buffer,
|
||||
Map* map;
|
||||
|
||||
if (is_ascii) {
|
||||
if (chars > SeqAsciiString::kMaxLength) {
|
||||
return Failure::OutOfMemoryException();
|
||||
}
|
||||
map = ascii_symbol_map();
|
||||
size = SeqAsciiString::SizeFor(chars);
|
||||
} else {
|
||||
if (chars > SeqTwoByteString::kMaxLength) {
|
||||
return Failure::OutOfMemoryException();
|
||||
}
|
||||
map = symbol_map();
|
||||
size = SeqTwoByteString::SizeFor(chars);
|
||||
}
|
||||
@ -2700,7 +2717,13 @@ Object* Heap::AllocateInternalSymbol(unibrow::CharacterStream* buffer,
|
||||
|
||||
|
||||
Object* Heap::AllocateRawAsciiString(int length, PretenureFlag pretenure) {
|
||||
if (length < 0 || length > SeqAsciiString::kMaxLength) {
|
||||
return Failure::OutOfMemoryException();
|
||||
}
|
||||
|
||||
int size = SeqAsciiString::SizeFor(length);
|
||||
ASSERT(size <= SeqAsciiString::kMaxSize);
|
||||
|
||||
AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
|
||||
AllocationSpace retry_space = OLD_DATA_SPACE;
|
||||
|
||||
@ -2728,7 +2751,11 @@ Object* Heap::AllocateRawAsciiString(int length, PretenureFlag pretenure) {
|
||||
|
||||
|
||||
Object* Heap::AllocateRawTwoByteString(int length, PretenureFlag pretenure) {
|
||||
if (length < 0 || length > SeqTwoByteString::kMaxLength) {
|
||||
return Failure::OutOfMemoryException();
|
||||
}
|
||||
int size = SeqTwoByteString::SizeFor(length);
|
||||
ASSERT(size <= SeqTwoByteString::kMaxSize);
|
||||
AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
|
||||
AllocationSpace retry_space = OLD_DATA_SPACE;
|
||||
|
||||
@ -2767,6 +2794,9 @@ Object* Heap::AllocateEmptyFixedArray() {
|
||||
|
||||
|
||||
Object* Heap::AllocateRawFixedArray(int length) {
|
||||
if (length < 0 || length > FixedArray::kMaxLength) {
|
||||
return Failure::OutOfMemoryException();
|
||||
}
|
||||
// Use the general function if we're forced to always allocate.
|
||||
if (always_allocate()) return AllocateFixedArray(length, TENURED);
|
||||
// Allocate the raw data for a fixed array.
|
||||
@ -2818,7 +2848,11 @@ Object* Heap::AllocateFixedArray(int length) {
|
||||
|
||||
|
||||
Object* Heap::AllocateFixedArray(int length, PretenureFlag pretenure) {
|
||||
ASSERT(length >= 0);
|
||||
ASSERT(empty_fixed_array()->IsFixedArray());
|
||||
if (length < 0 || length > FixedArray::kMaxLength) {
|
||||
return Failure::OutOfMemoryException();
|
||||
}
|
||||
if (length == 0) return empty_fixed_array();
|
||||
|
||||
AllocationSpace space =
|
||||
|
@ -6834,10 +6834,14 @@ void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) {
|
||||
|
||||
|
||||
template<typename Shape, typename Key>
|
||||
Object* HashTable<Shape, Key>::Allocate(
|
||||
int at_least_space_for) {
|
||||
Object* HashTable<Shape, Key>::Allocate(int at_least_space_for) {
|
||||
int capacity = RoundUpToPowerOf2(at_least_space_for);
|
||||
if (capacity < 4) capacity = 4; // Guarantee min capacity.
|
||||
if (capacity < 4) {
|
||||
capacity = 4; // Guarantee min capacity.
|
||||
} else if (capacity > HashTable::kMaxCapacity) {
|
||||
return Failure::OutOfMemoryException();
|
||||
}
|
||||
|
||||
Object* obj = Heap::AllocateHashTable(EntryToIndex(capacity));
|
||||
if (!obj->IsFailure()) {
|
||||
HashTable::cast(obj)->SetNumberOfElements(0);
|
||||
|
@ -1508,6 +1508,10 @@ class JSObject: public HeapObject {
|
||||
#endif
|
||||
Object* SlowReverseLookup(Object* value);
|
||||
|
||||
// Maximal number of elements (numbered 0 .. kMaxElementCount - 1).
|
||||
// Also maximal value of JSArray's length property.
|
||||
static const uint32_t kMaxElementCount = 0xffffffffu;
|
||||
|
||||
static const uint32_t kMaxGap = 1024;
|
||||
static const int kMaxFastElementsLength = 5000;
|
||||
static const int kInitialMaxFastElementArray = 100000;
|
||||
@ -1634,8 +1638,14 @@ class FixedArray: public Array {
|
||||
// Casting.
|
||||
static inline FixedArray* cast(Object* obj);
|
||||
|
||||
// Align data at kPointerSize, even if Array.kHeaderSize isn't aligned.
|
||||
static const int kHeaderSize = POINTER_SIZE_ALIGN(Array::kHeaderSize);
|
||||
static const int kHeaderSize = Array::kAlignedSize;
|
||||
|
||||
// Maximal allowed size, in bytes, of a single FixedArray.
|
||||
// Prevents overflowing size computations, as well as extreme memory
|
||||
// consumption.
|
||||
static const int kMaxSize = 512 * MB;
|
||||
// Maximally allowed length of a FixedArray.
|
||||
static const int kMaxLength = (kMaxSize - kHeaderSize) / kPointerSize;
|
||||
|
||||
// Dispatched behavior.
|
||||
int FixedArraySize() { return SizeFor(length()); }
|
||||
@ -1948,6 +1958,12 @@ class HashTable: public FixedArray {
|
||||
// Constant used for denoting a absent entry.
|
||||
static const int kNotFound = -1;
|
||||
|
||||
// Maximal capacity of HashTable. Based on maximal length of underlying
|
||||
// FixedArray. Staying below kMaxCapacity also ensures that EntryToIndex
|
||||
// cannot overflow.
|
||||
static const int kMaxCapacity =
|
||||
(FixedArray::kMaxLength - kElementsStartOffset) / kEntrySize;
|
||||
|
||||
// Find entry for key otherwise return -1.
|
||||
int FindEntry(Key key);
|
||||
|
||||
@ -1978,6 +1994,7 @@ class HashTable: public FixedArray {
|
||||
// use bit-wise AND with a mask, so the capacity must be positive
|
||||
// and non-zero.
|
||||
ASSERT(capacity > 0);
|
||||
ASSERT(capacity <= kMaxCapacity);
|
||||
fast_set(this, kCapacityIndex, Smi::FromInt(capacity));
|
||||
}
|
||||
|
||||
@ -2325,6 +2342,11 @@ class ByteArray: public Array {
|
||||
static const int kHeaderSize = Array::kHeaderSize;
|
||||
static const int kAlignedSize = Array::kAlignedSize;
|
||||
|
||||
// Maximal memory consumption for a single ByteArray.
|
||||
static const int kMaxSize = 512 * MB;
|
||||
// Maximal length of a single ByteArray.
|
||||
static const int kMaxLength = kMaxSize - kHeaderSize;
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(ByteArray);
|
||||
};
|
||||
@ -4042,6 +4064,12 @@ class SeqAsciiString: public SeqString {
|
||||
static const int kHeaderSize = String::kSize;
|
||||
static const int kAlignedSize = POINTER_SIZE_ALIGN(kHeaderSize);
|
||||
|
||||
// Maximal memory usage for a single sequential ASCII string.
|
||||
static const int kMaxSize = 512 * MB;
|
||||
// Maximal length of a single sequential ASCII string.
|
||||
// Q.v. String::kMaxLength which is the maximal size of concatenated strings.
|
||||
static const int kMaxLength = (kMaxSize - kHeaderSize);
|
||||
|
||||
// Support for StringInputBuffer.
|
||||
inline void SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* buffer,
|
||||
unsigned* offset,
|
||||
@ -4088,6 +4116,12 @@ class SeqTwoByteString: public SeqString {
|
||||
static const int kHeaderSize = String::kSize;
|
||||
static const int kAlignedSize = POINTER_SIZE_ALIGN(kHeaderSize);
|
||||
|
||||
// Maximal memory usage for a single sequential two-byte string.
|
||||
static const int kMaxSize = 512 * MB;
|
||||
// Maximal length of a single sequential two-byte string.
|
||||
// Q.v. String::kMaxLength which is the maximal size of concatenated strings.
|
||||
static const int kMaxLength = (kMaxSize - kHeaderSize) / sizeof(uint16_t);
|
||||
|
||||
// Support for StringInputBuffer.
|
||||
inline void SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* buffer,
|
||||
unsigned* offset_ptr,
|
||||
|
@ -5386,8 +5386,8 @@ class ArrayConcatVisitor {
|
||||
fast_elements_(fast_elements), index_offset_(0) { }
|
||||
|
||||
void visit(uint32_t i, Handle<Object> elm) {
|
||||
uint32_t index = i + index_offset_;
|
||||
if (index >= index_limit_) return;
|
||||
if (i >= index_limit_ - index_offset_) return;
|
||||
uint32_t index = index_offset_ + i;
|
||||
|
||||
if (fast_elements_) {
|
||||
ASSERT(index < static_cast<uint32_t>(storage_->length()));
|
||||
@ -5403,16 +5403,23 @@ class ArrayConcatVisitor {
|
||||
}
|
||||
|
||||
void increase_index_offset(uint32_t delta) {
|
||||
index_offset_ += delta;
|
||||
if (index_limit_ - index_offset_ < delta) {
|
||||
index_offset_ = index_limit_;
|
||||
} else {
|
||||
index_offset_ += delta;
|
||||
}
|
||||
}
|
||||
|
||||
Handle<FixedArray> storage() { return storage_; }
|
||||
|
||||
private:
|
||||
Handle<FixedArray> storage_;
|
||||
// Limit on the accepted indices. Elements with indices larger than the
|
||||
// limit are ignored by the visitor.
|
||||
uint32_t index_limit_;
|
||||
bool fast_elements_;
|
||||
// Index after last seen index. Always less than or equal to index_limit_.
|
||||
uint32_t index_offset_;
|
||||
bool fast_elements_;
|
||||
};
|
||||
|
||||
|
||||
@ -5584,6 +5591,11 @@ static uint32_t IterateElements(Handle<JSObject> receiver,
|
||||
*
|
||||
* If a ArrayConcatVisitor object is given, the visitor is called with
|
||||
* parameters, element's index + visitor_index_offset and the element.
|
||||
*
|
||||
* The returned number of elements is an upper bound on the actual number
|
||||
* of elements added. If the same element occurs in more than one object
|
||||
* in the array's prototype chain, it will be counted more than once, but
|
||||
* will only occur once in the result.
|
||||
*/
|
||||
static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
|
||||
ArrayConcatVisitor* visitor) {
|
||||
@ -5606,8 +5618,14 @@ static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
|
||||
uint32_t nof_elements = 0;
|
||||
for (int i = objects.length() - 1; i >= 0; i--) {
|
||||
Handle<JSObject> obj = objects[i];
|
||||
nof_elements +=
|
||||
uint32_t encountered_elements =
|
||||
IterateElements(Handle<JSObject>::cast(obj), range, visitor);
|
||||
|
||||
if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
|
||||
nof_elements = JSObject::kMaxElementCount;
|
||||
} else {
|
||||
nof_elements += encountered_elements;
|
||||
}
|
||||
}
|
||||
|
||||
return nof_elements;
|
||||
@ -5624,13 +5642,16 @@ static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
|
||||
* elements. If an argument is not an Array object, the function
|
||||
* visits the object as if it is an one-element array.
|
||||
*
|
||||
* If the result array index overflows 32-bit integer, the rounded
|
||||
* If the result array index overflows 32-bit unsigned integer, the rounded
|
||||
* non-negative number is used as new length. For example, if one
|
||||
* array length is 2^32 - 1, second array length is 1, the
|
||||
* concatenated array length is 0.
|
||||
* TODO(lrn) Change length behavior to ECMAScript 5 specification (length
|
||||
* is one more than the last array index to get a value assigned).
|
||||
*/
|
||||
static uint32_t IterateArguments(Handle<JSArray> arguments,
|
||||
ArrayConcatVisitor* visitor) {
|
||||
const uint32_t max_length = JSObject::kMaxElementCount;
|
||||
uint32_t visited_elements = 0;
|
||||
uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
|
||||
|
||||
@ -5643,16 +5664,23 @@ static uint32_t IterateArguments(Handle<JSArray> arguments,
|
||||
IterateArrayAndPrototypeElements(array, visitor);
|
||||
// Total elements of array and its prototype chain can be more than
|
||||
// the array length, but ArrayConcat can only concatenate at most
|
||||
// the array length number of elements.
|
||||
visited_elements += (nof_elements > len) ? len : nof_elements;
|
||||
// the array length number of elements. We use the length as an estimate
|
||||
// for the actual number of elements added.
|
||||
uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
|
||||
if (JSArray::kMaxElementCount - visited_elements < added_elements) {
|
||||
visited_elements = JSArray::kMaxElementCount;
|
||||
} else {
|
||||
visited_elements += added_elements;
|
||||
}
|
||||
if (visitor) visitor->increase_index_offset(len);
|
||||
|
||||
} else {
|
||||
if (visitor) {
|
||||
visitor->visit(0, obj);
|
||||
visitor->increase_index_offset(1);
|
||||
}
|
||||
visited_elements++;
|
||||
if (visited_elements < JSArray::kMaxElementCount) {
|
||||
visited_elements++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return visited_elements;
|
||||
@ -5662,6 +5690,8 @@ static uint32_t IterateArguments(Handle<JSArray> arguments,
|
||||
/**
|
||||
* Array::concat implementation.
|
||||
* See ECMAScript 262, 15.4.4.4.
|
||||
* TODO(lrn): Fix non-compliance for very large concatenations and update to
|
||||
* following the ECMAScript 5 specification.
|
||||
*/
|
||||
static Object* Runtime_ArrayConcat(Arguments args) {
|
||||
ASSERT(args.length() == 1);
|
||||
@ -5678,12 +5708,18 @@ static Object* Runtime_ArrayConcat(Arguments args) {
|
||||
{ AssertNoAllocation nogc;
|
||||
for (uint32_t i = 0; i < num_of_args; i++) {
|
||||
Object* obj = arguments->GetElement(i);
|
||||
uint32_t length_estimate;
|
||||
if (obj->IsJSArray()) {
|
||||
result_length +=
|
||||
length_estimate =
|
||||
static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
|
||||
} else {
|
||||
result_length++;
|
||||
length_estimate = 1;
|
||||
}
|
||||
if (JSObject::kMaxElementCount - result_length < length_estimate) {
|
||||
result_length = JSObject::kMaxElementCount;
|
||||
break;
|
||||
}
|
||||
result_length += length_estimate;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ namespace internal {
|
||||
// Implementation is from "Hacker's Delight" by Henry S. Warren, Jr.,
|
||||
// figure 3-3, page 48, where the function is called clp2.
|
||||
uint32_t RoundUpToPowerOf2(uint32_t x) {
|
||||
ASSERT(x <= 0x80000000u);
|
||||
x = x - 1;
|
||||
x = x | (x >> 1);
|
||||
x = x | (x >> 2);
|
||||
|
Loading…
Reference in New Issue
Block a user