From: Jordan Rome <linux@jordanrome.com>
To: bpf@vger.kernel.org
Cc: linux-mm@kvack.org, Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
Kernel Team <kernel-team@fb.com>,
Andrew Morton <akpm@linux-foundation.org>,
Shakeel Butt <shakeel.butt@linux.dev>
Subject: [bpf-next v2 1/2] bpf: Add bpf_copy_from_user_task_str kfunc
Date: Mon, 6 Jan 2025 18:06:31 -0800 [thread overview]
Message-ID: <20250107020632.170883-1-linux@jordanrome.com> (raw)
This new kfunc will be able to copy a string
from another process's/task's address space.
This is similar to `bpf_copy_from_user_str`
but accepts a `struct task_struct*` argument.
This required adding an additional function
in memory.c, namely `copy_str_from_process_vm`,
which works similar to `access_process_vm`
but utilizes the `strncpy_from_user` helper
and only supports reading/copying and not writing.
Signed-off-by: Jordan Rome <linux@jordanrome.com>
---
include/linux/mm.h | 3 ++
kernel/bpf/helpers.c | 46 ++++++++++++++++++++
mm/memory.c | 101 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 150 insertions(+)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index c39c4945946c..52b304b20630 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2484,6 +2484,9 @@ extern int access_process_vm(struct task_struct *tsk, unsigned long addr,
extern int access_remote_vm(struct mm_struct *mm, unsigned long addr,
void *buf, int len, unsigned int gup_flags);
+extern int copy_str_from_process_vm(struct task_struct *tsk, unsigned long addr,
+ void *buf, int len, unsigned int gup_flags);
+
long get_user_pages_remote(struct mm_struct *mm,
unsigned long start, unsigned long nr_pages,
unsigned int gup_flags, struct page **pages,
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index cd5f9884d85b..45d41b7a9906 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -3072,6 +3072,51 @@ __bpf_kfunc void bpf_local_irq_restore(unsigned long *flags__irq_flag)
local_irq_restore(*flags__irq_flag);
}
+/**
+ * bpf_copy_from_user_task_str() - Copy a string from an task's address space
+ * @dst: Destination address, in kernel space. This buffer must be
+ * at least @dst__sz bytes long.
+ * @dst__sz: Maximum number of bytes to copy, includes the trailing NUL.
+ * @unsafe_ptr__ign: Source address in the task's address space.
+ * @tsk: The task whose address space will be used
+ * @flags: The only supported flag is BPF_F_PAD_ZEROS
+ *
+ * Copies a NULL-terminated string from a task's address space to BPF space.
+ * If user string is too long this will still ensure zero termination in the
+ * dst buffer unless buffer size is 0.
+ *
+ * If BPF_F_PAD_ZEROS flag is set, memset the tail of @dst to 0 on success and
+ * memset all of @dst on failure.
+ */
+__bpf_kfunc int bpf_copy_from_user_task_str(void *dst, u32 dst__sz, const void __user *unsafe_ptr__ign, struct task_struct *tsk, u64 flags)
+{
+ int count = dst__sz - 1;
+ int ret = 0;
+
+ if (unlikely(flags & ~BPF_F_PAD_ZEROS))
+ return -EINVAL;
+
+ if (unlikely(!dst__sz))
+ return 0;
+
+ ret = copy_str_from_process_vm(tsk, (unsigned long)unsafe_ptr__ign, dst, count, 0);
+
+ if (ret <= 0) {
+ if (flags & BPF_F_PAD_ZEROS)
+ memset((char *)dst, 0, dst__sz);
+ return ret;
+ }
+
+ if (ret < count) {
+ if (flags & BPF_F_PAD_ZEROS)
+ memset((char *)dst + ret, 0, dst__sz - ret);
+ } else {
+ ((char *)dst)[count] = '\0';
+ }
+
+ return ret + 1;
+}
+
__bpf_kfunc_end_defs();
BTF_KFUNCS_START(generic_btf_ids)
@@ -3164,6 +3209,7 @@ BTF_ID_FLAGS(func, bpf_iter_bits_new, KF_ITER_NEW)
BTF_ID_FLAGS(func, bpf_iter_bits_next, KF_ITER_NEXT | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_iter_bits_destroy, KF_ITER_DESTROY)
BTF_ID_FLAGS(func, bpf_copy_from_user_str, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_copy_from_user_task_str, KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_get_kmem_cache)
BTF_ID_FLAGS(func, bpf_iter_kmem_cache_new, KF_ITER_NEW | KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_iter_kmem_cache_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE)
diff --git a/mm/memory.c b/mm/memory.c
index 75c2dfd04f72..514490bd7d6d 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -6673,6 +6673,75 @@ static int __access_remote_vm(struct mm_struct *mm, unsigned long addr,
return buf - old_buf;
}
+/*
+ * Copy a string from another process's address space as given in mm.
+ * Don't return partial results. If there is any error return -EFAULT.
+ */
+static int __copy_str_from_remote_vm(struct mm_struct *mm, unsigned long addr,
+ void *buf, int len, unsigned int gup_flags)
+{
+ void *old_buf = buf;
+ int err = 0;
+
+ if (mmap_read_lock_killable(mm))
+ return -EFAULT;
+
+ /* Untag the address before looking up the VMA */
+ addr = untagged_addr_remote(mm, addr);
+
+ /* Avoid triggering the temporary warning in __get_user_pages */
+ if (!vma_lookup(mm, addr)) {
+ mmap_read_unlock(mm);
+ return -EFAULT;
+ }
+
+ while (len) {
+ int bytes, offset, retval;
+ void *maddr;
+ struct vm_area_struct *vma = NULL;
+ struct page *page = get_user_page_vma_remote(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.
+ */
+ err = -EFAULT;
+ break;
+ }
+
+ bytes = len;
+ offset = addr & (PAGE_SIZE - 1);
+ if (bytes > PAGE_SIZE - offset)
+ bytes = PAGE_SIZE - offset;
+
+ maddr = kmap_local_page(page);
+ retval = strncpy_from_user(buf, (const char __user *)addr, bytes);
+ unmap_and_put_page(page, maddr);
+
+ if (retval < 0) {
+ err = retval;
+ break;
+ }
+
+ len -= retval;
+ buf += retval;
+ addr += retval;
+
+ /* Found the end of the string */
+ if (retval < bytes)
+ break;
+ }
+ mmap_read_unlock(mm);
+
+ if (err)
+ return err;
+
+ return buf - old_buf;
+}
+
/**
* access_remote_vm - access another process' address space
* @mm: the mm_struct of the target address space
@@ -6714,6 +6783,38 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr,
}
EXPORT_SYMBOL_GPL(access_process_vm);
+/**
+ * copy_str_from_process_vm - copy a string from another process's address space.
+ * @tsk: the task of the target address space
+ * @addr: start address to access
+ * @buf: source or destination buffer
+ * @len: number of bytes to transfer
+ * @gup_flags: flags modifying lookup behaviour
+ *
+ * The caller must hold a reference on @mm.
+ *
+ * Return: number of bytes copied from source to destination. If the string
+ * is shorter than @len then return the length of the string.
+ * On any error, return -EFAULT.
+ */
+int copy_str_from_process_vm(struct task_struct *tsk, unsigned long addr,
+ void *buf, int len, unsigned int gup_flags)
+{
+ struct mm_struct *mm;
+ int ret;
+
+ mm = get_task_mm(tsk);
+ if (!mm)
+ return -EFAULT;
+
+ ret = __copy_str_from_remote_vm(mm, addr, buf, len, gup_flags);
+
+ mmput(mm);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(copy_str_from_process_vm);
+
/*
* Print the name of a VMA.
*/
--
2.43.5
next reply other threads:[~2025-01-07 2:12 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-01-07 2:06 Jordan Rome [this message]
2025-01-07 2:06 ` [bpf-next v2 2/2] selftests/bpf: Add tests for bpf_copy_from_user_task_str Jordan Rome
2025-01-10 22:01 ` Andrii Nakryiko
2025-01-16 4:57 ` kernel test robot
2025-01-09 1:13 ` [bpf-next v2 1/2] bpf: Add bpf_copy_from_user_task_str kfunc kernel test robot
2025-01-10 0:48 ` kernel test robot
2025-01-10 21:50 ` Andrii Nakryiko
2025-01-17 2:22 ` Jordan Rome
2025-01-17 18:26 ` Andrii Nakryiko
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=20250107020632.170883-1-linux@jordanrome.com \
--to=linux@jordanrome.com \
--cc=akpm@linux-foundation.org \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=kernel-team@fb.com \
--cc=linux-mm@kvack.org \
--cc=shakeel.butt@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 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.