Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv2 bpf-next 0/8] bpf: Add user memory kfuncs
@ 2026-11-03  7:59 Jiri Olsa
  2026-11-03  7:59 ` [PATCHv2 bpf-next 1/8] bpf: Honor const on generic kfunc memory arguments Jiri Olsa
                   ` (8 more replies)
  0 siblings, 9 replies; 19+ messages in thread
From: Jiri Olsa @ 2026-11-03  7:59 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann
  Cc: bpf, linux-mm, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Lorenzo Stoakes, John Fastabend,
	Federico Di Pierro

hi,
we have few use cases where having the ability to allocate normal or
executable user space memory would help (like to replace function in
live process or change environment variables).

We also need writes to user memory to handle page faults, while the
existing bpf_probe_write_user() helper disables faults during the copy.

This series adds sleepable kfuncs for BPF programs to copy data into the
current task’s address space and to create or remove private anonymous
mappings with optional execute permissions.

rfc: https://lore.kernel.org/bpf/20261021195759.119421-1-jolsa@kernel.org/
v1:  https://lore.kernel.org/bpf/20261029212551.56964-1-jolsa@kernel.org/


v2 changes:
- infer read-only from constness of a pointer instead of srcptr
  argument suffix [Andrii]
- rename bpf_user_mmap to bpf_user_anon_mmap [Andrii]
- use arch_validate_prot to validate prot argument in mmap
- add the x86 compat mmap workaround
- assorted small changes

v1 changes:
- follow vm_mmap/vm_munmap design suggested by Alexei
- allow user space write only to current process, because we don't
  have remote task use case and it simplifies security checks
- many changes in tests

thanks,
jirka

---
Jiri Olsa (8):
      bpf: Honor const on generic kfunc memory arguments
      bpf: Add user memory kfuncs
      selftests/bpf: Add get_procmap_info helper
      selftests/bpf: Test const kfunc memory arguments
      selftests/bpf: Test user memory mmap kfuncs
      selftests/bpf: Test user memory page exec
      selftests/bpf: Test bpf_copy_to_user kfunc
      selftests/bpf: Test user memory kfunc errors

 Documentation/bpf/kfuncs.rst                               |  17 +++++
 kernel/bpf/helpers.c                                       | 132 +++++++++++++++++++++++++++++++++
 kernel/bpf/syscall.c                                       |  10 +--
 kernel/bpf/verifier.c                                      |  38 +++++++++-
 tools/testing/selftests/bpf/prog_tests/kfunc_call.c        |   2 +
 tools/testing/selftests/bpf/prog_tests/user_mem.c          | 350 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tools/testing/selftests/bpf/progs/kfunc_call_fail.c        |  10 +++
 tools/testing/selftests/bpf/progs/kfunc_call_test.c        |  14 ++++
 tools/testing/selftests/bpf/progs/user_mem.c               | 111 ++++++++++++++++++++++++++++
 tools/testing/selftests/bpf/test_kmods/bpf_testmod.c       |  10 +++
 tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h |   2 +
 tools/testing/selftests/bpf/trace_helpers.c                |  38 ++++++++--
 tools/testing/selftests/bpf/trace_helpers.h                |   1 +
 13 files changed, 722 insertions(+), 13 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/user_mem.c
 create mode 100644 tools/testing/selftests/bpf/progs/user_mem.c


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCHv2 bpf-next 1/8] bpf: Honor const on generic kfunc memory arguments
  2026-11-03  7:59 [PATCHv2 bpf-next 0/8] bpf: Add user memory kfuncs Jiri Olsa
@ 2026-11-03  7:59 ` Jiri Olsa
  2026-11-03  8:52   ` bot+bpf-ci
  2026-11-03 18:03   ` Alexei Starovoitov
  2026-11-03  7:59 ` [PATCHv2 bpf-next 2/8] bpf: Add user memory kfuncs Jiri Olsa
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 19+ messages in thread
From: Jiri Olsa @ 2026-11-03  7:59 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann
  Cc: bpf, linux-mm, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Lorenzo Stoakes, John Fastabend,
	Federico Di Pierro

Mark generic kfunc memory arguments as read-only when their BTF pointer
type points to const data. Preserve that access mode when resolving
scalar structs to fixed-size memory.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 kernel/bpf/verifier.c | 38 +++++++++++++++++++++++++++++++++++---
 1 file changed, 35 insertions(+), 3 deletions(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 935b163f81e4..5bc637a6fb13 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -13182,6 +13182,38 @@ static bool is_kfunc_arg_const_mem_size(const struct btf *btf,
 	return btf_param_match_suffix(btf, arg, "__szk");
 }
 
+static bool is_kfunc_arg_const_ptr(const struct btf *btf,
+				   const struct btf_param *arg)
+{
+	const struct btf_type *t;
+
+	t = btf_type_skip_modifiers(btf, arg->type, NULL);
+	if (!btf_type_is_ptr(t))
+		return false;
+
+	t = btf_type_by_id(btf, t->type);
+	while (t) {
+		switch (BTF_INFO_KIND(t->info)) {
+		case BTF_KIND_CONST:
+			return true;
+		case BTF_KIND_VOLATILE:
+		case BTF_KIND_RESTRICT:
+		case BTF_KIND_TYPEDEF:
+		case BTF_KIND_TYPE_TAG:
+			t = btf_type_by_id(btf, t->type);
+			break;
+		default:
+			return false;
+		}
+	}
+	return false;
+}
+
+static int kfunc_mem_prot(const struct btf *btf, const struct btf_param *arg)
+{
+	return is_kfunc_arg_const_ptr(btf, arg) ? MEM_RDONLY : MEM_WRITE;
+}
+
 static bool is_kfunc_arg_constant(const struct btf *btf, const struct btf_param *arg)
 {
 	return btf_param_match_suffix(btf, arg, "__k");
@@ -13550,7 +13582,7 @@ static int resolve_func_arg_type(struct bpf_verifier_env *env,
 			PTR_ERR(resolve_ret));
 		return -EINVAL;
 	}
-	*arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE | MEM_WRITE |
+	*arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE | kfunc_mem_prot(meta->btf, &args[arg]) |
 		    (*arg_type & (PTR_MAYBE_NULL | MEM_UNINIT));
 
 	return 0;
@@ -14102,7 +14134,7 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
 				reg_arg_name(env, argno), btf_type_str(ref_t), ref_tname);
 			return -EINVAL;
 		}
-		arg_type = ARG_PTR_TO_MEM | MEM_WRITE;
+		arg_type = ARG_PTR_TO_MEM | kfunc_mem_prot(meta->btf, &args[arg]);
 	} else if (btf_type_is_struct(ref_t))
 		/* A pointer to a struct without a size argument is classified as ARG_PTR_TO_BTF_ID */
 		arg_type = ARG_PTR_TO_BTF_ID;
@@ -14127,7 +14159,7 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
 			return -EINVAL;
 		}
 		proto->arg_size[arg] = type_size;
-		arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE | MEM_WRITE;
+		arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE | kfunc_mem_prot(meta->btf, &args[arg]);
 	}
 
 	if (is_kfunc_arg_uninit(meta->btf, &args[arg]))
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCHv2 bpf-next 2/8] bpf: Add user memory kfuncs
  2026-11-03  7:59 [PATCHv2 bpf-next 0/8] bpf: Add user memory kfuncs Jiri Olsa
  2026-11-03  7:59 ` [PATCHv2 bpf-next 1/8] bpf: Honor const on generic kfunc memory arguments Jiri Olsa
@ 2026-11-03  7:59 ` Jiri Olsa
  2026-11-03  8:19   ` sashiko-bot
                     ` (2 more replies)
  2026-11-03  7:59 ` [PATCHv2 bpf-next 3/8] selftests/bpf: Add get_procmap_info helper Jiri Olsa
                   ` (6 subsequent siblings)
  8 siblings, 3 replies; 19+ messages in thread
From: Jiri Olsa @ 2026-11-03  7:59 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann
  Cc: bpf, linux-mm, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Lorenzo Stoakes, John Fastabend,
	Federico Di Pierro

Add sleepable kfuncs to copy data into a task's address space and to map
or unmap private anonymous memory in the current process.

Require a usable user mm for mmap and munmap. Register the kfuncs for
KPROBE programs and gate them on CAP_SYS_ADMIN BPF token capability and
the BPF_WRITE_USER lockdown check.

Use FOLL_FORCE | FOLL_WRITE for task memory copies and return -EFAULT
when a copy is incomplete.

Rename the syscall info-copy helper to bpf_copy_to_user_buf to avoid
the name collision.

Assisted-by: Codex:GPT-6
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 Documentation/bpf/kfuncs.rst |  15 ++++
 kernel/bpf/helpers.c         | 132 +++++++++++++++++++++++++++++++++++
 kernel/bpf/syscall.c         |  10 +--
 3 files changed, 152 insertions(+), 5 deletions(-)

diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
index 73578a4c3b1f..8ca7aad8ea36 100644
--- a/Documentation/bpf/kfuncs.rst
+++ b/Documentation/bpf/kfuncs.rst
@@ -965,3 +965,18 @@ the verifier. bpf_cgroup_ancestor() can be used as follows:
 BPF provides a set of kfuncs that can be used to query, allocate, mutate, and
 destroy struct cpumask * objects. Please refer to :ref:`cpumasks-header-label`
 for more details.
+
+4.4 User memory kfuncs
+----------------------
+
+The user memory kfuncs operate on the current process's address space.
+``bpf_copy_to_user()`` writes to this address space, including private mappings
+that are not writable. A partial copy is not rolled back if it faults.
+
+``bpf_user_anon_mmap()`` and ``bpf_user_munmap()`` create and remove mappings
+by calling the kernel's mapping functions directly rather than issuing
+``mmap(2)`` or ``munmap(2)`` syscalls. As a result, seccomp filters do not
+mediate these mapping operations.
+
+.. kernel-doc:: kernel/bpf/helpers.c
+   :identifiers: bpf_copy_to_user bpf_user_anon_mmap bpf_user_munmap
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 58e357302850..ae7dfa58ae95 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -29,6 +29,12 @@
 #include <linux/task_work.h>
 #include <linux/irq_work.h>
 #include <linux/buildid.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/limits.h>
+#ifdef CONFIG_IA32_EMULATION
+#include <asm/thread_info.h>
+#endif
 
 #include "../../lib/kstrtox.h"
 
@@ -3741,6 +3747,110 @@ __bpf_kfunc int bpf_copy_from_user_mm(void *dst, u32 dst__sz,
 	return ret;
 }
 
