← All issues

[24] ReadableStreamDefaultReader GC-thread UAF on m_stream

Severity: Medium | Component: WebCore Streams | ad57b51

Rated Medium because the diff fixes the same cross-thread UAF class as #23 in a different DOM object: GC-thread visitors read m_stream while the main thread can null it via releaseLock/genericRelease.

Sister fix to the XMLHttpRequest commit. m_stream becomes WTF_GUARDED_BY_LOCK(m_streamLock); every access serialises through the lock.

Source/WebCore/Modules/streams/ReadableStreamDefaultReader.h

- RefPtr<ReadableStream> m_stream;
+ mutable Lock m_streamLock;
+ RefPtr<ReadableStream> m_stream WTF_GUARDED_BY_LOCK(m_streamLock);
template<typename Visitor>
void ReadableStreamDefaultReader::visitAdditionalChildrenInGCThread(Visitor& visitor)
{
+ Locker locker { m_streamLock };
if (m_stream)
SUPPRESS_UNCOUNTED_ARG m_stream->visitAdditionalChildrenInGCThread(visitor);
}

Unsynchronized access from a GC visitor thread to a main-thread-mutable reference-counted member, allowing the visitor to dereference a pointer concurrently being released.

The commit message explicitly notes "we cannot easily ref the stream on the GC thread" — atomic ref does not solve the race because the object could be destroying mid-ref.

A WHATWG ReadableStream may have at most one active reader; the reader holds m_stream back to the stream. releaseLock() and stream-side release nulls this. The lock serializes nulling against GC reads — the GC visit either sees a stable live pointer or nullptr.

This vulnerability weakens the invariant that data structures visited by the GC are either immutable or accessed under synchronization with main-thread mutators.