Medium CVSS 6.5 webkit Logic Error 🔧 Commit mapped

Overview

Medium
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionProcessing maliciously crafted web content may lead to an unexpected Safari crash
ComponentJSC Wasm
Bug ClassLogic Error
Tracker315365
Fix commit0f0de8f2a058 (WebKit/WebKit) +123/-3
CWECWE-787 (Out-of-bounds write)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H
CISA KEVNot listed
CreditedOpenAI Codex Security - Amy Burnett, Khai Tran
Disclosed2026-06-29

Background

Legacy vs modern wasm exceptions
The original exception-handling proposal (try/catch/rethrow) and the newer try_table construct; a module must not mix them.
Streaming vs non-streaming compilation
Wasm can be compiled from a full buffer (WebAssembly.Module) or incrementally as bytes arrive (instantiateStreaming); both must reach the same accept/reject decision.
EntryPlan / IPIntPlan
JSC compilation plans that drive wasm function compilation; IPIntPlan handles the in-place interpreter/streaming completion.
Validation parity
The security-relevant property that all compilation entry points enforce identical validation, so no path accepts a module another rejects.

Root Cause Analysis

WebAssembly modules may not mix the legacy exception-handling proposal (try/catch, rethrow) with the modern try_table proposal in the same module; JSC rejects such modules with a CompileError. That guard was implemented only in the non-streaming compilation path: EntryPlan::compileFunctions() checked m_moduleInformation->m_usesModernExceptions && m_usesLegacyExceptions and called fail(...). The streaming compilation path (IPIntPlan::completeInStreaming()) had no equivalent check, so streaming and non-streaming compilation disagreed on the same bytes: a module mixing both EH schemes was rejected when compiled normally but accepted when compiled via the streaming API. Accepting such a module runs it through exception-handling codegen/metadata that assumes a single EH scheme, violating an invariant the rest of the EH machinery relies on.

The fix factors the check into EntryPlan::failIfMixedExceptionHandlingProposals() (guarded by m_lock) and calls it from both the non-streaming path and IPIntPlan::completeInStreaming(), restoring the invariant that streaming and non-streaming compilation agree — both fail with a CompileError — for a module that mixes legacy exceptions and try_table. The added JSTests pin that streaming/non-streaming outcomes match for try_table-only and mixed-EH modules. That downstream EH handling of a wrongly-accepted mixed module is unsafe is an inference; the patch establishes the validation-parity gap and closes it.

Key insight
A validation invariant (reject modules mixing legacy exceptions and try_table) was enforced only on the non-streaming compile path, so streaming compilation accepted modules the engine otherwise forbids — a compile-path parity gap.

Attack Path

  1. Craft a mixed-EH module Build a wasm module that uses both a legacy try/catch (or rethrow) and a modern try_table in its functions.
  2. Compile via the streaming API Instantiate it through streaming compilation (WebAssembly.instantiateStreaming / the streaming compiler), whose completion path lacked the mixed-EH guard.
  3. Bypass validation Streaming accepts the module that non-streaming would reject with a CompileError, so it is compiled and instantiable.
  4. Exercise inconsistent EH codegen Running the module drives exception-handling code paths that assume a single EH scheme (inference), risking mis-compiled EH and a crash/memory unsafety in WebContent.

Impact Assessment

The patch establishes a validation-parity gap: streaming compilation failed to reject modules that mix legacy and modern exception handling, which non-streaming compilation rejects. Running a wrongly-accepted mixed-EH module exercises exception-handling codegen that assumes a single scheme, which is a plausible route to memory unsafety (inference) rather than a demonstrated primitive; the concrete, patch-supported effect is inconsistent accept/reject behavior. Confined to WebContent (the wasm compiler). Rated medium.

Changed Functions

FunctionChangeNotes
EntryPlan::failIfMixedExceptionHandlingProposals
Source/JavaScriptCore/wasm/WasmEntryPlan.cpp
added Extracts the 'uses both legacy exceptions and try_table' check into one lock-guarded helper that fails the plan.
EntryPlan::compileFunctions
Source/JavaScriptCore/wasm/WasmEntryPlan.cpp
modified Now calls failIfMixedExceptionHandlingProposals() instead of an inline check.
IPIntPlan::completeInStreaming
Source/JavaScriptCore/wasm/WasmIPIntPlan.cpp
modified Adds the previously-missing failIfMixedExceptionHandlingProposals() call so streaming rejects mixed-EH modules too.
EntryPlan::failIfMixedExceptionHandlingProposals (decl)
Source/JavaScriptCore/wasm/WasmEntryPlan.h
modified Declares the helper WTF_REQUIRES_LOCK(m_lock).

Files Changed

  • JSTests/wasm/stress/streaming-compile-try-table-agreement.js
  • Source/JavaScriptCore/wasm/WasmEntryPlan.cpp
  • Source/JavaScriptCore/wasm/WasmEntryPlan.h
  • Source/JavaScriptCore/wasm/WasmIPIntPlan.cpp

Audit Directions

  • Other single-path validation
    Diff EntryPlan/BBQ/OMG/IPInt streaming vs non-streaming completion for other checks present in one path only; grep for fail(makeString( guards not mirrored in completeInStreaming.
  • Proposal-mixing guards
    Audit uses of m_usesModernExceptions/m_usesLegacyExceptions and other ‘usesX’ module flags to ensure every consumer enforces the same constraints.
  • Streaming completion paths
    Review all *Plan::completeInStreaming()/completeSyncIfPossible() to confirm they run the full set of module-level validations before complete().

Original Bug Report

The reporter's bug is still restricted on the tracker.