+/**
+ * bpf_copy_to_user() - Copy data to the current task's address space
+ * @unsafe_ptr__ign: Destination address in the current task's address space
+ * @src:     Source address, in kernel space
+ * @src__sz: Number of bytes to copy
+ * @flags:   Reserved for future use; must be zero
+ *
+ * Faults in the destination and writes the way ptrace(PTRACE_POKETEXT) does:
+ * a private mapping doesn't have to be writable. A partial copy is not undone,
+ * the bytes before the fault stay written.
+ *
+ * The caller must coordinate with other threads so they do not execute the
+ * destination range while it is being modified, and ensure execution begins
+ * only after the copy completes.
+ *
+ * Return: 0 on success, -EINVAL if @flags is non-zero, or -EFAULT if the copy
+ * fails or is partial.
+ */
+__bpf_kfunc int bpf_copy_to_user(void __user *unsafe_ptr__ign, const void *src,
+				 u32 src__sz, u64 flags)
+{
+	int ret;
+
+	if (unlikely(flags))
+		return -EINVAL;
+	if (unlikely(src__sz > S32_MAX))
+		return -EINVAL;
+	if (unlikely(!src__sz))
+		return 0;
+	ret = access_process_vm(current, (unsigned long)unsafe_ptr__ign, (void *)src,
+				src__sz, FOLL_FORCE | FOLL_WRITE);
+	return ret == src__sz ? 0 : -EFAULT;
+}
+
+/* bpf_wq and bpf_task_work callbacks can run without a usable mm. */
+static bool bpf_user_mm_ok(void)
+{
+	return current->mm && !(current->flags & (PF_KTHREAD | PF_EXITING));
+}
+
+#ifdef CONFIG_IA32_EMULATION
+static unsigned long bpf_user_vm_mmap(unsigned long len, unsigned long prot)
+{
+	struct thread_info *ti = current_thread_info();
+	bool compat = test_thread_flag(TIF_ADDR32);
+	unsigned long addr;
+
+	/*
+	 * We are not in a syscall, but x86 get_unmapped_area() paths
+	 * ignore TIF_ADDR32 and rely on in_32bit_syscall().
+	 */
+	if (compat)
+		ti->status |= TS_COMPAT;
+	addr = vm_mmap(NULL, 0, len, prot, MAP_PRIVATE | MAP_ANONYMOUS, 0);
+	if (compat)
+		ti->status &= ~TS_COMPAT;
+	return addr;
+}
+#else
+static unsigned long bpf_user_vm_mmap(unsigned long len, unsigned long prot)
+{
+	return vm_mmap(NULL, 0, len, prot, MAP_PRIVATE | MAP_ANONYMOUS, 0);
+}
+#endif
+
+/**
+ * bpf_user_anon_mmap() - Map private anonymous memory into the current process
+ * @len:  Length of the mapping
+ * @prot: PROT_* flags of mmap(2), including PROT_EXEC
+ *
+ * This maps memory by calling vm_mmap() directly. It does not invoke the
+ * mmap(2) syscall, so seccomp filters do not mediate this mapping.
+ *
+ * Return: The address of the mapping on success, -EPERM if the caller has no
+ * user mm, or an unsigned-long encoded negative error code, as returned by
+ * vm_mmap().
+ */
+__bpf_kfunc unsigned long bpf_user_anon_mmap(unsigned long len, unsigned long prot)
+{
+	if (!bpf_user_mm_ok())
+		return -EPERM;
+	if (!arch_validate_prot(prot, 0))
+		return -EINVAL;
+	return bpf_user_vm_mmap(len, prot);
+}
+
+/**
+ * bpf_user_munmap() - Unmap a range of addresses of the current process
+ * @addr: Start of the range
+ * @len:  Length of the range
+ *
+ * This unmaps memory by calling vm_munmap() directly. It does not invoke the
+ * munmap(2) syscall, so seccomp filters do not mediate this unmapping.
+ *
+ * Return: 0 on success, -EPERM if the caller has no user mm, or a negative
+ * error code.
+ */
+__bpf_kfunc int bpf_user_munmap(unsigned long addr, unsigned long len)
+{
+	if (!bpf_user_mm_ok())
+		return -EPERM;
+	return vm_munmap(untagged_addr(addr), len);
+}
+
 /**
  * bpf_copy_from_user_mm_str() - Copy a string from an address space
  * @dst:             Destination address, in kernel space. This buffer must be
@@ -5142,6 +5252,27 @@ static const struct btf_kfunc_id_set generic_kfunc_set = {
 	.set   = &generic_btf_ids,
 };
 
+BTF_KFUNCS_START(user_mem_btf_ids)
+BTF_ID_FLAGS(func, bpf_copy_to_user, KF_SLEEPABLE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_user_anon_mmap, KF_SLEEPABLE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_user_munmap, KF_SLEEPABLE | KF_PERFMON)
+BTF_KFUNCS_END(user_mem_btf_ids)
+
+static int user_mem_kfunc_filter(const struct bpf_prog *prog, u32 kfunc_id)
+{
+	if (!btf_id_set8_contains(&user_mem_btf_ids, kfunc_id))
+		return 0;
+	if (!bpf_token_capable(prog->aux->token, CAP_SYS_ADMIN))
+		return -EPERM;
+	return security_locked_down(LOCKDOWN_BPF_WRITE_USER);
+}
+
+static const struct btf_kfunc_id_set user_mem_kfunc_set = {
+	.owner  = THIS_MODULE,
+	.set    = &user_mem_btf_ids,
+	.filter = user_mem_kfunc_filter,
+};
+
 BTF_ID_LIST(generic_dtor_ids)
 BTF_ID(struct, task_struct)
 BTF_ID(func, bpf_task_release_dtor)
@@ -5269,6 +5400,7 @@ static int __init kfunc_init(void)
 	};
 
 	ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &generic_kfunc_set);
+	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_KPROBE, &user_mem_kfunc_set);
 	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &generic_kfunc_set);
 	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &generic_kfunc_set);
 	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &generic_kfunc_set);
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 02ed5368f056..9004f2673ccf 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -3932,8 +3932,8 @@ static void bpf_raw_tp_link_show_fdinfo(const struct bpf_link *link,
 		   raw_tp_link->cookie);
 }
 
-static int bpf_copy_to_user(char __user *ubuf, const char *buf, u32 ulen,
-			    u32 len)
+static int bpf_copy_to_user_buf(char __user *ubuf, const char *buf, u32 ulen,
+			       u32 len)
 {
 	if (ulen >= len + 1) {
 		if (copy_to_user(ubuf, buf, len + 1))
@@ -3970,7 +3970,7 @@ static int bpf_raw_tp_link_fill_link_info(const struct bpf_link *link,
 	if (!ubuf)
 		return 0;
 
-	return bpf_copy_to_user(ubuf, tp_name, ulen, tp_len);
+	return bpf_copy_to_user_buf(ubuf, tp_name, ulen, tp_len);
 }
 
 static const struct bpf_link_ops bpf_raw_tp_link_lops = {
@@ -4031,7 +4031,7 @@ static int bpf_perf_link_fill_common(const struct perf_event *event,
 		goto out;
 
 	if (buf) {
-		err = bpf_copy_to_user(uname, buf, ulen, len);
+		err = bpf_copy_to_user_buf(uname, buf, ulen, len);
 	} else {
 		char zero = '\0';
 
@@ -5710,7 +5710,7 @@ static int bpf_task_fd_query_copy(const union bpf_attr *attr,
 			if (put_user(zero, ubuf))
 				return -EFAULT;
 		} else {
-			err = bpf_copy_to_user(ubuf, buf, input_len, len);
+			err = bpf_copy_to_user_buf(ubuf, buf, input_len, len);
 			if (err == -EFAULT)
 				return err;
 		}
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCHv2 bpf-next 3/8] selftests/bpf: Add get_procmap_info helper
  2026-11-03  7:59 [PATCHv2 bpf-next 0/8] bpf: Add user memory kfuncs Jiri Olsa
  2026-11-03  7:59 ` [PATCHv2 bpf-next 1/8] bpf: Honor const on generic kfunc memory arguments Jiri Olsa
  2026-11-03  7:59 ` [PATCHv2 bpf-next 2/8] bpf: Add user memory kfuncs Jiri Olsa
@ 2026-11-03  7:59 ` Jiri Olsa
  2026-11-03  7:59 ` [PATCHv2 bpf-next 4/8] selftests/bpf: Test const kfunc memory arguments Jiri Olsa
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 19+ messages in thread
From: Jiri Olsa @ 2026-11-03  7:59 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann
  Cc: bpf, linux-mm, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Lorenzo Stoakes, John Fastabend,
	Federico Di Pierro

Adding get_procmap_info that queries the current process VMA range
and permissions through PROCMAP_QUERY. Extend procmap_query to return
the VMA end address and update its callers.

I don't think we need the search fallback, the PROCMAP api is merged
for some time now.

Assisted-by: Codex:GPT-6
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 tools/testing/selftests/bpf/trace_helpers.c | 38 ++++++++++++++++++---
 tools/testing/selftests/bpf/trace_helpers.h |  1 +
 2 files changed, 34 insertions(+), 5 deletions(-)

diff --git a/tools/testing/selftests/bpf/trace_helpers.c b/tools/testing/selftests/bpf/trace_helpers.c
index 6d7a4b8c4ab8..d2b2ff0ec2a4 100644
--- a/tools/testing/selftests/bpf/trace_helpers.c
+++ b/tools/testing/selftests/bpf/trace_helpers.c
@@ -286,7 +286,8 @@ unsigned long long module_get_base_addr_sec(const char *module, const char *sec)
 #ifdef PROCMAP_QUERY
 int env_verbosity __weak = 0;
 
-static int procmap_query(int fd, const void *addr, __u32 query_flags, size_t *start, size_t *offset, int *flags)
+static int procmap_query(int fd, const void *addr, __u32 query_flags,
+		 size_t *start, size_t *end, size_t *offset, int *flags)
 {
 	char path_buf[PATH_MAX], build_id_buf[20];
 	struct procmap_query q;
@@ -325,6 +326,7 @@ static int procmap_query(int fd, const void *addr, __u32 query_flags, size_t *st
 	}
 
 	*start = q.vma_start;
+	*end = q.vma_end;
 	*offset = q.vma_offset;
 	*flags = q.vma_flags;
 	return 0;
@@ -333,13 +335,37 @@ static int procmap_query(int fd, const void *addr, __u32 query_flags, size_t *st
 # ifndef PROCMAP_QUERY_VMA_EXECUTABLE
 #  define PROCMAP_QUERY_VMA_EXECUTABLE 0x04
 # endif
-
-static int procmap_query(int fd, const void *addr, __u32 query_flags, size_t *start, size_t *offset, int *flags)
+static int procmap_query(int fd, const void *addr, __u32 query_flags,
+		 size_t *start, size_t *end, size_t *offset, int *flags)
 {
 	return -EOPNOTSUPP;
 }
 #endif
 
+int get_procmap_info(const void *addr, size_t *start, size_t *end, char perms[5])
+{
+	FILE *f;
+	size_t offset;
+	int flags, err;
+
+	f = fopen("/proc/self/maps", "r");
+	if (!f)
+		return -errno;
+
+	err = procmap_query(fileno(f), addr, 0, start, end, &offset, &flags);
+#ifdef PROCMAP_QUERY
+	if (!err && perms) {
+		snprintf(perms, 5, "%c%c%c%c",
+			 (flags & PROCMAP_QUERY_VMA_READABLE) ? 'r' : '-',
+			 (flags & PROCMAP_QUERY_VMA_WRITABLE) ? 'w' : '-',
+			 (flags & PROCMAP_QUERY_VMA_EXECUTABLE) ? 'x' : '-',
+			 (flags & PROCMAP_QUERY_VMA_SHARED) ? 's' : 'p');
+	}
+#endif
+	fclose(f);
+	return err;
+}
+
 ssize_t get_uprobe_offset(const void *addr)
 {
 	size_t start, base, end;
@@ -352,7 +378,8 @@ ssize_t get_uprobe_offset(const void *addr)
 		return -errno;
 
 	/* requested executable VMA only */
