[cocoa] AVStreamDataParser accepts media segments not preceded by an init segment
LayoutTests/media/media-source/media-source-append-media-before-init.html
WebKit의 Cocoa 기반 MSE 구현은 ISO-BMFF 파싱을 AVFoundation의 private API인 AVStreamDataParser에 위임합니다. MSE 명세는 엄격한 순서를 요구하는데, initialization segment(ftyp+moov)는 반드시 media segment(moof+mdat)보다 먼저 제공되어야 합니다. 그러나 AVStreamDataParser 자체는 이 순서를 강제하지 않습니다. 순서가 지켜지지 않으면 null CMFormatDescription을 가진 CMSampleBuffer가 생성되고, 이후 처리 단계에서 실패하게 됩니다. 한편 abort()/changeType() 없이 스트림 도중 새로운 ftyp가 도착하면, MoofManifold에서 CoreMedia 내부 오류 -16046이 발생하지만 외부로 전파되지 않고 무시됩니다.
이 commit은 AVStreamDataParser 앞단에 새로운 ISOBMFFPreParser를 추가했습니다. 이 컴포넌트는 box 내용을 파싱하지 않고 appendData() 경계를 넘어 ISO-BMFF box header를 순회합니다. init segment 없이 media segment가 먼저 도착하면 이를 거부하고, 스트림 중간에 새로운 ftyp가 나타나면 AVStreamDataParserStreamDataDiscontinuity 신호를 주입합니다. 기존 ISOBox::peekBox 대신 BitReader를 선택한 데는 이유가 있습니다. ISOBox::peekBox는 JSC::DataView를 필요로 하고 Gigacage를 통해 동작하는 구조로, SharedBuffer 내용은 Gigacage에 할당되지 않아 해당 경로에서 EXC_BAD_ACCESS가 발생하기 때문입니다. 이전까지 abort()/changeType() 외부에서는 한 번도 도달하지 않았던 AppendFlags::Discontinuity code path도 이번 commit으로 처음 활성화된 셈입니다.
Significance
Apple 플랫폼에서 AVStreamDataParser 앞단에 새로운 stateful binary parser가 추가되었습니다. 이 parser는 공격자가 제어할 수 있는 non-Gigacage 메모리 위에서 appendData() 경계를 넘어 ISO-BMFF box header를 순회합니다.
Audit directions
- Box size field decoding. pre-parser는 ISO-BMFF의 세 가지 size 인코딩을 모두 처리합니다. 표준 32-bit, 64-bit extended(size field == 1, 이후 8바이트 길이 값 포함), 개방형(size == 0, "스트림 끝까지")이 이에 해당합니다. 공격자가 제어하는 64-bit size 값을 바탕으로 다음 box의 offset을 계산할 때 integer overflow가 발생하는 것이 전통적인 공격 방식입니다. 32→64-bit 변환 경로와
size==0sentinel은 면밀히 점검할 필요가 있습니다. - Cross-boundary partial header state (
m_pendingHeaderBytes). 두appendData()호출에 걸쳐 분할된 header는 바이트 누적 버퍼를 통해 재조합됩니다. 처리된 바이트 수와 버퍼링된 바이트 수 계산에서 off-by-one 오류가 발생하면, parser가 box 경계를 잘못 파악하게 됩니다. 결과적으로 media segment를 init segment로, 혹은 그 반대로 잘못 분류하는 상황이 이론적으로 가능합니다. - Append split logic. append 도중 새로운
ftyp가 감지되면 pre-parser는 byte offset을 계산한 뒤SharedBuffer를 두 부분으로 분할합니다. header 크기가 가변적(32-bit는 8바이트, 64-bit extended는 16바이트)이라는 점에서, 산술 오류가 발생하면 정렬이 맞지 않는 데이터가AVStreamDataParser로 전달될 가능성이 있습니다. - Reactivated
AppendFlags::Discontinuitypath. 이 flag는resetParserState()외부에서는 한 번도 사용된 적이 없었습니다. flag 도착 시AVStreamDataParser가 어떤 상태에 있어야 하는지는 이후 처리 단계의 가정에 달려 있는데, 이 가정들은 스트림 중간 재초기화 시나리오에서 아직 검증된 바가 없습니다. BitReaderon non-Gigacaged memory. 이 commit은ISOBox::peekBox가SharedBuffer메모리에서 fault가 발생함을 명시적으로 문서화하고, 대신BitReader를 사용합니다.BitReader에 bounds check 누락이 있고 pre-parser가 남은 버퍼 길이를 잘못 판단하는 경우(예: truncate된 64-bit size field를 통해)를 가정할 수 있습니다. 이 경우 해당 경로는 Gigacage의 안전망 없이 공격자가 제어하는 데이터 위에서 동작하게 됩니다.