
CVE-2026-74943 · Использование после освобождения в Firefox RasterImage (sec-high)
Серьёзность: sec high. CWE 416. Исправлено в Firefox 154 и ESR 140.14. Bugzilla 2057308. Достижимо с обычной веб-страницы без взаимодействия с пользователем.
Firefox хранит декодированные поверхности изображений в SurfaceCache. Кэш содержит невладеющий сырой указатель на изображение-владельца. Когда поверхность вытесняется, кэш уведомляет это изображение в потоке декодера. Уведомление читало атомарный флаг mIsBeingDestroyed и лишь затем сохраняло RefPtr<RasterImage> image = this. Чтение флага и захват ссылки — две отдельные операции. Главный поток может сбросить последнюю ссылку между ними. Умирающий объект воскресает и затем читается после освобождения.
Поток декодера (удерживает блокировку SurfaceCache) Главный поток
OnSurfaceDiscarded читает mIsBeingDestroyed = false
Release обнуляет счётчик ссылок и запускает delete
~RasterImage устанавливает флаг и блокируется на блокировке кэша
RefPtr image = this (AddRef с нуля до единицы)
ставит runnable в очередь и освобождает блокировку
деструктор завершается и освобождает объект размером 248 байт
runnable выполняется и читает освобождённый mProgressTracker ==> use after free
Флаг из более раннего исправления сужает окно. Он не может его закрыть. Наблюдение флага как false не доказывает, что счётчик ссылок всё ещё выше нуля, потому что флаг устанавливается внутри деструктора строго после того, как счётчик уже достиг нуля.
Две инструкции, несущие ошибку, явно видны в скомпилированной функции. Проверка флага и инкремент счётчика ссылок на this раздельны.

Используйте ASAN-сборку затронутой версии. Примените настройки в prefs.js, чтобы кэш поверхностей оставался крошечным, а поток декодера постоянно вытеснял поверхности. Загрузите uaf-reduced.html и оставьте его в цикле. Это межпоточная гонка по времени, поэтому уязвимость воспроизводится периодически.
AddressSanitizer: heap-use-after-free
READ of size 8, 8 bytes into a freed 248-byte RasterImage
use : RasterImage::OnSurfaceDiscardedInternal (main thread)
freed : RasterImage::Release() via imgRequest teardown
alloc : ImageFactory::CreateRasterImage() on the ImageIO thread
Переполнение кучи после освобождения в контентном процессе. Достижимо с веб-страницы и не требует кликов или разрешений. Контролируемое выполнение не было продемонстрировано на протестированных сборках. Объект воскресает и затем освобождается второй раз.
OnSurfaceDiscarded больше не обращается к изображению. Он захватывает сильную ссылку на отдельно подсчитываемый по ссылкам ProgressTracker и восстанавливает изображение в главном потоке через слабую обратную ссылку трекера. Восстановление использует атомарное добавление, если объект жив. Устаревший флаг удалён.
Abdulaziz Alasaiqah · https://azoz.my/writeups.html