Firefox · SpiderMonkey
CVE-2026-74976
Logic Error in SpiderMonkey
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifjs/src/jit/Lowering.cpp |
modified |
Files Changed
js/src/jit/CodeGenerator.cppjs/src/jit/Lowering.cppjs/src/jit/MIR.h
Patch
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
index 1926b46c308..efcdc0c8d2c 100644
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1178,9 +1178,6 @@ void CodeGenerator::visitFloat32ToInt32(LFloat32ToInt32* lir) {
void CodeGenerator::visitInt32ToIntPtr(LInt32ToIntPtr* lir) {
#ifdef JS_64BIT
- // This LIR instruction is only used if the input can be negative.
- MOZ_ASSERT(lir->mir()->canBeNegative());
-
Register output = ToRegister(lir->output());
const LAllocation* input = lir->input();
if (input->isGeneralReg()) {
diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp
index bcbcde85983..73843102489 100644
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3631,30 +3631,50 @@ void LIRGenerator::visitInt32ToIntPtr(MInt32ToIntPtr* ins) {
// If the result is only used by instructions that expect a bounds-checked
// index, we must have eliminated or hoisted a bounds check and we can assume
// the index is non-negative. This lets us generate more efficient code.
- if (ins->canBeNegative()) {
- bool canBeNegative = false;
- for (MUseDefIterator iter(ins); iter; iter++) {
- if (iter.def()->isSpectreMaskIndex()) {
- continue;
- }
- if (iter.def()->isLoadUnboxedScalar() ||
- iter.def()->isStoreUnboxedScalar() ||
- iter.def()->isLoadDataViewElement() ||
- iter.def()->isStoreDataViewElement()) {
- MOZ_ASSERT(iter.def()->indexOf(iter.use()) == 1,
- "unexpected non-index operand use");
- continue;
- }
-
- canBeNegative = true;
- break;
+ //
+ // The no-op lowering with |redefine| shares the input's virtual register and
+ // 32-bit spill slot, so all instruction uses must read from a register:
+ // registers hold Int32 values zero-extended, which matches the sign-extended
+ // IntPtr value because we only do this when the value is non-negative. A
+ // pointer-width memory read would instead also consume bytes from the
+ // adjacent stack slot. Snapshots handle this separately with
+ // RValueAllocation::IntPtrInt32.
+ bool canBeNegative = false;
+ bool canRedefine = true;
+ for (MUseDefIterator iter(ins); iter; iter++) {
+ MDefinition* def = iter.def();
+ size_t index = def->indexOf(iter.use());
+ if (def->isLoadUnboxedScalar() || def->isStoreUnboxedScalar() ||
+ def->isLoadDataViewElement() || def->isStoreDataViewElement()) {
+ // The bounds-checked index is lowered with useRegisterOrIndexConstant.
+ static_assert(MLoadUnboxedScalar::indexOperand == 1);
+ static_assert(MStoreUnboxedScalar::indexOperand == 1);
+ static_assert(MLoadDataViewElement::indexOperand == 1);
+ static_assert(MStoreDataViewElement::indexOperand == 1);
+ MOZ_RELEASE_ASSERT(index == 1);
+ continue;
+ }
+ if (def->isSpectreMaskIndex() && index == MSpectreMaskIndex::indexOperand) {
+ // The bounds-checked index is lowered with useRegister.
+ continue;
}
- if (!canBeNegative) {
- ins->setCanNotBeNegative();
+ if (def->isBoundsCheck() && index == MBoundsCheck::indexOperand) {
+ // The index operand is lowered with useRegisterOrInt32Constant, but
+ // because this is the instruction performing the check it implies
+ // nothing about the sign.
+ canBeNegative = true;
+ continue;
}
+ // Don't optimize other uses.
+ canBeNegative = true;
+ canRedefine = false;
+ break;
+ }
+ if (!canBeNegative) {
+ ins->setCanNotBeNegative();
}
- if (ins->canBeNegative()) {
+ if (ins->canBeNegative() || !canRedefine) {
auto* lir = new (alloc()) LInt32ToIntPtr(useAnyAtStart(input));
define(lir, ins);
} else {
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index cef95b493d6..cb77524214a 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1137,8 +1137,10 @@ class MInstruction : public MDefinition, public InlineListNode<MInstruction> {
// NAMED_OPERANDS((0, lhs), (1, rhs))
//
// The above example defines 2 accessors, one named "lhs" accessing the first
-// operand, and a one named "rhs" accessing the second operand.
-#define NAMED_OPERAND_ACCESSOR(Index, Name) \
+// operand, and a one named "rhs" accessing the second operand. It also defines
+// the operand indices as |lhsOperand| and |rhsOperand|.
+#define NAMED_OPERAND_ACCESSOR(Index, Name) \
+ static constexpr size_t Name##Operand = Index; \
MDefinition* Name() const { return getOperand(Index); }
#define NAMED_OPERAND_ACCESSOR_APPLY(Args) NAMED_OPERAND_ACCESSOR Args
#define NAMED_OPERANDS(...) \
Loading diff…
References
On This Page