High firefox Logic Error 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionUse-after-free in the JavaScript Engine: JIT component
ComponentSpiderMonkey
Bug ClassLogic Error
Tracker2079040
Fix commitf8ee10bfd0ad (firefox) +63/-23
CISA KEVNot listed
CreditedMozilla
Disclosed2026-10-27

Changed Functions

FunctionChangeNotes
CACHEOP_CASE
js/src/vm/PortableBaselineInterpret.cpp
modified
if
js/src/vm/PortableBaselineInterpret.cpp
modified
with
js/src/jit-test/tests/cacheir/bug2079040.js
modified
for
js/src/jit-test/tests/cacheir/bug2079040.js
modified
if
js/src/jit-test/tests/cacheir/bug2079040.js
modified

Files Changed

  • js/src/jit-test/tests/cacheir/bug2079040.js
  • js/src/jit/CacheIRCompiler.cpp
  • js/src/jit/CacheIROps.yaml
  • js/src/jit/CacheIRWriter.h
  • js/src/jit/WarpCacheIRTranspiler.cpp
  • js/src/vm/PortableBaselineInterpret.cpp
diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp
index f3a8fb85d37..1cf65a9bc43 100644
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -3068,21 +3068,25 @@ bool CacheIRCompiler::emitDoubleParseIntResult(NumberOperandId numId) {
   return true;
 }
 
-bool CacheIRCompiler::emitStringToAtom(StringOperandId stringId) {
+bool CacheIRCompiler::emitStringToAtom(StringOperandId stringId,
+                                       StringOperandId resultId) {
   JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
   Register str = allocator.useRegister(masm, stringId);
-  AutoScratchRegister scratch(allocator, masm);
+  Register output = allocator.defineRegister(masm, resultId);
 
   FailurePath* failure;
   if (!addFailurePath(&failure)) {
     return false;
   }
 
-  Label done, vmCall;
-  masm.branchTest32(Assembler::NonZero, Address(str, JSString::offsetOfFlags()),
-                    Imm32(StringFlags::ATOM_BIT), &done);
+  Label done, notAtom, vmCall;
+  masm.branchTest32(Assembler::Zero, Address(str, JSString::offsetOfFlags()),
+                    Imm32(StringFlags::ATOM_BIT), &notAtom);
+  masm.movePtr(str, output);
+  masm.jump(&done);
 
-  masm.tryFastAtomize(str, scratch, str, &vmCall);
+  masm.bind(&notAtom);
+  masm.tryFastAtomize(str, output, output, &vmCall);
   masm.jump(&done);
 
   masm.bind(&vmCall);
@@ -3090,19 +3094,18 @@ bool CacheIRCompiler::emitStringToAtom(StringOperandId stringId) {
   masm.PushRegsInMask(save);
 
   using Fn = JSAtom* (*)(JSContext * cx, JSString * str);
-  masm.setupUnalignedABICall(scratch);
-  masm.loadJSContext(scratch);
-  masm.passABIArg(scratch);
+  masm.setupUnalignedABICall(output);
+  masm.loadJSContext(output);
+  masm.passABIArg(output);
   masm.passABIArg(str);
   masm.callWithABI<Fn, jit::AtomizeStringNoGC>();
-  masm.storeCallPointerResult(scratch);
+  masm.storeCallPointerResult(output);
 
   LiveRegisterSet ignore;
-  ignore.add(scratch);
+  ignore.add(output);
   masm.PopRegsInMaskIgnore(save, ignore);
 
-  masm.branchPtr(Assembler::Equal, scratch, Imm32(0), failure->label());
-  masm.movePtr(scratch.get(), str);
+  masm.branchTestPtr(Assembler::Zero, output, output, failure->label());
 
   masm.bind(&done);
   return true;
diff --git a/js/src/jit/CacheIROps.yaml b/js/src/jit/CacheIROps.yaml
index e7356f34685..196137a3984 100644
--- a/js/src/jit/CacheIROps.yaml
+++ b/js/src/jit/CacheIROps.yaml
@@ -640,10 +640,10 @@
 - name: StringToAtom
   shared: true
   transpile: true
-  custom_writer: true
   cost_estimate: 4
   args:
     str: StringId
+    result: StringId
 
 - name: BooleanToNumber
   shared: true
diff --git a/js/src/jit/CacheIRWriter.h b/js/src/jit/CacheIRWriter.h
index 3b13dc8b94e..86d3566780c 100644
--- a/js/src/jit/CacheIRWriter.h
+++ b/js/src/jit/CacheIRWriter.h
@@ -479,11 +479,6 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter {
     return NumberOperandId(input.id());
   }
 
-  StringOperandId stringToAtom(StringOperandId input) {
-    stringToAtom_(input);
-    return input;
-  }
-
   ValOperandId boxObject(ObjOperandId input) {
     return ValOperandId(input.id());
   }
diff --git a/js/src/jit/WarpCacheIRTranspiler.cpp b/js/src/jit/WarpCacheIRTranspiler.cpp
index 2442edcb4ce..476ee74872e 100644
--- a/js/src/jit/WarpCacheIRTranspiler.cpp
+++ b/js/src/jit/WarpCacheIRTranspiler.cpp
@@ -1683,14 +1683,14 @@ bool WarpCacheIRTranspiler::emitBooleanToNumber(BooleanOperandId inputId,
   return defineOperand(resultId, ins);
 }
 
-bool WarpCacheIRTranspiler::emitStringToAtom(StringOperandId strId) {
+bool WarpCacheIRTranspiler::emitStringToAtom(StringOperandId strId,
+                                             StringOperandId resultId) {
   MDefinition* str = getOperand(strId);
 
   auto* ins = MToHashableString::New(alloc(), str);
   add(ins);
 
-  setOperand(strId, ins);
-  return true;
+  return defineOperand(resultId, ins);
 }
 
 bool WarpCacheIRTranspiler::emitLoadInt32Result(Int32OperandId valId) {
diff --git a/js/src/vm/PortableBaselineInterpret.cpp b/js/src/vm/PortableBaselineInterpret.cpp
index f4396e1266e..0bdda223452 100644
--- a/js/src/vm/PortableBaselineInterpret.cpp
+++ b/js/src/vm/PortableBaselineInterpret.cpp
@@ -4758,13 +4758,15 @@ uint64_t ICInterpretOps(uint64_t arg0, uint64_t arg1, ICStub* stub,
 
       CACHEOP_CASE(StringToAtom) {
         StringOperandId strId = cacheIRReader.stringOperandId();
+        StringOperandId resultId = cacheIRReader.stringOperandId();
+        BOUNDSCHECK(resultId);
         JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
         JSAtom* result =
             AtomizeStringNoGC(ctx.frameMgr.cxForLocalUseOnly(), str);
         if (!result) {
           FAIL_IC();
         }
-        WRITE_REG(strId.id(), reinterpret_cast<uint64_t>(result), STRING);
+        WRITE_REG(resultId.id(), reinterpret_cast<uint64_t>(result), STRING);
         DISPATCH_CACHEOP();
       }
 
diff --git a/js/src/jit-test/tests/cacheir/bug2079040.js b/js/src/jit-test/tests/cacheir/bug2079040.js
new file mode 100644
index 00000000000..38322951360
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug2079040.js
@@ -0,0 +1,40 @@
+function f(o, a, b, h1, h2) {
+  var k = a + b;
+  h1.w = k;
+  var r = o.hasOwnProperty(k);
+  h2.x = k;
+  return r;
+}
+with ({}) {}
+var N = 9000;
+var hs1 = [], hs2 = [];
+for (var i = 0; i < N; i++) {
+  hs1.push({a: i});
+  hs2.push({a: i});
+}
+var objs = [];
+for (var i = 0; i < 5; i++) {
+  var o = {};
+  o["p" + i] = 1;
+  objs.push(o);
+}
+var small = {abcdefgh1: 1, abcdefgh2: 2};
+gc();
+for (var i = 0; i < N; i++) {
+  var o = small, b = "h1";
+  if (i >= 600 && i < 603) {
+    o = "primitive";
+  } else if (i >= 6000 && i < 6005) {
+    o = objs[i - 6000];
+  } else if (i >= 6005) {
+    b = "h2";
+  }
+  f(o, "abcdefg", b, hs1[i], hs2[i]);
+}
+minorgc();
+var sum = 0;
+for (var i = 6006; i < N; i++) {
+  var s = hs2[i].x;
+  sum += s.length + s.charCodeAt(0);
+}
+assertEq(sum, 317364);
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/js/src/jit-test/tests/cacheir/bug2079040.js b/js/src/jit-test/tests/cacheir/bug2079040.js
new file mode 100644
index 00000000000..38322951360
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug2079040.js
@@ -0,0 +1,40 @@
+function f(o, a, b, h1, h2) {
+  var k = a + b;
+  h1.w = k;
+  var r = o.hasOwnProperty(k);
+  h2.x = k;
+  return r;
+}
+with ({}) {}
+var N = 9000;
+var hs1 = [], hs2 = [];
+for (var i = 0; i < N; i++) {
+  hs1.push({a: i});
+  hs2.push({a: i});
+}
+var objs = [];
+for (var i = 0; i < 5; i++) {
+  var o = {};
+  o["p" + i] = 1;
+  objs.push(o);
+}
+var small = {abcdefgh1: 1, abcdefgh2: 2};
+gc();
+for (var i = 0; i < N; i++) {
+  var o = small, b = "h1";
+  if (i >= 600 && i < 603) {
+    o = "primitive";
+  } else if (i >= 6000 && i < 6005) {
+    o = objs[i - 6000];
+  } else if (i >= 6005) {
+    b = "h2";
+  }
+  f(o, "abcdefg", b, hs1[i], hs2[i]);
+}
+minorgc();
+var sum = 0;
+for (var i = 6006; i < N; i++) {
+  var s = hs2[i].x;
+  sum += s.length + s.charCodeAt(0);
+}
+assertEq(sum, 317364);
Loading diff…