High firefox Integer Overflow 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionInteger overflow in the JavaScript: WebAssembly component
ComponentSpiderMonkey
Bug ClassInteger Overflow
Tracker2051854
Fix commit612b38c7eac4 (firefox) +159/-60
CISA KEVNot listed
CreditedAmy Burnett of OpenAI
Disclosed2026-07-21

Changed Functions

FunctionChangeNotes
for
js/src/jit/SimpleAllocator.cpp
modified

Files Changed

  • js/src/jit/BacktrackingAllocator.cpp
  • js/src/jit/BacktrackingAllocator.h
  • js/src/jit/LIR.h
  • js/src/jit/SimpleAllocator.cpp
  • js/src/jit/SimpleAllocator.h
  • js/src/jit/StackSlotAllocator.h
diff --git a/js/src/jit/BacktrackingAllocator.cpp b/js/src/jit/BacktrackingAllocator.cpp
index 59d7c44564e..64e4d434d22 100644
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -2239,11 +2239,13 @@ void BacktrackingAllocator::tryMergeBundles(LiveBundle* bundle0,
 }
 
 // Helper for ::mergeAndQueueRegisters
-void BacktrackingAllocator::allocateStackDefinition(VirtualRegister& reg) {
+bool BacktrackingAllocator::allocateStackDefinition(VirtualRegister& reg) {
   LInstruction* ins = reg.ins()->toInstruction();
   if (reg.def()->type() == LDefinition::STACKRESULTS) {
     LStackArea alloc(ins->toInstruction());
-    stackSlotAllocator.allocateStackArea(&alloc);
+    if (!stackSlotAllocator.allocateStackArea(&alloc)) {
+      return false;
+    }
     reg.def()->setOutput(alloc);
   } else {
     // Because the definitions are visited in order, the area has been allocated
@@ -2253,6 +2255,7 @@ void BacktrackingAllocator::allocateStackDefinition(VirtualRegister& reg) {
     const LStackArea* areaAlloc = area.def()->output()->toStackArea();
     reg.def()->setOutput(areaAlloc->resultAlloc(ins, reg.def()));
   }
+  return true;
 }
 
 // Helper for ::mergeAndQueueRegisters
@@ -2481,8 +2484,9 @@ bool BacktrackingAllocator::mergeAndQueueRegisters() {
     VirtualRegister& reg = vregs[i];
 
     // Eagerly allocate stack result areas and their component stack results.
-    if (reg.def() && reg.def()->policy() == LDefinition::STACK) {
-      allocateStackDefinition(reg);
+    if (reg.def() && reg.def()->policy() == LDefinition::STACK &&
+        !allocateStackDefinition(reg)) {
+      return false;
     }
 
     for (VirtualRegister::RangeIterator iter(reg); iter; iter++) {
@@ -3970,7 +3974,10 @@ bool BacktrackingAllocator::pickStackSlot(SpillSet* spillSet) {
 
   // We need a new physical stack slot.
   LStackSlot::Width width = LStackSlot::width(type);
-  uint32_t stackSlot = stackSlotAllocator.allocateSlot(width);
+  uint32_t stackSlot;
+  if (!stackSlotAllocator.allocateSlot(width, &stackSlot)) {
+    return false;
+  }
 
   SpillSlot* spillSlot =
       new (alloc().fallible()) SpillSlot(stackSlot, width, alloc().lifoAlloc());
diff --git a/js/src/jit/BacktrackingAllocator.h b/js/src/jit/BacktrackingAllocator.h
index 82b1955aa50..89d8e905185 100644
--- a/js/src/jit/BacktrackingAllocator.h
+++ b/js/src/jit/BacktrackingAllocator.h
@@ -867,7 +867,7 @@ class MOZ_STACK_CLASS BacktrackingAllocator : protected RegisterAllocator {
 
   // Merging and queueing of LiveRange groups
   void tryMergeBundles(LiveBundle* bundle0, LiveBundle* bundle1);
-  void allocateStackDefinition(VirtualRegister& reg);
+  [[nodiscard]] bool allocateStackDefinition(VirtualRegister& reg);
   [[nodiscard]] bool tryMergeReusedRegister(VirtualRegister& def,
                                             VirtualRegister& input);
   [[nodiscard]] bool mergeAndQueueRegisters();
diff --git a/js/src/jit/LIR.h b/js/src/jit/LIR.h
index 3f4efeda795..cac3baca33e 100644
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -433,6 +433,9 @@ class LStackSlot : public LAllocation {
     Width width() const { return Width(data_ & WIDTH_MASK); }
   };
 
+  static constexpr uint32_t MAX_SLOT =
+      (uint64_t(1) << LAllocation::DATA_BITS) - 1;
+
   explicit LStackSlot(SlotAndWidth slotAndWidth)
       : LAllocation(STACK_SLOT, slotAndWidth.data()) {}
 
diff --git a/js/src/jit/SimpleAllocator.cpp b/js/src/jit/SimpleAllocator.cpp
index 1b668fec2f0..8f6b0d61827 100644
--- a/js/src/jit/SimpleAllocator.cpp
+++ b/js/src/jit/SimpleAllocator.cpp
@@ -383,16 +383,22 @@ void SimpleAllocator::removeAllocatedRegisterAtIndex(size_t index) {
   }
 }
 
-LAllocation SimpleAllocator::ensureStackLocation(uint32_t vregId) {
+bool SimpleAllocator::ensureStackLocation(uint32_t vregId, LAllocation* alloc) {
   // Allocate a stack slot for this virtual register if needed.
   VirtualRegister& vreg = vregs_[vregId];
   if (vreg.hasStackLocation()) {
-    return vreg.stackLocation();
+    *alloc = vreg.stackLocation();
+    return true;
   }
   LStackSlot::Width width = LStackSlot::width(vreg.def()->type());
-  LStackSlot::SlotAndWidth slot(stackSlotAllocator_.allocateSlot(width), width);
+  uint32_t slotOffset;
+  if (!stackSlotAllocator_.allocateSlot(width, &slotOffset)) {
+    return false;
+  }
+  LStackSlot::SlotAndWidth slot(slotOffset, width);
   vreg.setAllocatedStackSlot(slot);
-  return LStackSlot(slot);
+  *alloc = LStackSlot(slot);
+  return true;
 }
 
 LAllocation SimpleAllocator::registerOrStackLocation(LInstruction* ins,
@@ -418,7 +424,10 @@ bool SimpleAllocator::spillRegister(LInstruction* ins,
   }
   // Allocate a new stack slot and insert a register => stack move.
   LMoveGroup* input = getInputMoveGroup(ins);
-  LAllocation dest = ensureStackLocation(allocated.vregId());
+  LAllocation dest;
+  if (!ensureStackLocation(allocated.vregId(), &dest)) {
+    return false;
+  }
   return input->addAfter(LAllocation(allocated.reg()), dest,
                          vreg.def()->type());
 }
@@ -469,7 +478,10 @@ bool SimpleAllocator::allocateForBlockEnd(LBlock* block, LInstruction* ins) {
 
     LAllocation source =
         registerOrStackLocation(ins, sourceVreg, /* trackRegUse = */ true);
-    LAllocation dest = ensureStackLocation(destVreg);
+    LAllocation dest;
+    if (!ensureStackLocation(destVreg, &dest)) {
+      return false;
+    }
     if (!group->add(source, dest, phi->getDef(0)->type())) {
       return false;
     }
@@ -795,7 +807,9 @@ bool SimpleAllocator::allocateForDefinition(uint32_t blockLastId,
       MOZ_ASSERT(!isTemp);
       if (def->type() == LDefinition::STACKRESULTS) {
         LStackArea alloc(ins->toInstruction());
-        stackSlotAllocator_.allocateStackArea(&alloc);
+        if (!stackSlotAllocator_.allocateStackArea(&alloc)) {
+          return false;
+        }
         def->setOutput(alloc);
       } else {
         // Because the definitions are visited in order, the area has been
@@ -844,7 +858,10 @@ bool SimpleAllocator::allocateForInstruction(VirtualRegBitSet& liveGC,
     LMoveGroup* moves = getInputMoveGroup(ins);
     for (LDefinition* def : eagerSpillOutputs_) {
       MOZ_ASSERT(!vregs_[def->virtualRegister()].hasStackLocation());
-      LAllocation dest = ensureStackLocation(def->virtualRegister());
+      LAllocation dest;
+      if (!ensureStackLocation(def->virtualRegister(), &dest)) {
+        return false;
+      }
       if (!moves->add(*def->output(), dest, def->type())) {
         return false;
       }
@@ -1182,7 +1199,11 @@ bool SimpleAllocator::allocateRegisters() {
       LDefinition* def = phi->getDef(0);
       uint32_t vregId = def->virtualRegister();
       bool isGCType = vregs_[vregId].isGCType();
-      def->setOutput(ensureStackLocation(vregId));
+      LAllocation defAlloc;
+      if (!ensureStackLocation(vregId, &defAlloc)) {
+        return false;
+      }
+      def->setOutput(defAlloc);
       if (isGCType && !liveGC.insert(vregId)) {
         return false;
       }
diff --git a/js/src/jit/SimpleAllocator.h b/js/src/jit/SimpleAllocator.h
index dc282fd51e0..3aca8073908 100644
--- a/js/src/jit/SimpleAllocator.h
+++ b/js/src/jit/SimpleAllocator.h
@@ -323,7 +323,7 @@ class MOZ_STACK_CLASS SimpleAllocator : protected RegisterAllocator {
 
   [[nodiscard]] bool allocateForBlockEnd(LBlock* block, LInstruction* ins);
 
-  LAllocation ensureStackLocation(uint32_t vregId);
+  bool ensureStackLocation(uint32_t vregId, LAllocation* allocation);
   LAllocation registerOrStackLocation(LInstruction* ins, uint32_t vregId,
                                       bool trackRegUse);
 
diff --git a/js/src/jit/StackSlotAllocator.h b/js/src/jit/StackSlotAllocator.h
index ab666d9e53f..051c3d14e79 100644
--- a/js/src/jit/StackSlotAllocator.h
+++ b/js/src/jit/StackSlotAllocator.h
@@ -17,97 +17,165 @@ class StackSlotAllocator {
   js::Vector<uint32_t, 4, SystemAllocPolicy> quadSlots;
   uint32_t height_;
 
-  void addAvailableSlot(uint32_t index) {
-    // Ignoring OOM here (and below) is fine; it just means the stack slot
-    // will be unused.
-    (void)normalSlots.append(index);
-  }
-  void addAvailableDoubleSlot(uint32_t index) {
-    (void)doubleSlots.append(index);
+  [[nodiscard]] bool incrementHeight(uint32_t amount) {
+    // See MaxBytes for why we don't need to check for overflow here.
Loading diff…