Revision: 3614
Author: [email protected]
Date: Fri Jan 15 05:10:50 2010
Log: Merge r3560, r3562 and r3568 from bleeding_edge to 1.3 branch to fix
potential integer overflows.
Review URL: http://codereview.chromium.org/551045
http://code.google.com/p/v8/source/detail?r=3614
Modified:
/branches/1.3/src/heap.cc
/branches/1.3/src/objects.cc
/branches/1.3/src/objects.h
/branches/1.3/src/runtime.cc
/branches/1.3/src/utils.cc
/branches/1.3/src/version.cc
=======================================
--- /branches/1.3/src/heap.cc Wed Jan 6 23:49:31 2010
+++ /branches/1.3/src/heap.cc Fri Jan 15 05:10:50 2010
@@ -1990,6 +1990,9 @@
Object* Heap::AllocateByteArray(int length, PretenureFlag pretenure) {
+ if (length < 0 || length > ByteArray::kMaxLength) {
+ return Failure::OutOfMemoryException();
+ }
if (pretenure == NOT_TENURED) {
return AllocateByteArray(length);
}
@@ -2008,6 +2011,9 @@
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;
@@ -2666,12 +2672,16 @@
Object* Heap::AllocateInternalSymbol(unibrow::CharacterStream* buffer,
int chars,
uint32_t length_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();
@@ -2680,6 +2690,9 @@
Map* map;
if (is_ascii) {
+ if (chars > SeqAsciiString::kMaxLength) {
+ return Failure::OutOfMemoryException();
+ }
if (chars <= String::kMaxShortSize) {
map = short_ascii_symbol_map();
} else if (chars <= String::kMaxMediumSize) {
@@ -2689,6 +2702,9 @@
}
size = SeqAsciiString::SizeFor(chars);
} else {
+ if (chars > SeqTwoByteString::kMaxLength) {
+ return Failure::OutOfMemoryException();
+ }
if (chars <= String::kMaxShortSize) {
map = short_symbol_map();
} else if (chars <= String::kMaxMediumSize) {
@@ -2721,13 +2737,17 @@
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;
// New space can't cope with forced allocation.
if (always_allocate()) space = OLD_DATA_SPACE;
- int size = SeqAsciiString::SizeFor(length);
-
Object* result = Failure::OutOfMemoryException();
if (space == NEW_SPACE) {
result = size <= kMaxObjectSizeInNewSpace
@@ -2758,13 +2778,17 @@
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;
// New space can't cope with forced allocation.
if (always_allocate()) space = OLD_DATA_SPACE;
- int size = SeqTwoByteString::SizeFor(length);
-
Object* result = Failure::OutOfMemoryException();
if (space == NEW_SPACE) {
result = size <= kMaxObjectSizeInNewSpace
@@ -2806,6 +2830,9 @@
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.
@@ -2857,7 +2884,11 @@
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();
// New space can't cope with forced allocation.
=======================================
--- /branches/1.3/src/objects.cc Thu Jan 7 00:01:27 2010
+++ /branches/1.3/src/objects.cc Fri Jan 15 05:10:50 2010
@@ -6931,10 +6931,14 @@
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);
=======================================
--- /branches/1.3/src/objects.h Thu Jan 7 00:01:27 2010
+++ /branches/1.3/src/objects.h Fri Jan 15 05:10:50 2010
@@ -1757,6 +1757,10 @@
#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;
@@ -1883,8 +1887,14 @@
// 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()); }
@@ -2194,6 +2204,12 @@
// 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);
@@ -2224,6 +2240,7 @@
// 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));
}
@@ -2562,6 +2579,11 @@
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);
};
@@ -4267,6 +4289,12 @@
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,
@@ -4313,6 +4341,12 @@
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,
=======================================
--- /branches/1.3/src/runtime.cc Thu Jan 7 00:01:27 2010
+++ /branches/1.3/src/runtime.cc Fri Jan 15 05:10:50 2010
@@ -1406,7 +1406,7 @@
void IncrementCharacterCount(int by) {
- if (character_count_ > Smi::kMaxValue - by) {
+ if (character_count_ > String::kMaxLength - by) {
V8::FatalProcessOutOfMemory("String.replace result too large.");
}
character_count_ += by;
@@ -3252,6 +3252,7 @@
escaped_length += 3;
}
// We don't allow strings that are longer than a maximal length.
+ ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
if (escaped_length > String::kMaxLength) {
Top::context()->mark_out_of_memory();
return Failure::OutOfMemoryException();
@@ -3813,6 +3814,7 @@
bool ascii = special->IsAsciiRepresentation();
int position = 0;
+ int increment = 0;
for (int i = 0; i < array_length; i++) {
Object* elt = fixed_array->get(i);
if (elt->IsSmi()) {
@@ -3822,21 +3824,22 @@
if (pos + len > special_length) {
return Top::Throw(Heap::illegal_argument_symbol());
}
- position += len;
+ increment = len;
} else if (elt->IsString()) {
String* element = String::cast(elt);
int element_length = element->length();
- position += element_length;
+ increment = element_length;
if (ascii && !element->IsAsciiRepresentation()) {
ascii = false;
}
} else {
return Top::Throw(Heap::illegal_argument_symbol());
}
- if (position > String::kMaxLength) {
+ if (increment > String::kMaxLength - position) {
Top::context()->mark_out_of_memory();
return Failure::OutOfMemoryException();
}
+ position += increment;
}
int length = position;
@@ -5232,11 +5235,11 @@
uint32_t index_limit,
bool fast_elements) :
storage_(storage), index_limit_(index_limit),
- fast_elements_(fast_elements), index_offset_(0) { }
+ index_offset_(0), fast_elements_(fast_elements) { }
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()));
@@ -5252,16 +5255,23 @@
}
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_;
};
@@ -5433,6 +5443,11 @@
*
* 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) {
@@ -5455,8 +5470,14 @@
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;
@@ -5473,10 +5494,12 @@
* 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) {
@@ -5492,16 +5515,23 @@
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;
@@ -5511,6 +5541,8 @@
/**
* 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);
@@ -5527,12 +5559,18 @@
{ 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;
}
}
=======================================
--- /branches/1.3/src/utils.cc Wed Oct 7 02:00:33 2009
+++ /branches/1.3/src/utils.cc Fri Jan 15 05:10:50 2010
@@ -40,6 +40,7 @@
// 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);
=======================================
--- /branches/1.3/src/version.cc Thu Jan 7 00:01:27 2010
+++ /branches/1.3/src/version.cc Fri Jan 15 05:10:50 2010
@@ -35,7 +35,7 @@
#define MAJOR_VERSION 1
#define MINOR_VERSION 3
#define BUILD_NUMBER 18
-#define PATCH_LEVEL 19
+#define PATCH_LEVEL 20
#define CANDIDATE_VERSION false
// Define SONAME to have the SCons build the put a specific SONAME into the
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev