CVE-2026-31398: mm/rmap: fix incorrect pte restoration for lazyfree folios
In the Linux kernel, the following vulnerability has been resolved:
mm/rmap: fix incorrect pte restoration for lazyfree folios
We batch unmap anonymous lazyfree folios by folio_unmap_pte_batch. If the
batch has a mix of writable and non-writable bits, we may end up setting
the entire batch writable. Fix this by respecting writable bit during
batching.
Although on a successful unmap of a lazyfree folio, the soft-dirty bit is
lost, preserve it on pte restoration by respecting the bit during
batching, to make the fix consistent w.r.t both writable bit and
soft-dirty bit.
I was able to write the below reproducer and crash the kernel.
Explanation of reproducer (set 64K mTHP to always):
Fault in a 64K large folio. Split the VMA at mid-point with
MADV_DONTFORK. fork() - parent points to the folio with 8 writable ptes
and 8 non-writable ptes. Merge the VMAs with MADV_DOFORK so that
folio_unmap_pte_batch() can determine all the 16 ptes as a batch. Do
MADV_FREE on the range to mark the folio as lazyfree. Write to the memory
to dirty the pte, eventually rmap will dirty the folio. Then trigger
reclaim, we will hit the pte restoration path, and the kernel will crash
with the trace given below.
The BUG happens at:
BUG_ON(atomic_inc_return(&ptc->anon_map_count) > 1 && rw);
The code path is asking for anonymous page to be mapped writable into the
pagetable. The BUG_ON() firing implies that such a writable page has been
mapped into the pagetables of more than one process, which breaks
anonymous memory/CoW semantics.
[ 21.134473] kernel BUG at mm/page_table_check.c:118!
[ 21.134497] Internal error: Oops - BUG: 00000000f2000800 [#1] SMP
[ 21.135917] Modules linked in:
[ 21.136085] CPU: 1 UID: 0 PID: 1735 Comm: dup-lazyfree Not tainted 7.0.0-rc1-00116-g018018a17770 #1028 PREEMPT
[ 21.136858] Hardware name: linux,dummy-virt (DT)
[ 21.137019] pstate: 21400005 (nzCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)
[ 21.137308] pc : page_table_check_set+0x28c/0x2a8
[ 21.137607] lr : page_table_check_set+0x134/0x2a8
[ 21.137885] sp : ffff80008a3b3340
[ 21.138124] x29: ffff80008a3b3340 x28: fffffdffc3d14400 x27: ffffd1a55e03d000
[ 21.138623] x26: 0040000000000040 x25: ffffd1a55f7dd000 x24: 0000000000000001
[ 21.139045] x23: 0000000000000001 x22: 0000000000000001 x21: ffffd1a55f217f30
[ 21.139629] x20: 0000000000134521 x19: 0000000000134519 x18: 005c43e000040000
[ 21.140027] x17: 0001400000000000 x16: 0001700000000000 x15: 000000000000ffff
[ 21.140578] x14: 000000000000000c x13: 005c006000000000 x12: 0000000000000020
[ 21.140828] x11: 0000000000000000 x10: 005c000000000000 x9 : ffffd1a55c079ee0
[ 21.141077] x8 : 0000000000000001 x7 : 005c03e000040000 x6 : 000000004000ffff
[ 21.141490] x5 : ffff00017fffce00 x4 : 0000000000000001 x3 : 0000000000000002
[ 21.141741] x2 : 0000000000134510 x1 : 0000000000000000 x0 : ffff0000c08228c0
[ 21.141991] Call trace:
[ 21.142093] page_table_check_set+0x28c/0x2a8 (P)
[ 21.142265] __page_table_check_ptes_set+0x144/0x1e8
[ 21.142441] __set_ptes_anysz.constprop.0+0x160/0x1a8
[ 21.142766] contpte_set_ptes+0xe8/0x140
[ 21.142907] try_to_unmap_one+0x10c4/0x10d0
[ 21.143177] rmap_walk_anon+0x100/0x250
[ 21.143315] try_to_unmap+0xa0/0xc8
[ 21.143441] shrink_folio_list+0x59c/0x18a8
[ 21.143759] shrink_lruvec+0x664/0xbf0
[ 21.144043] shrink_node+0x218/0x878
[ 21.144285] __node_reclaim.constprop.0+0x98/0x338
[ 21.144763] user_proactive_reclaim+0x2a4/0x340
[ 21.145056] reclaim_store+0x3c/0x60
[ 21.145216] dev_attr_store+0x20/0x40
[ 21.145585] sysfs_kf_write+0x84/0xa8
[ 21.145835] kernfs_fop_write_iter+0x130/0x1c8
[ 21.145994] vfs_write+0x2b8/0x368
[ 21.146119] ksys_write+0x70/0x110
[ 21.146240] __arm64_sys_write+0x24/0x38
[ 21.146380] invoke_syscall+0x50/0x120
[ 21.146513] el0_svc_common.constprop.0+0x48/0xf8
[ 21.146679] do_el0_svc+0x28/0x40
[ 21.146798] el0_svc+0x34/0x110
[ 21.146926] el0t
---truncated---
Security readout for executives and security teams
Plain-English summary
A flaw in Linux memory management can incorrectly restore writable page-table entries when reclaiming certain lazy-free anonymous memory. This can violate copy-on-write protections shared between processes and crash the kernel. The supplied CVSS score is 7.8, reflecting potentially serious confidentiality, integrity, and availability impact from a local, low-privileged context.
Executive priority
Treat this as a high-priority kernel maintenance issue, especially on shared compute, container hosts, and systems allowing untrusted local code. It is not evidenced as remotely exploitable or actively exploited. Accelerate vendor verification and patching, while avoiding emergency internet-wide incident assumptions unsupported by the sources.
Technical view
folio_unmap_pte_batch may combine writable and non-writable PTEs for a lazyfree folio, then restore the whole batch as writable. This breaks anonymous-memory copy-on-write semantics when pages are mapped by multiple processes. The correction preserves each PTE's writable and soft-dirty state during batching. A supplied reproducer triggered a page-table-check BUG and kernel crash.
Likely exposure
The bundle marks Linux 6.15, 6.18.20, 6.19.10, and 7.0 as affected, but also contains an ambiguous "0" entry and commit-oriented data. Exposure depends on kernel lineage, configuration, and whether vendor builds include a cited fix. Internet reachability is not required; an attacker needs local access and low privileges according to CVSS.
Exploitation context
A researcher demonstrated a deliberate local sequence that crashes the kernel using large folios, VMA changes, lazy-free memory, and reclaim. This proves reproducibility under specific conditions, but the bundle provides no evidence of attacks in the wild. The CVE is not listed as KEV. Privilege escalation is not demonstrated in the supplied evidence.
Researcher notes
The core invariant violation is restoration of a writable anonymous mapping into more than one process, conflicting with copy-on-write semantics. Soft-dirty preservation is included for consistency. The demonstrated outcome is a BUG-triggered denial of service; broader confidentiality or integrity consequences are represented by CVSS but are not independently demonstrated in the supplied narrative. Exact affected-release boundaries remain unclear.
Mitigation direction
Install a vendor-supported kernel release that incorporates the applicable cited stable fix.
Map distribution kernel builds to upstream commits; do not rely only on displayed version numbers.
Prioritize multi-user, container-hosting, and other systems where untrusted users can execute local code.
If patching is delayed, restrict local access and untrusted workload execution where operationally feasible.
Validation and detection
Inventory running kernel versions and distribution build identifiers across all Linux systems.
Confirm with each distribution or vendor whether its kernel includes the applicable cited fix.
After updating, verify systems booted into the corrected kernel rather than an older installed image.
Review kernel logs for page-table-check BUGs or unexplained crashes, without treating absence as proof of safety.
Generated from the cited source records. This long-tail analysis has not been individually reviewed by a named human.
Potential ATT&CK relevance
Conservative CVE-to-ATT&CK context
These mappings and lookup hints may be relevant to the vulnerability behavior, CWE, affected product, or exposure path. Glexia-inferred context is not an official MITRE, ATT&CK, CWE, or CVE Program mapping.
ATT&CK lookup starting points
Use these exact CWE pages and searches to review the Glexia ATT&CK library from this CVE's weakness and description context.
cve · low confidence lookup
CVE-2026-31398 mapping review
Open the CVE-to-ATT&CK bridge for reviewed, inferred, or future official mappings tied to this CVE.
These fields come from the CVE record and ADP containers, not from Glexia's Take. They preserve time-varying source decisions such as CISA SSVC, KEV status, CVSS metrics, and provider references.
1CVSS vectors
3Timeline events
0ADP providers
4Source links
CVSS vector scores
1 official score
We collect every scored CVSS vector available in the official CNA and ADP containers. When more than one version is present, the table keeps the source vectors side by side instead of collapsing them into the highest score.