Bug 690933: Guard on LifoAlloc overflow. (r=luke)
authorChris Leary <cdleary@mozilla.com>
Tue, 11 Oct 2011 09:42:26 -0700
changeset 78568 ae2293392154
parent 78567 bf8b3a296e3f
child 78569 0e4cee9a1e75
push id21313
push userbmo@edmorley.co.uk
push date2011-10-11 23:18 +0000
treeherdermozilla-central@e0ae39a3298e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs690933
milestone10.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 690933: Guard on LifoAlloc overflow. (r=luke)
js/src/ds/LifoAlloc.cpp
js/src/ds/LifoAlloc.h
--- a/js/src/ds/LifoAlloc.cpp
+++ b/js/src/ds/LifoAlloc.cpp
@@ -60,24 +60,49 @@ BumpChunk::new_(size_t chunkSize)
      * We assume that the alignment of sAlign is less than that of
      * the underlying memory allocator -- creating a new BumpChunk should
      * always satisfy the sAlign alignment constraint.
      */
     JS_ASSERT(AlignPtr(result->bump) == result->bump);
     return result;
 }
 
+void
+BumpChunk::delete_(BumpChunk *chunk)
+{
+#ifdef DEBUG
+        memset(chunk, 0xcd, sizeof(*chunk) + chunk->bumpSpaceSize);
+#endif
+        js_free(chunk);
+}
+
+bool
+BumpChunk::canAlloc(size_t n)
+{
+    char *aligned = AlignPtr(bump);
+    char *bumped = aligned + n;
+    return bumped <= limit && bumped > headerBase();
+}
+
+bool
+BumpChunk::canAllocUnaligned(size_t n)
+{
+    char *bumped = bump + n;
+    return bumped <= limit && bumped > headerBase();
+}
+
 void *
 BumpChunk::tryAllocUnaligned(size_t n)
 {
     char *oldBump = bump;
     char *newBump = bump + n;
     if (newBump > limit)
         return NULL;
 
+    JS_ASSERT(canAllocUnaligned(n));
     setBump(newBump);
     return oldBump;
 }
 
 } /* namespace detail */
 } /* namespace js */
 
 void
@@ -131,19 +156,30 @@ LifoAlloc::getOrCreateChunk(size_t n)
             latest = latest->next();
             latest->resetBump(); /* This was an unused BumpChunk on the chain. */
             if (latest->canAlloc(n))
                 return latest;
         }
     }
 
     size_t defaultChunkFreeSpace = defaultChunkSize_ - sizeof(BumpChunk);
-    size_t chunkSize = n > defaultChunkFreeSpace
-                       ? RoundUpPow2(n + sizeof(BumpChunk))
-                       : defaultChunkSize_;
+    size_t chunkSize;
+    if (n > defaultChunkFreeSpace) {
+        size_t allocSizeWithHeader = n + sizeof(BumpChunk);
+
+        /* Guard for overflow. */
+        if (allocSizeWithHeader < n ||
+            (allocSizeWithHeader & (size_t(1) << (tl::BitSize<size_t>::result - 1)))) {
+            return NULL;
+        }
+
+        chunkSize = RoundUpPow2(allocSizeWithHeader);
+    } else {
+        chunkSize = defaultChunkSize_;
+    }
 
     /* If we get here, we couldn't find an existing BumpChunk to fill the request. */
     BumpChunk *newChunk = BumpChunk::new_(chunkSize);
     if (!newChunk)
         return NULL;
     if (!first) {
         latest = first = newChunk;
     } else {
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -74,17 +74,18 @@ AlignPtr(void *orig)
 /* Header for a chunk of memory wrangled by the LifoAlloc. */
 class BumpChunk
 {
     char        *bump;
     char        *limit;
     BumpChunk   *next_;
     size_t      bumpSpaceSize;
 
-    char *base() const { return limit - bumpSpaceSize; }
+    char *headerBase() { return reinterpret_cast<char *>(this); }
+    char *bumpBase() const { return limit - bumpSpaceSize; }
 
     BumpChunk *thisDuringConstruction() { return this; }
 
     explicit BumpChunk(size_t bumpSpaceSize)
       : bump(reinterpret_cast<char *>(thisDuringConstruction()) + sizeof(BumpChunk)),
         limit(bump + bumpSpaceSize),
         next_(NULL), bumpSpaceSize(bumpSpaceSize)
     {
@@ -93,82 +94,77 @@ class BumpChunk
 
     void clobberUnused() {
 #ifdef DEBUG
         memset(bump, 0xcd, limit - bump);
 #endif
     }
 
     void setBump(void *ptr) {
-        JS_ASSERT(base() <= ptr);
+        JS_ASSERT(bumpBase() <= ptr);
         JS_ASSERT(ptr <= limit);
         DebugOnly<char *> prevBump = bump;
         bump = static_cast<char *>(ptr);
         if (prevBump < bump)
             clobberUnused();
     }
 
   public:
     BumpChunk *next() const { return next_; }
     void setNext(BumpChunk *succ) { next_ = succ; }
 
-    size_t used() const { return bump - base(); }
+    size_t used() const { return bump - bumpBase(); }
 
     void resetBump() {
-        setBump(reinterpret_cast<char *>(this) + sizeof(BumpChunk));
+        setBump(headerBase() + sizeof(BumpChunk));
     }
 
     void *mark() const { return bump; }
 
     void release(void *mark) {
         JS_ASSERT(contains(mark));
         JS_ASSERT(mark <= bump);
         setBump(mark);
     }
 
     bool contains(void *mark) const {
-        return base() <= mark && mark <= limit;
+        return bumpBase() <= mark && mark <= limit;
     }
 
-    bool canAlloc(size_t n) {
-        return AlignPtr(bump) + n <= limit;
-    }
-
-    bool canAllocUnaligned(size_t n) {
-        return bump + n <= limit;
-    }
+    bool canAlloc(size_t n);
+    bool canAllocUnaligned(size_t n);
 
     /* Try to perform an allocation of size |n|, return null if not possible. */
     JS_ALWAYS_INLINE
     void *tryAlloc(size_t n) {
         char *aligned = AlignPtr(bump);
         char *newBump = aligned + n;
+
         if (newBump > limit)
             return NULL;
 
+        /* Check for overflow. */
+        if (JS_UNLIKELY(newBump < bump))
+            return NULL;
+
+        JS_ASSERT(canAlloc(n)); /* Ensure consistency between "can" and "try". */
         setBump(newBump);
         return aligned;
     }
 
     void *tryAllocUnaligned(size_t n);
 
     void *allocInfallible(size_t n) {
         void *result = tryAlloc(n);
         JS_ASSERT(result);
         return result;
     }
 
     static BumpChunk *new_(size_t chunkSize);
-
-    static void delete_(BumpChunk *chunk) {
-#ifdef DEBUG
-        memset(chunk, 0xcd, sizeof(*chunk) + chunk->bumpSpaceSize);
-#endif
-        js_free(chunk);
-    }
+    static void delete_(BumpChunk *chunk);
 };
 
 } /* namespace detail */
 
 /*
  * LIFO bump allocator: used for phase-oriented and fast LIFO allocations.
  *
  * Note: |latest| is not necessary "last". We leave BumpChunks latent in the