CVE-2026-89987 Overview
CVE-2026-89987 is a Linux kernel memory management flaw in zap_huge_pmd_folio() that fails to transfer the PMD dirty bit to the folio during unmapping. The omission causes silent data loss for MAP_SHARED tmpfs mappings when Transparent Huge Pages (THP) and swap are both enabled. Stores written through a PMD-mapped shmem folio are discarded during reclaim because the folio appears clean, so pageout() is skipped and the data is freed without being written to swap. The issue was discovered in production at Meta after enabling shmem THP.
Critical Impact
Processes that persist state in MAP_SHARED tmpfs segments across an unmap operation, such as caches shared through /dev/shm, may lose data silently and read back zeros after remap.
Affected Products
- Linux kernel builds with Transparent Huge Pages enabled for shmem (huge=within_size or huge=always)
- Systems using tmpfs with MAP_SHARED mappings and an active swap device
- Kernel branches receiving the fix via commits 9435cdd, effe3cc, and fe6cf98
Discovery Timeline
- 2026-09-16 - CVE-2026-89987 published to NVD
- 2026-09-16 - Last updated in NVD database
Technical Details for CVE-2026-89987
Vulnerability Analysis
The defect resides in zap_huge_pmd_folio() in mm/huge_memory.c. The function propagates the PMD young bit to the folio for the file case but omits the dirty bit. Sibling code paths handle the transfer correctly: zap_present_folio_ptes() covers the PTE path, and __split_huge_pmd_locked() covers the PMD split path. The inconsistency creates a narrow but reachable window where hardware dirty state is dropped.
For most file-backed mappings the omission is harmless because writes go through page_mkwrite(), which dirties the folio. Tmpfs behaves differently: it has no page_mkwrite() handler, and vma_wants_writenotify() returns false. A read fault on a MAP_SHARED tmpfs mapping therefore installs a writable PMD via do_read_fault(), which never calls fault_dirty_shared_page(). Subsequent stores set only the hardware dirty bit in the PMD and never call folio_mark_dirty().
Root Cause
A shmem folio allocated by a fault is marked uptodate but not dirty in shmem_get_folio_gfp(), so PG_dirty is never set. When the mapping is torn down by munmap() or exit_mmap(), zap_huge_pmd() drops the PMD without transferring the accumulated hardware dirty state. Reclaim in shrink_folio_list() gates the swap-out block on folio_test_dirty(folio), which is false, so pageout() is skipped. Control falls through to __remove_mapping(), where folio_is_file_lru() is false for a swap-backed folio, so no shadow entry is created and __filemap_remove_folio(folio, NULL) empties the i_pages slot. The written data is freed without being persisted.
Attack Vector
The issue is a data-integrity bug reachable through normal userspace operations rather than a memory-safety flaw. A local process reads-then-writes a PMD-backed MAP_SHARED tmpfs region, unmaps it, and triggers memory pressure sufficient to reclaim the cgroup. Remapping the same offset returns a zero-filled folio. Reproduction on x86_64 uses a tmpfs mount with huge=within_size, a 2 MB-backed region, a read fault followed by writes, munmap(), forced cgroup reclaim, and a subsequent read. With the bug present, vmstat shows zswpout 0 and the region reads back as zeros. See the kernel commit fe6cf98 for the corrective patch.
Detection Methods for CVE-2026-89987
Indicators of Compromise
- Unexpected zero-filled regions returned on remap of previously written MAP_SHARED tmpfs segments
- vmstat counters showing zswpout at zero while shmem THP is active and swap is configured
- Application-level checksum or cache-validation failures on data recovered from /dev/shm
Detection Strategies
- Audit kernel version and configuration for shmem THP enablement (/sys/kernel/mm/transparent_hugepage/shmem_enabled) combined with an active swap device
- Instrument tmpfs-backed cache consumers with checksum validation on reload to surface silent zeroing
- Correlate reclaim events with application errors following munmap() or process exit on shared tmpfs regions
Monitoring Recommendations
- Track /proc/vmstat fields pgpgout, pswpout, and zswpout on hosts using shmem THP to detect anomalously low swap-out volumes
- Monitor cgroup memory pressure events alongside application error rates for producers of shared tmpfs state
- Alert on kernel version drift where affected mainline or stable branches remain deployed after fixed builds are available
How to Mitigate CVE-2026-89987
Immediate Actions Required
- Inventory hosts running kernels with shmem THP enabled and confirm whether swap is configured, since both conditions are required to expose the bug
- Deploy kernel builds that include commits 9435cdd, effe3cc, and fe6cf98 from the stable tree
- Validate application correctness after patching by rerunning the read-fault, write, unmap, reclaim, remap reproduction sequence
Patch Information
The fix transfers the PMD dirty bit to the folio inside zap_huge_pmd_folio(), aligning behavior with the PTE and PMD-split code paths. Patches are available in the mainline and stable trees via kernel commit 9435cdd, kernel commit effe3cc, and kernel commit fe6cf98. Distribution vendors are backporting the fix to supported long-term stable branches.
Workarounds
- Disable shmem THP by writing never to /sys/kernel/mm/transparent_hugepage/shmem_enabled until patched kernels are deployed
- Mount tmpfs without huge page backing by omitting huge=within_size or huge=always from mount options
- Ensure the first touch on a MAP_SHARED tmpfs region is a write rather than a read, which routes the fault through a path that sets PG_dirty correctly
# Disable shmem transparent huge pages as a temporary mitigation
echo never > /sys/kernel/mm/transparent_hugepage/shmem_enabled
# Remount tmpfs without huge page backing
mount -o remount,huge=never /dev/shm
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