-	err = procmap_query(fileno(f), addr, PROCMAP_QUERY_VMA_EXECUTABLE, &start, &base, &flags);
+	err = procmap_query(fileno(f), addr, PROCMAP_QUERY_VMA_EXECUTABLE,
+			    &start, &end, &base, &flags);
 	if (err == -EOPNOTSUPP) {
 		bool found = false;
 
@@ -418,7 +445,8 @@ ssize_t get_rel_offset(uintptr_t addr)
 	if (!f)
 		return -errno;
 
-	err = procmap_query(fileno(f), (const void *)addr, 0, &start, &offset, &flags);
+	err = procmap_query(fileno(f), (const void *)addr, 0,
+			    &start, &end, &offset, &flags);
 	if (err == 0) {
 		fclose(f);
 		return (size_t)addr - start + offset;
diff --git a/tools/testing/selftests/bpf/trace_helpers.h b/tools/testing/selftests/bpf/trace_helpers.h
index 0f8d99746084..85b8f8e30232 100644
--- a/tools/testing/selftests/bpf/trace_helpers.h
+++ b/tools/testing/selftests/bpf/trace_helpers.h
@@ -59,6 +59,7 @@ int read_trace_pipe_iter(void (*cb)(const char *str, void *data),
 
 ssize_t get_uprobe_offset(const void *addr);
 ssize_t get_rel_offset(uintptr_t addr);
+int get_procmap_info(const void *addr, size_t *start, size_t *end, char perms[5]);
 
 int read_build_id(const char *path, char *build_id, size_t size);
 
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCHv2 bpf-next 4/8] selftests/bpf: Test const kfunc memory arguments
  2026-11-03  7:59 [PATCHv2 bpf-next 0/8] bpf: Add user memory kfuncs Jiri Olsa
                   ` (2 preceding siblings ...)
  2026-11-03  7:59 ` [PATCHv2 bpf-next 3/8] selftests/bpf: Add get_procmap_info helper Jiri Olsa
@ 2026-11-03  7:59 ` Jiri Olsa
  2026-11-03  8:10   ` sashiko-bot
  2026-11-03  7:59 ` [PATCHv2 bpf-next 5/8] selftests/bpf: Test user memory mmap kfuncs Jiri Olsa
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Jiri Olsa @ 2026-11-03  7:59 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann
  Cc: bpf, linux-mm, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Lorenzo Stoakes, John Fastabend,
	Federico Di Pierro

Verify that const kfunc arguments accept read-only global data,
writable stack data, and const scalar structs. Check that read-only
data is rejected when passed to a writable memory argument.

Assisted-by: Codex:GPT-6
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 .../testing/selftests/bpf/prog_tests/kfunc_call.c  |  2 ++
 .../testing/selftests/bpf/progs/kfunc_call_fail.c  | 10 ++++++++++
 .../testing/selftests/bpf/progs/kfunc_call_test.c  | 14 ++++++++++++++
 .../testing/selftests/bpf/test_kmods/bpf_testmod.c | 10 ++++++++++
 .../selftests/bpf/test_kmods/bpf_testmod_kfunc.h   |  2 ++
 5 files changed, 38 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c
index 0063e60d6f2f..f2152591982b 100644
--- a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c
+++ b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c
@@ -63,6 +63,7 @@ static struct kfunc_test_params kfunc_tests[] = {
 	 */
 	SYSCALL_NULL_CTX_FAIL(kfunc_syscall_test_fail, -EINVAL, "processed 4 insns"),
 	SYSCALL_NULL_CTX_FAIL(kfunc_syscall_test_null_fail, -EINVAL, "processed 4 insns"),
+	TC_FAIL(kfunc_call_test_mem_len_fail_readonly, 0, "write into map forbidden"),
 	TC_FAIL(kfunc_call_test_get_mem_fail_rdonly, 0, "R0 cannot write into rdonly_mem"),
 	TC_FAIL(kfunc_call_test_get_mem_fail_use_after_free, 0, "invalid mem access 'scalar'"),
 	TC_FAIL(kfunc_call_test_get_mem_fail_oob, 0, "min value is outside of the allowed memory range"),
@@ -82,6 +83,7 @@ static struct kfunc_test_params kfunc_tests[] = {
 	TC_TEST(kfunc_call_test5_asm, 0),
 	TC_TEST(kfunc_call_test_ref_btf_id, 0),
 	TC_TEST(kfunc_call_test_get_mem, 42),
+	TC_TEST(kfunc_call_test_const_mem, 0),
 	SYSCALL_TEST(kfunc_syscall_test, 0),
 	SYSCALL_NULL_CTX_TEST(kfunc_syscall_test_null, 0),
 	TC_TEST(kfunc_call_test_static_unused_arg, 0),
diff --git a/tools/testing/selftests/bpf/progs/kfunc_call_fail.c b/tools/testing/selftests/bpf/progs/kfunc_call_fail.c
index 7e93f7fb1329..bef956d3f89e 100644
--- a/tools/testing/selftests/bpf/progs/kfunc_call_fail.c
+++ b/tools/testing/selftests/bpf/progs/kfunc_call_fail.c
@@ -4,6 +4,8 @@
 #include <bpf/bpf_helpers.h>
 #include "../test_kmods/bpf_testmod_kfunc.h"
 
+static const char kfunc_mem_rodata[] = "read-only data";
+
 static struct bpf_spin_lock kfunc_call_lock SEC(".data.A");
 
 SEC("?tc")
@@ -47,6 +49,14 @@ int kfunc_syscall_test_null_fail(struct syscall_test_args *args)
 	return 0;
 }
 
+SEC("?tc")
+int kfunc_call_test_mem_len_fail_readonly(struct __sk_buff *skb)
+{
+	bpf_kfunc_call_test_mem_len_pass1((void *)kfunc_mem_rodata,
+					  sizeof(kfunc_mem_rodata));
+	return 0;
+}
+
 SEC("?tc")
 int kfunc_call_test_get_mem_fail_rdonly(struct __sk_buff *skb)
 {
diff --git a/tools/testing/selftests/bpf/progs/kfunc_call_test.c b/tools/testing/selftests/bpf/progs/kfunc_call_test.c
index 8e6560c31e78..4f4a6713aa6e 100644
--- a/tools/testing/selftests/bpf/progs/kfunc_call_test.c
+++ b/tools/testing/selftests/bpf/progs/kfunc_call_test.c
@@ -5,6 +5,9 @@
 #include "bpf_misc.h"
 #include "../test_kmods/bpf_testmod_kfunc.h"
 
+static const char kfunc_const_data[] = "read-only source";
+static const struct prog_test_pass1 kfunc_const_struct = { .x0 = 1 };
+
 static struct bpf_spin_lock kfunc_call_lock SEC(".data.A");
 
 SEC("tc")
@@ -208,6 +211,17 @@ int kfunc_call_test_pass(struct __sk_buff *skb)
 	return 0;
 }
 
+SEC("tc")
+int kfunc_call_test_const_mem(struct __sk_buff *skb)
+{
+	char stack_data[] = "read-write";
+
+	bpf_kfunc_call_test_const_mem(kfunc_const_data, sizeof(kfunc_const_data));
+	bpf_kfunc_call_test_const_mem(stack_data, sizeof(stack_data));
+	bpf_kfunc_call_test_const_struct(&kfunc_const_struct);
+	return 0;
+}
+
 struct syscall_test_args {
 	__u8 data[16];
 	size_t size;
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index 76fbe64755ef..41ff36f0ca77 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -1049,6 +1049,8 @@ BTF_ID_FLAGS(func, bpf_kfunc_arena_args5_test)
 BTF_ID_FLAGS(func, bpf_kfunc_arena_stack_arg_test)
 BTF_ID_FLAGS(func, bpf_kfunc_arena_mixed_test)
 BTF_ID_FLAGS(func, bpf_kfunc_call_test_mem_len_pass1)
+BTF_ID_FLAGS(func, bpf_kfunc_call_test_const_mem)
+BTF_ID_FLAGS(func, bpf_kfunc_call_test_const_struct)
 BTF_ID_FLAGS(func, bpf_kfunc_dynptr_test)
 BTF_ID_FLAGS(func, bpf_kfunc_nested_acquire_nonzero_offset_test, KF_ACQUIRE)
 BTF_ID_FLAGS(func, bpf_kfunc_nested_acquire_zero_offset_test, KF_ACQUIRE)
@@ -1552,6 +1554,14 @@ __bpf_kfunc void bpf_kfunc_call_test_mem_len_pass1(void *mem, int mem__sz)
 {
 }
 
+__bpf_kfunc void bpf_kfunc_call_test_const_mem(const void *mem, int mem__sz)
+{
+}
+
+__bpf_kfunc void bpf_kfunc_call_test_const_struct(const struct prog_test_pass1 *p)
+{
+}
+
 __bpf_kfunc void bpf_kfunc_call_test_mem_len_fail1(void *mem, int len)
 {
 }
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
index e9902e651d72..0a7df1798603 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
@@ -203,6 +203,8 @@ void bpf_kfunc_call_test_release(struct prog_test_ref_kfunc *p) __ksym;
 void bpf_kfunc_call_test_ref(struct prog_test_ref_kfunc *p) __ksym;
 
 void bpf_kfunc_call_test_mem_len_pass1(void *mem, int len) __ksym;
+void bpf_kfunc_call_test_const_mem(const void *mem, int mem__sz) __ksym;
+void bpf_kfunc_call_test_const_struct(const struct prog_test_pass1 *p) __ksym;
 __u64 bpf_kfunc_arena_arg_test(__u64 *val__arena) __ksym;
 __u64 bpf_kfunc_arena_cap_test(__u64 *val__arena) __ksym;
 __u64 bpf_kfunc_arena_cap_nullable_test(__u64 *val__arena__nullable) __ksym;
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCHv2 bpf-next 5/8] selftests/bpf: Test user memory mmap kfuncs
  2026-11-03  7:59 [PATCHv2 bpf-next 0/8] bpf: Add user memory kfuncs Jiri Olsa
                   ` (3 preceding siblings ...)
  2026-11-03  7:59 ` [PATCHv2 bpf-next 4/8] selftests/bpf: Test const kfunc memory arguments Jiri Olsa
@ 2026-11-03  7:59 ` Jiri Olsa
  2026-11-03  8:05   ` sashiko-bot
  2026-11-03  7:59 ` [PATCHv2 bpf-next 6/8] selftests/bpf: Test user memory page exec Jiri Olsa
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Jiri Olsa @ 2026-11-03  7:59 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann
  Cc: bpf, linux-mm, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Lorenzo Stoakes, John Fastabend,
	Federico Di Pierro

Add BPF selftests for bpf_user_anon_mmap and bpf_user_munmap from
a sleepable uprobe. Check that created mappings have the requested
permissions and that unmapped addresses are no longer mapped.

Assisted-by: Codex:GPT-6
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 .../selftests/bpf/prog_tests/user_mem.c       | 112 ++++++++++++++++++
 tools/testing/selftests/bpf/progs/user_mem.c  |  43 +++++++
 2 files changed, 155 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/user_mem.c
 create mode 100644 tools/testing/selftests/bpf/progs/user_mem.c

diff --git a/tools/testing/selftests/bpf/prog_tests/user_mem.c b/tools/testing/selftests/bpf/prog_tests/user_mem.c
new file mode 100644
index 000000000000..f301a1843706
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/user_mem.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <test_progs.h>
+#include <sys/mman.h>
+#include <sys/personality.h>
+#include <unistd.h>
+
+#include "user_mem.skel.h"
+#include "trace_helpers.h"
+
+enum {
+	USER_MEM_MMAP = 1,
+	USER_MEM_UNMAP,
+};
+
+static noinline void user_mem_trigger(void)
+{
+	asm volatile ("" ::: "memory");
+}
+
+static bool check_user_mem_vma(void *addr, unsigned long size, const char *perms)
+{
+	size_t start, end;
+	char vma_perms[5];
+	int err;
+
+	err = get_procmap_info(addr, &start, &end, vma_perms);
+	if (!ASSERT_OK(err, "user_mem_vma"))
+		return false;
+
+	/* VMA can be merged, so ensure it covers at least the requested range. */
+	if (!ASSERT_GE(end - (size_t)addr, size, "vma_size"))
+		return false;
+
+	return !perms || ASSERT_STREQ(vma_perms, perms, "vma_perms");
+}
+
+static bool check_user_mem_unmapped(void *addr)
+{
+	size_t start, end;
+	int err;
+
+	err = get_procmap_info(addr, &start, &end, NULL);
+	return ASSERT_EQ(err, -ESRCH, "user_mem_unmapped");
+}
+
+static bool user_mem_mmap(struct user_mem *skel, unsigned long len,
+			   unsigned long prot, unsigned long *addr)
+{
+	skel->bss->map_len = len;
+	skel->bss->map_prot = prot;
+	skel->bss->action = USER_MEM_MMAP;
+	user_mem_trigger();
+
+	/* vm_mmap encodes errors in the top MAX_ERRNO unsigned-long values. */
+	if (!ASSERT_LT(skel->bss->user_addr, -4095UL, "user_addr"))
+		return false;
+
+	*addr = skel->bss->user_addr;
+	return true;
+}
+
+static bool user_mem_munmap(struct user_mem *skel, unsigned long addr,
+			   unsigned long len)
+{
+	skel->bss->user_addr = addr;
+	skel->bss->map_len = len;
+	skel->bss->action = USER_MEM_UNMAP;
+	user_mem_trigger();
+
+	return ASSERT_OK(skel->bss->ret, "user_munmap");
+}
+
+static void test_mapping(struct user_mem *skel)
+{
+	long page_size = sysconf(_SC_PAGESIZE);
+	const unsigned long len = page_size * 3;
+	unsigned long addr = ~0UL;
+
+	if (!user_mem_mmap(skel, len, PROT_READ | PROT_WRITE, &addr))
+		goto out;
+	if (!check_user_mem_vma((void *)addr, len, "rw-p"))
+		goto out;
+	if (!user_mem_munmap(skel, addr, len))
+		goto out;
+	if (!check_user_mem_unmapped((void *)addr))
+		goto out;
+	addr = ~0UL;
+
+out:
+	if (addr != ~0UL)
+		munmap((void *)addr, len);
+}
+
+void test_user_mem(void)
+{
+	struct user_mem *skel;
+
+	skel = user_mem__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "open_and_load"))
+		return;
+
+	skel->bss->monitored_pid = getpid();
+	if (!ASSERT_OK(user_mem__attach(skel), "attach"))
+		goto out;
+
+	if (test__start_subtest("mapping"))
+		test_mapping(skel);
+
+out:
+	user_mem__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/user_mem.c b/tools/testing/selftests/bpf/progs/user_mem.c
new file mode 100644
index 000000000000..e3b0689f5f2a
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/user_mem.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+#include "bpf_misc.h"
+
+char _license[] SEC("license") = "GPL";
+
+extern unsigned long bpf_user_anon_mmap(unsigned long len, unsigned long prot) __ksym;
+extern int bpf_user_munmap(unsigned long addr, unsigned long len) __ksym;
+
+enum {
+	USER_MEM_MMAP = 1,
+	USER_MEM_UNMAP,
+};
+
+int monitored_pid;
+int action;
+int ret;
+
+unsigned long user_addr;
+unsigned long map_len;
+unsigned long map_prot;
+
+SEC("uprobe.s//proc/self/exe:user_mem_trigger")
+int user_mem_test(struct pt_regs *ctx)
+{
+	if ((bpf_get_current_pid_tgid() >> 32) != monitored_pid)
+		return 0;
+
+	switch (action) {
+	case USER_MEM_MMAP:
+		user_addr = bpf_user_anon_mmap(map_len, map_prot);
+		break;
+	case USER_MEM_UNMAP:
+		ret = bpf_user_munmap(user_addr, map_len);
+		break;
+	}
+
+	return 0;
+}
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCHv2 bpf-next 6/8] selftests/bpf: Test user memory page exec
  2026-11-03  7:59 [PATCHv2 bpf-next 0/8] bpf: Add user memory kfuncs Jiri Olsa
                   ` (4 preceding siblings ...)
  2026-11-03  7:59 ` [PATCHv2 bpf-next 5/8] selftests/bpf: Test user memory mmap kfuncs Jiri Olsa
