Voltar para todos os artigos
Por dentro do 'Bad Epoll' (CVE-2026-46242): Por que seu agente de IA não detectou a falha

Por dentro do 'Bad Epoll' (CVE-2026-46242): Por que seu agente de IA não detectou a falha

Analisamos a race condition de epoll no kernel Linux (CVE-2026-46242) e discutimos os limites críticos das IAs semânticas na detecção de bugs.

Pesquisa técnica projetada por humanos, sintetizada com assistência de personas de IA.
11 min de leitura

TL;DR / Sumário Executivo

Analisamos a race condition de epoll no kernel Linux (CVE-2026-46242) e discutimos os limites críticos das IAs semânticas na detecção de bugs.

💡 TL;DR (Too Long; Didn't Read)

Principais pontos em 90 segundos:

  • A vulnerabilidade: O CVE-2026-46242 (Bad Epoll) é uma falha de escalação de privilégio local no subsistema eventpoll (fs/eventpoll.c) do kernel Linux que permite a um atacante escapar de containers e obter acesso root.
  • A causa raiz: Uma race condition de concorrência entre ep_remove() e a desmontagem concorrente de __fput(). Sob condições específicas de tempo, a Thread B limpa o ponteiro privado do arquivo fora do lock do eventpoll, fazendo a Thread A assumir incorretamente que a limpeza foi feita e pular etapas cruciais de desmontagem.
  • A consequência: Um ponteiro órfão permanece ativo nas filas RCU apontando para a memória do eventpoll liberada, resultando em um exploit de Use-After-Free (UAF).
  • O ponto cego da IA: Agentes avançados de auditoria de segurança baseados em IA, como o modelo Mythos da Anthropic, identificaram vazamentos simples e problemas locais de travamento no mesmo arquivo, mas falharam completamente em mapear esta race condition por causa das limitações de análises heurísticas e semânticas frente a espaços de estados concorrentes.
  • A correção: Atualizar o kernel Linux. O patch adiciona pinagem à estrutura do arquivo no início da seção crítica para garantir que a estrutura eventpoll permaneça válida até a conclusão da remoção.

O subsistema eventpoll (epoll) é um dos componentes mais auditados e críticos do kernel Linux. Funcionando como o mecanismo principal para multiplexação de E/S assíncrona no Linux, ele gerencia quase todos os servidores web de alta performance, bancos de dados e sistemas de orquestração de containers em produção.

Devido à sua criticidade, ele é um alvo primário para pesquisadores de segurança e scanners automatizados de vulnerabilidades. Quando surgiram as notícias sobre o CVE-2026-46242 (apelidado de "Bad Epoll"), um exploit local de use-after-free para obter acesso root no eventpoll.c, engenheiros de sistemas fizeram uma pergunta inquietante: como esse bug sutil escapou de décadas de revisão manual e da última geração de scanners de código autônomos baseados em IA?

A resposta está na física de espaços de estados concorrentes. Embora análises estáticas semânticas sejam excelentes para identificar padrões estruturais e violações locais de APIs, elas continuam cegas para as interações complexas e temporais de threads de execução paralela.

Neste artigo técnico detalhado, examinaremos a mecânica exata de código da vulnerabilidade Bad Epoll, rastrearemos a colisão de execução que gera ponteiros órfãos na memória heap do kernel e avaliaremos os motivos pelos quais agentes de segurança de IA falham repetidamente em capturar defeitos de sincronização.


A Estrutura de Sincronização do eventpoll.c

Para monitorar descritores de arquivos, o epoll registra observadores usando uma estrutura de dados interna chamada struct epitem. Esses itens conectam os descritores de arquivos monitorados (como sockets ou pipes) à instância pai de struct eventpoll.

A limpeza desses observadores é governada por uma coordenação delicada entre dois subsistemas:

  1. O Subsistema Eventpoll: Remove os observadores quando um descritor de arquivo é explicitamente excluído via epoll_ctl(..., EPOLL_CTL_DEL, ...) ou quando a instância epoll é destruída.
  2. O Virtual File System (VFS): Remove os observadores associados quando a contagem de referências do descritor de arquivo cai para zero, disparando rotinas de liberação do arquivo.

Essa divisão de trabalho é gerenciada pela função ep_remove() dentro do código do eventpoll no kernel. O fluxo normal de limpeza exige adquirir o mutex global, remover o item da lista de epoll do arquivo correspondente e liberar as referências de forma segura.

O diagrama abaixo descreve o fluxo de limpeza planejado:

Sob condições normais, esse fluxo garante que nenhuma estrutura eventpoll aponte para arquivos fechados, e que nenhum arquivo fechado possua referências órfãs dentro das filas do kernel do eventpoll.


A Race Condition: Anatomia da Colisão

A vulnerabilidade no CVE-2026-46242 ocorre quando a Thread A (executando a rotina normal ep_remove()) e a Thread B (executando concorrentemente o daemon de limpeza de arquivo __fput()) se sobrepõem sem travas de exclusão mútua suficientes.

Especificamente, a vulnerabilidade afeta o caminho de código de ep_remove_file(), que limpa o ponteiro privado file->f_ep que mapeia o arquivo para seus observadores.

Aqui está a colisão de execução concorrente que causa a liberação de memória inválida (use-after-free):

1. Contexto Suspenso na Thread A

A Thread A entra em ep_remove() para desassociar um observador. Ela adquire com sucesso o mutex local do eventpoll, mas o agendador de tarefas do kernel suspende a execução da Thread A exatamente antes de avaliar se file->f_ep é válido.

2. Desmontagem na Thread B

Simultaneamente, a Thread B é ativada para limpar recursos porque a contagem de referências do arquivo no espaço do usuário zerou. A Thread B entra em __fput() e apaga o ponteiro compartilhado:

file->f_ep = NULL;

Como essa limpeza do VFS executa fora do mutex do eventpoll, a Thread B limpa esse ponteiro sem impedimentos enquanto a Thread A permanece suspensa.

3. Leitura Inconsistente no Retorno

Quando a Thread A retoma sua execução, ela lê o ponteiro compartilhado:

if (file->f_ep == NULL) return;

Como a Thread B já apagou o ponteiro, a Thread A assume que outra thread já cuidou da limpeza dos observadores do eventpoll. A Thread A encerra sua função imediatamente, ignorando a execução crucial da rotina eventpoll_release_file().

4. Estado de Use-After-Free

Isso cria uma inconsistência crítica de estados. O kernel desaloca a memória do objeto struct eventpoll da heap, mas referências ativas continuam registradas nas filas de RCU (Read-Copy-Update). Uma chamada posterior acessa esse endereço desalocado, permitindo operações de escrita arbitrária na heap do kernel e escalação de privilégios para root.

Verified SourceDetalhes do NIST NVD para a Vulnerabilidade CVE-2026-46242

Pesquisas do KernelCTF demonstraram que, embora a janela de execução concorrente seja de apenas seis instruções de CPU, o bug pode ser explorado de forma confiável usando técnicas de pulverização de heap (heap spraying) para obter acesso root local.


Widening the Race: Como Exploits Ignoram o Limite de 6 Instruções

Em um kernel preemptivo padrão, uma janela de corrida de apenas seis instruções é incrivelmente estreita, tornando colisões acidentais raras. Em arquiteturas de núcleo único, ou sistemas onde a preempção está desabilitada durante seções críticas, sofrer essa colisão por acaso é quase impossível.

No entanto, desenvolvedores de exploits não contam com a sorte. Eles utilizam primitivas do próprio kernel e comportamentos do agendador para alargar artificialmente a janela de corrida:

1. Falhas de Página com Userfaultfd e FUSE

Ao configurar a Thread A para acessar regiões de memória mapeadas por userfaultfd ou por um sistema de arquivos FUSE (File System in Userspace) customizado, um atacante pode forçar page faults que suspendem a Thread A dentro do kernel. Enquanto a Thread A aguarda a resolução no espaço do usuário, a Thread B executa a rotina __fput() completamente, garantindo a colisão de tempo.

2. Preempção por Agendamento de Alta Prioridade

Ao fixar a Thread A e a Thread B no mesmo núcleo de CPU usando APIs de afinidade de processador e rodando uma carga pesada de threads em tempo real, atacantes forçam a preempção de agendamento exatamente no limite das instruções críticas. Isso suspende a Thread A após a checagem de leitura, mas antes da desmontagem.

3. Heap Spraying e Atrasos na Fila RCU

Atacantes inundam as filas RCU do kernel com callbacks falsos. Isso atrasa a liberação real da estrutura struct eventpoll, mantendo os ponteiros órfãos apontando para blocos válidos da heap por mais tempo. Isso estabiliza o estado da memória corrompida antes que o use-after-free seja de fato ativado.


Simulador Interativo de Corrida Epoll

Utilize o simulador interativo abaixo para acompanhar passo a passo as mudanças de estado entre a Thread A e a Thread B, visualizando exatamente onde as verificações de sincronização quebram.

Linux Kernel CVE-2026-46242 Race Simulator

Visualizing the concurrent eventpoll use-after-free synchronization breakdown.

Step 0 of 5
Thread A
ep_remove() (Process Thread)
Idle (Awaiting file close)
// Thread A: ep_remove()
// file->f_ep points to active eventpoll struct
Thread B
__fput() (Cleanup Daemon)
Idle
// Thread B: concurrent __fput()
// file refcount > 0
State Analysis: Initial State: Steady OperationsThe application is running normally. The eventpoll structure monitors active file descriptors. Thread A and Thread B are idle, and eventpoll references are synchronized.

O Ponto Cego da Auditoria de IA

A descoberta do CVE-2026-46242 destacou uma limitação crítica na auditoria de segurança baseada em IA. Durante fases de testes privados, o modelo "Mythos" da Anthropic teve acesso ao arquivo fonte fs/eventpoll.c.

O modelo identificou com sucesso bugs mais simples, como vazamentos locais de memória em caminhos de saída de erro e problemas básicos de travamento duplo de mutex. No entanto, ele falhou completamente em detectar a race condition sutil em ep_remove_file().

Esse fracasso decorre da diferença fundamental entre entendimento semântico e execução de espaço de estados:

Heurísticas Semânticas vs Execução Abstrata

Os LLMs raciocinam por meio da associação de padrões semânticos. Eles entendem o "conceito" de travas, limpezas de recursos e falhas de use-after-free. No entanto, encontrar uma race condition requer simular as mudanças de estado de duas threads assíncronas executando blocos de código com deslocamentos arbitrários de tempo. Isso representa uma explosão exponencial de caminhos de execução que a correspondência de heurísticas semânticas não consegue modelar.

A Explosão de Estados pelo Produto Cartesiano

Para encontrar essa falha, um scanner precisa modelar o produto cartesiano de cada sequência de instruções possível em ep_remove() combinada com cada sequência de instruções possível em __fput(). Para seres humanos, isso exige raciocínio temporal complexo. Para modelos transformadores atuais, que leem e processam código de forma sequencial, essa representação de concorrência é matematicamente inacessível porque eles não possuem um ambiente de simulação ativo para execução de código.

Falta de Memória Temporal Dinâmica

Um modelo estático revisa o código linearmente. Ele lê as travas na Thread A e lê o processo sem travas na Thread B. A menos que o modelo seja explicitamente instruído a simular uma intercalação de instruções em um limite específico, ele assumirá que as funções executam de forma atômica, deixando passar a inconsistência de estado transiente.

Para mitigar essas vulnerabilidades, equipes de engenharia precisam reconhecer que agentes de segurança baseados em IA não podem atuar como seus próprios validadores. Auditorias devem ser acompanhadas de ferramentas de verificação dinâmica, sanitizadores de threads e mecanismos de asserção em tempo de execução.


Mitigando o Exploit

O patch para o CVE-2026-46242 resolve esse problema adicionando pinagem à contagem de referências do descritor de arquivo na entrada da seção crítica dentro de ep_remove().

Ao adquirir uma referência temporária na estrutura do arquivo antes de liberar o mutex, o kernel garante que, mesmo se uma thread paralela chamar close(), o daemon de desmontagem do VFS __fput() não poderá executar sua limpeza final até que a Thread A conclua toda a desassociação do eventpoll.

c
// Estrutura principal do patch dentro de fs/eventpoll.c void ep_remove_safe(struct eventpoll *ep, struct epitem *epi) { struct file *file = epi->ffd.file; // Fixa o descritor de arquivo para impedir a desmontagem concorrente por fput get_file(file); mutex_lock(&ep->mtx); // ... executa a remoção do eventpoll com segurança ... mutex_unlock(&ep->mtx); // Libera com segurança a fixação do descritor de arquivo fput(file); }

Isso garante que o estado transiente onde file->f_ep é definido como NULL nunca seja observado enquanto uma remoção estiver em andamento, fechando a janela de corrida permanentemente.


Fontes Externas

Leituras Relacionadas no gsstk

  • a0124: io_uring para Cargas de Trabalho de IA/ML: Quando o Kernel Parou de Esperar
  • a0125: O Passport Gate: Como Controles de Exportação dos EUA Desligaram o Claude Fable 5
  • a0119: NGINX Rift: Como uma IA Autônoma Encontrou um Bug de 18 Anos

Este artigo foi estruturado por humanos e sintetizado com assistência de inteligência artificial sob a persona Aether (AI).

Receba novos artigos

Cadastre-se para receber notificações sobre novos artigos direto no seu email

Não enviaremos spam. Você pode cancelar a inscrição a qualquer momento.