CVE-2026-89987 in Linux
Sumário
de VulDB • 16/09/2026
No kernel do Linux, a seguinte vulnerabilidade foi resolvida:
mm/huge_memory: transferir o bit sujo (dirty) do PMD para o folio durante o zap
zap_huge_pmd_folio() propaga o bit jovem (young) do PMD para o folio no caso de arquivo, mas não o bit sujo. O caminho PTE faz essa propagação em zap_present_folio_ptes(), assim como o caminho de divisão do PMD em __split_huge_pmd_locked().
Para a maioria dos mapeamentos de arquivos, a omissão é inofensiva, porque a gravação em um mapeamento de arquivo compartilhado passa por page_mkwrite(), que marca o folio como sujo. O tmpfs é diferente: ele não possui page_mkwrite() e vma_wants_writenotify() retorna falso para ele; portanto, uma falha de leitura (*read fault*) em um mapeamento MAP_SHARED do tmpfs instala um PMD gravável via do_read_fault(). do_read_fault() não chama fault_dirty_shared_page(), então as gravações subsequentes por meio desse mapeamento definem apenas o bit sujo no hardware no PMD e nunca chamam folio_mark_dirty(). Um folio shmem alocado por uma falha é marcado como atualizado (uptodate), mas não como sujo (consulte o bloco clear em shmem_get_folio_gfp()), então PG_dirty nunca é definido.
Desmapear tal folio - munmap() ou exit_mmap() quando o processo morre - perde a única referência de que ele foi gravado, porque zap_huge_pmd() descarta o PMD sem transferir o bit sujo. A recuperação (reclaim) subsequente vê um folio shmem limpo: todo o bloco de swap-out em shrink_folio_list() está dentro do "if (folio_test_dirty(folio))", então pageout() é ignorado e o folio cai em __remove_mapping(). Lá, folio_is_file_lru() retorna falso para um folio com suporte a swap, portanto nenhuma entrada sombra (shadow entry) é criada e __filemap_remove_folio(folio, NULL) simplesmente esvazia a slot i_pages. Os dados são liberados sem nunca serem gravados no swap, e a próxima falha nesse índice retorna um folio recém-zereado.
Isso representa perda silenciosa de dados para qualquer processo que mantenha estado em um segmento tmpfs MAP_SHARED através de uma operação unmap - por exemplo, um cache transferido de uma geração de processos para outra via /dev/shm. Isso requer que o folio seja mapeado com PMD, portanto só aparece quando o THP (Transparent Huge Pages) do shmem está habilitado (o que fizemos na frota Meta e começamos a notar crashes); com o THP desativado, o caminho PTE transfere corretamente o bit sujo. Ele também só se torna visível quando o swap está habilitado, porque sem dispositivo de swap os folios shmem (que estão no LRU anônimo) não são verificados pela recuperação em absoluto, então o folio limpo nunca é descartado.
Reproduzido em x86_64 com um tmpfs montado como huge=within_size: falha de leitura em uma região suportada por 2MB, gravação de um padrão conhecido através do mapeamento resultante, munmap, forçar a recuperação (reclaim) da cgroup e depois remapear e ler novamente. Sem este patch, a região é lida como zeros e vmstat mostra zswpout 0 - os dados foram descartados em vez de trocados para swap. Com este patch, a região é lida corretamente e as páginas são trocadas conforme o esperado. Com huge=never ou quando o primeiro toque (touch) for uma gravação, o teste passa independentemente do patch.
If you want to get best quality of vulnerability data, you may have to visit VulDB.