Bug 1530573 - Protect maybeResizeNursery() from overflow r=sfink
authorPaul Bone <pbone@mozilla.com>
Fri, 01 Mar 2019 00:09:09 +0000
changeset 519717 a456a1594cb1960c465dc7a582134710f286fcf9
parent 519716 623c6ca6ed6cf8128cebf9944524f3be13e0d4d2
child 519718 e4028e1ca6c7f6b62ba06ab10abfcb2301a4257d
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1530573
milestone67.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 1530573 - Protect maybeResizeNursery() from overflow r=sfink Differential Revision: https://phabricator.services.mozilla.com/D21158
js/src/gc/Nursery.cpp
js/src/gc/Nursery.h
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -1209,48 +1209,53 @@ void js::Nursery::maybeResizeNursery(JS:
    * This incorrect promotion rate results in better nursery sizing
    * decisions, however we should to better tuning based on the real
    * promotion rate in the future.
    */
   const float promotionRate =
       float(previousGC.tenuredBytes) / float(previousGC.nurseryCapacity);
 
   /*
-   * Object lifetimes arn't going to behave linearly, but a better
+   * Object lifetimes aren't going to behave linearly, but a better
    * relationship that works for all programs and can be predicted in
    * advance doesn't exist.
    */
   const float factor = promotionRate / PromotionGoal;
-  const unsigned newCapacity = unsigned(float(capacity()) * factor);
+  MOZ_ASSERT(factor >= 0.0f);
+
+  MOZ_ASSERT((float(capacity()) * factor) <= SIZE_MAX);
+  const size_t newCapacity = size_t(float(capacity()) * factor);
 
   // If one of these conditions is true then we always shrink or grow the
   // nursery.  This way the thresholds still have an effect even if the goal
   // seeking says the current size is ideal.
   if (maxChunkCount() < chunkCountLimit() && promotionRate > GrowThreshold) {
-    unsigned lowLimit = capacity() + SubChunkStep;
-    unsigned highLimit =
-        Min(chunkCountLimit() * NurseryChunkUsableSize, capacity() * 2);
+    size_t lowLimit = (CheckedInt<size_t>(capacity()) + SubChunkStep).value();
+    size_t highLimit =
+        Min((CheckedInt<size_t>(chunkCountLimit()) * NurseryChunkUsableSize)
+                .value(),
+            (CheckedInt<size_t>(capacity()) * 2).value());
 
     growAllocableSpace(mozilla::Clamp(newCapacity, lowLimit, highLimit));
   } else if (capacity() >= SubChunkLimit + SubChunkStep &&
              promotionRate < ShrinkThreshold) {
-    unsigned lowLimit = Max(SubChunkLimit, capacity() / 2);
-    unsigned highLimit = capacity() - SubChunkStep;
+    size_t lowLimit = Max(SubChunkLimit, capacity() / 2);
+    size_t highLimit = (CheckedInt<size_t>(capacity()) - SubChunkStep).value();
 
     shrinkAllocableSpace(mozilla::Clamp(newCapacity, lowLimit, highLimit));
   }
 
   // Assert that the limits are set such that we can shrink the nursery below
   // one chunk.
   static_assert(
       SubChunkLimit + SubChunkStep < NurseryChunkUsableSize,
       "Nursery limit must be at least one step from the full chunk size");
 }
 
-void js::Nursery::growAllocableSpace(unsigned newCapacity) {
+void js::Nursery::growAllocableSpace(size_t newCapacity) {
   MOZ_ASSERT_IF(!isSubChunkMode(),
                 newCapacity > currentChunk_ * NurseryChunkUsableSize);
   if (isSubChunkMode()) {
     capacity_ =
         Min(JS_ROUNDUP(newCapacity, SubChunkStep), NurseryChunkUsableSize);
   } else {
     capacity_ = JS_ROUNDUP(newCapacity, NurseryChunkUsableSize);
   }
@@ -1264,26 +1269,26 @@ void js::Nursery::freeChunksFrom(unsigne
     AutoLockGC lock(runtime());
     for (unsigned i = firstFreeChunk; i < chunks_.length(); i++) {
       runtime()->gc.recycleChunk(chunk(i).toChunk(runtime()), lock);
     }
   }
   chunks_.shrinkTo(firstFreeChunk);
 }
 
-void js::Nursery::shrinkAllocableSpace(unsigned newCapacity) {
+void js::Nursery::shrinkAllocableSpace(size_t newCapacity) {
 #ifdef JS_GC_ZEAL
   if (runtime()->hasZealMode(ZealMode::GenerationalGC)) {
     return;
   }
 #endif
 
-  unsigned stepSize = newCapacity < NurseryChunkUsableSize
-                          ? SubChunkStep
-                          : NurseryChunkUsableSize;
+  size_t stepSize = newCapacity < NurseryChunkUsableSize
+                        ? SubChunkStep
+                        : NurseryChunkUsableSize;
   newCapacity -= newCapacity % stepSize;
   // Don't shrink the nursery to zero (use Nursery::disable() instead)
   // This can't happen due to the rounding-down performed above because of the
   // clamping in maybeResizeNursery().
   MOZ_ASSERT(newCapacity != 0);
   // Don't attempt to shrink it to the same size.
   if (newCapacity == capacity()) {
     return;
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -634,18 +634,18 @@ class Nursery {
    */
   void clear();
 
   void sweepDictionaryModeObjects();
   void sweepMapAndSetObjects();
 
   /* Change the allocable space provided by the nursery. */
   void maybeResizeNursery(JS::GCReason reason);
-  void growAllocableSpace(unsigned newCapacity);
-  void shrinkAllocableSpace(unsigned newCapacity);
+  void growAllocableSpace(size_t newCapacity);
+  void shrinkAllocableSpace(size_t newCapacity);
   void minimizeAllocableSpace();
 
   // Free the chunks starting at firstFreeChunk until the end of the chunks
   // vector. Shrinks the vector but does not update maxChunkCount().
   void freeChunksFrom(unsigned firstFreeChunk);
 
   /* Profile recording and printing. */
   void maybeClearProfileDurations();