@ 2026-11-03  7:59 ` Jiri Olsa
  2026-11-03  8:39   ` bot+bpf-ci
  2026-11-03  7:59 ` [PATCHv2 bpf-next 7/8] selftests/bpf: Test bpf_copy_to_user kfunc Jiri Olsa
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Jiri Olsa @ 2026-11-03  7:59 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann
  Cc: bpf, linux-mm, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Lorenzo Stoakes, John Fastabend,
	Federico Di Pierro

Use bpf_copy_to_user from a uprobe to write architecture-specific
instructions into user memory, then execute them on x86-64 and arm64.

Test execution from a mapping created with read and execute permissions,
and from individual pages made executable with mprotect.

Assisted-by: Codex:GPT-6
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 .../selftests/bpf/prog_tests/user_mem.c       | 75 +++++++++++++++++++
 tools/testing/selftests/bpf/progs/user_mem.c  | 22 ++++++
 2 files changed, 97 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/user_mem.c b/tools/testing/selftests/bpf/prog_tests/user_mem.c
index f301a1843706..7fa827298d24 100644
--- a/tools/testing/selftests/bpf/prog_tests/user_mem.c
+++ b/tools/testing/selftests/bpf/prog_tests/user_mem.c
@@ -11,6 +11,7 @@
 enum {
 	USER_MEM_MMAP = 1,
 	USER_MEM_UNMAP,
+	USER_MEM_COPY_INSNS,
 };
 
 static noinline void user_mem_trigger(void)
@@ -92,6 +93,76 @@ static void test_mapping(struct user_mem *skel)
 		munmap((void *)addr, len);
 }
 
+static bool user_mem_copy_insns(struct user_mem *skel, unsigned long addr)
+{
+	skel->bss->user_addr = addr;
+	skel->bss->action = USER_MEM_COPY_INSNS;
+	user_mem_trigger();
+
+	return ASSERT_OK(skel->bss->ret, "user_write");
+}
+
+static void test_execute_user_page(struct user_mem *skel)
+{
+	long page_size = sysconf(_SC_PAGESIZE);
+	unsigned long addr = ~0UL;
+
+#if !defined(__x86_64__) && !defined(__aarch64__)
+	test__skip();
+	return;
+#endif
+
+	if (!user_mem_mmap(skel, page_size, PROT_READ | PROT_EXEC, &addr))
+		return;
+	if (!check_user_mem_vma((void *)addr, page_size, "r-xp"))
+		goto out;
+	if (!user_mem_copy_insns(skel, addr))
+		goto out;
+
+	/* Execute user page. */
+	ASSERT_EQ(((int (*)(void))addr)(), 42, "execute_user_page");
+
+out:
+	if (addr != ~0UL)
+		munmap((void *)addr, page_size);
+}
+
+static void test_partial_exec(struct user_mem *skel)
+{
+	long page_size = sysconf(_SC_PAGESIZE);
+	unsigned long addr = ~0UL;
+
+#if !defined(__x86_64__) && !defined(__aarch64__)
+	test__skip();
+	return;
+#endif
+
+	if (!user_mem_mmap(skel, page_size * 3, PROT_READ | PROT_WRITE, &addr))
+		return;
+
+	/* Copy instructions to middle page. */
+	if (!user_mem_copy_insns(skel, addr + page_size))
+		goto out;
+
+	/* Set +x to middle page. */
+	if (!ASSERT_OK(mprotect((void *)(addr + page_size), page_size,
+				PROT_READ | PROT_EXEC), "middle_page_mprotect"))
+		goto out;
+
+	if (!check_user_mem_vma((void *)addr, page_size, "rw-p") ||
+	    !check_user_mem_vma((void *)(addr + page_size), page_size, "r-xp") ||
+	    !check_user_mem_vma((void *)(addr + 2 * page_size), page_size, "rw-p"))
+		goto out;
+
+	/* Execute middle page. */
+	if (!ASSERT_EQ(((int (*)(void))(addr + page_size))(), 42, "execute_middle_page"))
+		goto out;
+
+out:
+	if (addr != ~0UL)
+		munmap((void *)addr, page_size * 3);
+}
+
 void test_user_mem(void)
 {
 	struct user_mem *skel;
@@ -106,6 +177,10 @@ void test_user_mem(void)
 
 	if (test__start_subtest("mapping"))
 		test_mapping(skel);
+	if (test__start_subtest("execute_user_page"))
+		test_execute_user_page(skel);
+	if (test__start_subtest("partial_exec"))
+		test_partial_exec(skel);
 
 out:
 	user_mem__destroy(skel);
diff --git a/tools/testing/selftests/bpf/progs/user_mem.c b/tools/testing/selftests/bpf/progs/user_mem.c
index e3b0689f5f2a..a62916ebc0b5 100644
--- a/tools/testing/selftests/bpf/progs/user_mem.c
+++ b/tools/testing/selftests/bpf/progs/user_mem.c
@@ -10,10 +10,13 @@ char _license[] SEC("license") = "GPL";
 
 extern unsigned long bpf_user_anon_mmap(unsigned long len, unsigned long prot) __ksym;
 extern int bpf_user_munmap(unsigned long addr, unsigned long len) __ksym;
+extern int bpf_copy_to_user(void *unsafe_ptr__ign, const void *src__srcptr,
+			    u32 src__sz, u64 flags) __ksym;
 
 enum {
 	USER_MEM_MMAP = 1,
 	USER_MEM_UNMAP,
+	USER_MEM_COPY_INSNS,
 };
 
 int monitored_pid;
@@ -24,6 +27,22 @@ unsigned long user_addr;
 unsigned long map_len;
 unsigned long map_prot;
 
+#if defined(__TARGET_ARCH_x86)
+static const unsigned char insns[] = {
+	0xf3, 0x0f, 0x1e, 0xfa, /* endbr64 */
+	0xb8, 42, 0, 0, 0, 0xc3, /* mov $42, %eax; ret */
+};
+#elif defined(__TARGET_ARCH_arm64)
+static const unsigned char insns[] = {
+	0x5f, 0x24, 0x03, 0xd5, /* bti c */
+	0x40, 0x05, 0x80, 0x52, /* mov w0, #42 */
+	0xc0, 0x03, 0x5f, 0xd6, /* ret */
+};
+#else
+/* The execute tests are skipped on other architectures. */
+static const unsigned char insns[] = { 0 };
+#endif
+
 SEC("uprobe.s//proc/self/exe:user_mem_trigger")
 int user_mem_test(struct pt_regs *ctx)
 {
@@ -37,6 +56,9 @@ int user_mem_test(struct pt_regs *ctx)
 	case USER_MEM_UNMAP:
 		ret = bpf_user_munmap(user_addr, map_len);
 		break;
+	case USER_MEM_COPY_INSNS:
+		ret = bpf_copy_to_user((void *)user_addr, insns, sizeof(insns), 0);
+		break;
 	}
 
 	return 0;
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCHv2 bpf-next 7/8] selftests/bpf: Test bpf_copy_to_user kfunc
  2026-11-03  7:59 [PATCHv2 bpf-next 0/8] bpf: Add user memory kfuncs Jiri Olsa
                   ` (5 preceding siblings ...)
  2026-11-03  7:59 ` [PATCHv2 bpf-next 6/8] selftests/bpf: Test user memory page exec Jiri Olsa
@ 2026-11-03  7:59 ` Jiri Olsa
  2026-11-03  7:59 ` [PATCHv2 bpf-next 8/8] selftests/bpf: Test user memory kfunc errors Jiri Olsa
  2026-11-03 18:08 ` [PATCHv2 bpf-next 0/8] bpf: Add user memory kfuncs Alexei Starovoitov
  8 siblings, 0 replies; 19+ messages in thread
From: Jiri Olsa @ 2026-11-03  7:59 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann
  Cc: bpf, linux-mm, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Lorenzo Stoakes, John Fastabend,
	Federico Di Pierro

Test that bpf_copy_to_user can fault in a paged-out page and write to a
read-only mapping. Also verify that a partial copy crossing into an unmapped
page returns -EFAULT while leaving the copied byte in place.

Assisted-by: Codex:GPT-6
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 .../selftests/bpf/prog_tests/user_mem.c       | 106 ++++++++++++++++++
 tools/testing/selftests/bpf/progs/user_mem.c  |  17 +++
 2 files changed, 123 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/user_mem.c b/tools/testing/selftests/bpf/prog_tests/user_mem.c
