Medium CVSS 6.5 webkit Logic Error 🔧 Commit mapped

Overview

Medium
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionA website may be able to access sensor information without user consent
ComponentWebKit UIProcess
Bug ClassLogic Error
Tracker296153
Fix commitc420ed2f891b (WebKit/WebKit) +93/-9
CWECWE-200 (Information exposure)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
CISA KEVNot listed
CreditedJaydev Ahire
Disclosed2025-09-15

Background

WebPageProxy (UIProcess)
The trusted UIProcess-side proxy for a web page that brokers privileged decisions such as media capture and muting on behalf of the sandboxed WebContent process.
MediaProducerMutedStateFlags
A bitfield describing which media kinds (e.g. audio capture, video capture) are muted for a page.
applyWebAppDesiredMutedKinds
A reconciliation helper that combines a requested muted state with the web app’s own desired muted capture kinds (m_mutedCaptureKindsDesiredByWebApp) to yield the authoritative muted state.
pageMutedStateChanged
A UIProcess-to-WebContent notification informing content processes of the page’s current muted state; it must carry the authoritative value.
getUserMedia capture muting
The mechanism by which active camera/microphone tracks are suppressed; if the muted state is not enforced, capture continues delivering sensor samples to the page.

Root Cause Analysis

The bug is an ordering/consistency defect in WebPageProxy::setMuted in the UIProcess, the trusted broker that decides whether camera/microphone capture is muted. setMuted receives a requested MediaProducerMutedStateFlags and must reconcile it with the web app’s own desired muted kinds via applyWebAppDesiredMutedKinds(state, m_mutedCaptureKindsDesiredByWebApp) to produce the authoritative newState that is then pushed to the WebContent process(es). In the vulnerable version the reconciliation was performed too late: the function first stored internals().mutedState = state (the raw, un-reconciled request) and first notified every web content process with process.pageMutedStateChanged(pageID, state) using the raw state, and only AFTER those side effects computed newState = applyWebAppDesiredMutedKinds(...). Consequently the UIProcess’s cached muted state and the pageMutedStateChanged notification reflected state rather than the web-app-adjusted newState, while the later async Messages::WebPage::SetMuted used newState — an internal inconsistency. The violated invariant is that all consumers of the page’s muted state (the cached internals().mutedState and every content-process notification) must observe the single authoritative, web-app-reconciled muted state; here two different values were propagated. In practice this meant capture could remain effectively unmuted/active when the reconciled policy should have muted it, allowing a website to access camera/microphone (sensor) data without proper consent — matching the advisory ‘a website may be able to access sensor information without user consent.’

The fix computes newState (via applyWebAppDesiredMutedKinds under ENABLE(MEDIA_STREAM), else newState = state) BEFORE any side effects, stores internals().mutedState = newState, and sends pageMutedStateChanged(pageID, newState), so the cache, the state-changed notification, and the SetMuted message all carry the same reconciled value. The added API test GetUserMediaAfterMuting and the startAudioCapture/startVideoCapture test helpers exercise muting camera and microphone and then re-requesting capture, asserting prompt counts, which validates that muting is applied consistently.

Key insight
The authoritative, web-app-reconciled muted state must be computed before it is cached or broadcast; propagating the raw requested state to internals().mutedState and to content processes while only the later async message used the reconciled state left capture effectively unmuted, exposing sensors without consent.

Attack Path

  1. Obtain capture access A website requests camera/microphone via navigator.mediaDevices.getUserMedia and the user grants it, establishing active capture tracks.
  2. Induce a setMuted transition A mute state change is triggered (e.g. app/embedder mutes capture, or a state transition flows through WebPageProxy::setMuted) so the reconciliation between requested state and the web app’s desired muted kinds is exercised.
  3. Exploit the stale propagation Because pre-patch the UIProcess cached and notified content processes with the raw state instead of the web-app-reconciled newState, the muted policy is not authoritatively enforced and capture can remain active/unmuted contrary to the intended muted state.
  4. Access sensor data without consent With capture effectively left unmuted, the website continues to receive camera/microphone samples that should have been suppressed, obtaining sensor information without proper user consent.

Impact Assessment

This is a privacy/consent defect, not a memory-safety issue: the patch shows an ordering bug where the UIProcess propagated a stale (un-reconciled) muted state to its cache and to content processes, allowing camera/microphone capture to remain active when it should have been muted. The realistic impact is unauthorized access to sensor (audio/video) data by a website without proper user consent, as stated in the advisory; there is no memory corruption, OOB, or code-execution primitive implied by the diff. The logic resides in the UIProcess but the consequence is that the sandboxed WebContent process (and thus the site) receives capture it should not; no sandbox escape is involved.

Changed Functions

FunctionChangeNotes
WebPageProxy::setMuted
Source/WebKit/UIProcess/WebPageProxy.cpp
modified Computes the web-app-reconciled newState before any side effects, and now stores internals().mutedState = newState and calls pageMutedStateChanged with newState, so the cache and all content-process notifications carry the single authoritative reconciled muted state instead of the raw request.
GetUserMediaAfterMuting (API test)
Tools/TestWebKitAPI/Tests/WebKit/GetUserMedia.mm
added New test that starts audio and video capture, mutes camera and microphone, re-requests capture, and asserts capture-state transitions and prompt counts to verify muting is consistently applied.
startAudioCapture / startVideoCapture (test helpers)
Tools/TestWebKitAPI/Tests/WebKitCocoa/media-session-capture.html
added JS helpers that request audio-only and video-only getUserMedia and report PASS/FAIL, used by the new test to drive per-kind capture.

Files Changed

  • Source/WebKit/UIProcess/WebPageProxy.cpp
  • Tools/TestWebKitAPI/Tests/WebKit/GetUserMedia.mm
  • Tools/TestWebKitAPI/Tests/WebKitCocoa/media-session-capture.html

Audit Directions

  • Ordering of state computation vs. propagation in setMuted
    In WebPageProxy.cpp, verify every consumer of muted state uses the reconciled value; grep for internals().mutedState, pageMutedStateChanged, and applyWebAppDesiredMutedKinds to confirm the reconciled newState is computed before any assignment or notification.
  • Other capture/permission state setters
    Audit sibling setters that reconcile a requested value with app/user policy then notify processes; grep in UIProcess for forEachWebContentProcess, SetMuted, and MediaProducerMutedStateFlags to find places that might notify with the pre-reconciliation value.
  • Raw-vs-derived value drift patterns
    Look across UIProcess for functions that store or send a parameter and only later transform it; grep for a computed newState/finalState local used in one message but where the original state is used in an earlier cache write or notification within the same function.

Original Bug Report

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