{"api_version":"1","generated_at":"2026-04-20T15:19:04+00:00","cve":"CVE-2026-31398","urls":{"html":"https://cve.report/CVE-2026-31398","api":"https://cve.report/api/cve/CVE-2026-31398.json","docs":"https://cve.report/api","cve_org":"https://www.cve.org/CVERecord?id=CVE-2026-31398","nvd":"https://nvd.nist.gov/vuln/detail/CVE-2026-31398"},"summary":{"title":"mm/rmap: fix incorrect pte restoration for lazyfree folios","description":"In the Linux kernel, the following vulnerability has been resolved:\n\nmm/rmap: fix incorrect pte restoration for lazyfree folios\n\nWe batch unmap anonymous lazyfree folios by folio_unmap_pte_batch.  If the\nbatch has a mix of writable and non-writable bits, we may end up setting\nthe entire batch writable.  Fix this by respecting writable bit during\nbatching.\n\nAlthough on a successful unmap of a lazyfree folio, the soft-dirty bit is\nlost, preserve it on pte restoration by respecting the bit during\nbatching, to make the fix consistent w.r.t both writable bit and\nsoft-dirty bit.\n\nI was able to write the below reproducer and crash the kernel. \nExplanation of reproducer (set 64K mTHP to always):\n\nFault in a 64K large folio.  Split the VMA at mid-point with\nMADV_DONTFORK.  fork() - parent points to the folio with 8 writable ptes\nand 8 non-writable ptes.  Merge the VMAs with MADV_DOFORK so that\nfolio_unmap_pte_batch() can determine all the 16 ptes as a batch.  Do\nMADV_FREE on the range to mark the folio as lazyfree.  Write to the memory\nto dirty the pte, eventually rmap will dirty the folio.  Then trigger\nreclaim, we will hit the pte restoration path, and the kernel will crash\nwith the trace given below.\n\nThe BUG happens at:\n\n\tBUG_ON(atomic_inc_return(&ptc->anon_map_count) > 1 && rw);\n\nThe code path is asking for anonymous page to be mapped writable into the\npagetable.  The BUG_ON() firing implies that such a writable page has been\nmapped into the pagetables of more than one process, which breaks\nanonymous memory/CoW semantics.\n\n[   21.134473] kernel BUG at mm/page_table_check.c:118!\n[   21.134497] Internal error: Oops - BUG: 00000000f2000800 [#1]  SMP\n[   21.135917] Modules linked in:\n[   21.136085] CPU: 1 UID: 0 PID: 1735 Comm: dup-lazyfree Not tainted 7.0.0-rc1-00116-g018018a17770 #1028 PREEMPT\n[   21.136858] Hardware name: linux,dummy-virt (DT)\n[   21.137019] pstate: 21400005 (nzCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)\n[   21.137308] pc : page_table_check_set+0x28c/0x2a8\n[   21.137607] lr : page_table_check_set+0x134/0x2a8\n[   21.137885] sp : ffff80008a3b3340\n[   21.138124] x29: ffff80008a3b3340 x28: fffffdffc3d14400 x27: ffffd1a55e03d000\n[   21.138623] x26: 0040000000000040 x25: ffffd1a55f7dd000 x24: 0000000000000001\n[   21.139045] x23: 0000000000000001 x22: 0000000000000001 x21: ffffd1a55f217f30\n[   21.139629] x20: 0000000000134521 x19: 0000000000134519 x18: 005c43e000040000\n[   21.140027] x17: 0001400000000000 x16: 0001700000000000 x15: 000000000000ffff\n[   21.140578] x14: 000000000000000c x13: 005c006000000000 x12: 0000000000000020\n[   21.140828] x11: 0000000000000000 x10: 005c000000000000 x9 : ffffd1a55c079ee0\n[   21.141077] x8 : 0000000000000001 x7 : 005c03e000040000 x6 : 000000004000ffff\n[   21.141490] x5 : ffff00017fffce00 x4 : 0000000000000001 x3 : 0000000000000002\n[   21.141741] x2 : 0000000000134510 x1 : 0000000000000000 x0 : ffff0000c08228c0\n[   21.141991] Call trace:\n[   21.142093]  page_table_check_set+0x28c/0x2a8 (P)\n[   21.142265]  __page_table_check_ptes_set+0x144/0x1e8\n[   21.142441]  __set_ptes_anysz.constprop.0+0x160/0x1a8\n[   21.142766]  contpte_set_ptes+0xe8/0x140\n[   21.142907]  try_to_unmap_one+0x10c4/0x10d0\n[   21.143177]  rmap_walk_anon+0x100/0x250\n[   21.143315]  try_to_unmap+0xa0/0xc8\n[   21.143441]  shrink_folio_list+0x59c/0x18a8\n[   21.143759]  shrink_lruvec+0x664/0xbf0\n[   21.144043]  shrink_node+0x218/0x878\n[   21.144285]  __node_reclaim.constprop.0+0x98/0x338\n[   21.144763]  user_proactive_reclaim+0x2a4/0x340\n[   21.145056]  reclaim_store+0x3c/0x60\n[   21.145216]  dev_attr_store+0x20/0x40\n[   21.145585]  sysfs_kf_write+0x84/0xa8\n[   21.145835]  kernfs_fop_write_iter+0x130/0x1c8\n[   21.145994]  vfs_write+0x2b8/0x368\n[   21.146119]  ksys_write+0x70/0x110\n[   21.146240]  __arm64_sys_write+0x24/0x38\n[   21.146380]  invoke_syscall+0x50/0x120\n[   21.146513]  el0_svc_common.constprop.0+0x48/0xf8\n[   21.146679]  do_el0_svc+0x28/0x40\n[   21.146798]  el0_svc+0x34/0x110\n[   21.146926]  el0t\n---truncated---","state":"PUBLISHED","assigner":"Linux","published_at":"2026-04-03 16:16:38","updated_at":"2026-04-07 13:20:55"},"problem_types":[],"metrics":[],"references":[{"url":"https://git.kernel.org/stable/c/a0911ccdba41b0871abbf8412857bafedec3dbe1","name":"https://git.kernel.org/stable/c/a0911ccdba41b0871abbf8412857bafedec3dbe1","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://git.kernel.org/stable/c/99888a4f340ca8e839a0524556bd4db76d63f4e0","name":"https://git.kernel.org/stable/c/99888a4f340ca8e839a0524556bd4db76d63f4e0","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://git.kernel.org/stable/c/29f40594a28114b9a9bc87f6cf7bbee9609628f2","name":"https://git.kernel.org/stable/c/29f40594a28114b9a9bc87f6cf7bbee9609628f2","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-31398","name":"CVE Program record","refsource":"CVE.ORG","tags":["canonical"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-31398","name":"NVD vulnerability detail","refsource":"NVD","tags":["canonical","analysis"]}],"affected":[{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 354dffd29575cdf13154e8fb787322354aa9efc4 99888a4f340ca8e839a0524556bd4db76d63f4e0 git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 354dffd29575cdf13154e8fb787322354aa9efc4 a0911ccdba41b0871abbf8412857bafedec3dbe1 git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 354dffd29575cdf13154e8fb787322354aa9efc4 29f40594a28114b9a9bc87f6cf7bbee9609628f2 git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 6.15","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 6.15 semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 6.18.20 6.18.* semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 6.19.10 6.19.* semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 7.0-rc5 * original_commit_for_fix","platforms":[]}],"timeline":[],"solutions":[],"workarounds":[],"exploits":[],"credits":[],"nvd_cpes":[],"vendor_comments":[],"enrichments":{"kev":null,"epss":{"cve_year":"2026","cve_id":"31398","cve":"CVE-2026-31398","epss":"0.000170000","percentile":"0.039750000","score_date":"2026-04-07","updated_at":"2026-04-08 00:03:39"},"legacy_qids":[]},"source_records":{"cve_program":{"containers":{"cna":{"affected":[{"defaultStatus":"unaffected","product":"Linux","programFiles":["mm/rmap.c"],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","vendor":"Linux","versions":[{"lessThan":"99888a4f340ca8e839a0524556bd4db76d63f4e0","status":"affected","version":"354dffd29575cdf13154e8fb787322354aa9efc4","versionType":"git"},{"lessThan":"a0911ccdba41b0871abbf8412857bafedec3dbe1","status":"affected","version":"354dffd29575cdf13154e8fb787322354aa9efc4","versionType":"git"},{"lessThan":"29f40594a28114b9a9bc87f6cf7bbee9609628f2","status":"affected","version":"354dffd29575cdf13154e8fb787322354aa9efc4","versionType":"git"}]},{"defaultStatus":"affected","product":"Linux","programFiles":["mm/rmap.c"],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","vendor":"Linux","versions":[{"status":"affected","version":"6.15"},{"lessThan":"6.15","status":"unaffected","version":"0","versionType":"semver"},{"lessThanOrEqual":"6.18.*","status":"unaffected","version":"6.18.20","versionType":"semver"},{"lessThanOrEqual":"6.19.*","status":"unaffected","version":"6.19.10","versionType":"semver"},{"lessThanOrEqual":"*","status":"unaffected","version":"7.0-rc5","versionType":"original_commit_for_fix"}]}],"cpeApplicability":[{"nodes":[{"cpeMatch":[{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"6.18.20","versionStartIncluding":"6.15","vulnerable":true},{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"6.19.10","versionStartIncluding":"6.15","vulnerable":true},{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"7.0-rc5","versionStartIncluding":"6.15","vulnerable":true}],"negate":false,"operator":"OR"}]}],"descriptions":[{"lang":"en","value":"In the Linux kernel, the following vulnerability has been resolved:\n\nmm/rmap: fix incorrect pte restoration for lazyfree folios\n\nWe batch unmap anonymous lazyfree folios by folio_unmap_pte_batch.  If the\nbatch has a mix of writable and non-writable bits, we may end up setting\nthe entire batch writable.  Fix this by respecting writable bit during\nbatching.\n\nAlthough on a successful unmap of a lazyfree folio, the soft-dirty bit is\nlost, preserve it on pte restoration by respecting the bit during\nbatching, to make the fix consistent w.r.t both writable bit and\nsoft-dirty bit.\n\nI was able to write the below reproducer and crash the kernel. \nExplanation of reproducer (set 64K mTHP to always):\n\nFault in a 64K large folio.  Split the VMA at mid-point with\nMADV_DONTFORK.  fork() - parent points to the folio with 8 writable ptes\nand 8 non-writable ptes.  Merge the VMAs with MADV_DOFORK so that\nfolio_unmap_pte_batch() can determine all the 16 ptes as a batch.  Do\nMADV_FREE on the range to mark the folio as lazyfree.  Write to the memory\nto dirty the pte, eventually rmap will dirty the folio.  Then trigger\nreclaim, we will hit the pte restoration path, and the kernel will crash\nwith the trace given below.\n\nThe BUG happens at:\n\n\tBUG_ON(atomic_inc_return(&ptc->anon_map_count) > 1 && rw);\n\nThe code path is asking for anonymous page to be mapped writable into the\npagetable.  The BUG_ON() firing implies that such a writable page has been\nmapped into the pagetables of more than one process, which breaks\nanonymous memory/CoW semantics.\n\n[   21.134473] kernel BUG at mm/page_table_check.c:118!\n[   21.134497] Internal error: Oops - BUG: 00000000f2000800 [#1]  SMP\n[   21.135917] Modules linked in:\n[   21.136085] CPU: 1 UID: 0 PID: 1735 Comm: dup-lazyfree Not tainted 7.0.0-rc1-00116-g018018a17770 #1028 PREEMPT\n[   21.136858] Hardware name: linux,dummy-virt (DT)\n[   21.137019] pstate: 21400005 (nzCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)\n[   21.137308] pc : page_table_check_set+0x28c/0x2a8\n[   21.137607] lr : page_table_check_set+0x134/0x2a8\n[   21.137885] sp : ffff80008a3b3340\n[   21.138124] x29: ffff80008a3b3340 x28: fffffdffc3d14400 x27: ffffd1a55e03d000\n[   21.138623] x26: 0040000000000040 x25: ffffd1a55f7dd000 x24: 0000000000000001\n[   21.139045] x23: 0000000000000001 x22: 0000000000000001 x21: ffffd1a55f217f30\n[   21.139629] x20: 0000000000134521 x19: 0000000000134519 x18: 005c43e000040000\n[   21.140027] x17: 0001400000000000 x16: 0001700000000000 x15: 000000000000ffff\n[   21.140578] x14: 000000000000000c x13: 005c006000000000 x12: 0000000000000020\n[   21.140828] x11: 0000000000000000 x10: 005c000000000000 x9 : ffffd1a55c079ee0\n[   21.141077] x8 : 0000000000000001 x7 : 005c03e000040000 x6 : 000000004000ffff\n[   21.141490] x5 : ffff00017fffce00 x4 : 0000000000000001 x3 : 0000000000000002\n[   21.141741] x2 : 0000000000134510 x1 : 0000000000000000 x0 : ffff0000c08228c0\n[   21.141991] Call trace:\n[   21.142093]  page_table_check_set+0x28c/0x2a8 (P)\n[   21.142265]  __page_table_check_ptes_set+0x144/0x1e8\n[   21.142441]  __set_ptes_anysz.constprop.0+0x160/0x1a8\n[   21.142766]  contpte_set_ptes+0xe8/0x140\n[   21.142907]  try_to_unmap_one+0x10c4/0x10d0\n[   21.143177]  rmap_walk_anon+0x100/0x250\n[   21.143315]  try_to_unmap+0xa0/0xc8\n[   21.143441]  shrink_folio_list+0x59c/0x18a8\n[   21.143759]  shrink_lruvec+0x664/0xbf0\n[   21.144043]  shrink_node+0x218/0x878\n[   21.144285]  __node_reclaim.constprop.0+0x98/0x338\n[   21.144763]  user_proactive_reclaim+0x2a4/0x340\n[   21.145056]  reclaim_store+0x3c/0x60\n[   21.145216]  dev_attr_store+0x20/0x40\n[   21.145585]  sysfs_kf_write+0x84/0xa8\n[   21.145835]  kernfs_fop_write_iter+0x130/0x1c8\n[   21.145994]  vfs_write+0x2b8/0x368\n[   21.146119]  ksys_write+0x70/0x110\n[   21.146240]  __arm64_sys_write+0x24/0x38\n[   21.146380]  invoke_syscall+0x50/0x120\n[   21.146513]  el0_svc_common.constprop.0+0x48/0xf8\n[   21.146679]  do_el0_svc+0x28/0x40\n[   21.146798]  el0_svc+0x34/0x110\n[   21.146926]  el0t\n---truncated---"}],"providerMetadata":{"dateUpdated":"2026-04-03T15:16:02.334Z","orgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","shortName":"Linux"},"references":[{"url":"https://git.kernel.org/stable/c/99888a4f340ca8e839a0524556bd4db76d63f4e0"},{"url":"https://git.kernel.org/stable/c/a0911ccdba41b0871abbf8412857bafedec3dbe1"},{"url":"https://git.kernel.org/stable/c/29f40594a28114b9a9bc87f6cf7bbee9609628f2"}],"title":"mm/rmap: fix incorrect pte restoration for lazyfree folios","x_generator":{"engine":"bippy-1.2.0"}}},"cveMetadata":{"assignerOrgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","assignerShortName":"Linux","cveId":"CVE-2026-31398","datePublished":"2026-04-03T15:16:02.334Z","dateReserved":"2026-03-09T15:48:24.085Z","dateUpdated":"2026-04-03T15:16:02.334Z","state":"PUBLISHED"},"dataType":"CVE_RECORD","dataVersion":"5.2"},"nvd":{"publishedDate":"2026-04-03 16:16:38","lastModifiedDate":"2026-04-07 13:20:55","problem_types":[],"metrics":[],"configurations":[]},"legacy_mitre":{"record":{"CveYear":"2026","CveId":"31398","Ordinal":"1","Title":"mm/rmap: fix incorrect pte restoration for lazyfree folios","CVE":"CVE-2026-31398","Year":"2026"},"notes":[{"CveYear":"2026","CveId":"31398","Ordinal":"1","NoteData":"In the Linux kernel, the following vulnerability has been resolved:\n\nmm/rmap: fix incorrect pte restoration for lazyfree folios\n\nWe batch unmap anonymous lazyfree folios by folio_unmap_pte_batch.  If the\nbatch has a mix of writable and non-writable bits, we may end up setting\nthe entire batch writable.  Fix this by respecting writable bit during\nbatching.\n\nAlthough on a successful unmap of a lazyfree folio, the soft-dirty bit is\nlost, preserve it on pte restoration by respecting the bit during\nbatching, to make the fix consistent w.r.t both writable bit and\nsoft-dirty bit.\n\nI was able to write the below reproducer and crash the kernel. \nExplanation of reproducer (set 64K mTHP to always):\n\nFault in a 64K large folio.  Split the VMA at mid-point with\nMADV_DONTFORK.  fork() - parent points to the folio with 8 writable ptes\nand 8 non-writable ptes.  Merge the VMAs with MADV_DOFORK so that\nfolio_unmap_pte_batch() can determine all the 16 ptes as a batch.  Do\nMADV_FREE on the range to mark the folio as lazyfree.  Write to the memory\nto dirty the pte, eventually rmap will dirty the folio.  Then trigger\nreclaim, we will hit the pte restoration path, and the kernel will crash\nwith the trace given below.\n\nThe BUG happens at:\n\n\tBUG_ON(atomic_inc_return(&ptc->anon_map_count) > 1 && rw);\n\nThe code path is asking for anonymous page to be mapped writable into the\npagetable.  The BUG_ON() firing implies that such a writable page has been\nmapped into the pagetables of more than one process, which breaks\nanonymous memory/CoW semantics.\n\n[   21.134473] kernel BUG at mm/page_table_check.c:118!\n[   21.134497] Internal error: Oops - BUG: 00000000f2000800 [#1]  SMP\n[   21.135917] Modules linked in:\n[   21.136085] CPU: 1 UID: 0 PID: 1735 Comm: dup-lazyfree Not tainted 7.0.0-rc1-00116-g018018a17770 #1028 PREEMPT\n[   21.136858] Hardware name: linux,dummy-virt (DT)\n[   21.137019] pstate: 21400005 (nzCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)\n[   21.137308] pc : page_table_check_set+0x28c/0x2a8\n[   21.137607] lr : page_table_check_set+0x134/0x2a8\n[   21.137885] sp : ffff80008a3b3340\n[   21.138124] x29: ffff80008a3b3340 x28: fffffdffc3d14400 x27: ffffd1a55e03d000\n[   21.138623] x26: 0040000000000040 x25: ffffd1a55f7dd000 x24: 0000000000000001\n[   21.139045] x23: 0000000000000001 x22: 0000000000000001 x21: ffffd1a55f217f30\n[   21.139629] x20: 0000000000134521 x19: 0000000000134519 x18: 005c43e000040000\n[   21.140027] x17: 0001400000000000 x16: 0001700000000000 x15: 000000000000ffff\n[   21.140578] x14: 000000000000000c x13: 005c006000000000 x12: 0000000000000020\n[   21.140828] x11: 0000000000000000 x10: 005c000000000000 x9 : ffffd1a55c079ee0\n[   21.141077] x8 : 0000000000000001 x7 : 005c03e000040000 x6 : 000000004000ffff\n[   21.141490] x5 : ffff00017fffce00 x4 : 0000000000000001 x3 : 0000000000000002\n[   21.141741] x2 : 0000000000134510 x1 : 0000000000000000 x0 : ffff0000c08228c0\n[   21.141991] Call trace:\n[   21.142093]  page_table_check_set+0x28c/0x2a8 (P)\n[   21.142265]  __page_table_check_ptes_set+0x144/0x1e8\n[   21.142441]  __set_ptes_anysz.constprop.0+0x160/0x1a8\n[   21.142766]  contpte_set_ptes+0xe8/0x140\n[   21.142907]  try_to_unmap_one+0x10c4/0x10d0\n[   21.143177]  rmap_walk_anon+0x100/0x250\n[   21.143315]  try_to_unmap+0xa0/0xc8\n[   21.143441]  shrink_folio_list+0x59c/0x18a8\n[   21.143759]  shrink_lruvec+0x664/0xbf0\n[   21.144043]  shrink_node+0x218/0x878\n[   21.144285]  __node_reclaim.constprop.0+0x98/0x338\n[   21.144763]  user_proactive_reclaim+0x2a4/0x340\n[   21.145056]  reclaim_store+0x3c/0x60\n[   21.145216]  dev_attr_store+0x20/0x40\n[   21.145585]  sysfs_kf_write+0x84/0xa8\n[   21.145835]  kernfs_fop_write_iter+0x130/0x1c8\n[   21.145994]  vfs_write+0x2b8/0x368\n[   21.146119]  ksys_write+0x70/0x110\n[   21.146240]  __arm64_sys_write+0x24/0x38\n[   21.146380]  invoke_syscall+0x50/0x120\n[   21.146513]  el0_svc_common.constprop.0+0x48/0xf8\n[   21.146679]  do_el0_svc+0x28/0x40\n[   21.146798]  el0_svc+0x34/0x110\n[   21.146926]  el0t\n---truncated---","Type":"Description","Title":"mm/rmap: fix incorrect pte restoration for lazyfree folios"}]}}}