← All issues

citroen.fr: one section fails to display properly after scrolling

013da9a

Source/WebCore/platform/graphics/MediaPlayer.cpp

+void MediaPlayer::attemptSniffAndReload(MediaPlayerEnums::NetworkState originalError)
+{
+ if (m_sniffAttempted)
+ return;
+ m_sniffAttempted = true;
+ m_sniffer = MediaResourceSniffer::create(*this, m_url, [this, originalError](auto sniffedType) mutable {
+ m_sniffer = nullptr;
+ if (!sniffedType.isEmpty() && sniffedType != m_loadOptions.contentType) {
+ m_attemptedEngines.clear();
+ m_loadOptions.contentType = sniffedType;
+ loadWithNextMediaEngine(nullptr);
+ return;
+ }
+ if (auto* client = m_client.get())
+ client->mediaPlayerNetworkStateChanged(originalError);
+ });
+}

WebKit's media loading pipeline has two layers: HTMLMediaElement implements the HTML resource-selection algorithm, and MediaPlayer is the platform abstraction picking a concrete engine and driving decode. Previously, when a src= attribute pointed at a misdeclared URL, HTMLMediaElement caught the decode failure and re-sniffed; the <source> path had no such recovery. The engine-fallback chain inside MediaPlayer is purely type-driven — every engine receives the same wrong content-type string. This commit moves the sniff-and-reload state machine from HTMLMediaElement into MediaPlayer. When every engine rejects with FormatError/DecodeError before reaching HaveMetadata, MediaPlayer fetches the first 1445 bytes, infers the real container from magic bytes, clears m_attemptedEngines, and re-runs engine selection.

This creates a new re-entrant load path inside MediaPlayer: a second network request triggered by the sniffer callback, then loadWithNextMediaEngine(nullptr) invoked from inside that callback — a pattern historically fertile for use-after-free and double-load races.

🔒

New async re-entrant load path in a security-sensitive media layer — lifetime and re-entry edge cases are worth investigating.

Subscribe to read more