[11] AudioContext destructor touches Document during Document's own destruction
Severity: Medium | Component: WebCore Web Audio | ed04ff4
Rated Medium because the diff fixes a UAF-during-destruction where ~AudioContext (reached via BaseAudioContext::deleteMarkedNodes inside ~Document) writes into a partially-destroyed Document; attacker influence over the freed slot's contents is indirect, bounded by what allocations land in the released Document storage.
When the Document destructor is called, it is possible for it to take a code path where it references the Document which is actively being destroyed. The path is: Document::~Document → ScriptExecutionContext::~ScriptExecutionContext → BaseAudioContext::deleteMarkedNodes → AudioContext::~AudioContext → Document::removeAudioProducer. The patch removes audio producers from AudioContext::stop() instead and guards ~AudioContext with !isStopped().
Source/WebCore/Modules/webaudio/AudioContext.cpp
AudioContext::~AudioContext()
{
m_mediaSession->invalidateClient();
- if (RefPtr document = this->document())
- document->removeAudioProducer(*this);
+ if (!isStopped()) {
+ if (RefPtr document = this->document())
+ document->removeAudioProducer(*this);
+ }
}
...
+void AudioContext::stop()
+{
+ if (RefPtr document = this->document())
+ document->removeAudioProducer(*this);
+ BaseAudioContext::stop();
+}
Source/WebCore/Modules/webaudio/AudioContext.h
// ActiveDOMObject
+ void stop() final;
void suspend(ReasonForSuspension) final;
Patch Details
Three changes restructure AudioContext teardown: ~AudioContext wraps the document->removeAudioProducer(*this) call in if (!isStopped()); a new override AudioContext::stop() calls document->removeAudioProducer(*this) before delegating to BaseAudioContext::stop(); and BaseAudioContext::stop() is promoted from private to public so the derived class can call it. stop() runs during Document::commonTeardown → ScriptExecutionContext::stopActiveDOMObjects, before the Document destructor begins. A manual ASAN reproducer is added.
Re-entrant call into a partially-destroyed owner object from a child's destructor that runs as a side effect of the owner's own teardown.
Background
AudioContext inherits from BaseAudioContext, which inherits from ActiveDOMObject (a per-Document lifecycle interface) and is ThreadSafeRefCounted. An ActiveDOMObject has a stop() hook that Document::commonTeardown calls via ScriptExecutionContext::stopActiveDOMObjects early in document teardown — before ~Document runs. BaseAudioContext::deleteMarkedNodes is a deferred-deletion sweep that drops references on audio nodes marked for deletion; when those references were the last ones, destruction of the owning AudioContext is chained from inside deleteMarkedNodes. Document::addAudioProducer/removeAudioProducer maintain a set of MediaProducer* on the Document tracking which contexts are currently producing audio. BaseAudioContext::isStopped() returns true once stop() has set m_isStopScheduled.
Analysis
The crash chain (from the commit message): Document::~Document runs, which transitively runs ~ScriptExecutionContext; that base destructor causes BaseAudioContext::deleteMarkedNodes to drop the last reference on an AudioContext whose deletion had been deferred; the resulting ~AudioContext then calls document->removeAudioProducer(*this). By that point, the Document subobject is mid-destruction — fields owned by Document (but not yet by ScriptExecutionContext) have already been destroyed in reverse-declaration order. removeAudioProducer mutates state on this half-destroyed Document object.
Aaa Aaa Aaaaaa Aaa Aaaaa Aa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaa Aaaaaaaa Aaa Aaaaaaaaaa Aaaaaaa Aaaaaaa Aaa Aaaaaaaaaaaaaa Aaaaa Aa Aaaaaaaaaaaaaaa Aa a Aaaaaaaaa Aaaaaaaa Aa Aaaa Aaa Aaaa Aaaaaaa Aaa Aaaaaaaaaa Aaaaaaa Aaaaaaaa Aaaaaa Aaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaaa Aaaaaa Aaaaaa Aa Aaa Aaaaaaaaa Aaa Aaaaaaa Aaaa Aaaaaaaa Aaa Aaaaaaaaa Aaaa Aa Aaaaaaaaaaaaaaaaa Aaaa Aaa Aaaaa Aaa Aaaaaa Aaaaaaaaaa Aaaaa Aaa Aaaaaaaaaa Aaa Aaaaa Aaaaaaaaaaaa Aaaa Aaaa Aaaaaaaaaaaaa Aa Aaaaaaaaa Aa Aaaaaa Aaaaa Aaaaa Aaaaaaaa Aaaaa Aaaaaa a Aaaaaaaaaa Aa Aaaaa Aaaa a Aaaaaaaaaaaaaa Aaaaaaaaaaa Aaaaaaaaaa Aaaaaaaaaaaa Aaaaa Aaaaa a Aaa Aaaaa Aaaa Aaaaaaaa Aaaa Aaaaaa Aaaaaaaaaa Aaaaa Aa Aaaaaaaaaa Aaaaaa Aaaaaaaaaa a Aaaaaaaaa Aaaaaaaaaa a Aaaaa Aaaaaaaaa Aaa Aaaaaaaaaa Aaaaaaaaaa Aaaaaaaa Aaa Aaaaa Aa Aaaaaaaaa Aa Aaaaaaaa Aaa Aaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaa Aa a Aaaaaaaaaaaaaaaaa Aaaaa Aaa Aaaa Aaa Aaaaaaaaaa Aaaaaaa Aaaa Aaaaaa Aaa Aaaaaa Aaaaaaaaaaaa Aaaaaaaaaa Aaaaaaa Aaaaaa Aaaa Aaaa Aaaaaaaaa Aaa Aaaaaaa Aaa Aa Aaaaaaaaaaa Aa Aaa Aaaaaaaaaaaaaa Aaaaaaa Aa Aaaaaaaaa Aaaa Aaaaaaaaaaa Aaaaaaaaaa Aa Aaaaaaaaa Aaaaaaaaaa Aaaaaaaaaa Aaa Aaa Aaaaaaaaaaaaa Aa a Aaaaaaaaa Aaaaaa
🔒The teardown ordering and re-entrant destructor chain behind this UAF are traced end-to-end, with an assessment of how much attacker control is realistic during the destruction window.
Subscribe to read more
Audit directions
a Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaaa Aaaa Aaaaa Aaaaa Aaaaaa Aaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaa Aaa Aa Aaaaaaa Aaaa Aaaaaa Aaaaaaaaaaa Aaa Aaaaaaaaaa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaaaaaaaaaaaaa Aaaaaaaa Aa Aaaaa Aaaaa Aaaaaaaaaa Aaaaaaa Aaa Aaaaaaa Aaaaa Aaaa Aaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaa Aaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aaa Aaaaaaaaaaaa
a Aaaaaaaaaaaaaaaaaaa Aaaaaa Aaaa Aaaa Aaaaa Aaaa Aaaa Aaaaaa a Aaaaaaaa Aaaaaaaaaaaaa Aaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aaa Aaa Aaaaaaaaa Aaaa Aaaaaaa Aaaaaaaaaaaa Aaaaaa Aaaaa Aaaaaaaa Aaaa Aa Aaaaaaaaaa Aaaa Aaaa Aaaaaaa Aaa Aaaaa a Aaa Aaaa Aaa Aaaaa Aa Aaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaa Aaaa a Aaaa Aaaaaaaaaaa
a Aaaaaaaaaaaaaaaaa Aaaaaaaaaaaa Aaaaaaa Aaaaaaaaa Aaaaaaaaaaaaa Aaaaaaaaaaaaaaaaa Aaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaa Aaaa Aaaa Aaaa Aaaa Aaa a Aaaaaaaa Aaaaaaaaaaaaaa Aaaa Aaaa Aaaaaa Aaaaaaaaaaaa
a Aaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaaaaa Aa Aaaa Aaaaaaaaaaa Aaa Aaaaaaa Aa Aaaaaa Aaaaaa Aaa Aaaa Aaaaaaaaaaaa Aaaa Aaaa Aaa Aaaaaa Aaaaa Aaaaaaa Aaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaa Aaaaaaaaaaaa Aaa Aaa Aaaa Aaaaaa a Aaaaaaaaaaaaaaa Aaaaa Aaa Aaaaa Aaaaaaa Aaaa Aaaa Aaa Aaaaaaaaaa Aaaaaa
🔒Four reusable audit patterns identified around ActiveDOMObject lifetimes and deferred-deletion queues, with concrete subsystem starting points for variant discovery.
Subscribe to read more