index 7fa827298d24..c87620e39abb 100644
--- a/tools/testing/selftests/bpf/prog_tests/user_mem.c
+++ b/tools/testing/selftests/bpf/prog_tests/user_mem.c
@@ -12,8 +12,11 @@ enum {
 	USER_MEM_MMAP = 1,
 	USER_MEM_UNMAP,
 	USER_MEM_COPY_INSNS,
+	USER_MEM_COPY,
 };
 
+static const char data[] = "fault-enabled BPF write";
+
 static noinline void user_mem_trigger(void)
 {
 	asm volatile ("" ::: "memory");
@@ -102,6 +105,18 @@ static bool user_mem_copy_insns(struct user_mem *skel, unsigned long addr)
 	return ASSERT_OK(skel->bss->ret, "user_write");
 }
 
+static int user_mem_copy(struct user_mem *skel, unsigned long addr,
+			u32 size, u64 flags)
+{
+	skel->bss->user_addr = addr;
+	skel->bss->copy_size = size;
+	skel->bss->copy_flags = flags;
+	skel->bss->action = USER_MEM_COPY;
+	user_mem_trigger();
+
+	return skel->bss->ret;
+}
+
 static void test_execute_user_page(struct user_mem *skel)
 {
 	long page_size = sysconf(_SC_PAGESIZE);
@@ -163,6 +178,91 @@ static void test_partial_exec(struct user_mem *skel)
 		munmap((void *)addr, page_size * 3);
 }
 
