BPF List
 help / color / mirror / Atom feed
* [PATCH bpf v2 1/4] bpf: Add KF_PERFMON kfunc flag
@ 2026-09-10 21:35 Daniel Borkmann
  2026-09-10 21:35 ` [PATCH bpf v2 2/4] bpf: Require CAP_PERFMON for kfuncs reading memory Daniel Borkmann
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Daniel Borkmann @ 2026-09-10 21:35 UTC (permalink / raw)
  To: ast; +Cc: memxor, eddyz87, info, bpf

Tracing related BPF helpers e.g. under bpf_base_func_proto() are gated
behind CAP_PERFMON. However, the same is currently not true for kfuncs
and they are accessible via plain CAP_BPF. Add a new KF_PERFMON flag
which can be used such that check_kfunc_call() ensures env->allow_ptr_leaks
is permitted. This follows similar pattern to existing KF_DESTRUCTIVE flag.

The rejection returns -EPERM to match the other CAP_PERFMON gates in the
verifier, that is, check_ptr_to_btf_access() and check_ptr_to_map_access(),
which report the very same policy to user space.

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 Documentation/bpf/kfuncs.rst | 10 ++++++++++
 include/linux/btf.h          |  1 +
 kernel/bpf/verifier.c        | 14 ++++++++++++++
 3 files changed, 25 insertions(+)

diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
index 85f73e0bbd0f..6691fe8a32c3 100644
--- a/Documentation/bpf/kfuncs.rst
+++ b/Documentation/bpf/kfuncs.rst
@@ -486,6 +486,16 @@ Example usage in BPF program:
 	/* note that the last argument is omitted */
         bpf_task_work_schedule_signal(task, &work->tw, &arrmap, task_work_callback);
 
+2.5.10 KF_PERFMON flag
+----------------------
+
+The KF_PERFMON flag is used for kfuncs that can expose kernel memory or kernel
+addresses to the BPF program, for example by reading through a pointer that the
+verifier does not check. Calling such a kfunc requires CAP_PERFMON, or
+CAP_SYS_ADMIN, in the same way that the equivalent BPF helpers are gated in
+bpf_base_func_proto(). A program loaded with CAP_BPF alone is rejected at load
+time.
+
 2.6 Registering the kfuncs
 --------------------------
 
diff --git a/include/linux/btf.h b/include/linux/btf.h
index 89d5a5c4f117..7c62ea17b116 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -80,6 +80,7 @@
 #define KF_ARENA_ARG2   (1 << 15) /* kfunc takes an arena pointer as its second argument */
 #define KF_IMPLICIT_ARGS (1 << 16) /* kfunc has implicit arguments supplied by the verifier */
 #define KF_SPINLOCK_SAFE (1 << 17) /* kfunc is allowed inside bpf_spin_lock-ed region */
+#define KF_PERFMON      (1 << 18) /* kfunc requires CAP_PERFMON */
 
 /*
  * Tag marking a kernel function as a kfunc. This is meant to minimize the
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 72a3f5998dd2..939e535a3442 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11356,6 +11356,11 @@ static bool is_kfunc_destructive(struct bpf_call_arg_meta *meta)
 	return meta->kfunc_flags & KF_DESTRUCTIVE;
 }
 
+static bool is_kfunc_perfmon(struct bpf_call_arg_meta *meta)
+{
+	return meta->kfunc_flags & KF_PERFMON;
+}
+
 static bool is_kfunc_rcu(struct bpf_call_arg_meta *meta)
 {
 	return meta->kfunc_flags & KF_RCU;
@@ -13834,6 +13839,15 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 		return -EACCES;
 	}
 
+	if (is_kfunc_perfmon(&meta) && !env->allow_ptr_leaks) {
+		verbose(env, "%s is allowed only to CAP_PERFMON and CAP_SYS_ADMIN\n",
+			func_name);
+		operation = bpf_diag_fmt(env, "kfunc %s", func_name);
+		bpf_diag_policy(env, insn_idx, operation, "the kfunc requires CAP_PERFMON",
+				"Load the program with CAP_PERFMON, or avoid the kfunc.");
+		return -EPERM;
+	}
+
 	sleepable = bpf_is_kfunc_sleepable(&meta);
 	if (sleepable && !in_sleepable(env)) {
 		verbose(env, "program must be sleepable to call sleepable kfunc %s\n", func_name);
-- 
2.43.0


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

* [PATCH bpf v2 2/4] bpf: Require CAP_PERFMON for kfuncs reading memory
  2026-09-10 21:35 [PATCH bpf v2 1/4] bpf: Add KF_PERFMON kfunc flag Daniel Borkmann
@ 2026-09-10 21:35 ` Daniel Borkmann
  2026-09-10 21:45   ` sashiko-bot
  2026-09-10 22:33   ` bot+bpf-ci
  2026-09-10 21:35 ` [PATCH bpf v2 3/4] bpf: Require CAP_PERFMON for untrusted read-only memory reads Daniel Borkmann
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 9+ messages in thread
From: Daniel Borkmann @ 2026-09-10 21:35 UTC (permalink / raw)
  To: ast; +Cc: memxor, eddyz87, info, bpf

Mark fault-safe probe reading kfuncs as KF_PERFMON, similarly as we do for
the old-style BPF helper equivalents. bpf_rdonly_cast() is included in this
list as well as it returns PTR_TO_MEM | MEM_RDONLY | PTR_UNTRUSTED for an
unchecked object and is using fault-safe BPF_PROBE_MEM.

Note that only the void form of bpf_rdonly_cast() produced a register that
was readable without CAP_PERFMON. For a struct type id the kfunc returns
PTR_TO_BTF_ID | PTR_UNTRUSTED, whose dereference has always been gated in
check_ptr_to_btf_access(). The flag is not conditional on the type id, so
for the latter it only moves the rejection from the dereference to the call
itself, which is the better place to report it anyway.

The bpf_stream_vprintk() and bpf_stream_print_stack() kfuncs are marked
as well. The former ends up in the same bpf_bprintf_prepare() as the
bpf_snprintf() helper, where %pks, %pus and %pI4 read through a program-
supplied address and %pB resolves one into a symbol. The latter walks the
stack and prints each instruction pointer via %pS.

Lastly, bpf_get_kmem_cache() takes a raw scalar address that the verifier
does not constrain and dereferences the page/slab metadata for it. Field
reads on the returned pointer are still blocked (PTR_TO_BTF_ID | PTR_UNTRUSTED
-> check_ptr_to_btf_access() results in -EPERM without the CAP), but the
NULL/non-NULL result is observable.

Reported-by: STAR Labs SG <info@starlabs.sg>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 v1 -> v2:
   - dropped Fixes tags (Alexei)
   - added bpf_get_kmem_cache (BPF CI, Alexei)

 kernel/bpf/helpers.c | 60 ++++++++++++++++++++++----------------------
 1 file changed, 30 insertions(+), 30 deletions(-)

diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index b3cc5c8fc875..712dca5a2c5b 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -4883,7 +4883,7 @@ BTF_ID(func, bpf_cgroup_release_dtor)
 
 BTF_KFUNCS_START(common_btf_ids)
 BTF_ID_FLAGS(func, bpf_cast_to_kern_ctx, KF_FASTCALL)
-BTF_ID_FLAGS(func, bpf_rdonly_cast, KF_FASTCALL)
+BTF_ID_FLAGS(func, bpf_rdonly_cast, KF_FASTCALL | KF_PERFMON)
 BTF_ID_FLAGS(func, bpf_rcu_read_lock)
 BTF_ID_FLAGS(func, bpf_rcu_read_unlock)
 BTF_ID_FLAGS(func, bpf_dynptr_slice, KF_RET_NULL)
@@ -4920,26 +4920,26 @@ BTF_ID_FLAGS(func, bpf_wq_set_callback, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_wq_start)
 BTF_ID_FLAGS(func, bpf_preempt_disable)
 BTF_ID_FLAGS(func, bpf_preempt_enable)
-BTF_ID_FLAGS(func, bpf_iter_bits_new, KF_ITER_NEW)
+BTF_ID_FLAGS(func, bpf_iter_bits_new, KF_ITER_NEW | KF_PERFMON)
 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_copy_from_user_str, KF_SLEEPABLE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_copy_from_user_task_str, KF_SLEEPABLE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_get_kmem_cache, KF_PERFMON)
 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)
 BTF_ID_FLAGS(func, bpf_iter_kmem_cache_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
 BTF_ID_FLAGS(func, bpf_local_irq_save)
 BTF_ID_FLAGS(func, bpf_local_irq_restore)
 #ifdef CONFIG_BPF_EVENTS
-BTF_ID_FLAGS(func, bpf_probe_read_user_dynptr)
-BTF_ID_FLAGS(func, bpf_probe_read_kernel_dynptr)
-BTF_ID_FLAGS(func, bpf_probe_read_user_str_dynptr)
-BTF_ID_FLAGS(func, bpf_probe_read_kernel_str_dynptr)
-BTF_ID_FLAGS(func, bpf_copy_from_user_dynptr, KF_SLEEPABLE)
-BTF_ID_FLAGS(func, bpf_copy_from_user_str_dynptr, KF_SLEEPABLE)
-BTF_ID_FLAGS(func, bpf_copy_from_user_task_dynptr, KF_SLEEPABLE)
-BTF_ID_FLAGS(func, bpf_copy_from_user_task_str_dynptr, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_probe_read_user_dynptr, KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_probe_read_kernel_dynptr, KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_probe_read_user_str_dynptr, KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_probe_read_kernel_str_dynptr, KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_copy_from_user_dynptr, KF_SLEEPABLE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_copy_from_user_str_dynptr, KF_SLEEPABLE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_copy_from_user_task_dynptr, KF_SLEEPABLE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_copy_from_user_task_str_dynptr, KF_SLEEPABLE | KF_PERFMON)
 #endif
 #ifdef CONFIG_DMA_SHARED_BUFFER
 BTF_ID_FLAGS(func, bpf_iter_dmabuf_new, KF_ITER_NEW | KF_SLEEPABLE)
@@ -4947,26 +4947,26 @@ BTF_ID_FLAGS(func, bpf_iter_dmabuf_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPAB
 BTF_ID_FLAGS(func, bpf_iter_dmabuf_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
 #endif
 BTF_ID_FLAGS(func, __bpf_trap)
-BTF_ID_FLAGS(func, bpf_strcmp);
-BTF_ID_FLAGS(func, bpf_strcasecmp);
-BTF_ID_FLAGS(func, bpf_strncasecmp);
-BTF_ID_FLAGS(func, bpf_strchr);
-BTF_ID_FLAGS(func, bpf_strchrnul);
-BTF_ID_FLAGS(func, bpf_strnchr);
-BTF_ID_FLAGS(func, bpf_strrchr);
-BTF_ID_FLAGS(func, bpf_strlen);
-BTF_ID_FLAGS(func, bpf_strnlen);
-BTF_ID_FLAGS(func, bpf_strspn);
-BTF_ID_FLAGS(func, bpf_strcspn);
-BTF_ID_FLAGS(func, bpf_strstr);
-BTF_ID_FLAGS(func, bpf_strcasestr);
-BTF_ID_FLAGS(func, bpf_strnstr);
-BTF_ID_FLAGS(func, bpf_strncasestr);
+BTF_ID_FLAGS(func, bpf_strcmp, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strcasecmp, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strncasecmp, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strchr, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strchrnul, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strnchr, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strrchr, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strlen, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strnlen, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strspn, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strcspn, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strstr, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strcasestr, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strnstr, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strncasestr, KF_PERFMON);
 #if defined(CONFIG_BPF_LSM) && defined(CONFIG_CGROUPS)
 BTF_ID_FLAGS(func, bpf_cgroup_read_xattr, KF_RCU)
 #endif
-BTF_ID_FLAGS(func, bpf_stream_vprintk, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE)
-BTF_ID_FLAGS(func, bpf_stream_print_stack, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE)
+BTF_ID_FLAGS(func, bpf_stream_vprintk, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_stream_print_stack, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE | KF_PERFMON)
 BTF_ID_FLAGS(func, bpf_task_work_schedule_signal, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_task_work_schedule_resume, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_dynptr_from_file)
-- 
2.43.0


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

* [PATCH bpf v2 3/4] bpf: Require CAP_PERFMON for untrusted read-only memory reads
  2026-09-10 21:35 [PATCH bpf v2 1/4] bpf: Add KF_PERFMON kfunc flag Daniel Borkmann
  2026-09-10 21:35 ` [PATCH bpf v2 2/4] bpf: Require CAP_PERFMON for kfuncs reading memory Daniel Borkmann
