CVE-2026-46013
Description
In the Linux kernel, the following vulnerability has been resolved:
mm/memfd_luo: fix physical address conversion in put_folios cleanup
In memfd_luo_retrieve_folios()'s put_folios cleanup path:
1. kho_restore_folio() expects a phys_addr_t (physical address) but receives a raw PFN (pfolio->pfn). This causes kho_restore_page() to check the wrong physical address (pfn << PAGE_SHIFT instead of the actual physical address).
2. This loop lacks the !pfolio->pfn check that exists in the main retrieval loop and memfd_luo_discard_folios(), which could incorrectly process sparse file holes where pfn=0.
Fix by converting PFN to physical address with PFN_PHYS() and adding the !pfolio->pfn check, matching the pattern used elsewhere in this file.
This issue was identified by the AI review. https://sashiko.dev/#/patchset/20260323110747.193569-1-duanchenghao@kylinos.cn
Affected products
1Patches
4bd0d6bde286amm/memfd_luo: fix physical address conversion in put_folios cleanup
2 files changed · +12 −4
mm/memfd_luo.c+6 −2 modifieddiff --git a/mm/memfd_luo.c b/mm/memfd_luo.c index b8edb9f981d7ff..cfd665a5b78748 100644 --- a/mm/memfd_luo.c +++ b/mm/memfd_luo.c @@ -466,8 +466,13 @@ put_folios: */ for (long j = i + 1; j < nr_folios; j++) { const struct memfd_luo_folio_ser *pfolio = &folios_ser[j]; + phys_addr_t phys; + + if (!pfolio->pfn) + continue; - folio = kho_restore_folio(pfolio->pfn); + phys = PFN_PHYS(pfolio->pfn); + folio = kho_restore_folio(phys); if (folio) folio_put(folio); } -- cgit 1.3-korg
mm/memfd_luo.c+6 −2 modifieddiff --git a/mm/memfd_luo.c b/mm/memfd_luo.c index b8edb9f981d7ff..cfd665a5b78748 100644 --- a/mm/memfd_luo.c +++ b/mm/memfd_luo.c @@ -466,8 +466,13 @@ put_folios: */ for (long j = i + 1; j < nr_folios; j++) { const struct memfd_luo_folio_ser *pfolio = &folios_ser[j]; + phys_addr_t phys; + + if (!pfolio->pfn) + continue; - folio = kho_restore_folio(pfolio->pfn); + phys = PFN_PHYS(pfolio->pfn); + folio = kho_restore_folio(phys); if (folio) folio_put(folio); } -- cgit 1.3-korg
3538f90ab89amm/memfd_luo: fix physical address conversion in put_folios cleanup
2 files changed · +12 −4
mm/memfd_luo.c+6 −2 modifieddiff --git a/mm/memfd_luo.c b/mm/memfd_luo.c index eb9f4cc0e7ae6a..eb611527dedd64 100644 --- a/mm/memfd_luo.c +++ b/mm/memfd_luo.c @@ -484,8 +484,13 @@ put_folios: */ for (long j = i + 1; j < nr_folios; j++) { const struct memfd_luo_folio_ser *pfolio = &folios_ser[j]; + phys_addr_t phys; + + if (!pfolio->pfn) + continue; - folio = kho_restore_folio(pfolio->pfn); + phys = PFN_PHYS(pfolio->pfn); + folio = kho_restore_folio(phys); if (folio) folio_put(folio); } -- cgit 1.3-korg
mm/memfd_luo.c+6 −2 modifieddiff --git a/mm/memfd_luo.c b/mm/memfd_luo.c index eb9f4cc0e7ae6a..eb611527dedd64 100644 --- a/mm/memfd_luo.c +++ b/mm/memfd_luo.c @@ -484,8 +484,13 @@ put_folios: */ for (long j = i + 1; j < nr_folios; j++) { const struct memfd_luo_folio_ser *pfolio = &folios_ser[j]; + phys_addr_t phys; + + if (!pfolio->pfn) + continue; - folio = kho_restore_folio(pfolio->pfn); + phys = PFN_PHYS(pfolio->pfn); + folio = kho_restore_folio(phys); if (folio) folio_put(folio); } -- cgit 1.3-korg
3538f90ab89amm/memfd_luo: fix physical address conversion in put_folios cleanup
2 files changed · +12 −4
mm/memfd_luo.c+6 −2 modifieddiff --git a/mm/memfd_luo.c b/mm/memfd_luo.c index eb9f4cc0e7ae6a..eb611527dedd64 100644 --- a/mm/memfd_luo.c +++ b/mm/memfd_luo.c @@ -484,8 +484,13 @@ put_folios: */ for (long j = i + 1; j < nr_folios; j++) { const struct memfd_luo_folio_ser *pfolio = &folios_ser[j]; + phys_addr_t phys; + + if (!pfolio->pfn) + continue; - folio = kho_restore_folio(pfolio->pfn); + phys = PFN_PHYS(pfolio->pfn); + folio = kho_restore_folio(phys); if (folio) folio_put(folio); } -- cgit 1.3-korg
mm/memfd_luo.c+6 −2 modifieddiff --git a/mm/memfd_luo.c b/mm/memfd_luo.c index eb9f4cc0e7ae6a..eb611527dedd64 100644 --- a/mm/memfd_luo.c +++ b/mm/memfd_luo.c @@ -484,8 +484,13 @@ put_folios: */ for (long j = i + 1; j < nr_folios; j++) { const struct memfd_luo_folio_ser *pfolio = &folios_ser[j]; + phys_addr_t phys; + + if (!pfolio->pfn) + continue; - folio = kho_restore_folio(pfolio->pfn); + phys = PFN_PHYS(pfolio->pfn); + folio = kho_restore_folio(phys); if (folio) folio_put(folio); } -- cgit 1.3-korg
bd0d6bde286amm/memfd_luo: fix physical address conversion in put_folios cleanup
2 files changed · +12 −4
mm/memfd_luo.c+6 −2 modifieddiff --git a/mm/memfd_luo.c b/mm/memfd_luo.c index b8edb9f981d7ff..cfd665a5b78748 100644 --- a/mm/memfd_luo.c +++ b/mm/memfd_luo.c @@ -466,8 +466,13 @@ put_folios: */ for (long j = i + 1; j < nr_folios; j++) { const struct memfd_luo_folio_ser *pfolio = &folios_ser[j]; + phys_addr_t phys; + + if (!pfolio->pfn) + continue; - folio = kho_restore_folio(pfolio->pfn); + phys = PFN_PHYS(pfolio->pfn); + folio = kho_restore_folio(phys); if (folio) folio_put(folio); } -- cgit 1.3-korg
mm/memfd_luo.c+6 −2 modifieddiff --git a/mm/memfd_luo.c b/mm/memfd_luo.c index b8edb9f981d7ff..cfd665a5b78748 100644 --- a/mm/memfd_luo.c +++ b/mm/memfd_luo.c @@ -466,8 +466,13 @@ put_folios: */ for (long j = i + 1; j < nr_folios; j++) { const struct memfd_luo_folio_ser *pfolio = &folios_ser[j]; + phys_addr_t phys; + + if (!pfolio->pfn) + continue; - folio = kho_restore_folio(pfolio->pfn); + phys = PFN_PHYS(pfolio->pfn); + folio = kho_restore_folio(phys); if (folio) folio_put(folio); } -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"In memfd_luo_retrieve_folios()'s put_folios cleanup path, a raw PFN is passed to kho_restore_folio() instead of a physical address, and the loop lacks a check for sparse file holes (pfn=0)."
Attack vector
An attacker who can trigger the put_folios cleanup path in memfd_luo_retrieve_folios() — for example by causing a partial failure during folio retrieval on a memfd_luo file — will cause kho_restore_folio() to receive a raw PFN instead of a physical address. This results in kho_restore_page() checking the wrong physical address (pfn << PAGE_SHIFT rather than the actual physical address). Additionally, sparse file holes where pfn=0 are not skipped, potentially causing incorrect processing of those entries. The exact preconditions depend on the memfd_luo subsystem's error-handling paths, which the advisory does not fully detail.
Affected code
The bug is in mm/memfd_luo.c in the function memfd_luo_retrieve_folios(), specifically in the put_folios cleanup loop at around line 466 (or line 484 depending on the tree). The loop passes pfolio->pfn (a raw PFN) directly to kho_restore_folio() instead of converting it to a physical address, and lacks a !pfolio->pfn guard.
What the fix does
The patch adds a local phys_addr_t variable and converts the PFN to a physical address using PFN_PHYS(pfolio->pfn) before passing it to kho_restore_folio(). It also adds a !pfolio->pfn guard to skip sparse file holes, matching the pattern already used in the main retrieval loop and in memfd_luo_discard_folios(). These two changes ensure the correct physical address is checked and that zero-PFN entries are safely ignored.
Preconditions
- inputThe attacker must trigger the put_folios cleanup path in memfd_luo_retrieve_folios(), which occurs on partial failure during folio retrieval.
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.