+static void test_faultable_write(struct user_mem *skel)
+{
+	long page_size = sysconf(_SC_PAGESIZE);
+	unsigned long addr = ~0UL;
+	unsigned char vec;
+	int i;
+
+	if (!user_mem_mmap(skel, page_size, PROT_READ | PROT_WRITE, &addr))
+		return;
+
+	/* Make sure there is a private resident page to page out. */
+	memset((void *)addr, 0, page_size);
+
+	/* Retry pageout until mincore confirms that the page is no longer resident. */
+	for (i = 0; i < 500; i++) {
+		if (madvise((void *)addr, page_size, MADV_PAGEOUT)) {
+			test__skip();
+			goto out;
+		}
+		if (!ASSERT_OK(mincore((void *)addr, page_size, &vec), "mincore"))
+			goto out;
+		if (!(vec & 1))
+			break;
+		usleep(100);
+	}
+	if (i == 500) {
+		test__skip();
+		goto out;
+	}
+
+	/* ..and do the copy to it. */
+	if (!ASSERT_OK(user_mem_copy(skel, addr, sizeof(data), 0), "user_write_data"))
+		goto out;
+
+	ASSERT_EQ(memcmp((void *)addr, data, sizeof(data)), 0, "written_data");
+
+out:
+	if (addr != ~0UL)
+		munmap((void *)addr, page_size);
+}
+
+static void test_copy_to_readonly_mapping(struct user_mem *skel)
+{
+	long page_size = sysconf(_SC_PAGESIZE);
+	unsigned long addr = ~0UL;
+
+	if (!user_mem_mmap(skel, page_size, PROT_READ, &addr))
+		return;
+
+	/* Assert we can copy to read-only mapping. */
+	if (!ASSERT_OK(user_mem_copy(skel, addr, sizeof(data), 0), "readonly_copy"))
+		goto out;
+
+	ASSERT_EQ(memcmp((void *)addr, data, sizeof(data)), 0, "readonly_copy_data");
+
+out:
+	if (addr != ~0UL)
+		munmap((void *)addr, page_size);
+}
+
+static void test_partial_copy_fault(struct user_mem *skel)
+{
+	long page_size = sysconf(_SC_PAGESIZE);
+	bool second_page_unmapped = false;
+	unsigned long addr = ~0UL;
+
+	if (!user_mem_mmap(skel, page_size * 2, PROT_READ | PROT_WRITE, &addr))
+		return;
+
+	if (!user_mem_munmap(skel, addr + page_size, page_size))
+		goto out;
+
+	second_page_unmapped = true;
+
+	ASSERT_EQ(user_mem_copy(skel, addr + page_size - 1, sizeof(data), 0),
+		  -EFAULT, "partial_copy");
+
+	/* The byte before the fault stays written. */
+	ASSERT_EQ(((char *)addr)[page_size - 1], 'f', "partial_copy_data");
+
+out:
+	if (addr != ~0UL)
+		munmap((void *)addr, second_page_unmapped ? page_size : page_size * 2);
+}
+
 void test_user_mem(void)
 {
 	struct user_mem *skel;
@@ -181,6 +281,12 @@ void test_user_mem(void)
 		test_execute_user_page(skel);
 	if (test__start_subtest("partial_exec"))
 		test_partial_exec(skel);
+	if (test__start_subtest("faultable_write"))
+		test_faultable_write(skel);
+	if (test__start_subtest("copy_to_readonly_mapping"))
+		test_copy_to_readonly_mapping(skel);
+	if (test__start_subtest("partial_copy_fault"))
+		test_partial_copy_fault(skel);
 
 out:
 	user_mem__destroy(skel);
diff --git a/tools/testing/selftests/bpf/progs/user_mem.c b/tools/testing/selftests/bpf/progs/user_mem.c
index a62916ebc0b5..a5dd347a02b4 100644
--- a/tools/testing/selftests/bpf/progs/user_mem.c
+++ b/tools/testing/selftests/bpf/progs/user_mem.c
@@ -17,6 +17,7 @@ enum {
 	USER_MEM_MMAP = 1,
 	USER_MEM_UNMAP,
 	USER_MEM_COPY_INSNS,
+	USER_MEM_COPY,
 };
 
 int monitored_pid;
@@ -27,6 +28,9 @@ unsigned long user_addr;
 unsigned long map_len;
 unsigned long map_prot;
 
+u64 copy_flags;
+u32 copy_size;
+
 #if defined(__TARGET_ARCH_x86)
 static const unsigned char insns[] = {
 	0xf3, 0x0f, 0x1e, 0xfa, /* endbr64 */
@@ -43,9 +47,13 @@ static const unsigned char insns[] = {
 static const unsigned char insns[] = { 0 };
 #endif
 
+static const char data[] = "fault-enabled BPF write";
+
 SEC("uprobe.s//proc/self/exe:user_mem_trigger")
 int user_mem_test(struct pt_regs *ctx)
 {
+	u32 size;
+
 	if ((bpf_get_current_pid_tgid() >> 32) != monitored_pid)
 		return 0;
 
@@ -59,6 +67,15 @@ int user_mem_test(struct pt_regs *ctx)
 	case USER_MEM_COPY_INSNS:
 		ret = bpf_copy_to_user((void *)user_addr, insns, sizeof(insns), 0);
 		break;
+	case USER_MEM_COPY:
+		/* Bound the kfunc length using a local copy of copy_size. */
+		size = copy_size;
+		if (size > sizeof(data)) {
+			ret = -22; /* -EINVAL */
+			break;
+		}
+		ret = bpf_copy_to_user((void *)user_addr, data, size, copy_flags);
+		break;
 	}
 
 	return 0;
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCHv2 bpf-next 8/8] selftests/bpf: Test user memory kfunc errors
  2026-11-03  7:59 [PATCHv2 bpf-next 0/8] bpf: Add user memory kfuncs Jiri Olsa
                   ` (6 preceding siblings ...)
  2026-11-03  7:59 ` [PATCHv2 bpf-next 7/8] selftests/bpf: Test bpf_copy_to_user kfunc Jiri Olsa
@ 2026-11-03  7:59 ` Jiri Olsa
  2026-11-03  8:39   ` bot+bpf-ci
  2026-11-03 18:08 ` [PATCHv2 bpf-next 0/8] bpf: Add user memory kfuncs Alexei Starovoitov
  8 siblings, 1 reply; 19+ messages in thread
From: Jiri Olsa @ 2026-11-03  7:59 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann
  Cc: bpf, linux-mm, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Lorenzo Stoakes, John Fastabend,
	Federico Di Pierro

Test invalid copy arguments, zero-length mapping operations, partial copy
faults, and copying to read-only mappings. Verify that a failed unmap
leaves the mapping intact, and that non-sleepable programs and programs
without CAP_SYS_ADMIN cannot call bpf_user_anon_mmap.

Assisted-by: Codex:GPT-6
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 .../selftests/bpf/prog_tests/user_mem.c       | 57 +++++++++++++++++++
 tools/testing/selftests/bpf/progs/user_mem.c  | 29 ++++++++++
 2 files changed, 86 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/user_mem.c b/tools/testing/selftests/bpf/prog_tests/user_mem.c
index c87620e39abb..120900bc1869 100644
--- a/tools/testing/selftests/bpf/prog_tests/user_mem.c
+++ b/tools/testing/selftests/bpf/prog_tests/user_mem.c
@@ -263,10 +263,59 @@ static void test_partial_copy_fault(struct user_mem *skel)
 		munmap((void *)addr, second_page_unmapped ? page_size : page_size * 2);
 }
 
+static void test_zero_length_mapping(struct user_mem *skel)
+{
+	skel->bss->map_len = 0;
+	skel->bss->map_prot = PROT_READ | PROT_WRITE;
+	skel->bss->action = USER_MEM_MMAP;
+	user_mem_trigger();
+
+	ASSERT_EQ(skel->bss->user_addr, (unsigned long)-EINVAL, "zero_length_mapping");
+}
+
+static void test_failed_unmap(struct user_mem *skel)
+{
+	long page_size = sysconf(_SC_PAGESIZE);
+	unsigned long addr = ~0UL;
+
+	if (!user_mem_mmap(skel, page_size, PROT_READ | PROT_WRITE, &addr))
+		return;
+
+	skel->bss->user_addr = addr;
+	skel->bss->map_len = 0;
+	skel->bss->action = USER_MEM_UNMAP;
+	user_mem_trigger();
+
+	if (ASSERT_EQ(skel->bss->ret, -EINVAL, "zero_length_unmap"))
+		check_user_mem_vma((void *)addr, page_size, "rw-p");
+
+	munmap((void *)addr, page_size);
+}
+
+static void test_invalid_copy(struct user_mem *skel)
+{
+	ASSERT_EQ(user_mem_copy(skel, 1, sizeof(data), 0), -EFAULT, "bad_copy");
+	ASSERT_EQ(user_mem_copy(skel, 1, sizeof(data), 1), -EINVAL, "bad_copy_flags");
+	ASSERT_EQ(user_mem_copy(skel, 1, 0, 0), 0, "zero_length_copy");
+	ASSERT_EQ(user_mem_copy(skel, 1, 0, 1), -EINVAL, "zero_length_copy_bad_flags");
+}
+
+static void test_invalid_prot(struct user_mem *skel)
+{
+	skel->bss->map_len = sysconf(_SC_PAGESIZE);
+	skel->bss->map_prot = 0x80000000;
+	skel->bss->action = USER_MEM_MMAP;
+	user_mem_trigger();
+
+	ASSERT_EQ(skel->bss->user_addr, -EINVAL, "invalid_prot");
+}
+
 void test_user_mem(void)
 {
 	struct user_mem *skel;
 
+	RUN_TESTS(user_mem);
+
 	skel = user_mem__open_and_load();
 	if (!ASSERT_OK_PTR(skel, "open_and_load"))
 		return;
@@ -287,6 +336,14 @@ void test_user_mem(void)
 		test_copy_to_readonly_mapping(skel);
 	if (test__start_subtest("partial_copy_fault"))
 		test_partial_copy_fault(skel);
+	if (test__start_subtest("failed_unmap"))
+		test_failed_unmap(skel);
+	if (test__start_subtest("invalid_copy"))
+		test_invalid_copy(skel);
+	if (test__start_subtest("invalid_prot"))
+		test_invalid_prot(skel);
+	if (test__start_subtest("zero_length_mapping"))
+		test_zero_length_mapping(skel);
 
 out:
 	user_mem__destroy(skel);
diff --git a/tools/testing/selftests/bpf/progs/user_mem.c b/tools/testing/selftests/bpf/progs/user_mem.c
index a5dd347a02b4..5a0e6a6dcccf 100644
--- a/tools/testing/selftests/bpf/progs/user_mem.c
+++ b/tools/testing/selftests/bpf/progs/user_mem.c
@@ -80,3 +80,32 @@ int user_mem_test(struct pt_regs *ctx)
 
 	return 0;
 }
+
+SEC("?uprobe//proc/self/exe:user_mem_trigger")
+__failure
+__msg("program must be sleepable to call sleepable kfunc bpf_user_anon_mmap")
+__msg("cannot be used in non-sleepable program")
+int user_mem_nonsleepable(struct pt_regs *ctx)
+{
+	bpf_user_anon_mmap(4096, 1);
+	return 0;
+}
+
+SEC("?kprobe")
+__failure
+__msg("program must be sleepable to call sleepable kfunc bpf_user_anon_mmap")
+__msg("cannot be used in non-sleepable program")
+int user_mem_kprobe(struct pt_regs *ctx)
+{
+	bpf_user_anon_mmap(4096, 1);
+	return 0;
+}
+
+SEC("?uprobe.s//proc/self/exe:user_mem_trigger")
+__failure_unpriv __caps_unpriv(CAP_BPF | CAP_PERFMON)
+__msg_unpriv("calling kernel function bpf_user_anon_mmap is not allowed")
+int user_mem_no_sysadmin(struct pt_regs *ctx)
+{
+	bpf_user_anon_mmap(4096, 1);
+	return 0;
+}
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCHv2 bpf-next 5/8] selftests/bpf: Test user memory mmap kfuncs
  2026-11-03  7:59 ` [PATCHv2 bpf-next 5/8] selftests/bpf: Test user memory mmap kfuncs Jiri Olsa
@ 2026-11-03  8:05   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-11-03  8:05 UTC (permalink / raw)
  To: Jiri Olsa, Alexei Starovoitov, Daniel Borkmann
  Cc: bpf, linux-mm, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Lorenzo Stoakes, John Fastabend,
	Federico Di Pierro

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] selftests/bpf: call to undefined get_procmap_info() in user_mem.c
- [Medium] selftests/bpf: unresolved kfunc externs in progs/user_mem.c
--

commit c7cc5f98b3a4b4d8d168ef0934e6c41fe197849d
Author: Jiri Olsa <jolsa@kernel.org>

selftests/bpf: Test user memory mmap kfuncs

This commit adds BPF selftests for bpf_user_anon_mmap and bpf_user_munmap
invoked from a sleepable uprobe, verifying mapping permissions and
unmapping behavior.

> diff --git a/tools/testing/selftests/bpf/prog_tests/user_mem.c b/tools/testing/selftests/bpf/prog_tests/user_mem.c
> new file mode 100644
> index 0000000000000..f301a18437062
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/user_mem.c
> @@ -0,0 +1,112 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <test_progs.h>
> +#include <sys/mman.h>
> +#include <sys/personality.h>
> +#include <unistd.h>
> +
> +#include "user_mem.skel.h"
> +#include "trace_helpers.h"
> +
> +enum {
> +	USER_MEM_MMAP = 1,
> +	USER_MEM_UNMAP,
> +};
> +
> +static noinline void user_mem_trigger(void)
> +{
> +	asm volatile ("" ::: "memory");
> +}
> +
> +static bool check_user_mem_vma(void *addr, unsigned long size, const char *perms)
> +{
> +	size_t start, end;
> +	char vma_perms[5];
> +	int err;
> +
> +	err = get_procmap_info(addr, &start, &end, vma_perms);
> +	if (!ASSERT_OK(err, "user_mem_vma"))
> +		return false;
> +
> +	/* VMA can be merged, so ensure it covers at least the requested range. */
> +	if (!ASSERT_GE(end - (size_t)addr, size, "vma_size"))
> +		return false;
> +
> +	return !perms || ASSERT_STREQ(vma_perms, perms, "vma_perms");
> +}
> +
> +static bool check_user_mem_unmapped(void *addr)
> +{
> +	size_t start, end;
> +	int err;
> +
> +	err = get_procmap_info(addr, &start, &end, NULL);

[Severity: Medium]
Where is get_procmap_info() defined? Both check_user_mem_vma() and
check_user_mem_unmapped() call get_procmap_info(), and this file includes
trace_helpers.h, but neither tools/testing/selftests/bpf/trace_helpers.h nor
tools/testing/selftests/bpf/trace_helpers.c declares or defines
get_procmap_info().

Because test_progs objects are compiled with -Wall -Werror, won't calling an
undeclared and undefined get_procmap_info() cause compiling and linking
tools/testing/selftests/bpf to fail?

> diff --git a/tools/testing/selftests/bpf/progs/user_mem.c b/tools/testing/selftests/bpf/progs/user_mem.c
> new file mode 100644
> index 0000000000000..e3b0689f5f2a5
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/user_mem.c
> @@ -0,0 +1,43 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <vmlinux.h>
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_tracing.h>
> +
> +#include "bpf_misc.h"
> +
> +char _license[] SEC("license") = "GPL";
> +
> +extern unsigned long bpf_user_anon_mmap(unsigned long len, unsigned long prot) __ksym;
> +extern int bpf_user_munmap(unsigned long addr, unsigned long len) __ksym;

[ ... ]

> +SEC("uprobe.s//proc/self/exe:user_mem_trigger")
> +int user_mem_test(struct pt_regs *ctx)
> +{
> +	if ((bpf_get_current_pid_tgid() >> 32) != monitored_pid)
> +		return 0;
> +
> +	switch (action) {
> +	case USER_MEM_MMAP:
> +		user_addr = bpf_user_anon_mmap(map_len, map_prot);
> +		break;
> +	case USER_MEM_UNMAP:
> +		ret = bpf_user_munmap(user_addr, map_len);
> +		break;
> +	}

[Severity: Medium]
Can user_mem__open_and_load() in test_user_mem() succeed when loading this
program?

user_mem_test() calls bpf_user_anon_mmap() and bpf_user_munmap(), which are
declared as required __ksym externs, but neither symbol is defined or
registered as a BPF kfunc in the kernel or bpf_testmod. Won't libbpf fail to
resolve these kfunc BTF IDs during user_mem__open_and_load(), causing
test_user_mem() to fail unconditionally?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261103075916.100050-6-jolsa@kernel.org?part=1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv2 bpf-next 4/8] selftests/bpf: Test const kfunc memory arguments
  2026-11-03  7:59 ` [PATCHv2 bpf-next 4/8] selftests/bpf: Test const kfunc memory arguments Jiri Olsa
@ 2026-11-03  8:10   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-11-03  8:10 UTC (permalink / raw)
  To: Jiri Olsa, Alexei Starovoitov, Daniel Borkmann
  Cc: bpf, linux-mm, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Lorenzo Stoakes, John Fastabend,
	Federico Di Pierro

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] selftests/bpf: kfunc_call_test_const_mem() fails verifier load with MEM_WRITE
--

commit d0fe747011b126dd39ad19108026e34fdd9f18ce
Author: Jiri Olsa <jolsa@kernel.org>

selftests/bpf: Test const kfunc memory arguments

This commit adds BPF selftests and bpf_testmod kfuncs to test passing
read-only global data, stack data, and const scalar structs to const-qualified
kfunc memory arguments, as well as rejecting read-only data for writable
memory arguments.

> diff --git a/tools/testing/selftests/bpf/progs/kfunc_call_test.c b/tools/testing/selftests/bpf/progs/kfunc_call_test.c
> index 8e6560c31e781..4f4a6713aa6e5 100644
> --- a/tools/testing/selftests/bpf/progs/kfunc_call_test.c
> +++ b/tools/testing/selftests/bpf/progs/kfunc_call_test.c
> @@ -5,6 +5,9 @@
>  #include "bpf_misc.h"
>  #include "../test_kmods/bpf_testmod_kfunc.h"
>  
> +static const char kfunc_const_data[] = "read-only source";
> +static const struct prog_test_pass1 kfunc_const_struct = { .x0 = 1 };
> +
>  static struct bpf_spin_lock kfunc_call_lock SEC(".data.A");
>  
>  SEC("tc")
> @@ -208,6 +211,17 @@ int kfunc_call_test_pass(struct __sk_buff *skb)
>  	return 0;
>  }
>  
> +SEC("tc")
> +int kfunc_call_test_const_mem(struct __sk_buff *skb)
> +{
> +	char stack_data[] = "read-write";
> +
> +	bpf_kfunc_call_test_const_mem(kfunc_const_data, sizeof(kfunc_const_data));
> +	bpf_kfunc_call_test_const_mem(stack_data, sizeof(stack_data));
> +	bpf_kfunc_call_test_const_struct(&kfunc_const_struct);
> +	return 0;
> +}

[Severity: Medium]
Does kfunc_call_test_const_mem() pass the verifier without a corresponding
verifier change to preserve const qualifiers on kfunc memory arguments?

In kernel/bpf/verifier.c, get_kfunc_arg_type() strips all type modifiers
including BTF_KIND_CONST from the pointed-to type via
btf_type_skip_modifiers(), advances ref_id_ptr past them, and unconditionally
sets MEM_WRITE on sized memory arguments:

kernel/bpf/verifier.c:get_kfunc_arg_type() {
	...
	ref_id_ptr = &t->type;
	ref_t = btf_type_skip_modifiers(meta->btf, *ref_id_ptr, &ref_id);
	while (*ref_id_ptr != ref_id)
		ref_id_ptr = &btf_type_by_id(meta->btf, *ref_id_ptr)->type;
	...
	} else if (arg + 1 < nargs &&
		 (is_kfunc_arg_mem_size(meta->btf, &args[arg + 1]) ||
		  is_kfunc_arg_const_mem_size(meta->btf, &args[arg + 1]))) {
		...
		arg_type = ARG_PTR_TO_MEM | MEM_WRITE;
	}
	...
	if (base_type(arg_type) == ARG_PTR_TO_BTF_ID) {
		if (is_kfunc_arg_map(meta->btf, &args[arg]))
			proto->arg_btf_id[arg] = reg2btf_ids[CONST_PTR_TO_MAP];
		else
			proto->arg_btf_id[arg] = ref_id_ptr;
}

Similarly, for bpf_kfunc_call_test_const_struct(), when &kfunc_const_struct
(a PTR_TO_MAP_VALUE in .rodata) is passed, resolve_func_arg_type() converts
the scalar-struct pointer argument to fixed-size memory with MEM_WRITE set:

kernel/bpf/verifier.c:resolve_func_arg_type() {
	...
	if (!btf_type_is_scalar_struct(env, btf, ref_t))
		return 0;
	...
	*arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE | MEM_WRITE |
		    (*arg_type & (PTR_MAYBE_NULL | MEM_UNINIT));
}

When check_func_arg() validates kfunc_const_data and &kfunc_const_struct,
func_arg_access_type() returns BPF_READ | BPF_WRITE because MEM_WRITE is set,
causing check_helper_mem_access() -> check_map_access_type() to reject the
read-only .rodata map with -EACCES ("write into map forbidden").

Because kfunc_call_test_const_mem is an autoloaded SEC("tc") program in
kfunc_call_test.c, wouldn't this cause kfunc_call_test__open_and_load() and
kfunc_call_test_lskel__open_and_load() in verify_success() to fail and break
the kfunc_call test suite?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261103075916.100050-5-jolsa@kernel.org?part=1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv2 bpf-next 2/8] bpf: Add user memory kfuncs
  2026-11-03  7:59 ` [PATCHv2 bpf-next 2/8] bpf: Add user memory kfuncs Jiri Olsa
@ 2026-11-03  8:19   ` sashiko-bot
  2026-11-03  8:52   ` bot+bpf-ci
  2026-11-03 18:05   ` Alexei Starovoitov
  2 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-11-03  8:19 UTC (permalink / raw)
  To: Jiri Olsa, Alexei Starovoitov, Daniel Borkmann
  Cc: bpf, linux-mm, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Lorenzo Stoakes, John Fastabend,
	Federico Di Pierro

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] bpf: bpf_user_vm_mmap() clears active TS_COMPAT on TIF_ADDR32 tasks
- [Medium] bpf: bpf_user_vm_mmap() triggers false in_32bit_syscall() on x86_64
--

commit f91c9a5c8eeb0a10c48d0c7030bd2cd248f6d3fd
Author: Jiri Olsa <jolsa@kernel.org>

bpf: Add user memory kfuncs

This commit adds the sleepable kfuncs bpf_copy_to_user(),
bpf_user_anon_mmap(), and bpf_user_munmap() for KPROBE BPF programs to
modify a user task's address space and manage private anonymous mappings.

> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> index 58e357302850e..ae7dfa58ae95c 100644
> --- a/kernel/bpf/helpers.c
> +++ b/kernel/bpf/helpers.c

[ ... ]

> @@ -3741,6 +3747,110 @@ __bpf_kfunc int bpf_copy_from_user_mm(void *dst, u32 dst__sz,

[ ... ]

> +/* bpf_wq and bpf_task_work callbacks can run without a usable mm. */
> +static bool bpf_user_mm_ok(void)
> +{
> +	return current->mm && !(current->flags & (PF_KTHREAD | PF_EXITING));
> +}
> +
> +#ifdef CONFIG_IA32_EMULATION
> +static unsigned long bpf_user_vm_mmap(unsigned long len, unsigned long prot)
> +{
> +	struct thread_info *ti = current_thread_info();
> +	bool compat = test_thread_flag(TIF_ADDR32);
> +	unsigned long addr;
> +
> +	/*
> +	 * We are not in a syscall, but x86 get_unmapped_area() paths
> +	 * ignore TIF_ADDR32 and rely on in_32bit_syscall().
> +	 */
> +	if (compat)
> +		ti->status |= TS_COMPAT;
> +	addr = vm_mmap(NULL, 0, len, prot, MAP_PRIVATE | MAP_ANONYMOUS, 0);

[Severity: Medium]
As reported by bot+bpf-ci@kernel.org on v1 [1], on x86_64 kernels with
CONFIG_X86_X32_ABI=y, will calling bpf_user_anon_mmap() from a sleepable
uprobe in a native 64-bit process falsely trigger in_32bit_syscall() and
restrict mappings to the 32-bit compat range below 4 GiB?

When a 64-bit task enters a uprobe breakpoint exception (or sys_uprobe /
sys_uretprobe), task_pt_regs(current)->orig_ax is -1. Under
CONFIG_X86_X32_ABI=y, in_32bit_syscall() checks in_x32_syscall():

arch/x86/include/asm/compat.h:in_x32_syscall() {
#ifdef CONFIG_X86_X32_ABI
	if (task_pt_regs(current)->orig_ax & __X32_SYSCALL_BIT)
		return true;
#endif
	return false;
}

Because bpf_user_vm_mmap() passes addr = 0 to vm_mmap(),
arch_get_unmapped_area_topdown() and arch_get_unmapped_area() call
get_mmap_base() and find_start_end(), which both check in_32bit_syscall()
and select mm->mmap_compat_base and task_size_32bit() instead of the 64-bit
address space.

> +	if (compat)
> +		ti->status &= ~TS_COMPAT;

[Severity: Medium]
Could clearing TS_COMPAT unconditionally whenever TIF_ADDR32 is set clobber
an active 32-bit syscall's TS_COMPAT state?

When a 32-bit IA32 task enters a syscall via syscall_32_enter(), TS_COMPAT
is set in current_thread_info()->status and must remain set until
arch_exit_to_user_mode_prepare() clears it after signal handling and
syscall restart fixups:

arch/x86/include/asm/entry-common.h:arch_exit_to_user_mode_prepare() {
	...
	/*
	 * Compat syscalls set TS_COMPAT.  Make sure we clear it before
	 * returning to user mode.  We need to clear it *after* signal
	 * handling, because syscall restart has a fixup for compat
	 * syscalls.  The fixup is exercised by the ptrace_syscall_32
	 * selftest.
	...
	 */
	current_thread_info()->status &= ~(TS_COMPAT | TS_I386_REGS_POKED);
}

If a kprobe schedules a sleepable bpf_task_work callback that runs during
syscall exit (for example via task_work_run() inside get_signal() or
ptrace_notify()) and calls bpf_user_anon_mmap() -> bpf_user_vm_mmap(),
ti->status &= ~TS_COMPAT will clear TS_COMPAT before signal handling
finishes. Subsequent calls to syscall_get_error(), in_ia32_syscall(), and
in_compat_syscall() will then treat the active 32-bit syscall as a 64-bit
syscall.

Should bpf_user_vm_mmap() only set and clear TS_COMPAT when
!(ti->status & TS_COMPAT)?

[1] https://lore.kernel.org/r/571f32a410a85199bc9bcecfe07a9a9b6ef425b2052643e64031dd246c13691e@mail.kernel.org

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261103075916.100050-1-jolsa@kernel.org?part=1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv2 bpf-next 6/8] selftests/bpf: Test user memory page exec
  2026-11-03  7:59 ` [PATCHv2 bpf-next 6/8] selftests/bpf: Test user memory page exec Jiri Olsa
@ 2026-11-03  8:39   ` bot+bpf-ci
  0 siblings, 0 replies; 19+ messages in thread
From: bot+bpf-ci @ 2026-11-03  8:39 UTC (permalink / raw)
  To: jolsa, ast, daniel
  Cc: bpf, linux-mm, andrii, eddyz87, memxor, ljs, john.fastabend,
	nierro92, ast, andrii, daniel, martin.lau, eddyz87, yonghong.song,
	mason, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 1356 bytes --]

> selftests/bpf: Test user memory page exec

TL;DR: Nit only: in user_mem.c selftest, the bpf_copy_to_user() ksym
declaration names its second parameter src__srcptr, which may not match the
kernel prototype's src and isn't a recognised verifier suffix.

> diff --git a/tools/testing/selftests/bpf/progs/user_mem.c b/tools/testing/selftests/bpf/progs/user_mem.c
> --- a/tools/testing/selftests/bpf/progs/user_mem.c
> +++ b/tools/testing/selftests/bpf/progs/user_mem.c

[ ... ]

> @@ -10,10 +10,13 @@ char _license[] SEC("license") = "GPL";
>  
>  extern unsigned long bpf_user_anon_mmap(unsigned long len, unsigned long prot) __ksym;
>  extern int bpf_user_munmap(unsigned long addr, unsigned long len) __ksym;
> +extern int bpf_copy_to_user(void *unsafe_ptr__ign, const void *src__srcptr,
> +			    u32 src__sz, u64 flags) __ksym;

This isn't a bug, but should the second parameter be named src, as in the
kernel prototype of bpf_copy_to_user()?

The __srcptr suffix looks like a verifier annotation, but the verifier does
not recognise it, and it sits between unsafe_ptr__ign and src__sz, which are
real suffixes.


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/41602296728

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv2 bpf-next 8/8] selftests/bpf: Test user memory kfunc errors
  2026-11-03  7:59 ` [PATCHv2 bpf-next 8/8] selftests/bpf: Test user memory kfunc errors Jiri Olsa
@ 2026-11-03  8:39   ` bot+bpf-ci
  0 siblings, 0 replies; 19+ messages in thread
From: bot+bpf-ci @ 2026-11-03  8:39 UTC (permalink / raw)
  To: jolsa, ast, daniel
  Cc: bpf, linux-mm, andrii, eddyz87, memxor, ljs, john.fastabend,
	nierro92, ast, andrii, daniel, martin.lau, eddyz87, yonghong.song,
	mason, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 1728 bytes --]

> selftests/bpf: Test user memory kfunc errors

TL;DR: Test nit only: the user_mem_kprobe() selftest looks redundant with
user_mem_nonsleepable(), since both have the same body and expected failure
and load as the same program type. One may be droppable.

> diff --git a/tools/testing/selftests/bpf/progs/user_mem.c b/tools/testing/selftests/bpf/progs/user_mem.c
> --- a/tools/testing/selftests/bpf/progs/user_mem.c
> +++ b/tools/testing/selftests/bpf/progs/user_mem.c

[ ... ]

> @@ -80,3 +80,32 @@ int user_mem_test(struct pt_regs *ctx)
> +SEC("?uprobe//proc/self/exe:user_mem_trigger")
> +__failure
> +__msg("program must be sleepable to call sleepable kfunc bpf_user_anon_mmap")
> +__msg("cannot be used in non-sleepable program")
> +int user_mem_nonsleepable(struct pt_regs *ctx)
> +{
> +	bpf_user_anon_mmap(4096, 1);
> +	return 0;
> +}
> +
> +SEC("?kprobe")
> +__failure
> +__msg("program must be sleepable to call sleepable kfunc bpf_user_anon_mmap")
> +__msg("cannot be used in non-sleepable program")
> +int user_mem_kprobe(struct pt_regs *ctx)
> +{
> +	bpf_user_anon_mmap(4096, 1);
> +	return 0;
> +}

This isn't a bug, but user_mem_kprobe() has the same body and the same
__failure and __msg() annotations as user_mem_nonsleepable() above it.

Since SEC("kprobe") and SEC("uprobe") both load as BPF_PROG_TYPE_KPROBE
and the attach target is not used at load time, does user_mem_kprobe()
test anything that user_mem_nonsleepable() does not, or could one of the
two be dropped?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/41602296728

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv2 bpf-next 1/8] bpf: Honor const on generic kfunc memory arguments
  2026-11-03  7:59 ` [PATCHv2 bpf-next 1/8] bpf: Honor const on generic kfunc memory arguments Jiri Olsa
@ 2026-11-03  8:52   ` bot+bpf-ci
  2026-11-03 18:03   ` Alexei Starovoitov
  1 sibling, 0 replies; 19+ messages in thread
From: bot+bpf-ci @ 2026-11-03  8:52 UTC (permalink / raw)
  To: jolsa, ast, daniel
  Cc: bpf, linux-mm, andrii, eddyz87, memxor, ljs, john.fastabend,
	nierro92, ast, andrii, daniel, martin.lau, eddyz87, yonghong.song,
	mason, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 1847 bytes --]

> bpf: Honor const on generic kfunc memory arguments

TL;DR: Commit message only: it lacks motivation and doesn't say the verifier
now trusts const in kfunc prototypes, so a kfunc casting const away and
writing could make it unsound; kfuncs.rst doesn't say this either.

>     bpf: Honor const on generic kfunc memory arguments
>
>     Mark generic kfunc memory arguments as read-only when their BTF pointer
>     type points to const data. Preserve that access mode when resolving
>     scalar structs to fixed-size memory.
>
>     Signed-off-by: Jiri Olsa <jolsa@kernel.org>

This isn't a bug, but the changelog describes what changes without saying
why.  Could it explain the motivation, for example that read-only memory
such as .rodata can now be passed to these kfuncs?

Before this patch, generic kfunc memory arguments were checked as
BPF_READ | BPF_WRITE.  After it, a const pointee is checked as BPF_READ
only.  As a result:

  - check_reg_type() now accepts read-only registers (rdonly_mem,
    rdonly_buf, BPF_F_RDONLY_PROG map values, map keys, and
    PTR_TO_BTF_ID | PTR_TRUSTED).

  - Program types that may only read packets can now pass packet
    pointers.

  - check_stack_range_initialized() no longer marks the stack bytes as
    clobbered, so the verifier keeps what it knew about the stack across
    the call.

Could the changelog mention that the verifier now relies on the const
qualifier in a kfunc's C prototype, so a kfunc that casts away const and
writes through the pointer would make the verifier unsound?  Neither the
changelog nor Documentation/bpf/kfuncs.rst says this.


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/41602296728

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv2 bpf-next 2/8] bpf: Add user memory kfuncs
  2026-11-03  7:59 ` [PATCHv2 bpf-next 2/8] bpf: Add user memory kfuncs Jiri Olsa
  2026-11-03  8:19   ` sashiko-bot