@ 2026-09-10 21:35 ` Daniel Borkmann
  2026-09-10 22:33   ` bot+bpf-ci
  2026-09-10 21:35 ` [PATCH bpf v2 4/4] selftests/bpf: Add tests for the KF_PERFMON gates Daniel Borkmann
  2026-09-11  0:00 ` [PATCH bpf v2 1/4] bpf: Add KF_PERFMON kfunc flag patchwork-bot+netdevbpf
  3 siblings, 1 reply; 9+ messages in thread
From: Daniel Borkmann @ 2026-09-10 21:35 UTC (permalink / raw)
  To: ast; +Cc: memxor, eddyz87, info, bpf

Marking bpf_rdonly_cast() KF_PERFMON CAP-limits one producer of PTR_TO_MEM |
MEM_RDONLY | PTR_UNTRUSTED, but not the type itself. A global subprogram
argument tagged __arg_untrusted results in the same register with no kfunc
call.

Reported-by: STAR Labs SG <info@starlabs.sg>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 v1 -> v2:
   - Fixed formatting bpf_diag_policy (Alexei)
   - Dropped Fixes tags (Alexei)

 kernel/bpf/verifier.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 939e535a3442..8058f684a9ea 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -6453,6 +6453,15 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b
 			return -EACCES;
 		}
 
+		if (rdonly_untrusted && !env->allow_ptr_leaks) {
+			verbose(env, "%s access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN\n",
+				reg_type_str(env, reg->type));
+			bpf_diag_policy(env, insn_idx, "read from untrusted read-only memory",
+					"the access requires CAP_PERFMON",
+					"Load the program with CAP_PERFMON, or avoid dereferencing untrusted pointers.");
+			return -EPERM;
+		}
+
 		/*
 		 * Accesses to untrusted PTR_TO_MEM are done through probe
 		 * instructions, hence no need to check bounds in that case.
-- 
2.43.0


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

* [PATCH bpf v2 4/4] selftests/bpf: Add tests for the KF_PERFMON gates
  2026-09-10 21:35 [PATCH bpf v2 1/4] bpf: Add KF_PERFMON kfunc flag Daniel Borkmann
  2026-09-10 21:35 ` [PATCH bpf v2 2/4] bpf: Require CAP_PERFMON for kfuncs reading memory Daniel Borkmann
  2026-09-10 21:35 ` [PATCH bpf v2 3/4] bpf: Require CAP_PERFMON for untrusted read-only memory reads Daniel Borkmann
@ 2026-09-10 21:35 ` Daniel Borkmann
  2026-09-10 22:33   ` bot+bpf-ci
  2026-09-11  0:00 ` [PATCH bpf v2 1/4] bpf: Add KF_PERFMON kfunc flag patchwork-bot+netdevbpf
  3 siblings, 1 reply; 9+ messages in thread
From: Daniel Borkmann @ 2026-09-10 21:35 UTC (permalink / raw)
  To: ast; +Cc: memxor, eddyz87, info, bpf

Add test cases where each one loads with CAP_BPF alone and checks that
the program is correctly rejected.

  # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t verifier_kfunc_perfmon
  [...]
  #627/1   verifier_kfunc_perfmon/rdonly_cast_noperfmon:OK
  #627/2   verifier_kfunc_perfmon/rdonly_cast_noperfmon @unpriv:OK
  #627/3   verifier_kfunc_perfmon/probe_read_kernel_dynptr_noperfmon:OK
  #627/4   verifier_kfunc_perfmon/probe_read_kernel_dynptr_noperfmon @unpriv:OK
  #627/5   verifier_kfunc_perfmon/stream_vprintk_noperfmon:OK
  #627/6   verifier_kfunc_perfmon/stream_vprintk_noperfmon @unpriv:OK
  #627/7   verifier_kfunc_perfmon/get_kmem_cache_noperfmon:OK
  #627/8   verifier_kfunc_perfmon/get_kmem_cache_noperfmon @unpriv:OK
  #627/9   verifier_kfunc_perfmon/arg_untrusted_read_noperfmon:OK
  #627/10  verifier_kfunc_perfmon/arg_untrusted_read_noperfmon @unpriv:OK
  #627     verifier_kfunc_perfmon:OK
  Summary: 1/10 PASSED, 0 SKIPPED, 0/0 FAILED

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 .../selftests/bpf/prog_tests/verifier.c       |  2 +
 .../bpf/progs/verifier_kfunc_perfmon.c        | 75 +++++++++++++++++++
 2 files changed, 77 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/progs/verifier_kfunc_perfmon.c

diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index 64ac49ad67e6..ec9e3907c75d 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -53,6 +53,7 @@
 #include "verifier_iterating_callbacks.skel.h"
 #include "verifier_jeq_infer_not_null.skel.h"
 #include "verifier_jit_convergence.skel.h"
+#include "verifier_kfunc_perfmon.skel.h"
 #include "verifier_ld_ind.skel.h"
 #include "verifier_ldsx.skel.h"
 #include "verifier_leak_ptr.skel.h"
@@ -216,6 +217,7 @@ void test_verifier_int_ptr(void)              { RUN(verifier_int_ptr); }
 void test_verifier_iterating_callbacks(void)  { RUN(verifier_iterating_callbacks); }
 void test_verifier_jeq_infer_not_null(void)   { RUN(verifier_jeq_infer_not_null); }
 void test_verifier_jit_convergence(void)      { RUN(verifier_jit_convergence); }
+void test_verifier_kfunc_perfmon(void)        { RUN(verifier_kfunc_perfmon); }
 void test_verifier_load_acquire(void)         { RUN(verifier_load_acquire); }
 void test_verifier_ld_ind(void)               { RUN(verifier_ld_ind); }
 void test_verifier_ldsx(void)                  { RUN(verifier_ldsx); }
diff --git a/tools/testing/selftests/bpf/progs/verifier_kfunc_perfmon.c b/tools/testing/selftests/bpf/progs/verifier_kfunc_perfmon.c
new file mode 100644
index 000000000000..76c39ef30e96
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_kfunc_perfmon.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+void *user_ptr;
+char dynptr_buf[8];
+u64 kaddr;
+
+extern struct kmem_cache *bpf_get_kmem_cache(u64 addr) __ksym;
+
+SEC("socket")
+__success
+__caps_unpriv(CAP_BPF)
+__failure_unpriv
+__msg_unpriv("bpf_rdonly_cast is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
+int rdonly_cast_noperfmon(void *ctx)
+{
+	char *p = bpf_rdonly_cast(0, 0);
+
+	return p[0x7fff];
+}
+
+SEC("socket")
+__success
+__caps_unpriv(CAP_BPF)
+__failure_unpriv
+__msg_unpriv("bpf_probe_read_kernel_dynptr is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
+int probe_read_kernel_dynptr_noperfmon(void *ctx)
+{
+	struct bpf_dynptr dptr;
+
+	bpf_dynptr_from_mem(dynptr_buf, sizeof(dynptr_buf), 0, &dptr);
+	bpf_probe_read_kernel_dynptr(&dptr, 0, sizeof(dynptr_buf), user_ptr);
+	return 0;
+}
+
+SEC("socket")
+__success
+__caps_unpriv(CAP_BPF)
+__failure_unpriv
+__msg_unpriv("bpf_stream_vprintk is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
+int stream_vprintk_noperfmon(void *ctx)
+{
+	bpf_stream_printk(BPF_STDOUT, "%pB", (void *)kaddr);
+	return 0;
+}
+
+SEC("socket")
+__success
+__caps_unpriv(CAP_BPF)
+__failure_unpriv
+__msg_unpriv("bpf_get_kmem_cache is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
+int get_kmem_cache_noperfmon(void *ctx)
+{
+	return !!bpf_get_kmem_cache(kaddr);
+}
+
+__weak int subprog_untrusted_read(void *p __arg_untrusted)
+{
+	return *(char *)p;
+}
+
+SEC("socket")
+__success
+__caps_unpriv(CAP_BPF)
+__failure_unpriv
+__msg_unpriv("rdonly_untrusted_mem access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
+int arg_untrusted_read_noperfmon(void *ctx)
+{
+	return subprog_untrusted_read(0);
+}
+
+char _license[] SEC("license") = "GPL";
-- 
2.43.0


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

* Re: [PATCH bpf v2 2/4] bpf: Require CAP_PERFMON for kfuncs reading memory
  2026-09-10 21:35 ` [PATCH bpf v2 2/4] bpf: Require CAP_PERFMON for kfuncs reading memory Daniel Borkmann
@ 2026-09-10 21:45   ` sashiko-bot
  2026-09-10 22:33   ` bot+bpf-ci
  1 sibling, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-09-10 21:45 UTC (permalink / raw)
  To: Daniel Borkmann; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Missing bounds check for negative enum values in `bpf_mem_cgroup_memory_events` kfunc.
--

commit cdc4306f1568d5f9112eea54321dd73200076861
Author: Daniel Borkmann <daniel@iogearbox.net>

bpf: Require CAP_PERFMON for kfuncs reading memory

This commit restricts several memory-reading and fault-safe probe BPF kernel
functions (kfuncs) by marking them with the KF_PERFMON flag. It ensures that
these functions, such as bpf_rdonly_cast and bpf_get_kmem_cache, now require
CAP_PERFMON privileges to prevent unprivileged memory access.

> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
[ ... ]

While reviewing the BPF kfuncs context, I noticed a related concern in
bpf_mem_cgroup_memory_events() located in mm/bpf_memcontrol.c.

[Severity: High]
This isn't a bug introduced by this patch, but is it possible for a negative
enum value to bypass the bounds check here?

mm/bpf_memcontrol.c:bpf_mem_cgroup_memory_events() {
        ...
        if (unlikely(event >= MEMCG_NR_MEMORY_EVENTS))
                return (unsigned long)-1;

        return atomic_long_read(&memcg->memory_events[event]);
}

The BPF verifier allows unconstrained scalar arguments for enums and does
not enforce enum bounds internally. If a BPF program passes a negative
integer as the event parameter (such as -1), the >= check evaluates to
false because enums are treated as signed integers.

Could this lead to an out-of-bounds read of kernel memory when the negative
event index is used to access the memory_events array?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260910213510.49358-1-daniel@iogearbox.net?part=2

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

* Re: [PATCH bpf v2 3/4] bpf: Require CAP_PERFMON for untrusted read-only memory reads
  2026-09-10 21:35 ` [PATCH bpf v2 3/4] bpf: Require CAP_PERFMON for untrusted read-only memory reads Daniel Borkmann
@ 2026-09-10 22:33   ` bot+bpf-ci
  0 siblings, 0 replies; 9+ messages in thread
From: bot+bpf-ci @ 2026-09-10 22:33 UTC (permalink / raw)
  To: daniel, ast
  Cc: memxor, eddyz87, info, bpf, ast, andrii, daniel, martin.lau,
	eddyz87, yonghong.song, mason, ihor.solodrai

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

> bpf: Require CAP_PERFMON for untrusted read-only memory reads
>
> Marking bpf_rdonly_cast() KF_PERFMON CAP-limits one producer of PTR_TO_MEM |
> MEM_RDONLY | PTR_UNTRUSTED, but not the type itself. A global subprogram
> argument tagged __arg_untrusted results in the same register with no kfunc
> call.
>
> Reported-by: STAR Labs SG <info@starlabs.sg>
> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>

This looks like a bug fix for PTR_TO_MEM|MEM_RDONLY|PTR_UNTRUSTED support
introduced in an earlier commit. Should this include:

  Fixes: f2362a57aeff ("bpf: allow void* cast using bpf_rdonly_cast()")

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 939e535a3442..8058f684a9ea 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -6453,6 +6453,15 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b
>                         return -EACCES;
>                 }
>
> +               if (rdonly_untrusted && !env->allow_ptr_leaks) {
> +                       verbose(env, "%s access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN\n",
> +                               reg_type_str(env, reg->type));
> +                       bpf_diag_policy(env, insn_idx, "read from untrusted read-only memory",
> +                                       "the access requires CAP_PERFMON",
> +                                       "Load the program with CAP_PERFMON, or avoid dereferencing untrusted pointers.");
> +                       return -EPERM;
> +               }
> +
>                 /*
>                  * Accesses to untrusted PTR_TO_MEM are done through probe
>                  * instructions, hence no need to check bounds in that case.


---
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/34534885787

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

* Re: [PATCH bpf v2 2/4] bpf: Require CAP_PERFMON for kfuncs reading memory
  2026-09-10 21:35 ` [PATCH bpf v2 2/4] bpf: Require CAP_PERFMON for kfuncs reading memory Daniel Borkmann
  2026-09-10 21:45   ` sashiko-bot
@ 2026-09-10 22:33   ` bot+bpf-ci
  1 sibling, 0 replies; 9+ messages in thread
From: bot+bpf-ci @ 2026-09-10 22:33 UTC (permalink / raw)
  To: daniel, ast
  Cc: memxor, eddyz87, info, bpf, ast, andrii, daniel, martin.lau,
	eddyz87, yonghong.song, mason, ihor.solodrai

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

> commit 1ccfa5df2ce00bdc57d58ab0b5aa22cdf8181454
> Author: Daniel Borkmann <daniel@iogearbox.net>
>
>     bpf: Require CAP_PERFMON for kfuncs reading memory
>
>     Mark fault-safe probe reading kfuncs as KF_PERFMON, similarly as we do for
>     the old-style BPF helper equivalents. bpf_rdonly_cast() is included in this
>     list as well as it returns PTR_TO_MEM | MEM_RDONLY | PTR_UNTRUSTED for an
>     unchecked object and is using fault-safe BPF_PROBE_MEM.
>
>     Note that only the void form of bpf_rdonly_cast() produced a register that
>     was readable without CAP_PERFMON. For a struct type id the kfunc returns
>     PTR_TO_BTF_ID | PTR_UNTRUSTED, whose dereference has always been gated in
>     check_ptr_to_btf_access(). The flag is not conditional on the type id, so
>     for the latter it only moves the rejection from the dereference to the call
>     itself, which is the better place to report it anyway.
>
>     The bpf_stream_vprintk() and bpf_stream_print_stack() kfuncs are marked
>     as well. The former ends up in the same bpf_bprintf_prepare() as the
>     bpf_snprintf() helper, where %pks, %pus and %pI4 read through a program-
>     supplied address and %pB resolves one into a symbol. The latter walks the
>     stack and prints each instruction pointer via %pS.
>
>     Lastly, bpf_get_kmem_cache() takes a raw scalar address that the verifier
>     does not constrain and dereferences the page/slab metadata for it. Field
>     reads on the returned pointer are still blocked (PTR_TO_BTF_ID | PTR_UNTRUSTED
>     -> check_ptr_to_btf_access() results in -EPERM without the CAP), but the
>     NULL/non-NULL result is observable.

This isn't a bug, but would it be worth a sentence on the bpf_str* and
bpf_iter_bits_new group too? The changelog gives detailed rationale for
bpf_rdonly_cast(), bpf_stream_vprintk(), bpf_stream_print_stack() and
bpf_get_kmem_cache(), but the 15 bpf_str* kfuncs, bpf_iter_bits_new(),
bpf_copy_from_user_str() and bpf_copy_from_user_task_str() are never named.

The bpf_str* family seems like it might be the widest exposure being closed
here, since __bpf_strncasecmp() reads an unchecked address with
__get_kernel_nofault() and returns a comparison result, and bpf_strchr() and
bpf_strnlen() return byte indices, forming a byte-granularity kernel-memory
read oracle that was reachable from a plain CAP_BPF program.

Also, would it help to note that only CAP_BPF-only non-tracing program types
actually change behaviour? is_perfmon_prog_type() already requires CAP_PERFMON
at load for KPROBE, TRACEPOINT, PERF_EVENT, RAW_TRACEPOINT, TRACING, LSM,
STRUCT_OPS and EXT, so tracing/LSM/sched_ext users are unaffected; the
newly-restricted set is networking, cgroup, socket-filter and syscall
programs (for example bpf_core_cast() calls into bpf_rdonly_cast() via
tools/lib/bpf/bpf_core_read.h, and the bpf_str* helpers are used on the
program's own rodata/map values).


---
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/34534885787

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

* Re: [PATCH bpf v2 4/4] selftests/bpf: Add tests for the KF_PERFMON gates
  2026-09-10 21:35 ` [PATCH bpf v2 4/4] selftests/bpf: Add tests for the KF_PERFMON gates Daniel Borkmann
@ 2026-09-10 22:33   ` bot+bpf-ci
  0 siblings, 0 replies; 9+ messages in thread
From: bot+bpf-ci @ 2026-09-10 22:33 UTC (permalink / raw)
  To: daniel, ast
  Cc: memxor, eddyz87, info, bpf, ast, andrii, daniel, martin.lau,
	eddyz87, yonghong.song, mason, ihor.solodrai

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

> selftests/bpf: Add tests for the KF_PERFMON gates
>
> Add test cases where each one loads with CAP_BPF alone and checks that
> the program is correctly rejected.
>
>   # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t verifier_kfunc_perfmon
>   [...]
>   #627/1   verifier_kfunc_perfmon/rdonly_cast_noperfmon:OK
>   #627/2   verifier_kfunc_perfmon/rdonly_cast_noperfmon @unpriv:OK
>   #627/3   verifier_kfunc_perfmon/probe_read_kernel_dynptr_noperfmon:OK
>   #627/4   verifier_kfunc_perfmon/probe_read_kernel_dynptr_noperfmon @unpriv:OK
>   #627/5   verifier_kfunc_perfmon/stream_vprintk_noperfmon:OK
>   #627/6   verifier_kfunc_perfmon/stream_vprintk_noperfmon @unpriv:OK
>   #627/7   verifier_kfunc_perfmon/get_kmem_cache_noperfmon:OK
>   #627/8   verifier_kfunc_perfmon/get_kmem_cache_noperfmon @unpriv:OK
>   #627/9   verifier_kfunc_perfmon/arg_untrusted_read_noperfmon:OK
>   #627/10  verifier_kfunc_perfmon/arg_untrusted_read_noperfmon @unpriv:OK
>   #627     verifier_kfunc_perfmon:OK
>   Summary: 1/10 PASSED, 0 SKIPPED, 0/0 FAILED

This isn't a bug, but would it be worth saying in the changelog which gates
are covered? The changelog describes only the rejection half (CAP_BPF alone
correctly rejects), but each program also carries __success so it asserts
the privileged load still succeeds. The series gates roughly 30 kfuncs plus
one mem-access path, and this new file exercises four kfuncs
(bpf_rdonly_cast, bpf_probe_read_kernel_dynptr, bpf_stream_vprintk,
bpf_get_kmem_cache) and __arg_untrusted read. Could the changelog mention
this coverage, perhaps also adding a stream_print_stack case alongside
stream_vprintk_noperfmon since it sits in the same helpers.c hunk with the
same flags?

> diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
> index 64ac49ad67e6..ec9e3907c75d 100644
> --- a/tools/testing/selftests/bpf/prog_tests/verifier.c
> +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
> @@ -53,6 +53,7 @@
>  #include "verifier_iterating_callbacks.skel.h"
>  #include "verifier_jeq_infer_not_null.skel.h"
>  #include "verifier_jit_convergence.skel.h"
> +#include "verifier_kfunc_perfmon.skel.h"
>  #include "verifier_ld_ind.skel.h"
>  #include "verifier_ldsx.skel.h"
>  #include "verifier_leak_ptr.skel.h"
> @@ -216,6 +217,7 @@ void test_verifier_int_ptr(void)              { RUN(verifier_int_ptr); }
>  void test_verifier_iterating_callbacks(void)  { RUN(verifier_iterating_callbacks); }
>  void test_verifier_jeq_infer_not_null(void)   { RUN(verifier_jeq_infer_not_null); }
>  void test_verifier_jit_convergence(void)      { RUN(verifier_jit_convergence); }
> +void test_verifier_kfunc_perfmon(void)        { RUN(verifier_kfunc_perfmon); }
>  void test_verifier_load_acquire(void)         { RUN(verifier_load_acquire); }
>  void test_verifier_ld_ind(void)               { RUN(verifier_ld_ind); }
>  void test_verifier_ldsx(void)                  { RUN(verifier_ldsx); }
> diff --git a/tools/testing/selftests/bpf/progs/verifier_kfunc_perfmon.c b/tools/testing/selftests/bpf/progs/verifier_kfunc_perfmon.c
> new file mode 100644
> index 000000000000..76c39ef30e96
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/verifier_kfunc_perfmon.c
> @@ -0,0 +1,75 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <vmlinux.h>
> +#include <bpf/bpf_helpers.h>
> +#include "bpf_misc.h"
> +
> +void *user_ptr;
> +char dynptr_buf[8];
> +u64 kaddr;
> +
> +extern struct kmem_cache *bpf_get_kmem_cache(u64 addr) __ksym;
> +
> +SEC("socket")
> +__success
> +__caps_unpriv(CAP_BPF)
> +__failure_unpriv
> +__msg_unpriv("bpf_rdonly_cast is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
> +int rdonly_cast_noperfmon(void *ctx)
> +{
> +	char *p = bpf_rdonly_cast(0, 0);
> +
> +	return p[0x7fff];
> +}
> +
> +SEC("socket")
> +__success
> +__caps_unpriv(CAP_BPF)
> +__failure_unpriv
> +__msg_unpriv("bpf_probe_read_kernel_dynptr is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
> +int probe_read_kernel_dynptr_noperfmon(void *ctx)
> +{
> +	struct bpf_dynptr dptr;
> +
> +	bpf_dynptr_from_mem(dynptr_buf, sizeof(dynptr_buf), 0, &dptr);
> +	bpf_probe_read_kernel_dynptr(&dptr, 0, sizeof(dynptr_buf), user_ptr);
> +	return 0;
> +}

This isn't a bug, but could probe_read_kernel_dynptr_noperfmon() use kaddr
(or a separate kernel-address global) rather than user_ptr? The same file
already uses kaddr for the other two kernel-address kfuncs
(stream_vprintk_noperfmon and get_kmem_cache_noperfmon). A kernel-read
kfunc taking an argument named user_ptr inverts the meaning of the name.
This doesn't affect the test outcome since the KF_PERFMON check fires before
any argument is validated, but it makes the case harder to read.

> +
> +SEC("socket")
> +__success
> +__caps_unpriv(CAP_BPF)
> +__failure_unpriv
> +__msg_unpriv("bpf_stream_vprintk is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
> +int stream_vprintk_noperfmon(void *ctx)
> +{
> +	bpf_stream_printk(BPF_STDOUT, "%pB", (void *)kaddr);
> +	return 0;
> +}
> +
> +SEC("socket")
> +__success
> +__caps_unpriv(CAP_BPF)
> +__failure_unpriv
> +__msg_unpriv("bpf_get_kmem_cache is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
> +int get_kmem_cache_noperfmon(void *ctx)
> +{
> +	return !!bpf_get_kmem_cache(kaddr);
> +}
> +
> +__weak int subprog_untrusted_read(void *p __arg_untrusted)
> +{
> +	return *(char *)p;
> +}
> +
> +SEC("socket")
> +__success
> +__caps_unpriv(CAP_BPF)
> +__failure_unpriv
> +__msg_unpriv("rdonly_untrusted_mem access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
> +int arg_untrusted_read_noperfmon(void *ctx)
> +{
> +	return subprog_untrusted_read(0);
> +}
> +
> +char _license[] SEC("license") = "GPL";

---
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/34534885787

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

* Re: [PATCH bpf v2 1/4] bpf: Add KF_PERFMON kfunc flag
  2026-09-10 21:35 [PATCH bpf v2 1/4] bpf: Add KF_PERFMON kfunc flag Daniel Borkmann
                   ` (2 preceding siblings ...)
  2026-09-10 21:35 ` [PATCH bpf v2 4/4] selftests/bpf: Add tests for the KF_PERFMON gates Daniel Borkmann
@ 2026-09-11  0:00 ` patchwork-bot+netdevbpf
  3 siblings, 0 replies; 9+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-09-11  0:00 UTC (permalink / raw)
  To: Daniel Borkmann; +Cc: ast, memxor, eddyz87, info, bpf

Hello:

This series was applied to bpf/bpf.git (master)
by Alexei Starovoitov <ast@kernel.org>:

On Thu, 10 Sep 2026 23:35:07 +0200 you wrote:
> Tracing related BPF helpers e.g. under bpf_base_func_proto() are gated
> behind CAP_PERFMON. However, the same is currently not true for kfuncs
> and they are accessible via plain CAP_BPF. Add a new KF_PERFMON flag
> which can be used such that check_kfunc_call() ensures env->allow_ptr_leaks
> is permitted. This follows similar pattern to existing KF_DESTRUCTIVE flag.
> 
> The rejection returns -EPERM to match the other CAP_PERFMON gates in the
> verifier, that is, check_ptr_to_btf_access() and check_ptr_to_map_access(),
> which report the very same policy to user space.
> 
> [...]

Here is the summary with links:
  - [bpf,v2,1/4] bpf: Add KF_PERFMON kfunc flag
    https://git.kernel.org/bpf/bpf/c/88ce88e933e4
  - [bpf,v2,2/4] bpf: Require CAP_PERFMON for kfuncs reading memory
    https://git.kernel.org/bpf/bpf/c/81c975aae375
  - [bpf,v2,3/4] bpf: Require CAP_PERFMON for untrusted read-only memory reads
    https://git.kernel.org/bpf/bpf/c/f9191460cd80
  - [bpf,v2,4/4] selftests/bpf: Add tests for the KF_PERFMON gates
    https://git.kernel.org/bpf/bpf/c/a903f145a891

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



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

end of thread, other threads:[~2026-09-11  0:01 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-10 21:35 [PATCH bpf v2 1/4] bpf: Add KF_PERFMON kfunc flag Daniel Borkmann
2026-09-10 21:35 ` [PATCH bpf v2 2/4] bpf: Require CAP_PERFMON for kfuncs reading memory Daniel Borkmann
2026-09-10 21:45   ` sashiko-bot
2026-09-10 22:33   ` bot+bpf-ci
2026-09-10 21:35 ` [PATCH bpf v2 3/4] bpf: Require CAP_PERFMON for untrusted read-only memory reads Daniel Borkmann
2026-09-10 22:33   ` bot+bpf-ci
2026-09-10 21:35 ` [PATCH bpf v2 4/4] selftests/bpf: Add tests for the KF_PERFMON gates Daniel Borkmann
2026-09-10 22:33   ` bot+bpf-ci
2026-09-11  0:00 ` [PATCH bpf v2 1/4] bpf: Add KF_PERFMON kfunc flag patchwork-bot+netdevbpf

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