Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 2df698f59386b2ffccd45c7ef28422b69eba8c44
https://github.com/WebKit/WebKit/commit/2df698f59386b2ffccd45c7ef28422b69eba8c44
Author: David Kilzer <[email protected]>
Date: 2026-08-23 (Sun, 23 Aug 2026)
Changed paths:
A
LayoutTests/fast/scrolling/mac/event-dispatcher-timer-close-crash-expected.txt
A LayoutTests/fast/scrolling/mac/event-dispatcher-timer-close-crash.html
Log Message:
-----------
Heap-use-after-free of RemoteLayerTreeEventDispatcher's RunLoop::Timer on the
scrolling thread
<https://bugs.webkit.org/show_bug.cgi?id=315533>
<rdar://177889453>
Reviewed by Simon Fraser.
`RemoteLayerTreeEventDispatcher::scheduleDelayedRenderingUpdateDetectionTimer()`
lazily creates `m_delayedRenderingUpdateDetectionTimer` on the
scrolling thread, against the scrolling thread's run loop. When
the owning page is closed,
`RemoteScrollingCoordinatorProxyMac::~RemoteScrollingCoordinatorProxyMac()`
runs on the main thread, calls
`RemoteLayerTreeEventDispatcher::invalidate()`, and then drops the
last reference, destroying the dispatcher (and the timer) on the
main thread.
`CFRunLoopTimerInvalidate()` called from `~TimerBase()` on the main
thread does not synchronize with a CFRunLoopTimer callback that the
scrolling thread's run loop has already begun dispatching, so the
callback can read the freed `TimerBase` via its raw context
pointer. ASan reports this as a heap-use-after-free in
`RunLoop::TimerBase::start()::$_0::__invoke` on the scrolling
thread.
Fix by destroying `m_delayedRenderingUpdateDetectionTimer` on the
scrolling thread from `invalidate()`, holding a strong reference to
the dispatcher across the dispatch so it outlives the timer.
Because the dispatch and the timer callback both run serially on
the scrolling thread's run loop, the timer is either destroyed
before the callback fires, or after the callback has returned.
This mirrors the equivalent fix in
`ThreadedScrollingTree::invalidate()` (225114@main).
The dispatch is performed after `m_scrollingTree` has been cleared
so any in-flight or subsequently queued `didRefreshDisplay()`
returns early and cannot recreate the timer.
Also reorder the ASSERTs in `~RemoteLayerTreeEventDispatcher` to
match the declaration order of the asserted members.
Test: fast/scrolling/mac/event-dispatcher-timer-close-crash.html
*
LayoutTests/fast/scrolling/mac/event-dispatcher-timer-close-crash-expected.txt:
Add.
* LayoutTests/fast/scrolling/mac/event-dispatcher-timer-close-crash.html: Add.
* Source/WebKit/UIProcess/RemoteLayerTree/mac/RemoteLayerTreeEventDispatcher.mm:
(WebKit::RemoteLayerTreeEventDispatcher::~RemoteLayerTreeEventDispatcher):
(WebKit::RemoteLayerTreeEventDispatcher::invalidate):
Originally-landed-as: [email protected] (fb7f8555bdf0).
rdar://185368389
Canonical link: https://commits.webkit.org/319667@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications