
Некоторые ошибки, обнаруженные с помощью бинарной инструментации и фаззинга
media-gen — это небольшой инструмент командной строки на Rust с минимумом зависимостей для создания двух тестовых примеров для медиапарсеров. Он редактирует существующие корректные медиафайлы на месте; он не вызывает FFmpeg, не патчит браузер, не обращается к серверу и ничего не загружает.
Пример WebM включает мостовую схему как для старого пути Vorbis с задержкой в один буфер, так и для текущего пути с немедленным отбрасыванием. Включённый в репозиторий короткий образец был проверен с Discord Desktop 1.0.9257 (Electron 42.11.1 / Chromium 148.0.7778.280) и Chrome 153.0.8010.48. Пример M4A остаётся специфичным для зафиксированной сборки Discord/FFmpeg. Другие версии могут отклонять эти файлы, обрабатывать их безопасно или сбоить иначе.
| Пример | Вход | Эффект в затронутой сборке | Триггер |
|---|
| Мост отбрасывания WebM/Vorbis | Существующий WebM с дорожкой A_VORBIS | Рендерер завершается с 0x80000003 (STATUS_BREAKPOINT) в проверке релиза AudioDiscardHelper в Chromium для обоих протестированных режимов отбрасывания | Воспроизведение/декодирование; только загрузка метаданных недостаточна |
Постоянное значение count в stsz для M4A | Существующий fast-start AAC/M4A seed | Рендерер временно выделяет около 6,6 ГБ (6,16 ГиБ) приватной памяти при загрузке метаданных | Загрузка метаданных после того, как аудиоэлемент меняется с preload="none" на метаданные |
Это тестовые примеры отказа в обслуживании/потребления ресурсов, а не продемонстрированные эксплойты выполнения кода. Запускайте их только в изолированной, ограниченной тестовой среде.
media-gen — это слой воспроизводимости, а не то, как эти примеры были обнаружены. Сложной частью было найти и доказать поведение внутри большого нативного исполняемого файла Discord и его встроенных медиабиблиотек. blare2 сделал это практичным, переписав изолированную копию точной среды выполнения и позволив узким пробам наблюдать за исполнением без изменения установленного приложения.
Для примера WebM семантическая проба blare2 остановилась точно на проверке AudioDiscardHelper::ProcessBuffers и зафиксировала состояние сбоя: discarded_frames = 129 и decoder_delay = 128. Без этого наблюдения выход рендерера с STATUS_BREAKPOINT лишь указал бы на общую проверку релиза; он не установил бы, какой медиаинвариант нарушен или действительно ли сконструированное заполнение до него дошло.
Для примера M4A крупные выделения памяти являются временными и могут исчезнуть до взятия обычной выборки процесса. Покрытие точной среды выполнения и целевая инструментация blare2 в сочетании с высокочастотной выборкой процессов и дизассемблированием показали, что крошечный файл достиг построителя таблицы образцов MOV и что объявленное количество масштабировало выделения AVIndexEntry и таблицы таймингов. Это отделило проблему от обычного сбоя декодирования AAC или вводящей в заблуждение корреляции размера файла/OOM. Одного широкого покрытия точек входа функций было недостаточно: файлы с низким и высоким count проходили через одни и те же функции, тогда как размеры их выделений радикально различались.
blare2 не заменил анализ контейнера, обзор исходного кода или контроли и не доказал, что производственный путь загрузки/CDN Discord сохраняет эти байты. Его ценность была в атрибуции во время выполнения: он превратил подозрительное поведение парсера в воспроизводимую, привязанную к версии находку с точным состоянием сбоя и обоснованным объяснением выделения памяти. Без бинарной инструментации такого рода поиск этих двух примеров был бы существенно медленнее и гораздо труднее поддавался бы проверке.
Из корня репозитория:
cargo build --release -p media-gen
Бинарный файл — target/release/media-gen (media-gen.exe в Windows). Самому генератору нужны только Rust и зависимости в Cargo.toml.
cargo run --release -p media-gen -- --help
cargo run --release -p media-gen -- webm --help
cargo run --release -p media-gen -- m4a --help
Отрицательный DiscardPadding в Matroska становится фронтальным пропуском Vorbis. Старый путь Vorbis в Chromium задерживает эти метаданные на один закодированный пакет; текущий Chromium применяет их к текущему декодированному выводу и отбрасывает метаданные пакета 0, когда этот прайминг-пакет не выдаёт PCM. Повторение одного большого пропуска на пакетах 0 и 1 связывает оба поведения:
| Аудиопакет | Декодированный PCM | Фронтальный пропуск |
|---|---|---|
| 0 | нет (прайминг Vorbis) | 577 кадров |
| 1 | 576 кадров | 577 кадров |
| 2 | 1 024 кадра | 1 кадр |
Дорожка имеет CodecDelay в 128 кадров. В старом режиме с задержкой пропуск пакета 0 в 577 кадров применяется к пакету 1. В текущем режиме пропуск пакета 0 отбрасывается, а идентичный пропуск пакета 1 применяется напрямую. В любом случае после смещения задержки кодека можно удалить только 448 кадров, поэтому 129 кадров переносятся в пакет 2. Его положительный фронтальный пропуск достигает проверки релиза Chromium после того, как эти 129 кадров уже удалены. Требуемый инвариант — discarded_frames <= decoder_delay; 129 <= 128 не выполняется и завершает рендерер.
Генератор разбирает заголовки настройки Vorbis, вычисляет декодированные размеры пакетов 1 и 2 и отказывает безопасно, если мост нежизнеспособен. Затем он:
SimpleBlock в элементы BlockGroup;DiscardPadding на пакетах 0 и 1 и положительный триггер в один кадр на пакете 2;SeekHead, Cues и затронутые CRC кластеров, чтобы перезаписанный контейнер оставался разбираемым.Манифест сообщает о немедленном и отложенном путях отдельно. Для включённого в репозиторий образца оба пути называют пакет 2 проверочным пакетом и сообщают expected_carry_frames: 129 и expected_check_fails: true.
Репозиторий содержит контрольный WebM длительностью 43 мс и размером 4 185 байт:
cargo run --release -- webm \
--input samples/short-vorbis-dual-control.webm \
--output .build/short-vorbis-dual-crash.webm \
--manifest .build/short-vorbis-dual-crash.json \
--force
Входные данные должны содержать:
A_VORBIS;CodecDelay (обычно считывается из дорожки); иПолезные опции — --trigger-skip, --codec-delay и --sample-rate. --second-skip остаётся псевдонимом для переименованной опции --trigger-skip. Значения по умолчанию — это значения, необходимые для известного состояния сбоя. Команда выводит JSON-отчёт в stdout и записывает тот же отчёт в --manifest, когда эта опция указана.
Включённый в репозиторий кандидат имеет длительность 43 мс и размер 4 206 байт. Его SHA-256:
a0ab9e146c629f037b86612addc1ab6fff9200711d45aa5f5edbb9576cc206ac
Ожидается, что хеш вывода изменится, если изменятся входные данные, окно пакетов или опции.
Пример M4A злоупотребляет корректной формой таблицы образцов MP4, а не закодированной полезной нагрузкой AAC. Генератор изменяет seed следующим образом:
stsz на sample_size = 1.stsz, первом прогоне stsc и первом прогоне stts в одно и то же большое значение.stco, чтобы однобайтовая полезная нагрузка AAC по-прежнему указывала внутрь mdat.Зафиксированный демультиплексор FFmpeg считает объявленное количество авторитетным при построении своих таблиц индекса и таймингов. Соответствующие структуры используют примерно 24 байта на объявленный образец для AVIndexEntry и 12 байт на образец для данных таймингов: всего 36 байт на запись count. Максимальное принимаемое количество в протестированной сборке — 178,956,969 (0x0AAAAAA9), что проецируется в:
178,956,969 * 24 = 4,294,967,256 bytes
178,956,969 * 12 = 2,147,483,628 bytes
combined = 6,442,450,884 bytes
Точный рендерер Discord достиг пика приватной памяти в 6 614 761 472 байта при загрузке метаданных. Соседнее значение count 178,956,970 отклоняется зафиксированной границей FFmpeg и остаётся вблизи нормального объёма памяти. Это неконтролируемое потребление ресурсов (CWE-400), а не наблюдаемое целочисленное переполнение, выделение отрицательного размера или запись за границы. Фактическая полезная нагрузка AAC может быть усечена; успешное воспроизведение не требуется для достижения крупного выделения.
Генератор намеренно принимает seed вместо встраивания кодировщика AAC. Используйте fast-start файл AAC/M4A с одной явной таблицей stsz, одним прогоном stsc, одной или несколькими записями stts и смещениями чанков stco. Он отказывает безопасно, если требуемая структура отсутствует.
cargo run --release -p media-gen -- m4a \
--input path/to/base-faststart.m4a \
--output .build/media-gen/constant-stsz.m4a \
--manifest .build/media-gen/constant-stsz.json \
--force
Значение по умолчанию --sample-count — 178956969, максимальное принимаемое значение в зафиксированной сборке. Используйте меньшее значение для дымового теста с низким потреблением памяти, например:
cargo run --release -p media-gen -- m4a \
--input path/to/base-faststart.m4a \
--output .build/media-gen/constant-stsz-32000000.m4a \
--sample-count 32000000 \
--force
--sample-count 178956970 — полезный соседний контроль отклонения, а не триггерное значение. Необязательный флаг --moov-at-end перемещает moov после mdat и обновляет stco/co64; он полезен при тестировании файла, физические байты AAC которого находятся перед его метаданными, но он не требуется для механизма выделения памяти.
Чтобы явно создать такую компоновку:
cargo run --release -p media-gen -- m4a \
--input path/to/base-faststart.m4a \
--output .build/media-gen/constant-stsz-moov-end.m4a \
--moov-at-end \
--force
Соответствующие артефакты —
samples/short-vorbis-dual-control.webm,
samples/short-vorbis-dual-crash.webm,
и samples/short-vorbis-dual-crash.json.