← WebKit — 2026-W39
5a3139cdf3e05dfcbeedffaff64b5a01d2a29485 Cross-thread UAF of GPUProcessConnection in WebMediaStrategy::canDecodeExtendedType via worker MSE changeType probing
severity medium
class Race
confidence 0.7
signal score 10.47
Source/WebKit/WebProcess
patch-now
Primitive: Data race / UAF on WebProcess::m_gpuProcessConnection RefPtr read off the main thread while main thread resets it
Triage note: RC_REMOVE_SEC_ACCEL_3-bracketed fix adds m_gpuProcessConnectionLock guarding m_gpuProcessConnection and existingGPUProcessConnectionOffMainThread(), and keeps the closed connection alive until lock release; WebMediaStrategy::canDecodeExtendedType read the RefPtr off the main thread concurrently with gpuProcessConnectionClosed() nulling it, a cross-thread race on the connection pointer.
Triage note: RC_REMOVE_SEC_ACCEL_3-bracketed fix adds m_gpuProcessConnectionLock guarding m_gpuProcessConnection and existingGPUProcessConnectionOffMainThread(), and keeps the closed connection alive until lock release; WebMediaStrategy::canDecodeExtendedType read the RefPtr off the main thread concurrently with gpuProcessConnectionClosed() nulling it, a cross-thread race on the connection pointer.
Structural signals: smartptr_member_added (+6.47), msg_strong (+3.0), msg_soft (+1.0)
Commit message
Cross-thread UAF of GPUProcessConnection in WebMediaStrategy::canDecodeExtendedType via worker MSE changeType probing
https://bugs.webkit.org/show_bug.cgi?id=325112
rdar://187952722
Reviewed by Youenn Fablet.
WebProcess::m_gpuProcessConnection is written only on the main run loop, but
WebMediaStrategy::canDecodeExtendedType() runs on whichever thread is probing
codec support. With MediaSource-in-workers that is a worker thread, and it is
reachable from ordinary web content at shipping defaults:
SourceBuffer.changeType() with a codec string that has not been seen before
misses MIMETypeCache, so the whole support probe
(SourceBufferPrivateAVFObjC::canSwitchToType ->
SourceBufferParserAVFObjC::isContentTypeSupported ->
AVStreamDataParserMIMETypeCache::canDecodeExtendedType) re-runs synchronously
on the worker and ends up in WebMediaStrategy::canDecodeExtendedType().
existingGPUProcessConnection() was an inline, unsynchronized raw load of that
member, so the worker did a raw load followed by a separate ref(). Meanwhile
GPUProcessConnection::didClose() on the main thread calls
WebProcess::gpuProcessConnectionClosed(), which nulls the member, and the
client cascade hands the last Ref to a WorkQueue lambda, so the connection is
freed off the main thread. A worker caught between its load and its ref()
increments a refcount inside freed memory and then reads the IPC connection
pointer out of the freed block.
Locking that single accessor and returning an owning RefPtr would fix the
race, but it charges a lock and a reference to every caller. Almost all of
them run on the main run loop, where the member cannot change under them and
there is no race; only the worker path needs protection. The common, safe
path should not pay for the one unsafe path.
Instead keep the fast accessor and add a slow, safe one:
- existingGPUProcessConnection() still returns a raw pointer with no lock,
but is now annotated WTF_REQUIRES_CAPABILITY(mainRunLoop). Every call that
is not proven to be on the main run loop is a build error rather than a
latent crash.
- existingGPUProcessConnectionOffMainThread() is added for the worker path.
It takes a new lock and returns an owning RefPtr, so the caller observes
either null or a connection it already holis no
window between the load and the reference.
- The two main-thread writes also take the lock so a locked reader never
observes a torn value. gpuProcessConnectionClosed() moves the old value
into a local, releases the lock, and only the
connection is never destroyed while the lo
The member is annotated WTF_GUARDED_BY_CAPABILITY(mainRunLoop), so every
main-thread access is verified at build time; only
existingGPUProcessConnectionOffMainThread() is exempt, because it reads under
the lock.
Test: media/media-source/worker/media-source-worker-changetype-gpu-process-crash.html
* LayoutTests/media/media-source/worker/media-gpu-process-crash-expected.txt: Added.
* LayoutTests/media/media-source/worker/media-gpu-process-crash.html: Added.
* Source/WebKit/WebProcess/GPU/GPUProcessConne
(WebKit::GPUProcessConnection::didBecomeUnresponsive): Assert the main run
loop before the main-thread accessor.
(WebKit::GPUProcessConnection::didClose): Ditto.
* Source/WebKit/WebProcess/GPU/media/WebMediaStrategy.cpp:
(WebKit::WebMediaStrategy::canDecodeExtendedType): Take the connection through
the off-main-thread accessor.
(WebKit::NowPlayingInfoForGPUManager::clearNowPlayingInfoPrivate): Assert the
main run loop.
* Source/WebKit/WebProcess/WebPage/WebPage.cpp
(WebKit::WebPage::~WebPage): Assert the main run loop before the accessor.
(WebKit::WebPage::updatePreferences): Ditto.
(WebKit::WebPage::configureLoggingChannel): Ditto.
* Source/WebKit/WebProcess/WebPage/Cocoa/WebPa
(WebKit::WebPage::setMediaPlaybackEnvironment): Ditto.
(WebKit::WebPage::setDisplayCaptureEnvironment): Ditto.
* Source/WebKit/WebProcess/WebProcess.cpp:
(WebKit::WebProcess::createWebPage): Assert the main run loop.
(WebKit::WebProcess::ensureGPUProcessConnection): Assign the member under the
lock.
(WebKit::WebProcess::existingGPUProcessConnectionOffMainThread): Added; take
the reference under the lock.
(WebKit::WebProcess::gpuProcessConnectionClosed): Clear the member under the
lock, and destroy the connection after the lock is released.
(WebKit::WebProcess::gpuProcessConnectionDidBecomeUnresponsive): Assert the
main run loop.
* Source/WebKit/WebProcess/WebProcess.h:
Identifier: 316606.786@safari-7625-branch
Before / after
Loading diff…
On This Page