FAILED: patch "[PATCH] mm: khugepaged: fix swap entry value to folio_pfn()" failed to apply to 6.1-stable tree
Subsystems (4)
[email protected]
maintainerThe patch below does not apply to the 6.1-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.1.y
git checkout FETCH_HEAD
git cherry-pick -x 942c8ec27aa4ffc587511650718349964e1696e6
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026110435-preview-regally-12b1@gregkh' --subject-prefix 'PATCH 6.1.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 942c8ec27aa4ffc587511650718349964e1696e6 Mon Sep 17 00:00:00 2001
From: Vernon Yang <[email protected]>
Date: Wed, 9 Sep 2026 10:58:01 +0800
Subject: [PATCH] mm: khugepaged: fix swap entry value to folio_pfn()
Patch series "mm: khugepaged: fix tracepoint UAF", v5.
The khugepaged tracepoints take a folio pointer and call folio_pfn(), but by then the folio may no longer be valid: freed after folio_put(), folio_unlock() or pte_unmap_unlock(), or not a folio at all but an xarray-encoded swap entry. On classic SPARSEMEM, dereferencing it oopses khugepaged as soon as the trace event is enabled; on other memory models it merely prints a bogus pfn.
Pass the pfn to the tracepoints directly, captured while the folio is still pinned, closing the use-after-free windows in mm_khugepaged_scan_file(), mm_khugepaged_scan_pmd() and mm_khugepaged_collapse_file().
This patch (of 3):
When the swap entries found exceed max_ptes_swap, the loop is left via break with folio still holding the xarray value that encodes the swap entry, not valid folio pointer.
That value is passed to trace_mm_khugepaged_scan_file(), which feeds it to folio_pfn(). On FLATMEM and SPARSEMEM_VMEMMAP, the page_to_pfn() is plain pointer arithmetic, so the trace event merely prints bogus scan_pfn. On classic SPARSEMEM, the page_to_pfn() reads page->flags, dereferencing the tiny encoded integer and oopsing khugepaged whenever the trace event is enabled.
So when folio is the swap entry value, simply set pfn to -1, just like exhausted scan naturally.
And the folio_put() has maybe dropped the last reference of folio. The trace_mm_khugepaged_scan_file() is left with a dangling folio pointer. so using the folio_pfn() before dropping the reference, closing use-after-free window.
include/trace/events/huge_memory.h +3 -3
@@ -178,10 +178,10 @@ TRACE_EVENT(mm_collapse_huge_page_swapin, TRACE_EVENT(mm_khugepaged_scan_file, - TP_PROTO(struct mm_struct *mm, struct folio *folio, struct file *file, + TP_PROTO(struct mm_struct *mm, unsigned long pfn, struct file *file, int present, int swap, int result), - TP_ARGS(mm, folio, file, present, swap, result), + TP_ARGS(mm, pfn, file, present, swap, result), TP_STRUCT__entry( __field(struct mm_struct *, mm)
@@ -194,7 +194,7 @@ TRACE_EVENT(mm_khugepaged_scan_file,TP_fast_assign ( __entry->mm = mm; - __entry->pfn = folio ?folio_pfn (folio) :-1 ; + __entry->pfn = pfn;__assign_str (filename); __entry->present = present; __entry->swap = swap;
mm/khugepaged.c +6 -1
@@ -2683,6 +2683,7 @@ static enum scan_result collapse_scan_file(struct mm_struct *mm,int present, swap;int node =NUMA_NO_NODE ;enum scan_result result =SCAN_SUCCEED ; +unsigned long failed_pfn =-1 ; present =0 ; swap =0 ;
@@ -2715,6 +2716,7 @@ static enum scan_result collapse_scan_file(struct mm_struct *mm,if (is_pmd_order (folio_order (folio))) { result =SCAN_PTE_MAPPED_HUGEPAGE ; + failed_pfn =folio_pfn (folio);/* * PMD-sized THP implies that we can only try * retracting the PTE table.
@@ -2726,6 +2728,7 @@ static enum scan_result collapse_scan_file(struct mm_struct *mm, node =folio_nid (folio);if (collapse_scan_abort (node, cc)) { result =SCAN_SCAN_ABORT ; + failed_pfn =folio_pfn (folio);folio_put (folio);break ; }
@@ -2733,12 +2736,14 @@ static enum scan_result collapse_scan_file(struct mm_struct *mm,if (!folio_test_lru (folio)) { result =SCAN_PAGE_LRU ; + failed_pfn =folio_pfn (folio);folio_put (folio);break ; }if (folio_expected_ref_count (folio) +1 !=folio_ref_count (folio)) { result =SCAN_PAGE_COUNT ; + failed_pfn =folio_pfn (folio);folio_put (folio);break ; }
@@ -2773,7 +2778,7 @@ static enum scan_result collapse_scan_file(struct mm_struct *mm, } } -trace_mm_khugepaged_scan_file (mm, folio, file, present, swap, result); +trace_mm_khugepaged_scan_file (mm, failed_pfn, file, present, swap, result);return result; }
Sasha Levin
maintainerPATCH 1/1 mm: khugepaged: fix swap entry value to folio_pfn()
From: Vernon Yang <[email protected]>
[ Upstream commit 942c8ec27aa4ffc587511650718349964e1696e6 ]
Patch series "mm: khugepaged: fix tracepoint UAF", v5.
The khugepaged tracepoints take a folio pointer and call folio_pfn(), but by then the folio may no longer be valid: freed after folio_put(), folio_unlock() or pte_unmap_unlock(), or not a folio at all but an xarray-encoded swap entry. On classic SPARSEMEM, dereferencing it oopses khugepaged as soon as the trace event is enabled; on other memory models it merely prints a bogus pfn.
Pass the pfn to the tracepoints directly, captured while the folio is still pinned, closing the use-after-free windows in mm_khugepaged_scan_file(), mm_khugepaged_scan_pmd() and mm_khugepaged_collapse_file().
This patch (of 3):
When the swap entries found exceed max_ptes_swap, the loop is left via break with folio still holding the xarray value that encodes the swap entry, not valid folio pointer.
That value is passed to trace_mm_khugepaged_scan_file(), which feeds it to folio_pfn(). On FLATMEM and SPARSEMEM_VMEMMAP, the page_to_pfn() is plain pointer arithmetic, so the trace event merely prints bogus scan_pfn. On classic SPARSEMEM, the page_to_pfn() reads page->flags, dereferencing the tiny encoded integer and oopsing khugepaged whenever the trace event is enabled.
So when folio is the swap entry value, simply set pfn to -1, just like exhausted scan naturally.
And the folio_put() has maybe dropped the last reference of folio. The trace_mm_khugepaged_scan_file() is left with a dangling folio pointer. so using the folio_pfn() before dropping the reference, closing use-after-free window.
--- include/trace/events/huge_memory.h | 6 +++--- mm/khugepaged.c | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-)
include/trace/events/huge_memory.h +3 -3
@@ -172,10 +172,10 @@ TRACE_EVENT(mm_collapse_huge_page_swapin, TRACE_EVENT(mm_khugepaged_scan_file, - TP_PROTO(struct mm_struct *mm, struct page *page, struct file *file, + TP_PROTO(struct mm_struct *mm, unsigned long pfn, struct file *file, int present, int swap, int result), - TP_ARGS(mm, page, file, present, swap, result), + TP_ARGS(mm, pfn, file, present, swap, result), TP_STRUCT__entry( __field(struct mm_struct *, mm)
@@ -188,7 +188,7 @@ TRACE_EVENT(mm_khugepaged_scan_file,TP_fast_assign ( __entry->mm = mm; - __entry->pfn = page ?page_to_pfn (page) :-1 ; + __entry->pfn = pfn;__assign_str (filename, file->f_path.dentry->d_iname); __entry->present = present; __entry->swap = swap;
mm/khugepaged.c +6 -1
@@ -2167,6 +2167,7 @@ static int hpage_collapse_scan_file(struct mm_struct *mm, unsigned long addr,int present, swap;int node =NUMA_NO_NODE ;int result =SCAN_SUCCEED ; +unsigned long failed_pfn =-1 ; present =0 ; swap =0 ;
@@ -2200,6 +2201,7 @@ static int hpage_collapse_scan_file(struct mm_struct *mm, unsigned long addr,/* Maybe PMD-mapped */ ? SCAN_PTE_MAPPED_HUGEPAGE :SCAN_PAGE_COMPOUND ; + failed_pfn =page_to_pfn (page);/* * For SCAN_PTE_MAPPED_HUGEPAGE, further processing * by the caller won't touch the page cache, and so
@@ -2212,18 +2214,21 @@ static int hpage_collapse_scan_file(struct mm_struct *mm, unsigned long addr, node =page_to_nid (page);if (hpage_collapse_scan_abort (node, cc)) { result =SCAN_SCAN_ABORT ; + failed_pfn =page_to_pfn (page);break ; } cc->node_load[node]++;if (!PageLRU (page)) { result =SCAN_PAGE_LRU ; + failed_pfn =page_to_pfn (page);break ; }if (page_count (page) !=1 +page_mapcount (page) +page_has_private (page)) { result =SCAN_PAGE_COUNT ; + failed_pfn =page_to_pfn (page);break ; }
@@ -2252,7 +2257,7 @@ static int hpage_collapse_scan_file(struct mm_struct *mm, unsigned long addr, } } -trace_mm_khugepaged_scan_file (mm, page, file, present, swap, result); +trace_mm_khugepaged_scan_file (mm, failed_pfn, file, present, swap, result);return result; }#else
Signature · 1 line
2.53.0