CVE-2026-65340

Overview

Advisory: Apple Security Advisory

Impact:

Description: Processing maliciously crafted web content may lead to an unexpected Safari crash

Researchers: Claudio Bozzato and Francesco Benvenuto of Cisco Talos, Josef Korbel (Citadelo)

Attribute Value
CVE CVE-2026-65340
Bugzilla 316996
Component JSC
Bug Class LogicError
Severity medium
Commit 30b9a27b47e842c4…
Advisory Apple Advisory

Root Cause Analysis

This issue was addressed through improved state management.

Files Changed

Source Files

  • Source/JavaScriptCore/runtime/RegExpMatchesArray.h
  • Source/JavaScriptCore/yarr/YarrJIT.cpp

Test Files

  • JSTests/stress/yarr-jit-non-bmp-backtrack-index-overflow.js
  • JSTests/stress/yarr-jit-non-bmp-backtrack-index-underflow.js

Patch Preview

diff --git a/JSTests/stress/yarr-jit-non-bmp-backtrack-index-overflow.js b/JSTests/stress/yarr-jit-non-bmp-backtrack-index-overflow.js
new file mode 100644
index 000000000000..8657a02fb82c
--- /dev/null
+++ b/JSTests/stress/yarr-jit-non-bmp-backtrack-index-overflow.js
@@ -0,0 +1,12 @@
+const re = /d\0e?|\u{10000}c/u;
+const subj = "\u{10000}d";
+const m = re.exec(subj);
+
+if (m !== null) {
+    throw new Error(
+        "expected null, got match=" + JSON.stringify(m[0]) +
+        " at index=" + m.index +
+        " (m.index + m[0].length = " + (m.index + m[0].length) +
+        " > subj.length = " + subj.length + ")"
+    );
+}
diff --git a/JSTests/stress/yarr-jit-non-bmp-backtrack-index-underflow.js b/JSTests/stress/yarr-jit-non-bmp-backtrack-index-underflow.js
new file mode 100644
index 000000000000..52fb0bbf4021
--- /dev/null
+++ b/JSTests/stress/yarr-jit-non-bmp-backtrack-index-underflow.js
@@ -0,0 +1,5 @@
+const re = /(?!\u{10000})a*|\u{10000}b/u;
+const subj = "\u{10000}cc";
+const m = re.exec(subj);
+if (m[0].length > subj.length)
+    throw new Error("m[0].length (" + m[0].length + ") > subj.length (" + subj.length + ")");
diff --git a/Source/JavaScriptCore/runtime/RegExpMatchesArray.h b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h
index 4d5d2304e1d1..4d46a7f017f8 100644
--- a/Source/JavaScriptCore/runtime/RegExpMatchesArray.h
+++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h
@@ -76,7 +76,8 @@ ALWAYS_INLINE JSArray* createRegExpMatchesArray(
 
     result.start = position;
     result.end = subpatternResults[1];
-    
+    RELEASE_ASSERT(result.end >= result.start);
+
     JSArray* array;
     JSArray* indicesArray = nullptr;
 
diff --git a/Source/JavaScriptCore/yarr/YarrJIT.cpp b/Source/JavaScriptCore/yarr/YarrJIT.cpp
index d5dc7005cc4d..90ead5391420 100644
--- a/Source/JavaScriptCore/yarr/YarrJIT.cpp
+++ b/Source/JavaScriptCore/yarr/YarrJIT.cpp
@@ -3925,9 +3925,19 @@ class YarrGenerator final : public YarrJITInfo {
                             // already correctly incremented, if more than one then decrement as appropriate.
                             unsigned delta = alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize;
                             ASSERT(delta);
+                            bool advancedIndexForNonBMP = false;
+#if ENABLE(YARR_JIT_UNICODE_EXPRESSIONS) && ENABLE(YARR_JIT_UNICODE_CAN_INCREMENT_INDEX_FOR_NON_BMP)
+                            if (m_useFirstNonBMPCharacterOptimization) {
+                                m_jit.add32(m_regs.firstCharacterAdditionalReadSize, m_regs.index);
+                                advancedIndexForNonBMP = true;
+                            }
+#endif
                             if (delta != 1)
                                 m_jit.sub32(MacroAssembler::Imm32(delta - 1), m_regs.index);
-                            m_jit.jump(beginOp->m_reentry);
+                            if (advancedIndexForNonBMP)
+                                checkInput().linkTo(beginOp->m_reentry, &m_jit);
+                            else
+                                m_jit.jump(beginOp->m_reentry);
                         } else {
                             // If the first alternative has minimum size 0xFFFFFFFFu, then there cannot
                             // be sufficent input available to handle this, so just fall through.