Released January 22, 2024

CVE-2024-23213

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

Overview

Impact:

Description: Processing web content may lead to arbitrary code execution

Researchers: Wangtaiyu of Zhongfu info

Key insight. The asynchronous EME grant continuation held the Document by raw reference (and ran its completion handler unconditionally), so processing a grant after the frame was torn down dereferenced a freed context; WeakPtr/context null-checks and a run-once handler restore safe lifetime.

Patch Details

diff --git a/Source/WebKit/CMakeLists.txt b/Source/WebKit/CMakeLists.txt
index 3b782a4b3d6f..bd39f0753235 100644
--- a/Source/WebKit/CMakeLists.txt
+++ b/Source/WebKit/CMakeLists.txt
@@ -40,6 +40,7 @@ set(WebKit_PRIVATE_INCLUDE_DIRECTORIES
     "${WEBKIT_DIR}/Shared/CoreIPCSupport"
     "${WEBKIT_DIR}/Shared/Databases"
     "${WEBKIT_DIR}/Shared/Databases/IndexedDB"
+    "${WEBKIT_DIR}/Shared/Extensions"
     "${WEBKIT_DIR}/Shared/FileAPI"
     "${WEBKIT_DIR}/Shared/Gamepad"
     "${WEBKIT_DIR}/Shared/Notifications"

Background

Advisory: Apple Security Advisory

Attribute Value
CVE CVE-2024-23213
Bugzilla 266619
Component WebCore
Bug Class UAF
Severity critical
Commit 77a6809901292311…
Advisory Apple Advisory

Terminology

  • Encrypted Media Extensions (EME) — The DRM API (navigator.requestMediaKeySystemAccess) that negotiates a media key system, gated by an asynchronous user/UI-process permission grant.
  • ScriptExecutionContext / Document lifetime — A frame’s Document/context can be destroyed by navigation or detach; async continuations must not dereference it after teardown.
  • WeakPtr vs raw capture — Capturing a Document by WeakPtr lets a callback detect destruction and bail; a raw reference dereferences freed memory.
  • queueTaskKeepingObjectAlive / std::exchange — Keeps the request object alive across the queued task and ensures the completion handler runs at most once, preventing use-after-free and double-invoke.

Root Cause Analysis

This fixes a document/execution-context lifetime bug (a use-after-free, rated critical / arbitrary code execution) in the Encrypted Media Extensions requestMediaKeySystemAccess permission flow. The EME grant is asynchronous and user-gated: NavigatorEME::requestMediaKeySystemAccess creates a MediaKeySystemRequest and installs an ‘allow’ callback; when the user/UI process approves, the WebProcess receives the MediaKeySystemWasGranted IPC and calls MediaKeySystemRequest::allow(), which resolves the JS promise and continues the algorithm on the document. Pre-patch, two lifetime assumptions were unsafe. First, NavigatorEME’s setAllowCallback lambda captured the Document by RAW reference (&document) and later did document.postTask(…); between the async request and the grant, the document/frame can be navigated away or detached and destroyed, so the callback dereferenced a freed Document. Second, MediaKeySystemRequest::allow() captured raw ’this’ and unconditionally moved and invoked m_allowCompletionHandler (and a second handler), with no check that the ScriptExecutionContext still existed and no guard against the handler being run twice/after teardown. The result is a use-after-free of the Document/ScriptExecutionContext (or the request state) when the EME permission grant is processed after the associated context has gone away — a strong, attacker-timable primitive consistent with the advisory’s arbitrary-code-execution rating. The fix makes the callback capture weakDocument = WeakPtr { document } and, on invocation, upgrade to RefPtr and reject with InvalidStateError if the document is gone (instead of touching a raw &document); the posted task now derives the document from the passed ScriptExecutionContext via downcast. allow() now early-returns if !scriptExecutionContext(), uses queueTaskKeepingObjectAlive to keep the request alive, and runs the completion handler at most once via std::exchange(m_allowCompletionHandler, {}). Supporting this, the MediaKeySystemWasGranted IPC is changed from a sync/async-reply message (-> ()) to a one-way send, and the CompletionHandler plumbing is removed from WebPage / MediaKeySystemPermissionRequestManager, eliminating the reply-handler lifetime chain that tied a UI-process reply to WebProcess object lifetime. The restored invariant is that the EME grant continuation never dereferences a Document/context/request that may have been destroyed between the (async, user-gated) permission decision and the continuation running.

Attack Path

1. Request media key system access

Web content calls navigator.requestMediaKeySystemAccess(), creating a MediaKeySystemRequest and prompting for permission (async, user-gated).

2. Tear down the context

Before the grant is processed, the page navigates away or detaches the frame so the Document/ScriptExecutionContext is destroyed.

3. Deliver the grant

The UI process approves and sends MediaKeySystemWasGranted; the WebProcess runs allow()/the allow-callback capturing the now-freed document by raw reference.

4. Use-after-free

The continuation dereferences the freed Document/context (and completion handler), a controllable UAF.

5. Escalate to code execution

Reclaim the freed allocation with controlled data to turn the UAF into arbitrary read/write and code execution in WebContent.

Impact Assessment

A critical use-after-free in the WebContent process on the EME permission-grant path, reachable by racing frame/navigation teardown against an approved requestMediaKeySystemAccess grant. Dereferencing a freed Document/ScriptExecutionContext is a strong, attacker-timable primitive; consistent with the advisory’s arbitrary-code-execution rating, it can be groomed into memory disclosure/corruption and full WebContent code execution.

Audit Directions

Same module: EME callbacks

Audit MediaKeySystemRequest/NavigatorEME and MediaKeySystemPermissionRequestManager for other raw Document/context captures or completion handlers invoked without a scriptExecutionContext() / WeakPtr guard.

Async permission grants

Review other user-gated async grant flows (getUserMedia, geolocation, notifications, WebXR) for callbacks capturing Document/frame by raw pointer/reference across the UI-process round trip.

IPC reply-handler lifetimes

Grep for sendWithAsyncReply grant/permission messages whose reply handler assumes WebProcess objects survive; prefer one-way sends plus keep-alive tasks.

Changed Functions

Function File Change Note
MediaKeySystemRequest::allow Source/WebCore/Modules/encryptedmedia/MediaKeySystemRequest.cpp modified Drops the CompletionHandler parameter; early-returns if !scriptExecutionContext(); uses queueTaskKeepingObjectAlive and runs the completion handler at most once via std::exchange(m_allowCompletionHandler, {}).
NavigatorEME::requestMediaKeySystemAccess (allow callback) Source/WebCore/Modules/encryptedmedia/NavigatorEME.cpp modified Captures the document as a WeakPtr; on invocation upgrades to RefPtr and rejects with InvalidStateError if gone, and derives the Document from the posted task’s ScriptExecutionContext instead of a raw &document capture.
MediaKeySystemPermissionRequestManager::mediaKeySystemWasGranted Source/WebKit/WebProcess/EncryptedMedia/MediaKeySystemPermissionRequestManager.cpp modified Drops the CompletionHandler; simply takes the pending request and calls request->allow().
MediaKeySystemPermissionRequestManagerProxy::grantRequest Source/WebKit/UIProcess/MediaKeySystemPermissionRequestManagerProxy.cpp modified Sends MediaKeySystemWasGranted as a one-way message (m_page.send) instead of sendWithAsyncReply, removing the reply-handler lifetime chain.
WebPage::mediaKeySystemWasGranted + WebPage.messages.in Source/WebKit/WebProcess/WebPage/WebPage.cpp modified Removes the CompletionHandler and changes MediaKeySystemWasGranted from a reply message (-> ()) to a plain asynchronous message.
WebMediaKeySystemClient::requestMediaKeySystem Source/WebKitLegacy/mac/WebCoreSupport/WebMediaKeySystemClient.mm modified Updates the legacy caller to the no-argument allow().

Files Changed

Source Files

  • Source/WebCore/Modules/encryptedmedia/MediaKeySystemRequest.cpp
  • Source/WebCore/Modules/encryptedmedia/MediaKeySystemRequest.h
  • Source/WebCore/Modules/encryptedmedia/NavigatorEME.cpp
  • Source/WebKit/UIProcess/MediaKeySystemPermissionRequestManagerProxy.cpp
  • Source/WebKit/WebProcess/EncryptedMedia/MediaKeySystemPermissionRequestManager.cpp
  • Source/WebKit/WebProcess/EncryptedMedia/MediaKeySystemPermissionRequestManager.h
  • Source/WebKit/WebProcess/WebPage/WebPage.cpp
  • Source/WebKit/WebProcess/WebPage/WebPage.h
  • Source/WebKit/WebProcess/WebPage/WebPage.messages.in
  • Source/WebKitLegacy/mac/WebCoreSupport/WebMediaKeySystemClient.mm