Ignore:
Timestamp:
Jul 22, 2008, 7:26:01 AM (17 years ago)
Author:
[email protected]
Message:

JavaScriptCore:

2008-07-22 Gavin Barraclough <[email protected]>

Reviewed by Alexey Proskuryakov.

Prevent integer overflow when reallocating storage vector for arrays.

Sunspider reports 1.005x as fast (no change expected).

  • kjs/JSArray.cpp:

WebCore:

2008-07-22 Gavin Barraclough <[email protected]>

Reviewed by Alexey Proskuryakov.

New test to check that arrays fail gracefully (throw an out of memory exception)
when the vector grows to large.

  • manual-tests/array-out-of-memory.html: Added.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/kjs/JSArray.cpp

    r35203 r35285  
    2828#include <wtf/AVLTree.h>
    2929#include <wtf/Assertions.h>
     30#include <operations.h>
    3031
    3132#define CHECK_ARRAY_CONSISTENCY 0
     
    3536namespace KJS {
    3637
     38// Overview of JSArray
     39//
     40// Properties of JSArray objects may be stored in one of three locations:
     41//   * The regular JSObject property map.
     42//   * A storage vector.
     43//   * A sparse map of array entries.
     44//
     45// Properties with non-numeric identifiers, with identifiers that are not representable
     46// as an unsigned integer, or where the value is greater than  MAX_ARRAY_INDEX
     47// (specifically, this is only one property - the value 0xFFFFFFFFU as an unsigned 32-bit
     48// integer) are not considered array indices and will be stored in the JSObject property map.
     49//
     50// All properties with a numeric identifer, representable as an unsigned integer i,
     51// where (i <= MAX_ARRAY_INDEX), are an array index and will be stored in either the
     52// storage vector or the sparse map.  An array index i will be handled in the following
     53// fashion:
     54//
     55//   * Where (i < MIN_SPARSE_ARRAY_INDEX) the value will be stored in the storage vector.
     56//   * Where (MIN_SPARSE_ARRAY_INDEX <= i <= MAX_STORAGE_VECTOR_INDEX) the value will either
     57//     be stored in the storage vector or in the sparse array, depending on the density of
     58//     data that would be stored in the vector (a vector being used where at least
     59//     (1 / minDensityMultiplier) of the entries would be populated).
     60//   * Where (MAX_STORAGE_VECTOR_INDEX < i <= MAX_ARRAY_INDEX) the value will always be stored
     61//     in the sparse array.
     62
     63// The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize
     64// function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage
     65// size calculation cannot overflow.  (sizeof(ArrayStorage) - sizeof(JSValue*)) +
     66// (vectorLength * sizeof(JSValue*)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).
     67#define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(JSValue*))) / sizeof(JSValue*))
     68
     69// These values have to be macros to be used in max() and min() without introducing
     70// a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
     71#define MIN_SPARSE_ARRAY_INDEX 10000U
     72#define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)
    3773// 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer.
    38 static const unsigned maxArrayIndex = 0xFFFFFFFEU;
     74#define MAX_ARRAY_INDEX 0xFFFFFFFEU
    3975
    4076// Our policy for when to use a vector and when to use a sparse map.
    41 // For all array indices under sparseArrayCutoff, we always use a vector.
    42 // When indices greater than sparseArrayCutoff are involved, we use a vector
     77// For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector.
     78// When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector
    4379// as long as it is 1/8 full. If more sparse than that, we use a map.
    44 // This value has to be a macro to be used in max() and min() without introducing
    45 // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
    46 #define sparseArrayCutoff 10000U
    4780static const unsigned minDensityMultiplier = 8;
    4881
     
    5184static inline size_t storageSize(unsigned vectorLength)
    5285{
    53     return sizeof(ArrayStorage) - sizeof(JSValue*) + vectorLength * sizeof(JSValue*);
     86    ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH);
     87
     88    // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH)
     89    // - as asserted above - the following calculation cannot overflow.
     90    size_t size = (sizeof(ArrayStorage) - sizeof(JSValue*)) + (vectorLength * sizeof(JSValue*));
     91    // Assertion to detect integer overflow in previous calculation (should not be possible, provided that
     92    // MAX_STORAGE_VECTOR_LENGTH is correctly defined).
     93    ASSERT(((size - (sizeof(ArrayStorage) - sizeof(JSValue*))) / sizeof(JSValue*) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(JSValue*))));
     94
     95    return size;
    5496}
    5597
    5698static inline unsigned increasedVectorLength(unsigned newLength)
    5799{
    58     return (newLength * 3 + 1) / 2;
     100    ASSERT(newLength <= MAX_STORAGE_VECTOR_LENGTH);
     101
     102    // Mathematically equivalent to:
     103    //   increasedLength = (newLength * 3 + 1) / 2;
     104    // or:
     105    //   increasedLength = (unsigned)ceil(newLength * 1.5));
     106    // This form is not prone to internal overflow.
     107    unsigned increasedLength = newLength + (newLength >> 1) + (newLength & 1);
     108    ASSERT(increasedLength >= newLength);
     109
     110    return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH);
    59111}
    60112
     
    75127    : JSObject(prototype)
    76128{
    77     unsigned initialCapacity = min(initialLength, sparseArrayCutoff);
     129    unsigned initialCapacity = min(initialLength, MIN_SPARSE_ARRAY_INDEX);
    78130
    79131    m_length = initialLength;
     
    132184
    133185    if (i >= m_length) {
    134         if (i > maxArrayIndex)
     186        if (i > MAX_ARRAY_INDEX)
    135187            return getOwnPropertySlot(exec, Identifier::from(exec, i), slot);
    136188        return false;
     
    144196        }
    145197    } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
    146         if (i >= sparseArrayCutoff) {
     198        if (i >= MIN_SPARSE_ARRAY_INDEX) {
    147199            SparseArrayValueMap::iterator it = map->find(i);
    148200            if (it != map->end()) {
     
    199251
    200252    unsigned length = m_length;
    201     if (i >= length && i <= maxArrayIndex) {
     253    if (i >= length && i <= MAX_ARRAY_INDEX) {
    202254        length = i + 1;
    203255        m_length = length;
     
    226278    SparseArrayValueMap* map = storage->m_sparseValueMap;
    227279
    228     if (i >= sparseArrayCutoff) {
    229         if (i > maxArrayIndex) {
     280    if (i >= MIN_SPARSE_ARRAY_INDEX) {
     281        if (i > MAX_ARRAY_INDEX) {
    230282            put(exec, Identifier::from(exec, i), value);
    231283            return;
     
    234286        // We miss some cases where we could compact the storage, such as a large array that is being filled from the end
    235287        // (which will only be compacted as we reach indices that are less than cutoff) - but this makes the check much faster.
    236         if (!isDenseEnoughForVector(i + 1, storage->m_numValuesInVector + 1)) {
     288        if ((i > MAX_STORAGE_VECTOR_INDEX) || !isDenseEnoughForVector(i + 1, storage->m_numValuesInVector + 1)) {
    237289            if (!map) {
    238290                map = new SparseArrayValueMap;
     
    247299    // Fast case is when there is no sparse map, so we can increase the vector size without moving values from it.
    248300    if (!map || map->isEmpty()) {
    249         increaseVectorLength(i + 1);
    250         storage = m_storage;
    251         ++storage->m_numValuesInVector;
    252         storage->m_vector[i] = value;
    253         checkConsistency();
     301        if (increaseVectorLength(i + 1)) {
     302            storage = m_storage;
     303            ++storage->m_numValuesInVector;
     304            storage->m_vector[i] = value;
     305            checkConsistency();
     306        } else
     307            throwOutOfMemoryError(exec);
    254308        return;
    255309    }
     
    258312    unsigned newNumValuesInVector = storage->m_numValuesInVector + 1;
    259313    unsigned newVectorLength = increasedVectorLength(i + 1);
    260     for (unsigned j = max(storage->m_vectorLength, sparseArrayCutoff); j < newVectorLength; ++j)
     314    for (unsigned j = max(storage->m_vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j)
    261315        newNumValuesInVector += map->contains(j);
    262     if (i >= sparseArrayCutoff)
     316    if (i >= MIN_SPARSE_ARRAY_INDEX)
    263317        newNumValuesInVector -= map->contains(i);
    264318    if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) {
    265319        unsigned proposedNewNumValuesInVector = newNumValuesInVector;
    266         while (true) {
     320        // If newVectorLength is already the maximum - MAX_STORAGE_VECTOR_LENGTH - then do not attempt to grow any further.
     321        while (newVectorLength < MAX_STORAGE_VECTOR_LENGTH) {
    267322            unsigned proposedNewVectorLength = increasedVectorLength(newVectorLength + 1);
    268             for (unsigned j = max(newVectorLength, sparseArrayCutoff); j < proposedNewVectorLength; ++j)
     323            for (unsigned j = max(newVectorLength, MIN_SPARSE_ARRAY_INDEX); j < proposedNewVectorLength; ++j)
    269324                proposedNewNumValuesInVector += map->contains(j);
    270325            if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector))
     
    276331
    277332    storage = static_cast<ArrayStorage*>(fastRealloc(storage, storageSize(newVectorLength)));
     333    if (!storage) {
     334        throwOutOfMemoryError(exec);
     335        return;
     336    }
    278337
    279338    unsigned vectorLength = storage->m_vectorLength;
     
    281340        for (unsigned j = vectorLength; j < newVectorLength; ++j)
    282341            storage->m_vector[j] = 0;
    283         if (i > sparseArrayCutoff)
     342        if (i > MIN_SPARSE_ARRAY_INDEX)
    284343            map->remove(i);
    285344    } else {
    286         for (unsigned j = vectorLength; j < max(vectorLength, sparseArrayCutoff); ++j)
     345        for (unsigned j = vectorLength; j < max(vectorLength, MIN_SPARSE_ARRAY_INDEX); ++j)
    287346            storage->m_vector[j] = 0;
    288         for (unsigned j = max(vectorLength, sparseArrayCutoff); j < newVectorLength; ++j)
     347        for (unsigned j = max(vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j)
    289348            storage->m_vector[j] = map->take(j);
    290349    }
     
    334393
    335394    if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
    336         if (i >= sparseArrayCutoff) {
     395        if (i >= MIN_SPARSE_ARRAY_INDEX) {
    337396            SparseArrayValueMap::iterator it = map->find(i);
    338397            if (it != map->end()) {
     
    346405    checkConsistency();
    347406
    348     if (i > maxArrayIndex)
     407    if (i > MAX_ARRAY_INDEX)
    349408        return deleteProperty(exec, Identifier::from(exec, i));
    350409
     
    384443    unsigned vectorLength = storage->m_vectorLength;
    385444    ASSERT(newLength > vectorLength);
     445    ASSERT(newLength <= MAX_STORAGE_VECTOR_INDEX);
    386446    unsigned newVectorLength = increasedVectorLength(newLength);
    387447
     
    474534    unsigned lengthNotIncludingUndefined = compactForSorting();
    475535    if (m_storage->m_sparseValueMap) {
    476         exec->setException(Error::create(exec, GeneralError, "Out of memory"));
     536        throwOutOfMemoryError(exec);
    477537        return;
    478538    }
     
    488548    Vector<ArrayQSortPair> values(lengthNotIncludingUndefined);
    489549    if (!values.begin()) {
    490         exec->setException(Error::create(exec, GeneralError, "Out of memory"));
     550        throwOutOfMemoryError(exec);
    491551        return;
    492552    }
     
    625685
    626686    if (!tree.abstractor().m_nodes.begin()) {
    627         exec->setException(Error::create(exec, GeneralError, "Out of memory"));
     687        throwOutOfMemoryError(exec);
    628688        return;
    629689    }
     
    660720        newUsedVectorLength += map->size();
    661721        if (newUsedVectorLength > m_storage->m_vectorLength) {
    662             if (!increaseVectorLength(newUsedVectorLength)) {
    663                 exec->setException(Error::create(exec, GeneralError, "Out of memory"));
     722            // Check that it is possible to allocate an array large enough to hold all the entries.
     723            if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength)) {
     724                throwOutOfMemoryError(exec);
    664725                return;
    665726            }
     
    734795        newUsedVectorLength += map->size();
    735796        if (newUsedVectorLength > storage->m_vectorLength) {
    736             if (!increaseVectorLength(newUsedVectorLength))
     797            // Check that it is possible to allocate an array large enough to hold all the entries - if not,
     798            // exception is thrown by caller.
     799            if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength))
    737800                return 0;
    738801            storage = m_storage;
     
    802865            ASSERT(index < m_length);
    803866            ASSERT(index >= m_storage->m_vectorLength);
    804             ASSERT(index <= maxArrayIndex);
     867            ASSERT(index <= MAX_ARRAY_INDEX);
    805868            ASSERT(it->second);
    806869            if (type != DestructorConsistencyCheck)
Note: See TracChangeset for help on using the changeset viewer.