PT-2026-98609 · Linux · Linux
CVE-2026-97945
·
Published
2026-09-25
·
Updated
2026-09-25
None
No severity ratings or metrics are available. When they are, we'll update the corresponding info on the page.
In the Linux kernel, the following vulnerability has been resolved:
x86/mm: Fix user-space data loss with MADV FREE and THP
Some of users of Polars (a data analytics library) have lost production
data from this bug. They seem to have just the right combination of
huge pages, MADV FREE and heavy reclaim pressure.
pmd modify() masks the old value with ( HPAGE CHG MASK & ~ PAGE DIRTY),
silently discarding the hardware dirty bit. The subsequent
pmd mksaveddirty() call is supposed to transfer PAGE DIRTY into
PAGE SAVED DIRTY when write-protecting, but the dirty bit was already
stripped from the value, so there is nothing left to transfer.
Contrast with pte modify(), which keeps PAGE DIRTY BITS in its mask,
and pud modify(), which keeps HPAGE CHG MASK untouched: pmd modify()
is the odd one out. Any pmd modify() on a writable, dirty PMD loses
the dirty state.
One visible consequence is data loss with MADV FREE on PMD-mapped THP:
memset(buf, 0x5A, size); // PMD-mapped THP, PMD dirty
madvise(buf, size, MADV FREE); // PMD cleaned but left writable,
// folio marked lazyfree
memset(buf, 0x5A, size); // hardware sets PAGE DIRTY again
mprotect(buf, size, PROT READ); // pmd modify() drops the dirty bit
mprotect(buf, size, PROT READ|PROT WRITE);
// ... memory pressure ...
Reclaim (e.g. under memcg pressure) then finds the lazyfree folio with
no dirty bit set anywhere and frees it in
discard anon folio pmd locked(), even though the data was rewritten
after MADV FREE; subsequent reads fault in fresh zero pages. NUMA
hinting alone can trigger the same loss, as do huge pmd numa page()
restores the PMD through pmd modify() as well.
PMD-mapped file THPs are affected too: mprotect()/NUMA hinting dropping
the dirty bit means rewritten data is never written back.
Fix it by keeping PAGE DIRTY in the preserved mask, exactly like
pte modify() and pud modify() do. The existing
pmd mksaveddirty()/pmd clear saveddirty() pair then performs the
hardware-dirty <-> saved-dirty transition based on the write bit,
preserving the shadow-stack encoding rules.
Found an issue in the description? Have something to add? Feel free to write us 👾
Related Identifiers
Affected Products
Linux