@ 2026-11-03  8:52   ` bot+bpf-ci
  2026-11-03 18:05   ` Alexei Starovoitov
  2 siblings, 0 replies; 19+ messages in thread
From: bot+bpf-ci @ 2026-11-03  8:52 UTC (permalink / raw)
  To: jolsa, ast, daniel
  Cc: bpf, linux-mm, andrii, eddyz87, memxor, ljs, john.fastabend,
	nierro92, ast, andrii, daniel, martin.lau, eddyz87, yonghong.song,
	mason, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 1320 bytes --]

> bpf: Add user memory kfuncs

TL;DR: Commit message only: it lists what the diff does; could it describe the
use case and why FOLL_FORCE writes and PROT_EXEC anonymous mappings are
needed? No code bug reported.

>     bpf: Add user memory kfuncs
>
>     Add sleepable kfuncs to copy data into a task's address space and to map
>     or unmap private anonymous memory in the current process.
>
>     Require a usable user mm for mmap and munmap. Register the kfuncs for
>     KPROBE programs and gate them on CAP_SYS_ADMIN BPF token capability and
>     the BPF_WRITE_USER lockdown check.
>
>     Use FOLL_FORCE | FOLL_WRITE for task memory copies and return -EFAULT
>     when a copy is incomplete.
>
>     Rename the syscall info-copy helper to bpf_copy_to_user_buf to avoid
>     the name collision.
>
>     Assisted-by: Codex:GPT-6
>     Signed-off-by: Jiri Olsa <jolsa@kernel.org>

this isn't a bug, but could the changelog describe the use case for these
kfuncs, and why FOLL_FORCE writes and PROT_EXEC anonymous mappings are
needed, instead of listing what the diff does?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/41602296728

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv2 bpf-next 1/8] bpf: Honor const on generic kfunc memory arguments
  2026-11-03  7:59 ` [PATCHv2 bpf-next 1/8] bpf: Honor const on generic kfunc memory arguments Jiri Olsa
  2026-11-03  8:52   ` bot+bpf-ci
@ 2026-11-03 18:03   ` Alexei Starovoitov
  1 sibling, 0 replies; 19+ messages in thread
From: Alexei Starovoitov @ 2026-11-03 18:03 UTC (permalink / raw)
  To: Jiri Olsa, Daniel Borkmann
  Cc: bpf, linux-mm, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Lorenzo Stoakes, John Fastabend,
	Federico Di Pierro

On Mon, Nov 02, 2026 at 11:59 PM Jiri Olsa <jolsa@kernel.org> wrote:
> +	t = btf_type_skip_modifiers(btf, arg->type, NULL);
> +	if (!btf_type_is_ptr(t))
> +		return false;
> +
> +	t = btf_type_by_id(btf, t->type);
> +	while (t) {
> +		switch (BTF_INFO_KIND(t->info)) {
> +		case BTF_KIND_CONST:
> +			return true;
> +		case BTF_KIND_VOLATILE:
> +		case BTF_KIND_RESTRICT:
> +		case BTF_KIND_TYPEDEF:
> +		case BTF_KIND_TYPE_TAG:

My earlier feedback must have been lost ?

This switch is a copy of btf_type_is_modifier().
All three callers already know that the arg is a pointer.
t is never NULL.
One helper of half the size is enough.


The patch from the v1 thread, rebased (compile tested only):

diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
index 73578a4..54d4ce2 100644
--- a/Documentation/bpf/kfuncs.rst
+++ b/Documentation/bpf/kfuncs.rst
@@ -137,6 +137,10 @@ argument as its size. By default, without __sz annotation, the size of the type
 of the pointer is used. Without __sz annotation, a kfunc cannot accept a void
 pointer.

+The verifier checks such memory for read and write access. When the argument
+is a pointer to const, like ``const void *mem``, the verifier checks it for
+read access only, and read-only memory (e.g. ``.rodata``) can be passed.
+
 2.3.2 __k Annotation
 --------------------

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 935b163..21dd273 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -13182,6 +13182,22 @@ static bool is_kfunc_arg_const_mem_size(const struct btf *btf,
 	return btf_param_match_suffix(btf, arg, "__szk");
 }

+/* A kfunc only reads through 'const T *arg'. The compiler enforces it. */
+static enum bpf_type_flag kfunc_arg_mem_flag(const struct btf *btf,
+					     const struct btf_param *arg)
+{
+	const struct btf_type *t = btf_type_skip_modifiers(btf, arg->type, NULL);
+	u32 id, ref_id;
+
+	btf_type_skip_modifiers(btf, t->type, &ref_id);
+	for (id = t->type; id != ref_id; id = t->type) {
+		t = btf_type_by_id(btf, id);
+		if (btf_kind(t) == BTF_KIND_CONST)
+			return MEM_RDONLY;
+	}
+	return MEM_WRITE;
+}
+
 static bool is_kfunc_arg_constant(const struct btf *btf, const struct btf_param *arg)
 {
 	return btf_param_match_suffix(btf, arg, "__k");
@@ -13550,7 +13566,8 @@ static int resolve_func_arg_type(struct bpf_verifier_env *env,
 			PTR_ERR(resolve_ret));
 		return -EINVAL;
 	}
-	*arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE | MEM_WRITE |
+	*arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE |
+		    kfunc_arg_mem_flag(meta->btf, &args[arg]) |
 		    (*arg_type & (PTR_MAYBE_NULL | MEM_UNINIT));

 	return 0;
@@ -14102,7 +14119,7 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
 				reg_arg_name(env, argno), btf_type_str(ref_t), ref_tname);
 			return -EINVAL;
 		}
-		arg_type = ARG_PTR_TO_MEM | MEM_WRITE;
+		arg_type = ARG_PTR_TO_MEM | kfunc_arg_mem_flag(meta->btf, &args[arg]);
 	} else if (btf_type_is_struct(ref_t))
 		/* A pointer to a struct without a size argument is classified as ARG_PTR_TO_BTF_ID */
 		arg_type = ARG_PTR_TO_BTF_ID;
@@ -14127,7 +14144,8 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
 			return -EINVAL;
 		}
 		proto->arg_size[arg] = type_size;
-		arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE | MEM_WRITE;
+		arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE |
+			   kfunc_arg_mem_flag(meta->btf, &args[arg]);
 	}

 	if (is_kfunc_arg_uninit(meta->btf, &args[arg]))


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCHv2 bpf-next 2/8] bpf: Add user memory kfuncs
  2026-11-03  7:59 ` [PATCHv2 bpf-next 2/8] bpf: Add user memory kfuncs Jiri Olsa
  2026-11-03  8:19   ` sashiko-bot
  2026-11-03  8:52   ` bot+bpf-ci
@ 2026-11-03 18:05   ` Alexei Starovoitov
  2 siblings, 0 replies; 19+ messages in thread
From: Alexei Starovoitov @ 2026-11-03 18:05 UTC (permalink / raw)
  To: Jiri Olsa, Daniel Borkmann
  Cc: bpf, linux-mm, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Lorenzo Stoakes, John Fastabend,
	Federico Di Pierro

On Mon, Nov 02, 2026 at 11:59 PM Jiri Olsa <jolsa@kernel.org> wrote:
> +#ifdef CONFIG_IA32_EMULATION
> +static unsigned long bpf_user_vm_mmap(unsigned long len, unsigned long prot)
> +{
> +	struct thread_info *ti = current_thread_info();
> +	bool compat = test_thread_flag(TIF_ADDR32);
> +	unsigned long addr;
> +
> +	/*
> +	 * We are not in a syscall, but x86 get_unmapped_area() paths
> +	 * ignore TIF_ADDR32 and rely on in_32bit_syscall().
> +	 */
> +	if (compat)
> +		ti->status |= TS_COMPAT;
> +	addr = vm_mmap(NULL, 0, len, prot, MAP_PRIVATE | MAP_ANONYMOUS, 0);
> +	if (compat)
> +		ti->status &= ~TS_COMPAT;

Do you have users with ia32 tasks ?
Without this code bpf_user_anon_mmap() returns -ENOMEM in an ia32 task.
Nothing breaks.

arch_uprobe_get_xol_area() calls it a HACK, and that one is in arch/x86.
kernel/bpf/helpers.c has no arch #ifdef today. Let's keep it this way.

Drop it and say in the comment that ia32 tasks are not supported.
Pls don't add code just because a bot said so.

pw-bot: cr


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv2 bpf-next 0/8] bpf: Add user memory kfuncs
  2026-11-03  7:59 [PATCHv2 bpf-next 0/8] bpf: Add user memory kfuncs Jiri Olsa
                   ` (7 preceding siblings ...)
  2026-11-03  7:59 ` [PATCHv2 bpf-next 8/8] selftests/bpf: Test user memory kfunc errors Jiri Olsa
@ 2026-11-03 18:08 ` Alexei Starovoitov
  8 siblings, 0 replies; 19+ messages in thread
From: Alexei Starovoitov @ 2026-11-03 18:08 UTC (permalink / raw)
  To: Jiri Olsa, Daniel Borkmann
  Cc: bpf, linux-mm, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Lorenzo Stoakes, John Fastabend,
	Federico Di Pierro

On Mon, Nov 02, 2026 at 11:59 PM Jiri Olsa <jolsa@kernel.org> wrote:
> hi,
> we have few use cases where having the ability to allocate normal or
> executable user space memory would help (like to replace function in
> live process or change environment variables).

Too abstract.
See
https://lore.kernel.org/bpf/20261016224310.3135990-1-alexei.starovoitov@gmail.com/

I don't see answers to these 5 questions.


^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2026-11-03 18:08 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-03  7:59 [PATCHv2 bpf-next 0/8] bpf: Add user memory kfuncs Jiri Olsa
2026-11-03  7:59 ` [PATCHv2 bpf-next 1/8] bpf: Honor const on generic kfunc memory arguments Jiri Olsa
2026-11-03  8:52   ` bot+bpf-ci
2026-11-03 18:03   ` Alexei Starovoitov
2026-11-03  7:59 ` [PATCHv2 bpf-next 2/8] bpf: Add user memory kfuncs Jiri Olsa
2026-11-03  8:19   ` sashiko-bot
2026-11-03  8:52   ` bot+bpf-ci
2026-11-03 18:05   ` Alexei Starovoitov
2026-11-03  7:59 ` [PATCHv2 bpf-next 3/8] selftests/bpf: Add get_procmap_info helper Jiri Olsa
2026-11-03  7:59 ` [PATCHv2 bpf-next 4/8] selftests/bpf: Test const kfunc memory arguments Jiri Olsa
2026-11-03  8:10   ` sashiko-bot
2026-11-03  7:59 ` [PATCHv2 bpf-next 5/8] selftests/bpf: Test user memory mmap kfuncs Jiri Olsa
2026-11-03  8:05   ` sashiko-bot
2026-11-03  7:59 ` [PATCHv2 bpf-next 6/8] selftests/bpf: Test user memory page exec Jiri Olsa
2026-11-03  8:39   ` bot+bpf-ci
2026-11-03  7:59 ` [PATCHv2 bpf-next 7/8] selftests/bpf: Test bpf_copy_to_user kfunc Jiri Olsa
2026-11-03  7:59 ` [PATCHv2 bpf-next 8/8] selftests/bpf: Test user memory kfunc errors Jiri Olsa
2026-11-03  8:39   ` bot+bpf-ci
2026-11-03 18:08 ` [PATCHv2 bpf-next 0/8] bpf: Add user memory kfuncs Alexei Starovoitov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox