Commit dddadf9f authored by lrn@chromium.org's avatar lrn@chromium.org

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
parent c4416d62
......@@ -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);
......
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