Released August 17, 2026

CVE-2026-64780

CVSS 4.3 severity medium CWE-119 vector CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L

Overview

Impact:

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

Researchers: OpenAI Codex Security - Amy Burnett

Key insight. A misspelled setter (setUsessSIMD) and an empty notifyFunctionUsesSIMD meant B3 was never told a WASM function uses SIMD, so it miscompiled the vector code; wiring notifyFunctionUsesSIMD to setUsesSIMD() fixes it.

Patch Details

diff --git a/JSTests/wasm/stress/inline-wasm-simd-into-non-simd.js b/JSTests/wasm/stress/inline-wasm-simd-into-non-simd.js
new file mode 100644
index 000000000000..20ba8b3113d6
--- /dev/null
+++ b/JSTests/wasm/stress/inline-wasm-simd-into-non-simd.js
@@ -0,0 +1,54 @@
+load("../libwabt.js");
+
+const NUM_REFS = 49;
+
+let wabt = await WabtModule();
+
+let refParams = "", refResults = "", pushResults = "";
+
+for (let i = 1; i <= NUM_REFS; i++) {
+    refParams  += " externref";
+    refResults += " externref";
+    pushResults += `(local.get ${i})\n`;
+}
+
+const wat = `
+(module
+  (tag $T (param i64 i64 i64 v128))
+  (func $B (param i64)
+    (throw $T (i64.const 0) (i64.const 0) (i64.const 0)
+              (i64x2.splat (local.get 0))))
+  (func $A (export "A") (param i64${refParams}) (result${refResults})
+    (try
+      (do (call $B (local.get 0)))
+      (catch_all))
+    ${pushResults}
+  )
+)`;
+
+const bin = wabt.parseWat("filenamesAreCool", wat, { simd: true, exceptions: true, reference_types: true, multi_value: true }).toBinary({}).buffer;
+const inst = new WebAssembly.Instance(new WebAssembly.Module(bin), {});
+const A = inst.exports.A;
+
+const sentinel = { marker: "SENTINEL" };
+const args = [0xan];
+for (let i = 0; i < NUM_REFS; i++)
+    args.push(sentinel);
+
+// Tier $A up to OMG.
+let last;
+for (let i = 0; i < testLoopCount; i++)
+    last = A.apply(null, args);
+
+let bad = -1;
+
+for (let k = 0; k < NUM_REFS; k++) {
+    if (last[k] !== sentinel) {
+        bad = k;
+        break;
+    }
+}
+
+if (bad >= 0) {
+    throw new Error("usesSIMD desync");
+}
diff --git a/Source/JavaScriptCore/b3/B3Procedure.cpp b/Source/JavaScriptCore/b3/B3Procedure.cpp
index 6f0d8bb8b466..1b1c1bf6d036 100644
--- a/Source/JavaScriptCore/b3/B3Procedure.cpp
+++ b/Source/JavaScriptCore/b3/B3Procedure.cpp
@@ -58,7 +58,7 @@ Procedure::Procedure(bool usesSIMD)
     , m_heaps(makeUniqueRef<AbstractHeapRepository>())
 {
     if (usesSIMD)
-        setUsessSIMD();
+        setUsesSIMD();
     // Initialize all our fields before constructing Air::Code since
     // it looks into our fields.
     m_code = std::unique_ptr<Air::Code>(new Air::Code(*this));
diff --git a/Source/JavaScriptCore/b3/B3Procedure.h b/Source/JavaScriptCore/b3/B3Procedure.h
index e25a2ef198c7..47986dcdebbe 100644
--- a/Source/JavaScriptCore/b3/B3Procedure.h
+++ b/Source/JavaScriptCore/b3/B3Procedure.h
@@ -292,7 +292,7 @@ public:
     bool shouldDumpIR() const { return m_shouldDumpIR; }
     JS_EXPORT_PRIVATE void NODELETE setShouldDumpIR();
 
-    void setUsessSIMD()
+    void setUsesSIMD()
     { 
         RELEASE_ASSERT(Options::useWasmSIMD());
         m_usesSIMD = true;
diff --git a/Source/JavaScriptCore/wasm/WasmOMGIRGenerator.cpp b/Source/JavaScriptCore/wasm/WasmOMGIRGenerator.cpp
index 05391ae63e35..e36dd2043b4e 100644
--- a/Source/JavaScriptCore/wasm/WasmOMGIRGenerator.cpp
+++ b/Source/JavaScriptCore/wasm/WasmOMGIRGenerator.cpp
@@ -497,7 +497,13 @@ public:
     }
 
     // SIMD
-    void NODELETE notifyFunctionUsesSIMD() { ASSERT(m_info.usesSIMD(m_functionIndex)); }
+    bool NODELETE usesSIMD() const { return m_info.usesSIMD(m_functionIndex); }
+    void NODELETE notifyFunctionUsesSIMD()
+    {
+        ASSERT(m_info.usesSIMD(m_functionIndex));
+        m_proc.setUsesSIMD();
+    }
+
     [[nodiscard]] PartialResult addSIMDLoad(ExpressionType pointer, uint64_t offset, ExpressionType& result, uint8_t memoryIndex);
     [[nodiscard]] PartialResult addSIMDStore(ExpressionType value, ExpressionType pointer, uint64_t offset, uint8_t memoryIndex);
     [[nodiscard]] PartialResult addSIMDSplat(SIMDLane, ExpressionType scalar, ExpressionType& result);

Background

Advisory: Apple Security Advisory

Attribute Value
CVE CVE-2026-64780
Bugzilla 316918
Component JSC
Bug Class TypeConfusion
Severity medium
Commit 9a17cd1100ad6cd4…
Advisory Apple Advisory

Terminology

  • B3 Procedure usesSIMD — A flag telling B3 the compilation uses SIMD so it configures vector register allocation and codegen; if unset, SIMD ops are mishandled.
  • OMG tier — JSC’s optimizing WebAssembly compiler built on B3; it must notify B3 when a function uses SIMD.
  • notifyFunctionUsesSIMD — The hook that should propagate a function’s SIMD usage to the Procedure; it was a no-op.

Root Cause Analysis

This fixes a WebAssembly SIMD miscompilation caused by a typo that left the B3 Procedure’s ‘uses SIMD’ flag unset. B3 must know when a compilation uses SIMD so it configures vector register handling and related codegen correctly (setUsesSIMD sets m_usesSIMD and asserts Options::useWasmSIMD()). Two defects combined: the setter was misspelled setUsessSIMD() (double ’s’), and the WASM OMG IR generator’s notifyFunctionUsesSIMD() — invoked when a WASM function is known to use SIMD — was an empty body that only ASSERTed m_info.usesSIMD(…) and NEVER propagated the fact to B3 (it did not call m_proc.setUsesSIMD()). As a result, a WASM function that uses SIMD could be compiled with the Procedure’s m_usesSIMD flag still false, so B3/Air generate code without the SIMD-aware handling the vector operations require — a miscompilation that mishandles vector registers/values, corrupting state or crashing. The fix renames the setter to setUsesSIMD(), makes notifyFunctionUsesSIMD() actually call m_proc.setUsesSIMD() (and adds a usesSIMD() query), so the flag is set whenever a function uses SIMD. The restored invariant is that B3 is told a compilation uses SIMD before it generates code for SIMD operations.

Attack Path

1. Compile a SIMD WASM function

Run a WebAssembly module whose function uses SIMD so it reaches the OMG tier’s B3 pipeline.

2. Miss the usesSIMD flag

notifyFunctionUsesSIMD did not set the Procedure’s flag, so B3 compiles the function as if it uses no SIMD.

3. Generate wrong code

B3/Air handle vector operations without the SIMD-aware configuration, mismanaging vector registers/values.

4. Corrupt state / crash

The miscompiled SIMD code corrupts execution state in the WebContent process (advisory: crash).

Impact Assessment

A WebAssembly SIMD JIT miscompilation in the WebContent process, reachable from any page running SIMD WASM. Generating code without the SIMD flag mishandles vector registers/values, a memory-safety hazard the advisory rates as a crash that can underpin further corruption.

Audit Directions

SIMD-usage propagation

Audit all paths that detect SIMD usage (notifyFunctionUsesSIMD and callers) to confirm they set the Procedure flag before codegen.

Empty/ASSERT-only notifiers

Grep the WASM IR generators for notify*/hook methods whose body is only an ASSERT and that fail to propagate state to B3/Air.

Changed Functions

Function File Change Note
Procedure::setUsesSIMD (was setUsessSIMD) Source/JavaScriptCore/b3/B3Procedure.h modified Fixes the misspelled setter name; sets m_usesSIMD with a RELEASE_ASSERT on Options::useWasmSIMD().
OMGIRGenerator::notifyFunctionUsesSIMD / usesSIMD Source/JavaScriptCore/wasm/WasmOMGIRGenerator.cpp modified Now calls m_proc.setUsesSIMD() when a function uses SIMD (previously an empty body with only an ASSERT), and adds a usesSIMD() query.
Procedure::Procedure Source/JavaScriptCore/b3/B3Procedure.cpp modified Calls the corrected setUsesSIMD() during construction when usesSIMD is set.

Files Changed

Source Files

  • Source/JavaScriptCore/b3/B3Procedure.cpp
  • Source/JavaScriptCore/b3/B3Procedure.h
  • Source/JavaScriptCore/wasm/WasmOMGIRGenerator.cpp

Test Files

  • JSTests/wasm/stress/inline-wasm-simd-into-non-simd.js

Regression Test

The fix ships this proof-of-concept / regression test:

diff --git a/JSTests/wasm/stress/inline-wasm-simd-into-non-simd.js b/JSTests/wasm/stress/inline-wasm-simd-into-non-simd.js
new file mode 100644
index 000000000000..20ba8b3113d6
--- /dev/null
+++ b/JSTests/wasm/stress/inline-wasm-simd-into-non-simd.js
@@ -0,0 +1,54 @@
+load("../libwabt.js");
+
+const NUM_REFS = 49;
+
+let wabt = await WabtModule();
+
+let refParams = "", refResults = "", pushResults = "";
+
+for (let i = 1; i <= NUM_REFS; i++) {
+    refParams  += " externref";
+    refResults += " externref";
+    pushResults += `(local.get ${i})\n`;
+}
+
+const wat = `
+(module
+  (tag $T (param i64 i64 i64 v128))
+  (func $B (param i64)
+    (throw $T (i64.const 0) (i64.const 0) (i64.const 0)
+              (i64x2.splat (local.get 0))))
+  (func $A (export "A") (param i64${refParams}) (result${refResults})
+    (try
+      (do (call $B (local.get 0)))
+      (catch_all))
+    ${pushResults}
+  )
+)`;
+
+const bin = wabt.parseWat("filenamesAreCool", wat, { simd: true, exceptions: true, reference_types: true, multi_value: true }).toBinary({}).buffer;
+const inst = new WebAssembly.Instance(new WebAssembly.Module(bin), {});
+const A = inst.exports.A;
+
+const sentinel = { marker: "SENTINEL" };
+const args = [0xan];
+for (let i = 0; i < NUM_REFS; i++)
+    args.push(sentinel);
+
+// Tier $A up to OMG.
+let last;
+for (let i = 0; i < testLoopCount; i++)
+    last = A.apply(null, args);
+
+let bad = -1;
+
+for (let k = 0; k < NUM_REFS; k++) {
+    if (last[k] !== sentinel) {
+        bad = k;
+        break;
+    }
+}
+
+if (bad >= 0) {
+    throw new Error("usesSIMD desync");
+}