Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 15c117a5444a7bf7956f62facb7c0cb8008dc15c
https://github.com/WebKit/WebKit/commit/15c117a5444a7bf7956f62facb7c0cb8008dc15c
Author: Keith Miller <[email protected]>
Date: 2026-07-02 (Thu, 02 Jul 2026)
Changed paths:
A JSTests/stress/wasm-compile-stale-ticket.js
M Source/JavaScriptCore/CMakeLists.txt
M Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
M Source/JavaScriptCore/jsc.cpp
M Source/JavaScriptCore/runtime/DeferredWorkTimer.cpp
M Source/JavaScriptCore/runtime/DeferredWorkTimer.h
R Source/JavaScriptCore/runtime/DeferredWorkTimerInlines.h
M Source/JavaScriptCore/runtime/JSFinalizationRegistry.cpp
M Source/JavaScriptCore/runtime/JSGlobalObject.cpp
M Source/JavaScriptCore/runtime/JSGlobalObject.h
M Source/JavaScriptCore/runtime/VM.cpp
M Source/JavaScriptCore/runtime/WaiterListManager.cpp
M Source/JavaScriptCore/runtime/WaiterListManager.h
M Source/JavaScriptCore/wasm/WasmStreamingCompiler.cpp
M Source/JavaScriptCore/wasm/WasmStreamingCompiler.h
M Source/JavaScriptCore/wasm/js/JSWebAssembly.cpp
Log Message:
-----------
[JSC] DeferredWorkTimer should vend Weak references to Tickets
https://bugs.webkit.org/show_bug.cgi?id=314671
rdar://176445072
Reviewed by Yijia Huang.
DeferredWorkTimer::addPendingWork returned a raw Ticket (alias for TicketData*),
and callers routinely captured that raw pointer into SharedTask lambdas that
run on background threads (Wasm worklist, etc.). If the associated
JSGlobalObject died before the background work completed, the GC End phase
cancelled the ticket, doWork() removed the cancelled entry from
m_pendingTickets, and the last Ref<TicketData> dropped — freeing the slot. The
background thread then dereferenced the dangling pointer in
scheduleWorkSoon(stalePtr, ...). Because the TZone allocator reuses freed
slots, m_pendingTickets.find(stalePtr) could match a fresh ticket at the same
address, running the stale callback against the wrong target and tripping a
jsCast / JSPromise::flags assertion in doWork.
0e0b5050 fixed WasmStreamingCompiler for this pattern by storing
ThreadSafeWeakPtr<TicketData> and promote-and-checking at every use site.
This patch moves the safety requirement into the API so future callers can't
regress:
1. Rename DeferredWorkTimer::TicketData to DeferredWorkTimer::Ticket and
remove the old `using Ticket = TicketData*` alias.
2. Add `using WeakTicket = ThreadSafeWeakPtr<Ticket>`. addPendingWork now
returns WeakTicket.
3. Replace scheduleWorkSoon(Ticket, Task&&) with the new
scheduleWorkSoonIfActive(const WeakTicket&, Task&&) which internally
promotes the weak ticket, drops the request if it's gone or cancelled,
and returns whether it was queued. This is the only way to enqueue work,
so callers cannot forget the liveness check.
4. Change m_tasks from Deque<std::tuple<Ticket, Task>> to
Deque<std::tuple<Ref<Ticket>, Task>> so queued work keeps the ticket
alive until doWork() runs, closing the slot-reuse window the repro
exploits.
5. hasPendingWork, hasDependencyInPendingWork, cancelPendingWork take
Ticket& so callers already holding a RefPtr can't accidentally pass a
null / raw pointer across an async boundary.
6. Task signature is Function<void(Ticket&)>; doWork dereferences the
stored Ref when dispatching.
Call-site cleanups:
* jsc.cpp functionSetTimeout — no more manual promote/check; the dispatch
lambda just forwards the WeakTicket into scheduleWorkSoonIfActive.
* WasmStreamingCompiler — m_ticket becomes ThreadSafeWeakPtr<Ticket>; the
destructor, didComplete, fail, and cancel paths use the new API.
* JSWebAssembly.cpp webAssemblyModuleValidateAsync / instantiate /
compileAndInstantiate — three shared-task lambdas that previously
captured raw ticket / globalObject / promise pointers now capture a
WeakTicket, recover promise / globalObject / dependencies from
Ticket::target() and Ticket::dependencies() inside the scheduled task,
and let scheduleWorkSoonIfActive handle liveness.
* WaiterListManager — m_ticket type rename; scheduleWorkAndClear collapses
to a single scheduleWorkSoonIfActive that clears on success.
* JSFinalizationRegistry — synchronous path uses scheduleWorkSoonIfActive
and RELEASE_ASSERTs the return to match the existing contract.
The now-empty DeferredWorkTimerInlines.h is removed.
Test: JSTests/stress/wasm-compile-stale-ticket.js
Originally-landed-as: 305413.892@safari-7624-branch (824ba19ea552).
rdar://180437200
Canonical link: https://commits.webkit.org/316448@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications