From: sashiko-bot@kernel.org
To: "Kiryl Shutsemau" <kirill@shutemov.name>
Cc: kvm@vger.kernel.org
Subject: Re: [PATCH v7 14/15] selftests/mm: add userfaultfd RWP tests
Date: Mon, 29 Jun 2026 12:46:49 +0000 [thread overview]
Message-ID: <20260629124650.2978E1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260629120749.566063-15-kirill@shutemov.name>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Medium] Dereferencing a `void *` pointer causes a compilation error in selftests.
- [Medium] Impossible PAGEMAP_SCAN condition causes `rwp-mprotect` test to vacuously pass.
- [Medium] `rwp-mprotect` test will falsely pass if `mprotect()` completely strips the uffd-wp marker.
- [Medium] `rwp-fork-pin` test will falsely pass if the uffd-wp marker is not inherited by the child.
- [Low] Uninitialized stack memory read when PAGEMAP_SCAN returns 0 regions.
--
commit 636eb23e25c814d00a52fbe1a8d08dc386ad4472
Author: Kiryl Shutsemau (Meta) <kas@kernel.org>
selftests/mm: add userfaultfd RWP tests
This commit adds several tests for UFFDIO_REGISTER_MODE_RWP and
UFFDIO_RWPROTECT to the userfaultfd selftests. It covers async and sync
modes, PAGEMAP_SCAN, mprotect round-trips, GUP, and fork interactions.
> diff --git a/tools/testing/selftests/mm/uffd-unit-tests.c b/tools/testing/selftests/mm/uffd-unit-tests.c
> index a6c14109e8188..8c49191835ea1 100644
> --- a/tools/testing/selftests/mm/uffd-unit-tests.c
> +++ b/tools/testing/selftests/mm/uffd-unit-tests.c
[ ... ]
> @@ -602,6 +617,687 @@ void uffd_minor_collapse_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *
[ ... ]
> +static void uffd_rwp_pagemap_test(uffd_global_test_opts_t *gopts,
> + uffd_test_args_t *args)
> +{
[ ... ]
> + if (ret != 1 || regions[0].start != start ||
> + regions[0].end != boundary) {
> + uffd_test_fail("hot set wrong: got %ld regions [0x%lx,0x%lx), expected 1 [0x%lx,0x%lx)",
> + ret, (unsigned long)regions[0].start,
> + (unsigned long)regions[0].end,
> + (unsigned long)start, (unsigned long)boundary);
> + return;
> + }
[Severity: Low]
Will this evaluate uninitialized array elements if PAGEMAP_SCAN returns 0?
The regions array is uninitialized, so if ret == 0, the kernel won't populate
regions[0]. The check ret != 1 is true, which leads to reading
regions[0].start and regions[0].end in the failure message, printing
undefined stack data.
> +
> + uffd_test_pass();
> +}
[ ... ]
> +static void uffd_rwp_mprotect_test(uffd_global_test_opts_t *gopts,
> + uffd_test_args_t *args)
> +{
[ ... ]
> + /* Round-trip mprotect(): PROT_NONE -> PROT_READ|PROT_WRITE */
> + if (mprotect(gopts->area_dst, nr_pages * page_size, PROT_NONE))
> + err("mprotect() PROT_NONE");
> + if (mprotect(gopts->area_dst, nr_pages * page_size,
> + PROT_READ | PROT_WRITE))
> + err("mprotect() PROT_READ|PROT_WRITE");
> +
[Severity: Medium]
Should the test verify the uffd-wp marker's presence immediately after the
mprotect round-trip, before touching the pages?
If a kernel bug causes mprotect to overwrite the PTE and clear the uffd-wp
marker completely, the subsequent memory access will succeed without a fault
and set the hardware accessed bit. A later scan for cold pages would still
find 0 pages, allowing the test to falsely pass.
> + /* Touch every page. Async RWP must auto-resolve each fault. */
> + for (p = 0; p < nr_pages; p++) {
> + volatile char *page = gopts->area_dst + p * page_size;
> + (void)*page;
> + }
> +
> + /*
> + * After touching, no page should remain RW-protected. A stuck
> + * uffd-wp bit would mean mprotect() silently dropped PROT_NONE and
> + * the access never faulted.
> + */
> + pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
> + if (pagemap_fd < 0)
> + err("open pagemap");
> +
> + memset(&pm_arg, 0, sizeof(pm_arg));
> + pm_arg.size = sizeof(pm_arg);
> + pm_arg.start = (uint64_t)gopts->area_dst;
> + pm_arg.end = (uint64_t)gopts->area_dst + nr_pages * page_size;
> + pm_arg.vec = (uint64_t)regions;
> + pm_arg.vec_len = ARRAY_SIZE(regions);
> + pm_arg.category_mask = PAGE_IS_ACCESSED;
> + pm_arg.category_inverted = PAGE_IS_ACCESSED;
[Severity: Medium]
Does this create an impossible PAGEMAP_SCAN condition?
The test sets pm_arg.category_mask to PAGE_IS_ACCESSED, which requires the
bit to be set, but also sets pm_arg.category_inverted to PAGE_IS_ACCESSED,
requiring the bit to be clear.
This impossible condition will cause PAGEMAP_SCAN to always return 0 regions.
Since the test passes if ret == 0, it will silently pass regardless of the
actual page state, missing regressions.
> + pm_arg.return_mask = PAGE_IS_ACCESSED;
> +
> + ret = ioctl(pagemap_fd, PAGEMAP_SCAN, &pm_arg);
> + close(pagemap_fd);
[ ... ]
> +static void uffd_rwp_fork_test(uffd_global_test_opts_t *gopts,
> + uffd_test_args_t *args)
> +{
> + unsigned long nr_pages = gopts->nr_pages;
> + unsigned long page_size = gopts->page_size;
> + int pagemap_fd;
> + uint64_t value;
> +
> + if (uffd_register_rwp(gopts->uffd, gopts->area_dst,
> + nr_pages * page_size))
> + err("register failed");
> +
> + /* Populate + RWP-protect */
> + *gopts->area_dst = 1;
[Severity: Medium]
Will this cause a compilation error?
The area_dst field in uffd_global_test_opts_t is a void pointer.
Dereferencing a void pointer directly violates C semantics and will cause an
invalid use of void expression error.
Should this be cast to char * or volatile char * first?
> + rwprotect_range(gopts->uffd, (uint64_t)gopts->area_dst,
> + page_size, true);
[ ... ]
> +static void uffd_rwp_fork_pin_test(uffd_global_test_opts_t *gopts,
> + uffd_test_args_t *args)
> +{
> + unsigned long page_size = gopts->page_size;
> + fork_event_args fevent_args = { .gopts = gopts, .child_uffd = -1 };
> + pin_args pin_args = {};
> + int pagemap_fd, status;
> + pthread_t fevent_thread;
> + uint64_t value;
> + pid_t child;
> +
> + if (uffd_register_rwp(gopts->uffd, gopts->area_dst, page_size))
> + err("register failed");
> +
> + /* Populate. */
> + *gopts->area_dst = 1;
[Severity: Medium]
Will this also cause a compilation error due to dereferencing a void pointer,
similar to the instance in uffd_rwp_fork_test?
> +
> + /* RO-longterm pin so fork() takes copy_present_page() for this PTE. */
> + if (pin_pages(&pin_args, gopts->area_dst, page_size)) {
[ ... ]
> + child = fork();
> + if (child < 0)
> + err("fork");
> + if (child == 0) {
> + volatile char c;
> + int cfd;
> +
> + /*
> + * Read the pinned page. Only reaches the fault path if the
> + * child PTE is protnone + uffd; async mode auto-resolves and
> + * clears the uffd bit. If copy_present_page() dropped
> + * PAGE_NONE, the read would silently succeed and the bit
> + * would still be set.
> + */
> + c = *(volatile char *)gopts->area_dst;
[Severity: Medium]
Should the child assert that the PM_UFFD_WP bit is set before performing this
read?
If a kernel bug causes fork to fail to copy the uffd-wp marker to the child,
the read here will succeed without a fault, and the bit will already be
clear. The child will exit 0, and the test will falsely pass, masking a
potential regression in copy_present_page().
> + (void)c;
> +
> + cfd = pagemap_open();
> + value = pagemap_get_entry(cfd, gopts->area_dst);
> + close(cfd);
> + _exit((value & PM_UFFD_WP) ? 1 : 0);
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260629120749.566063-1-kirill@shutemov.name?part=14
next prev parent reply other threads:[~2026-06-29 12:46 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-29 12:07 [PATCH v7 00/15] userfaultfd: working set tracking for VM guest memory Kiryl Shutsemau
2026-06-29 12:07 ` [PATCH v7 01/15] mm: decouple protnone helpers from CONFIG_NUMA_BALANCING Kiryl Shutsemau
2026-06-29 12:07 ` [PATCH v7 02/15] mm: rename uffd-wp PTE bit macros to uffd Kiryl Shutsemau
2026-06-29 12:07 ` [PATCH v7 03/15] mm: rename uffd-wp PTE accessors " Kiryl Shutsemau
2026-06-29 12:07 ` [PATCH v7 04/15] userfaultfd: test uffd VMA flags through the vma_flags_t API Kiryl Shutsemau
2026-06-29 12:07 ` [PATCH v7 05/15] mm: add VM_UFFD_RWP VMA flag Kiryl Shutsemau
2026-06-29 12:07 ` [PATCH v7 06/15] mm: add MM_CP_UFFD_RWP change_protection() flag Kiryl Shutsemau
2026-06-29 12:27 ` sashiko-bot
2026-06-29 12:07 ` [PATCH v7 07/15] mm: preserve RWP marker across PTE rewrites Kiryl Shutsemau
2026-06-29 12:33 ` sashiko-bot
2026-06-29 16:02 ` Kiryl Shutsemau
2026-06-29 12:07 ` [PATCH v7 08/15] mm: handle VM_UFFD_RWP in khugepaged, rmap, and GUP Kiryl Shutsemau
2026-06-29 12:50 ` sashiko-bot
2026-06-29 12:07 ` [PATCH v7 09/15] userfaultfd: add UFFDIO_REGISTER_MODE_RWP and UFFDIO_RWPROTECT plumbing Kiryl Shutsemau
2026-06-29 12:40 ` sashiko-bot
2026-06-29 12:07 ` [PATCH v7 10/15] mm/userfaultfd: add RWP fault delivery and expose UFFDIO_REGISTER_MODE_RWP Kiryl Shutsemau
2026-06-29 12:42 ` sashiko-bot
2026-06-29 12:07 ` [PATCH v7 11/15] mm/pagemap: add PAGE_IS_ACCESSED for RWP tracking Kiryl Shutsemau
2026-06-29 12:07 ` [PATCH v7 12/15] userfaultfd: add UFFD_FEATURE_RWP_ASYNC for async fault resolution Kiryl Shutsemau
2026-06-29 12:07 ` [PATCH v7 13/15] userfaultfd: add UFFDIO_SET_MODE for runtime sync/async toggle Kiryl Shutsemau
2026-06-29 12:07 ` [PATCH v7 14/15] selftests/mm: add userfaultfd RWP tests Kiryl Shutsemau
2026-06-29 12:46 ` sashiko-bot [this message]
2026-06-29 12:07 ` [PATCH v7 15/15] Documentation/userfaultfd: document RWP working set tracking Kiryl Shutsemau
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260629124650.2978E1F000E9@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=kirill@shutemov.name \
--cc=kvm@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox