← WebKit — 2026-W37
2c0010b8b5f10b72b103387717a6652c8eb4b306 [JSC] FinalizationRegistry keeps a dangling unregister-token key after the token is collected
severity medium
class UAF
confidence 0.5
signal score 6.8
Source/JavaScriptCore/runtime
Primitive: FinalizationRegistry dead registrations keyed on a collected unregister token not swept during GC finalizeUnconditionally
Triage note: JSFinalizationRegistry::finalizeUnconditionally now removeIf's m_deadRegistrations whose key (unregister token) is unmarked, moving their registrations into m_noUnregistrationDead. Fixes a GC-liveness gap in a weak-ref structure; plausibly a leak/stale-entry issue but exploitability not demonstrable from the diff alone.
Triage note: JSFinalizationRegistry::finalizeUnconditionally now removeIf's m_deadRegistrations whose key (unregister token) is unmarked, moving their registrations into m_noUnregistrationDead. Fixes a GC-liveness gap in a weak-ref structure; plausibly a leak/stale-entry issue but exploitability not demonstrable from the diff alone.
Structural signals: msg_medium (+2.0), msg_soft (+1.0), area:Source/JavaScriptCore/runtime (+2.5), surgical (+1.3)
Commit message
[JSC] FinalizationRegistry keeps a dangling unregister-token key after the token is collected
https://bugs.webkit.org/show_bug.cgi?id=323478
rdar://180297597
Reviewed by Yusuke Suzuki.
m_deadRegistrations is keyed by unregister token, and those keys are weakly held.
finalizeUnconditionally() prunes dead-token buckets from m_liveRegistrations but never
swept m_deadRegistrations, so a bucket kept the token's address as a key after the token
was collected. A cell allocated there next was then treated as the token: unregister()
reported a match for an unrelated object and dropped the bucket's pending holdings.
deadRegistrations() also handed the freed cell out as unregisterToken, which Web
Inspector's getInternalProperties dereferences.
Sweep m_deadRegistrations for unmarked keys, moving their holdings to
m_noUnregistrationDead so they stay pending but are no longer cancellable.
Test: JSTests/stress/finalization-registry-dead-token-key-is-pruned.js
* JSTests/stress/finalization-registry-dead-token-key-is-pruned.js: Added.
(assert):
(const.registry.new.FinalizationRegistry):
* Source/JavaScriptCore/runtime/JSFinalizationRegistry.cpp:
(JSC::JSFinalizationRegistry::finalizeUnconditionally):
Identifier: 316606.654@safari-7625-branch
Before / after
Loading diff…
On This Page