Low firefox Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactlow
DescriptionJIT miscompilation in the JavaScript Engine: JIT component
ComponentSpiderMonkey
Bug ClassLogic Error
Tracker2053680
Fix commitcacd0898986e (firefox) +29/-7
CISA KEVNot listed
CreditedAmy Burnett of OpenAI
Disclosed2026-07-21

Changed Functions

FunctionChangeNotes
with
js/src/jit-test/tests/ion/bug2053680.js
modified
for
js/src/jit-test/tests/ion/bug2053680.js
modified

Files Changed

  • js/src/jit-test/tests/ion/bug2053680.js
  • js/src/jit/RangeAnalysis.cpp
diff --git a/js/src/jit-test/tests/ion/bug2053680.js b/js/src/jit-test/tests/ion/bug2053680.js
new file mode 100644
index 00000000000..02969289f87
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug2053680.js
@@ -0,0 +1,17 @@
+// |jit-test| --disable-main-thread-denormals; --fast-warmup; --ion-check-range-analysis; skip-if: !getBuildConfiguration("can-disable-main-thread-denormals")
+function f(x) {
+  x = Math.fround(x);
+  if (x >= Math.fround(-1e-40)) {
+    return x ? x : 0;
+  }
+  return 7;
+}
+function test() {
+  with ({}) {} // Don't Ion-compile this function.
+  var res = 0;
+  for (var i = 0; i < 1000; i++) {
+    res += f(Math.fround(-1e-40));
+  }
+  assertEq(res, 0);
+}
+test();
diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp
index 5d538b81875..6ff34ea874e 100644
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -718,14 +718,19 @@ void Range::setDouble(double l, double h) {
   canHaveFractionalPart_ = ExcludesFractionalParts;
   canBeNegativeZero_ = ExcludesNegativeZero;
 
-  // If denormals are disabled, any value with exponent 0 will be immediately
-  // flushed to 0. This gives 2**53 bit patterns that compare equal to zero.
+  // If denormals are disabled, any denormal value will be immediately flushed
+  // to 0, so any bit pattern in the denormal range compares equal to zero.
   //
-  // Check whether the range [l .. h] can cross any of the 2^53 zeros. We have
-  // to be conservative as the main thread might not interpret doubles the same
-  // way as the compiler thread.
-  const double doubleMin = mozilla::BitwiseCast<double>(
-      mozilla::SpecificFloatingPointBits<double, 0, 1, 0>::value);
+  // Check whether the range [l .. h] can cross any of these zeros. We have to
+  // be conservative as the main thread might not interpret floating point
+  // values the same way as the compiler thread.
+  //
+  // This Range may describe a Float32 value, whose denormal range begins at
+  // the smallest normal binary32 (2**-126) rather than the smallest normal
+  // binary64 (2**-1022). Use the (wider) binary32 threshold so we stay
+  // conservative for both float32 and double values.
+  const double doubleMin = double(mozilla::BitwiseCast<float>(
+      mozilla::SpecificFloatingPointBits<float, 0, 1, 0>::value));
   bool includesNegative = std::isnan(l) || l < doubleMin;
   bool includesPositive = std::isnan(h) || h > -doubleMin;
   bool crossesZero = includesNegative && includesPositive;
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/js/src/jit-test/tests/ion/bug2053680.js b/js/src/jit-test/tests/ion/bug2053680.js
new file mode 100644
index 00000000000..02969289f87
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug2053680.js
@@ -0,0 +1,17 @@
+// |jit-test| --disable-main-thread-denormals; --fast-warmup; --ion-check-range-analysis; skip-if: !getBuildConfiguration("can-disable-main-thread-denormals")
+function f(x) {
+  x = Math.fround(x);
+  if (x >= Math.fround(-1e-40)) {
+    return x ? x : 0;
+  }
+  return 7;
+}
+function test() {
+  with ({}) {} // Don't Ion-compile this function.
+  var res = 0;
+  for (var i = 0; i < 1000; i++) {
+    res += f(Math.fround(-1e-40));
+  }
+  assertEq(res, 0);
+}
+test();
Loading diff…