Medium CVSS 7.5 webkit Type Confusion 🔧 Commit mapped

Overview

Medium
Severity
7.5
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionProcessing maliciously crafted web content may lead to an unexpected process crash
ComponentJSC Wasm
Bug ClassType Confusion
Tracker278497
Fix commitf95652711d7e (WebKit/WebKit)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CISA KEVNot listed
CreditedSeunghyun Lee
Disclosed2024-12-11

Background

Wasm reference types / subtyping
Reference types (e.g. (ref null func) vs a specific (ref null $t)) form a subtype lattice used to check import compatibility.
Covariance vs invariance
Read-only positions may be covariant (one-way subtype); read-write (mutable) positions must be invariant (both directions) to stay type-safe.
Mutable global / table
Wasm storage that can be both read and written through its typed view, shared across importing modules.
Import linking
initializeImports binds a module’s imports to provided values, checking their types before use.

Root Cause Analysis

WebAssemblyModuleRecord::initializeImports validates that an imported global or table is type-compatible with the module’s declared import type. For these it used a single one-directional subtype check: for globals isSubtype(globalValue->global()->type(), global.type) and for tables Wasm::isSubtype(actualType, expectedType). Covariant (one-way) subtyping is correct only for immutable/read-only positions; a MUTABLE global and a table are read-write, so their element type must be INVARIANT — compatible in both directions — otherwise a write through one view can store a value of a type the other view considers illegal.

Pre-patch, importing e.g. a table typed (ref null 0) (a specific function type) where (ref null func) is expected (or vice versa) passed the one-way check, so the two modules disagreed on the element type of the same mutable storage — a type confusion in the wasm type system that the engine’s later type-directed accesses rely on.

The fix makes both checks bidirectional: ... || !isSubtype(global.type, globalValue->global()->type()) and ... || !Wasm::isSubtype(expectedType, actualType), i.e. requires mutual subtyping (invariance) for mutable globals and tables, and the removed linking tests are exactly the cases that were wrongly accepted.

The restored invariant is that a shared mutable wasm storage has one agreed-upon element type across importer and exporter.

Key insight
Mutable wasm globals and tables were type-checked covariantly at import instead of invariantly, letting two modules share the same mutable storage under incompatible reference types — a type confusion; requiring mutual subtyping fixes it.

Attack Path

  1. Export a mutable global/table Build a wasm module exporting a mutable global or a table with one reference element type.
  2. Import it under a different type Instantiate another module importing that global/table declared with a merely one-way-compatible (not invariant) reference type.
  3. Pass the weak check Pre-patch the one-directional isSubtype check accepts the mismatch, linking the two modules.
  4. Type-confuse via the shared storage Writing through one typed view and reading through the other yields a value of an unexpected reference type — type confusion / memory unsafety in the WebContent process.

Impact Assessment

A wasm type-safety hole (advisory class LogicError, effectively type confusion): a one-directional subtype check let mutable globals/tables link under mismatched reference types, so reads/writes disagree on the element type. That is a memory-unsafety primitive within the wasm type system in the WebContent process, reachable from a crafted pair of modules; the observable is a crash but the class is type confusion. Rated medium.

Changed Functions

FunctionChangeNotes
WebAssemblyModuleRecord::initializeImports (global import)
Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp
modified Requires mutual subtyping for imported globals: adds `|| !isSubtype(global.type, importedGlobalType)` so a mutable global's type is invariant, not just covariant.
WebAssemblyModuleRecord::initializeImports (table import)
Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp
modified Adds the reverse `|| !Wasm::isSubtype(expectedType, actualType)` so an imported table's element type must match invariantly.

Audit Directions

  • Other one-way subtype checks
    grep wasm/ for isSubtype(…) guarding mutable/read-write positions (globals, tables, mutable fields) that should require invariance.
  • Reference-type linking
    Audit table/global/tag import and export matching for covariance used where invariance is required.
  • GC struct/array mutable fields
    Check wasm GC mutable struct/array field subtyping for the same covariance-vs-invariance error.

Original Bug Report

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