From: Rik van Riel <riel@surriel.com>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: linux-kernel@vger.kernel.org, linux-mm@kvack.org,
kernel-team@meta.com, Dave Hansen <dave.hansen@linux.intel.com>,
Peter Zijlstra <peterz@infradead.org>,
Suren Baghdasaryan <surenb@google.com>,
Lorenzo Stoakes <ljs@kernel.org>,
Vlastimil Babka <vbabka@kernel.org>,
David Hildenbrand <david@kernel.org>,
"Liam R. Howlett" <liam@infradead.org>,
Mike Rapoport <rppt@kernel.org>, Michal Hocko <mhocko@suse.com>,
Jason Gunthorpe <jgg@ziepe.ca>,
John Hubbard <jhubbard@nvidia.com>, Peter Xu <peterx@redhat.com>,
Matthew Wilcox <willy@infradead.org>,
Usama Arif <usamaarif642@gmail.com>,
Rik van Riel <riel@surriel.com>,
bpf@vger.kernel.org
Subject: [PATCH RFC v4 07/12] mm: read remote strings under the per-VMA lock
Date: Fri, 24 Jul 2026 18:29:29 -0400 [thread overview]
Message-ID: <20260724222934.1463812-8-riel@surriel.com> (raw)
In-Reply-To: <20260724222934.1463812-1-riel@surriel.com>
__copy_remote_vm_str() reads another process's memory under the mmap read
lock for the whole copy, and looks the VMA up again for every page through
get_user_page_lookup_vma().
This is the mmap-lock-only form that __access_remote_vm() used before it
moved to the per-VMA lock, and it shares the same contention: an mmap() or
munmap() stalls a bpf_copy_from_user_task_str() reader even though the
string is resident.
Read the string through remote_vm_walk(), the walk __access_remote_vm()
already uses, with a copy_vm_str() action that strscpy()s each page and
stops at the NUL. Bounding the copy by its maximum length keeps the per-VMA
path inside a single VMA, so no VMA is looked up again while that lock is
held.
A copy the per-VMA lock cannot finish falls back to the mmap lock: a fault
that dropped the lock, or a string running past the VMA. Memory with no
struct page (VM_IO/VM_PFNMAP) and stack expansion stay on the mmap lock
path and still return -EFAULT, as before; ->access() support is left for
later.
Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Rik van Riel <riel@surriel.com>
---
mm/memory.c | 111 ++++++++++++++++++++++------------------------------
1 file changed, 47 insertions(+), 64 deletions(-)
diff --git a/mm/memory.c b/mm/memory.c
index 273dfe12bc6d..aaf620017f34 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -7296,84 +7296,67 @@ EXPORT_SYMBOL_GPL(access_process_vm);
#ifdef CONFIG_BPF_SYSCALL
/*
- * Copy a string from another process's address space as given in mm.
- * If there is any error return -EFAULT.
+ * Copy a NUL-terminated string from @addr into *@buf, up to @len bytes,
+ * stopping at the NUL. strscpy() always NUL terminates, so recopy the last
+ * byte of a page when more pages follow. A string is never read from
+ * struct-page-less VM_IO / VM_PFNMAP memory.
*/
-static int __copy_remote_vm_str(struct mm_struct *mm, unsigned long addr,
- void *buf, int len, unsigned int gup_flags)
+static int copy_vm_str(struct vm_area_struct *vma, struct page *page,
+ unsigned long addr, void **buf, int len, int write)
{
- void *old_buf = buf;
- int err = 0;
-
- *(char *)buf = '\0';
+ struct folio *folio;
+ int bytes, offset, retval;
+ void *maddr;
- if (mmap_read_lock_killable(mm))
+ if (!page)
return -EFAULT;
- addr = untagged_addr_remote(mm, addr);
+ bytes = len;
+ offset = addr & (PAGE_SIZE - 1);
+ if (bytes > PAGE_SIZE - offset)
+ bytes = PAGE_SIZE - offset;
- /* Avoid triggering the temporary warning in __get_user_pages */
- if (!vma_lookup(mm, addr)) {
- err = -EFAULT;
- goto out;
+ folio = page_folio(page);
+ maddr = kmap_local_folio(folio, folio_page_idx(folio, page) * PAGE_SIZE);
+ retval = strscpy(*buf, maddr + offset, bytes);
+ if (retval >= 0) {
+ /* Found the end of the string. */
+ *buf += retval;
+ folio_release_kmap(folio, maddr);
+ return 0;
}
- while (len) {
- int bytes, offset, retval;
- void *maddr;
- struct folio *folio;
- struct page *page;
- struct vm_area_struct *vma = NULL;
+ *buf += bytes - 1;
+ if (bytes != len) {
+ copy_from_user_page(vma, page, addr + bytes - 1, *buf,
+ maddr + (PAGE_SIZE - 1), 1);
+ *buf += 1;
+ }
+ folio_release_kmap(folio, maddr);
- page = get_user_page_lookup_vma(mm, addr, gup_flags, &vma);
- if (IS_ERR(page)) {
- /*
- * Treat as a total failure for now until we decide how
- * to handle the CONFIG_HAVE_IOREMAP_PROT case and
- * stack expansion.
- */
- *(char *)buf = '\0';
- err = -EFAULT;
- goto out;
- }
+ return bytes;
+}
- folio = page_folio(page);
- bytes = len;
- offset = addr & (PAGE_SIZE - 1);
- if (bytes > PAGE_SIZE - offset)
- bytes = PAGE_SIZE - offset;
-
- maddr = kmap_local_folio(folio, folio_page_idx(folio, page) * PAGE_SIZE);
- retval = strscpy(buf, maddr + offset, bytes);
- if (retval >= 0) {
- /* Found the end of the string */
- buf += retval;
- folio_release_kmap(folio, maddr);
- break;
- }
+/*
+ * Copy a string from another process's address space as given in mm.
+ * If there is any error return -EFAULT.
+ */
+static int __copy_remote_vm_str(struct mm_struct *mm, unsigned long addr,
+ void *buf, int len, unsigned int gup_flags)
+{
+ int bytes, err;
- buf += bytes - 1;
- /*
- * Because strscpy always NUL terminates we need to
- * copy the last byte in the page if we are going to
- * load more pages
- */
- if (bytes != len) {
- addr += bytes - 1;
- copy_from_user_page(vma, page, addr, buf, maddr + (PAGE_SIZE - 1), 1);
- buf += 1;
- addr += 1;
- }
- len -= bytes;
+ *(char *)buf = '\0';
- folio_release_kmap(folio, maddr);
+ bytes = remote_vm_walk(mm, addr, buf, len, gup_flags, false,
+ copy_vm_str, &err);
+ if (err) {
+ /* The contract guarantees a terminated buffer even on error. */
+ ((char *)buf)[bytes] = '\0';
+ return err;
}
-out:
- mmap_read_unlock(mm);
- if (err)
- return err;
- return buf - old_buf;
+ return bytes;
}
/**
--
2.53.0-Meta
next prev parent reply other threads:[~2026-07-24 22:30 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-24 22:29 [PATCH RFC v4 0/12] mm: use per-VMA lock in __access_remote_vm for improved monitoring reliability Rik van Riel
2026-07-24 22:29 ` [PATCH RFC v4 01/12] x86/mm: add untagged_addr_remote_unlocked() Rik van Riel
2026-07-24 22:29 ` [PATCH RFC v4 02/12] riscv/mm: " Rik van Riel
2026-07-24 22:29 ` Rik van Riel
2026-07-24 22:29 ` [PATCH RFC v4 03/12] mm: rename get_user_page_vma_remote() to get_user_page_lookup_vma() Rik van Riel
2026-07-24 22:29 ` [PATCH RFC v4 04/12] mm/gup: let check_vma_flags() ignore selected VMA flags Rik van Riel
2026-07-24 22:29 ` [PATCH RFC v4 05/12] mm/gup: add get_user_page_vma() to fault in a page under a held lock Rik van Riel
2026-07-24 22:29 ` [PATCH RFC v4 06/12] mm: use per-VMA lock in __access_remote_vm() for single-VMA accesses Rik van Riel
2026-07-24 22:29 ` Rik van Riel [this message]
2026-07-24 22:29 ` [PATCH RFC v4 08/12] selftests/mm: cover /proc/pid/mem access to VM_PFNMAP memory Rik van Riel
2026-07-24 22:29 ` [PATCH RFC v4 09/12] mm/gup: build get_user_page_lookup_vma() on get_user_page_vma() Rik van Riel
2026-07-24 22:29 ` [PATCH RFC v4 10/12] mm/gup: pass an end address to follow_page_mask() and return a page count Rik van Riel
2026-07-24 22:29 ` [PATCH RFC v4 11/12] mm/gup: batch contiguous PTE-mapped large folios in follow_page_mask() Rik van Riel
2026-07-24 22:29 ` [PATCH RFC v4 12/12] selftests/mm: add a slow-GUP content and COW test for mTHP Rik van Riel
2026-07-26 11:56 ` Mike Rapoport
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=20260724222934.1463812-8-riel@surriel.com \
--to=riel@surriel.com \
--cc=akpm@linux-foundation.org \
--cc=bpf@vger.kernel.org \
--cc=dave.hansen@linux.intel.com \
--cc=david@kernel.org \
--cc=jgg@ziepe.ca \
--cc=jhubbard@nvidia.com \
--cc=kernel-team@meta.com \
--cc=liam@infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@suse.com \
--cc=peterx@redhat.com \
--cc=peterz@infradead.org \
--cc=rppt@kernel.org \
--cc=surenb@google.com \
--cc=usamaarif642@gmail.com \
--cc=vbabka@kernel.org \
--cc=willy@infradead.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.