← WebKit — 2026-W38

71e6e852300a5fd4bef4fec1e59f7f0a3f85846a  Forged IsPlayingMediaDidChange activates the display-capture process capability without user consent

severity medium class AuthorizationError confidence 0.6 signal score 7.07 Source/WebKit/UIProcess patch-now
Primitive: display-capture sandbox capability activated/retained without user authorization
Triage note: shouldActivate/shouldDeactivateDisplayCaptureCapability now gate on a new hasDisplayCaptureAuthorization() (m_isPromptingForDisplayCapture || m_hasGrantedDisplayCapture), which is set in willStartCapture and reset in didCommitLoadForFrame/resetState/stopMediaCapture; previously the display-capture extension capability could be activated from mediaState alone and lingered across navigation without the user having authorized screen capture.
Structural signals: smartptr_member_added (+4.87), null_guard (+1.2), msg_soft (+1.0)

Commit message

Forged IsPlayingMediaDidChange activates the display-capture process capability without user consent
https://bugs.webkit.org/show_bug.cgi?id=323828
rdar://178652280

Reviewed by Jean-Yves Avenard.

internals().mediaState is stored verbatim from WebContent IPC, but shouldActivateDisplayCaptureCapability()
used it to drive a privileged action: a compromised WebContent process could set display-capture bits and
have the UI process grant BEProcessCapability screenCapture back to it and to the GPU process, with no
getDisplayMedia() prompt. Both HasActiveScreenCaptureDevice and the IsPromptingFor*Capture bits reach the
predicate and both are forgeable.

Gate activation on a UI-process authorization record instead. m_isPromptingForDisplayCapture is set in
setIsPromptingForGetDisplayMedia, reachable only from UserMediaPermissionRequestProxyCocoa;
m_hasGrantedDisplayCapture is set in willStartCapture's DisplayMedia case, the chokepoint both grantRequest
paths funnel through, and cleared on main-frame didCommitLoadForFrame, resetState, and stopMediaCapture for
Display or EveryKind. hasDisplayCaptureAuthorization() gates both shouldActivate and shouldDeactivate, so
losing authorization revokes an active capability rather than only declining a new one.

Not a MESSAGE_CHECK: an in-flight message still carrying display-capture bits after the UI process resets
access or navigates would terminate a legitimate WebContent process. Gating activation is fail-safe and
sits where the harm occurs.

Also remove ASSERT(userMediaPermissionRequestManager) from updatePlayingMediaDidChange, which forged
capture bits make reachable from WebContent IPC; the null check below it already handles the case.

Test: Tools/TestWebKitAPI/Tests/WebKit/WKWebView/DisplayCaptureCapability.mm

* Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivateForTesting.h:
* Source/WebKit/UIProcess/API/Cocoa/WKWebViewTesting.mm:
(-[WKWebView _isDisplayCaptureCapabilityActiveForTesting]):
* Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm:
(WebKit::WebPageProxy::hasDisplayCaptureAuthorization const):
(WebKit::WebPageProxy::shouldActivateDisplayCaptureCapability const):
(WebKit::WebPageProxy::shouldDeactivateDisplayCaptureCapability const):
* Source/WebKit/UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::didCommitLoadForFrame):
(WebKit::WebPageProxy::stopMediaCapture):
(WebKit::WebPageProxy::resetState):
(WebKit::WebPageProxy::willStartCapture):
(WebKit::WebPageProxy::setIsPromptingForGetDisplayMedia):
(WebKit::WebPageProxy::updatePlayingMediaDidChange):
* Source/WebKit/UIProcess/WebPageProxy.h:
* Source/WebKit/UIProcess/WebPageProxyTesting.cpp:
(WebKit::WebPageProxyTesting::isDisplayCaptureCapabilityActive const):
* Source/WebKit/UIProcess/WebPageProxyTesting.h:
* Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* Tools/TestWebKitAPI/Tests/WebKit/WKWebView/DisplayCaptureCapability.mm: Added.
(TestWebKitAPI::TEST(DisplayCaptureCapability, IgnoresForgedDisplayCaptureMediaState)):

Identifier: 316606.740@safari-7625-branch

Before / after

Loading diff…