← 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의 media loading pipeline은 두 계층으로 나뉩니다. HTMLMediaElement는 HTML 리소스 선택 알고리즘을 구현하고, MediaPlayer는 구체적인 engine을 선택하고 decode를 주도하는 플랫폼 추상화 계층입니다. 기존에는 src= 속성이 잘못 선언된 URL을 가리킬 때 HTMLMediaElement가 decode 실패를 감지하고 re-sniff를 수행했습니다. 다만 <source> 경로에는 이런 복구 메커니즘이 없었습니다. MediaPlayer 내부의 engine fallback chain은 순수하게 type 기반으로 동작하기 때문에, 모든 engine이 동일한 잘못된 content-type 문자열을 전달받는 구조였습니다.

이 commit은 sniff-and-reload state machine을 HTMLMediaElement에서 MediaPlayer로 이전했습니다. 모든 engine이 HaveMetadata에 도달하기 전에 FormatError 또는 DecodeError로 거부하면, MediaPlayer는 첫 1445바이트를 가져와 magic bytes로부터 실제 container를 추론합니다. 이후 m_attemptedEngines를 초기화하고 engine 선택을 다시 수행합니다.

이로 인해 MediaPlayer 내부에 새로운 re-entrant load 경로가 생겼습니다. sniffer callback이 두 번째 network 요청을 유발하고, 그 callback 안에서 loadWithNextMediaEngine(nullptr)이 호출되는 구조입니다. 이 패턴은 use-after-free와 double-load race가 역사적으로 자주 발생하던 구조입니다.

🔒

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

더 확인하려면 구독해 주세요