
CVE-2026-74943, uso após liberação (use after free) em Firefox RasterImage (sec-high)
Este diretório contém um scaffold de reprodução honesto para um
use-after-free entre threads em mozilla::image::RasterImage::OnSurfaceDiscarded. Ele foi feito
para ser executado contra uma build vulnerável do Firefox baixada localmente, usando apenas
dados de imagem sintéticos. Ele nunca toca em nenhum sistema ativo ou de terceiros.
Repositório PoC correspondente: https://github.com/defineid/Revenant
Writeup: writeup-rasterimage-uaf.html neste portfólio.
Estes foram confirmados desmontando a build vulnerável (libxul.so, Firefox
140.9.0esr) durante a pesquisa original; a captura de tela commitada
assets/writeup-uaf-disasm.png é essa análise. Os nomes de funções vêm do
arquivo de símbolos oficial do Mozilla Breakpad para exatamente essa build; o
libxul.so distribuído está stripped. Eles não foram redesmontados no ambiente
que montou este pacote PoC (veja o status de verificação).
| Fato | Evidência | Fonte |
|---|---|---|
A função vulnerável é RasterImage::OnSurfaceDiscarded(SurfaceKey const&) | FUNC 3eba200 16c 0 mozilla::image::RasterImage::OnSurfaceDiscarded(...) | .sym do servidor de símbolos da Mozilla |
Leitura de byte antecipada em this+0xC0 (writeup: mIsBeingDestroyed) | 3eba20a: cmp BYTE PTR [rdi+0xc0],0x0 com rdi = this | objdump -d libxul.so |
AddRef no refcount do RasterImage em this+0x40 | 3eba25a: lock inc QWORD PTR [rbx+0x40] (emitido duas vezes), rbx = this | objdump -d libxul.so |
Um runnable de 0x20 bytes é alocado e captura this | 3eba264: mov edi,0x20 → call moz_xmalloc@plt; 3eba280: mov QWORD PTR [rax+0x10],rbx | objdump -d libxul.so |
O runnable é despachado, então this é liberado | 3eba298: call QWORD PTR [rcx+0x28] (despacho virtual); 3eba29b: lock dec QWORD PTR [rbx+0x40] | objdump -d libxul.so |
Refcount chegando a 0 executa ~RasterImage e depois free | je 3eba345 → call 3ebbe90 <~RasterImage> → call free@plt | objdump + .sym |
O runnable despachado captura a lambda OnSurfaceDiscarded::$_0 | FUNC 5279d90 ... RunnableFunction<...OnSurfaceDiscarded(...)::$_0>::Run() | .sym do servidor de símbolos da Mozilla |
| Campo | Valor |
|---|---|
| CVE | CVE-2026-74943 |
| Componente | Core / Graphics: ImageLib |
| Classe | Use-After-Free (CWE-416) |
| Severidade | sec-high (segurança de memória alcançável pela web) |
| Corrigido em | Firefox 154 · ESR 140.14 |
| Bug | bugzilla.mozilla.org/show_bug.cgi?id=2057308 (restrito por segurança) |
SurfaceCacheImpl::Remove notifica a imagem proprietária de que uma superfície decodificada foi
descartada chamando OnSurfaceDiscarded através de um ponteiro ImageKey bruto, não proprietário,
em uma thread de decoder / ImageIO, enquanto mantém o mutex do cache de superfícies.
Na build vulnerável, esse handler:
this+0xC0 logo no início (cmp BYTE PTR [rdi+0xc0],0x0).
O writeup identifica isso como mIsBeingDestroyed, uma mitigação anterior.this incrementando o refcount do RasterImage em
this+0x40 (lock inc QWORD PTR [rbx+0x40]). Este é um
add-ref simples, não um add-ref-if-nonzero atômico.moz_xmalloc(0x20) um RunnableFunction que captura this
(mov QWORD PTR [rax+0x10],rbx), o despacha para um event target global
(call QWORD PTR [rcx+0x28]) e então libera a referência local
(lock dec QWORD PTR [rbx+0x40]).Como a leitura da flag e o incremento do contador são duas operações
separadas sobre um refcount lock-free, elas podem atravessar o momento em que a thread principal
descarta a última referência. A thread do decoder então faz AddRef a partir de zero,
ressuscitando um objeto cujo destrutor já foi executado. Quando o
destrutor termina e faz free do RasterImage, o runnable enfileirado posteriormente executa
na thread principal e lê memória liberada.
A correção deixa de referenciar a imagem diretamente: OnSurfaceDiscarded captura uma
referência forte ao ProgressTracker contado separadamente por referência e recupera a
imagem na thread principal através da referência fraca inversa do tracker, promovendo-a
com um add-ref-if-live atómico; o sinalizador mIsBeingDestroyed é removido.
Uma página web comum no processo de conteúdo provoca pressão na cache de superfícies (muitas superfícies descodificadas descartáveis) ao mesmo tempo que liberta a frame que detém os pedidos de imagem. Não é necessário qualquer clique, permissão, extensão ou alteração de preferência. A corrida ocorre entre a notificação de descarte da thread do descodificador e a libertação final da mesma imagem pela thread principal.
https://ftp.mozilla.org/pub/firefox/releases/140.9.0esr/.
Uma build ASAN (Nightly asan ou uma build local com --enable-address-sanitizer)
torna o use-after-free observável como um relatório limpo em vez de um
crash silencioso ocasional.<canvas> em tempo de execução; não carrega nada da rede.Abra trigger.html na build vulnerável, idealmente a partir de um caminho file:// ou de um
servidor estático local que controle:
/path/to/firefox --new-instance --profile ./poc-profile ./trigger.html
Por padrão, o driver executa um loop de microtarefas apertado para maximizar a sobreposição entre decodificação e liberação. Anexar ?tick=N à URL o alterna para um loop setTimeout(N) que cede o controle, que gira com a mesma intensidade, mas deixa o event loop respirar, o que permite que um driver automatizado avance e capture a tela da página enquanto ela é executada. O trigger-running.png commitado foi capturado exatamente dessa forma, conduzindo a build local 140.11.0esr através do Marionette com ?tick=4: ele mostra o harness em execução (ciclos e teardowns aumentando). Essa build está corrigida, então nenhum crash ocorre; a imagem apenas prova que o driver funciona. Em modo headless, as imagens fora da tela não são pintadas, então o contador "images decoded" permanece em 0 ali; uma build vulnerável com interface gráfica as decodifica e é onde a corrida é exercitada.
Deixe a página rodar. Ela decodifica repetidamente muitas imagens grandes distintas (para preencher o cache de superfícies e forçar a despejo) enquanto desmonta a subárvore do DOM que as possui (para conduzir a liberação final da imagem proprietária). Isso maximiza a sobreposição entre "superfície descartada" na thread do decodificador e "última liberação" na thread principal. As corridas são probabilísticas; deixe-a rodando.
heap-use-after-free cujo frame de READ está dentro de
RasterImage::OnSurfaceDiscardedInternal / do runnable despachado, com o
frame freed no teardown de RasterImage::Release. Sem ASAN, pode se manifestar
como um crash intermitente do processo de conteúdo.ProgressTracker e descarta a
notificação quando a destruição já foi efetivada.