Critical CVSS 8.8 webkit UAF 🔧 Commit mapped

Overview

Critical
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionProcessing web content may lead to arbitrary code execution
ComponentWebKit WebProcess
Bug ClassUAF
Tracker266619
Fix commit77a680990129 (WebKit/WebKit) +27/-20
CWECWE-119 (Buffer bounds error)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedWangtaiyu of Zhongfu info
Disclosed2024-01-22

Background

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.

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.

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.

Changed Functions

FunctionChangeNotes
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/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

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.
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"
Loading diff…

Original Bug Report

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