* Re: [PATCH] fs/proc/task_mmu: fix stale walk_end after a completed scan
2026-11-12 17:41 [PATCH] fs/proc/task_mmu: fix stale walk_end after a completed scan Matt Salsamendi
@ 2026-11-13 1:05 ` Andrew Morton
2026-11-13 4:33 ` Chris Down
1 sibling, 0 replies; 3+ messages in thread
From: Andrew Morton @ 2026-11-13 1:05 UTC (permalink / raw)
To: Matt Salsamendi
Cc: Liam R. Howlett, Lorenzo Stoakes, Vlastimil Babka, Jann Horn,
Pedro Falcato, Muhammad Usama Anjum, linux-mm, linux-kernel,
linux-fsdevel
On Thu, 12 Nov 2026 12:41:02 -0500 Matt Salsamendi <matthewsalsamendi@gmail.com> wrote:
> From: Matthew Salsamendi <matthewsalsamendi@gmail.com>
>
> After PAGEMAP_SCAN fills its internal buffer, a successful final walk can
> leave walk_end at the earlier stopping point. On x86-64, 513 disjoint
> written pages return 513 ranges with the last range beyond walk_end.
> This trips a bounds assertion in a userspace dirty-page tracker.
>
> Update walk_end on successful completion before replacing ret with the
> range count. Preserve the stopping address for partial scans.
Bug seems real, thanks.
> Fixes: 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and
> optionally clear info about PTEs")
> Cc: stable@vger.kernel.org
This should be justified, please. Hopefully full and clear
justification for backports will soon be required.
https://lore.kernel.org/20261111171055.527b4bf3da705367bc78a64c@linux-foundation.org
and followups explain this.
> Validated on x86-64 v7.4-rc2 in KVM: all 12 scan cases and all 119
> pagemap_ioctl selftests pass. The unpatched kernel reproduces the bug.
Thing is, those 119 test already pass. And the mysterious 12 scan
cases are undisclosed.
So.
Please prepare a selftests patch to catch this bug [1] then confirm
that the altered selftests fail before the proposed patch and pass after
it. Then present both as a two-patch series.
Thanks.
[1] my thing suggested the below. I haven't run it.
--- a/tools/testing/selftests/mm/pagemap_ioctl.c~selftests-mm-pagemap_ioctl-check-walk_end-after-buffer-refill
+++ a/tools/testing/selftests/mm/pagemap_ioctl.c
@@ -1476,6 +1476,51 @@ static void *thread_proc(void *mem)
if (getrusage(RUSAGE_THREAD, &r))
ksft_exit_fail_msg("getrusage\n");
+static void test_walk_end_after_refill(void)
+{
+ const int nr_ranges = 513;
+ const int nr_pages = nr_ranges * 2;
+ const int len = nr_pages * page_size;
+ struct page_region *vec;
+ char *mem;
+ long walk_end = 0;
+ long ret;
+ int i;
+ bool ok;
+
+ mem = mmap(NULL, len, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (mem == MAP_FAILED)
+ ksft_exit_fail_msg("mmap failed: %s\n", strerror(errno));
+ if (madvise(mem, len, MADV_NOHUGEPAGE))
+ ksft_exit_fail_msg("MADV_NOHUGEPAGE failed: %s\n", strerror(errno));
+ vec = calloc(nr_ranges, sizeof(*vec));
+ if (!vec)
+ ksft_exit_fail_msg("calloc failed\n");
+
+ wp_init(mem, len);
+ wp_addr_range(mem, len);
+ for (i = 0; i < nr_ranges; i++)
+ mem[2 * i * page_size] = 1;
+
+ ret = pagemap_ioc(mem, len, vec, nr_ranges, 0, 0,
+ PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end);
+ ok = ret == nr_ranges && walk_end == (long)(mem + len);
+ if (ret == nr_ranges) {
+ for (i = 0; i < nr_ranges; i++) {
+ if (vec[i].start != (uintptr_t)(mem + 2 * i * page_size) ||
+ vec[i].end != (uintptr_t)(mem + (2 * i + 1) * page_size)) {
+ ok = false;
+ break;
+ }
+ }
+ }
+ ksft_test_result(ok, "PAGEMAP_SCAN walk_end after bounce buffer refill\n");
+ wp_free(mem, len);
+ munmap(mem, len);
+ free(vec);
+}
+
faults = r.ru_minflt - curr_faults;
if (faults < access_per_thread)
ksft_exit_fail_msg("faults < access_per_thread");
@@ -1661,12 +1706,14 @@ int main(int __attribute__((unused)) arg
if (!hugetlb_setup_default(4))
ksft_print_msg("HugeTLB test will be skipped\n");
- ksft_set_plan(119);
+ ksft_set_plan(120);
pagemap_fd = open(PAGEMAP, O_RDONLY);
if (pagemap_fd < 0)
ksft_exit_fail_msg("Failed to open " PAGEMAP "\n");
+ test_walk_end_after_refill();
+
/* 1. Sanity testing */
sanity_tests_sd();
_
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH] fs/proc/task_mmu: fix stale walk_end after a completed scan
2026-11-12 17:41 [PATCH] fs/proc/task_mmu: fix stale walk_end after a completed scan Matt Salsamendi
2026-11-13 1:05 ` Andrew Morton
@ 2026-11-13 4:33 ` Chris Down
1 sibling, 0 replies; 3+ messages in thread
From: Chris Down @ 2026-11-13 4:33 UTC (permalink / raw)
To: Matt Salsamendi
Cc: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Vlastimil Babka,
Jann Horn, Pedro Falcato, Muhammad Usama Anjum, linux-mm,
linux-kernel, linux-fsdevel
Hi Matt,
This patch doesn't apply as sent, because your mail client mangled it. It
wrapped long lines, including the hunk header and the Fixes tag, and took
the leading space off the blank context lines. git am rejects it as
corrupt.
Please send the next version with git send-email or b4 send rather than
through Gmail, or set your client up as
Documentation/process/email-clients.rst describes. It's worth sending it to
yourself first and checking that git am takes it.
Matt Salsamendi writes:
>+ /* A completed walk may follow an earlier full bounce buffer. */
>+ if (!ret || !p.arg.walk_end)
>+ p.arg.walk_end = p.arg.end;
While this fixes the refill case, ret being 0 doesn't always mean the walk
got to the end of the range. pagemap_scan_hugetlb_entry() can't
write-protect only part of a huge page. So with PM_SCAN_WP_MATCHING, when
only part of a written huge page fits in what's left of max_pages, it backs
that page out. It sets walk_end to the start of the page and returns 0 on
purpose.
Before this patch, the caller got walk_end pointing at that page. With it,
walk_end comes back as end, so the caller is told the whole range was
scanned. But the page was neither returned nor write-protected. From a
quick test with a single written 2M hugetlb page, PM_SCAN_WP_MATCHING and
max_pages = 1, the ioctl returns 0 with walk_end at the start of the page
before this patch, and at the end of the range with it.
Maybe it would be simpler to clear walk_end before each walk_page_range()
call, and keep the existing check after the loop as it is? Then that check
only sees what the last walk set. In the same quick test, that fixes the
refill case too, and the hugetlb case behaves as it did before. It would be
good for the selftest Andrew asked for to cover the hugetlb case as well.
Thanks!
Chris
^ permalink raw reply [flat|nested] 3+ messages in thread