From: Rik van Riel <riel@surriel.com>
To: linux-kernel@vger.kernel.org, Andrew Morton <akpm@linux-foundation.org>
Cc: kernel-team@meta.com, Rik van Riel <riel@surriel.com>,
David Hildenbrand <david@kernel.org>,
Lorenzo Stoakes <ljs@kernel.org>,
"Liam R. Howlett" <liam@infradead.org>,
Vlastimil Babka <vbabka@kernel.org>,
Mike Rapoport <rppt@kernel.org>,
Suren Baghdasaryan <surenb@google.com>,
Michal Hocko <mhocko@suse.com>,
linux-mm@kvack.org, Shuah Khan <shuah@kernel.org>,
linux-kselftest@vger.kernel.org
Subject: [PATCH RFC v3 6/6] selftests/mm: cover /proc/pid/mem access to VM_PFNMAP memory
Date: Fri, 17 Jul 2026 13:00:36 -0400 [thread overview]
Message-ID: <20260717170036.743149-7-riel@surriel.com> (raw)
In-Reply-To: <20260717170036.743149-1-riel@surriel.com>
Reading a VM_PFNMAP mapping through /proc/pid/mem exercises
__access_remote_vm() two ways: a COWed page has a struct page and is
returned by get_user_page_vma(), while a raw PFN has none and is reached
through vma->vm_ops->access().
Add two tests to pfnmap.c, both reading VM_PFNMAP memory through
/proc/self/mem.
procmem_cow_read maps the file MAP_PRIVATE and writable, writes to COW a
page, then reads it back. Without the struct-page path in
get_user_page_vma() this read is short: the access falls back to
generic_access_phys(), which ioremaps the PFN, and ioremap of a COWed RAM
page is rejected.
procmem_pfn_read reads a raw PFN back through ->access(). ioremap rejects
RAM, so it runs only for genuine device memory and is skipped for the
default /dev/mem System RAM target.
Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Rik van Riel <riel@surriel.com>
---
tools/testing/selftests/mm/pfnmap.c | 66 +++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/tools/testing/selftests/mm/pfnmap.c b/tools/testing/selftests/mm/pfnmap.c
index 4f550822385a..6ff5d1029517 100644
--- a/tools/testing/selftests/mm/pfnmap.c
+++ b/tools/testing/selftests/mm/pfnmap.c
@@ -31,6 +31,7 @@ static sigjmp_buf sigjmp_buf_env;
static char *file = "/dev/mem";
static off_t file_offset;
static int fd;
+static int target_is_ram;
static void signal_handler(int sig)
{
@@ -113,6 +114,7 @@ static void pfnmap_init(void)
if (err)
ksft_exit_skip("Cannot find ram target in '/proc/iomem': %s\n",
strerror(-err));
+ target_is_ram = 1;
} else {
file_offset = 0;
}
@@ -271,6 +273,70 @@ TEST_F(pfnmap, fork)
ASSERT_EQ(ret, 0);
}
+TEST_F(pfnmap, procmem_cow_read)
+{
+ char *priv, *buf;
+ ssize_t rc;
+ int mem_fd;
+
+ /*
+ * A COWed page in a VM_PFNMAP mapping has a struct page, so reading it
+ * through /proc/self/mem -- __access_remote_vm() -> get_user_page_vma()
+ * -- returns it directly, instead of routing to vma->vm_ops->access(),
+ * which ioremaps the PFN and cannot reach a COWed RAM page.
+ *
+ * Map the file MAP_PRIVATE and writable, write to COW a page into anon
+ * memory, then read the page back through /proc/self/mem.
+ */
+ self->size2 = self->pagesize;
+ self->addr2 = mmap(NULL, self->size2, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, fd, file_offset);
+ if (self->addr2 == MAP_FAILED)
+ SKIP(return, "Cannot create a writable private pfnmap mapping");
+ priv = self->addr2;
+
+ /* COW the page and stamp known bytes into the anon copy. */
+ priv[0] = 0x42;
+ priv[self->pagesize - 1] = 0x24;
+
+ buf = malloc(self->pagesize);
+ ASSERT_NE(buf, NULL);
+
+ mem_fd = open("/proc/self/mem", O_RDONLY);
+ ASSERT_GE(mem_fd, 0);
+ rc = pread(mem_fd, buf, self->pagesize, (off_t)(uintptr_t)priv);
+ close(mem_fd);
+
+ ASSERT_EQ(rc, (ssize_t)self->pagesize);
+ EXPECT_EQ(buf[0], 0x42);
+ EXPECT_EQ(buf[self->pagesize - 1], 0x24);
+
+ free(buf);
+}
+
+TEST_F(pfnmap, procmem_pfn_read)
+{
+ char buf[64];
+ ssize_t rc;
+ int mem_fd;
+
+ /*
+ * A raw PFN of a VM_IO/VM_PFNMAP mapping has no struct page, so
+ * __access_remote_vm() reaches it through vma->vm_ops->access()
+ * (generic_access_phys()). That ioremaps the PFN, which is rejected for
+ * RAM, so this only applies to genuine device memory.
+ */
+ if (target_is_ram)
+ SKIP(return, "Target is System RAM; ->access() cannot ioremap RAM");
+
+ mem_fd = open("/proc/self/mem", O_RDONLY);
+ ASSERT_GE(mem_fd, 0);
+ rc = pread(mem_fd, buf, sizeof(buf), (off_t)(uintptr_t)self->addr1);
+ close(mem_fd);
+
+ ASSERT_EQ(rc, (ssize_t)sizeof(buf));
+}
+
int main(int argc, char **argv)
{
for (int i = 1; i < argc; i++) {
--
2.53.0-Meta
next prev parent reply other threads:[~2026-07-17 17:00 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-17 17:00 [PATCH v3 0/6] mm: access remote process memory under the per-VMA lock Rik van Riel
2026-07-17 17:00 ` [PATCH RFC v3 1/6] x86/mm: add untagged_addr_remote_unlocked() Rik van Riel
2026-07-20 11:12 ` Usama Arif
2026-07-17 17:00 ` [PATCH RFC v3 2/6] riscv/mm: " Rik van Riel
2026-07-17 17:00 ` Rik van Riel
2026-07-20 11:57 ` Usama Arif
2026-07-20 11:57 ` Usama Arif
2026-07-20 15:08 ` Rik van Riel
2026-07-20 15:08 ` Rik van Riel
2026-07-20 16:46 ` Usama Arif
2026-07-20 16:46 ` Usama Arif
2026-07-20 17:34 ` Rik van Riel
2026-07-20 17:34 ` Rik van Riel
2026-07-20 18:46 ` Usama Arif
2026-07-20 18:46 ` Usama Arif
2026-07-20 19:21 ` Rik van Riel
2026-07-20 19:21 ` Rik van Riel
2026-07-20 19:39 ` Usama Arif
2026-07-20 19:39 ` Usama Arif
2026-07-17 17:00 ` [PATCH RFC v3 3/6] mm: rename get_user_page_vma_remote() to get_user_page_lookup_vma() Rik van Riel
2026-07-20 12:00 ` Usama Arif
2026-07-17 17:00 ` [PATCH RFC v3 4/6] mm/gup: add get_user_page_vma() to fault in a page under a held lock Rik van Riel
2026-07-20 12:35 ` Usama Arif
2026-07-20 15:24 ` Rik van Riel
2026-07-17 17:00 ` [PATCH RFC v3 5/6] mm: use per-VMA lock in __access_remote_vm() for single-VMA accesses Rik van Riel
2026-07-17 17:00 ` Rik van Riel [this message]
2026-07-21 18:12 ` [PATCH v3 0/6] mm: access remote process memory under the per-VMA lock David Hildenbrand (Arm)
2026-07-22 17:33 ` Rik van Riel
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=20260717170036.743149-7-riel@surriel.com \
--to=riel@surriel.com \
--cc=akpm@linux-foundation.org \
--cc=david@kernel.org \
--cc=kernel-team@meta.com \
--cc=liam@infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@suse.com \
--cc=rppt@kernel.org \
--cc=shuah@kernel.org \
--cc=surenb@google.com \
--cc=vbabka@kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.