Firefox · SpiderMonkey
CVE-2026-16355
Logic Error in SpiderMonkey
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
forjs/src/jit/IonAnalysis.cpp |
modified | |
ifjs/src/jit/IonAnalysis.cpp |
modified |
Files Changed
js/src/jit/IonAnalysis.cppjs/src/jit/MacroAssembler.cpp
Patch
diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp
index a2c88692e66..36597cc8752 100644
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -14,6 +14,7 @@
#include "jit/DominatorTree.h"
#include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h"
+#include "js/HashTable.h"
#include "vm/BytecodeUtil-inl.h"
@@ -2485,6 +2486,85 @@ static MObjectToIterator* FindObjectToIteratorUse(MDefinition* ins) {
return nullptr;
}
+using IteratorMoreSet =
+ InlineSet<MIteratorMore*, 8, DefaultHasher<MIteratorMore*>,
+ BackgroundSystemAllocPolicy>;
+
+static bool FindSafeIteratorMoreInstructions(MIRGraph& graph,
+ IteratorMoreSet& safeIterMores) {
+ // Fill |safeIterMores| with MIteratorMore instructions where no instruction
+ // use is dominated by an MIteratorEnd for the same iterator.
+
+ using InstructionVector =
+ Vector<MInstruction*, 8, BackgroundSystemAllocPolicy>;
+
+ auto hasDominatingIteratorEnd = [](const InstructionVector& iteratorEnds,
+ MInstruction* access) {
+ for (MInstruction* iteratorEnd : iteratorEnds) {
+ if (iteratorEnd->dominates(access)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ for (MBasicBlockIterator block(graph.begin()); block != graph.end();
+ block++) {
+ for (MInstructionIterator ins(block->begin()); ins != block->end(); ins++) {
+ if (!ins->isObjectToIterator()) {
+ continue;
+ }
+
+ InstructionVector iteratorMores;
+ InstructionVector iteratorEnds;
+ bool hasPhiUse = false;
+
+ for (MUseDefIterator uses(*ins); uses; uses++) {
+ MDefinition* def = uses.def();
+ if (def->isIteratorMore()) {
+ if (!iteratorMores.append(def->toInstruction())) {
+ return false;
+ }
+ } else if (def->isIteratorEnd()) {
+ if (!iteratorEnds.append(def->toInstruction())) {
+ return false;
+ }
+ } else if (def->isLoadIteratorElement() ||
+ def->isObjectKeysFromIterator() || def->isIteratorLength() ||
+ def->isPostWriteBarrier() || def->isStoreElement()) {
+ continue;
+ } else if (def->isPhi()) {
+ hasPhiUse = true;
+ break;
+ } else {
+ MOZ_CRASH("Unexpected ObjectToIterator use");
+ }
+ }
+ if (hasPhiUse) {
+ continue;
+ }
+
+ for (MInstruction* iterMore : iteratorMores) {
+ bool hasUnsafeUse = false;
+ for (MUseDefIterator iterMoreUses(iterMore); iterMoreUses;
+ iterMoreUses++) {
+ MDefinition* def = iterMoreUses.def();
+ if (def->isInstruction() &&
+ hasDominatingIteratorEnd(iteratorEnds, def->toInstruction())) {
+ hasUnsafeUse = true;
+ break;
+ }
+ }
+ if (!hasUnsafeUse && !safeIterMores.put(iterMore->toIteratorMore())) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
bool jit::OptimizeIteratorIndices(const MIRGenerator* mir, MIRGraph& graph) {
bool changed = false;
@@ -2493,6 +2573,11 @@ bool jit::OptimizeIteratorIndices(const MIRGenerator* mir, MIRGraph& graph) {
return block->id() >= numInitialBlocks;
};
+ IteratorMoreSet safeIteratorMores;
+ if (!FindSafeIteratorMoreInstructions(graph, safeIteratorMores)) {
+ return false;
+ }
+
for (ReversePostorderIterator blockIter = graph.rpoBegin();
blockIter != graph.rpoEnd();) {
MBasicBlock* block = *blockIter++;
@@ -2594,6 +2679,9 @@ bool jit::OptimizeIteratorIndices(const MIRGenerator* mir, MIRGraph& graph) {
SkipIterObjectUnbox(receiver)) {
continue;
}
+ if (!safeIteratorMores.has(iterNext)) {
+ continue;
+ }
} else if (supportObjectKeys && SkipBox(idVal)->isLoadIteratorElement()) {
auto* iterLoad = SkipBox(idVal)->toLoadIteratorElement();
diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp
index 32ee888f311..7afb8570506 100644
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -3160,9 +3160,28 @@ void MacroAssembler::extractCurrentIndexAndKindFromIterator(Register iterator,
PropertyIteratorObject::offsetOfIteratorSlot());
loadPrivate(nativeIterAddr, outIndex);
+#ifdef DEBUG
+ // Assert the Active flag is set.
+ Label iterActive;
+ branchTest32(Assembler::NonZero,
+ Address(outIndex, NativeIterator::offsetOfFlags()),
+ Imm32(NativeIterator::Flags::Active), &iterActive);
+ assumeUnreachable("iterator-index fast path on an inactive iterator");
+ bind(&iterActive);
+#endif
+
// Load the property count into outKind.
load32(Address(outIndex, NativeIterator::offsetOfPropertyCount()), outKind);
+ // The cursor must not be 0 because then we would access indices[cursor - 1]
+ // below.
+ Label cursorOk;
+ branch32(Assembler::NotEqual,
+ Address(outIndex, NativeIterator::offsetOfPropertyCursor()),
+ Imm32(0), &cursorOk);
+ assumeUnreachable("iterator-index fast path on a closed iterator");
+ bind(&cursorOk);
+
// We need two bits of wiggle room in a u32 here for the logic below.
static_assert(NativeIterator::PropCountLimit <= 1 << 30);
Loading diff…
References
On This Page