High firefox Integer Overflow 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionAn integer overflow was present in <code>OrderedHashTable</code> used by the JavaScript engine
ComponentSpiderMonkey
Bug ClassInteger Overflow
Tracker1970095
Fix commitd578778fe18e (firefox) +22/-13
CISA KEVNot listed
CreditedShaheen Fazim
Disclosed2025-06-10

Files Changed

  • js/src/builtin/OrderedHashTableObject.h
diff --git a/js/src/builtin/OrderedHashTableObject.h b/js/src/builtin/OrderedHashTableObject.h
index 5fbb14309af..c971b308404 100644
--- a/js/src/builtin/OrderedHashTableObject.h
+++ b/js/src/builtin/OrderedHashTableObject.h
@@ -450,6 +450,14 @@ class MOZ_STACK_CLASS OrderedHashTableImpl {
     obj->setReservedSlotPrivateUnbarriered(Slots::HashCodeScramblerSlot, hcs);
   }
 
+  static constexpr uint32_t hashShiftToNumHashBuckets(uint32_t hashShift) {
+    return 1 << (js::kHashNumberBits - hashShift);
+  }
+  static constexpr uint32_t numHashBucketsToDataCapacity(
+      uint32_t numHashBuckets) {
+    return uint32_t(numHashBuckets * FillFactor);
+  }
+
   // Logarithm base 2 of the number of buckets in the hash table initially.
   static constexpr uint32_t InitialBucketsLog2 = 1;
   static constexpr uint32_t InitialBuckets = 1 << InitialBucketsLog2;
@@ -470,6 +478,13 @@ class MOZ_STACK_CLASS OrderedHashTableImpl {
   // If that ratio drops below this value, we shrink the table.
   static constexpr double MinDataFill = 0.25;
 
+  // Note: a lower hash shift results in a higher capacity.
+  static constexpr uint32_t MinHashShift = 8;
+  static constexpr uint32_t MaxHashBuckets =
+      hashShiftToNumHashBuckets(MinHashShift);  // 16777216
+  static constexpr uint32_t MaxDataCapacity =
+      numHashBucketsToDataCapacity(MaxHashBuckets);  // 44739242
+
   template <typename F>
   void forEachIterator(F&& f) {
     TableIteratorObject* next;
@@ -507,6 +522,9 @@ class MOZ_STACK_CLASS OrderedHashTableImpl {
       std::tuple<Data*, Data**, HashCodeScrambler*, size_t>;
   AllocationResult allocateBuffer(JSContext* cx, uint32_t dataCapacity,
                                   uint32_t buckets) {
+    MOZ_ASSERT(dataCapacity <= MaxDataCapacity);
+    MOZ_ASSERT(buckets <= MaxHashBuckets);
+
     size_t numBytes = 0;
     if (MOZ_UNLIKELY(!calcAllocSize(dataCapacity, buckets, &numBytes))) {
       ReportAllocationOverflow(cx);
@@ -1064,7 +1082,7 @@ class MOZ_STACK_CLASS OrderedHashTableImpl {
 
   /* The size of the hash table, in elements. Always a power of two. */
   uint32_t hashBuckets() const {
-    return 1 << (js::kHashNumberBits - getHashShift());
+    return hashShiftToNumHashBuckets(getHashShift());
   }
 
   void destroyData(Data* data, uint32_t length) {
@@ -1196,22 +1214,13 @@ class MOZ_STACK_CLASS OrderedHashTableImpl {
       return true;
     }
 
-    // Ensure the new capacity fits into INT32_MAX.
-    constexpr size_t maxCapacityLog2 =
-        mozilla::tl::FloorLog2<size_t(INT32_MAX / FillFactor)>::value;
-    static_assert(maxCapacityLog2 < kHashNumberBits);
-
-    // Fail if |(js::kHashNumberBits - newHashShift) > maxCapacityLog2|.
-    //
-    // Reorder |kHashNumberBits| so both constants are on the right-hand side.
-    if (MOZ_UNLIKELY(newHashShift < (js::kHashNumberBits - maxCapacityLog2))) {
+    if (MOZ_UNLIKELY(newHashShift < MinHashShift)) {
       ReportAllocationOverflow(cx);
       return false;
     }
 
-    uint32_t newHashBuckets = uint32_t(1)
-                              << (js::kHashNumberBits - newHashShift);
-    uint32_t newCapacity = uint32_t(newHashBuckets * FillFactor);
+    uint32_t newHashBuckets = hashShiftToNumHashBuckets(newHashShift);
+    uint32_t newCapacity = numHashBucketsToDataCapacity(newHashBuckets);
 
     auto [newData, newHashTable, newHcs, numBytes] =
         allocateBuffer(cx, newCapacity, newHashBuckets);
Loading diff…