All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf-next v5 0/3] Add validation for bpf_set_retval helper
@ 2026-06-05 14:02 Xu Kuohai
  2026-06-05 14:02 ` [PATCH bpf-next v5 1/3] selftests/bpf: Restrict bpf_set_retval argument in sk_bypass_prot_mem Xu Kuohai
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Xu Kuohai @ 2026-06-05 14:02 UTC (permalink / raw)
  To: bpf, linux-kernel
  Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Yonghong Song, Stanislav Fomichev, YiFei Zhu, Matt Bobrowski,
	Quan Sun

From: Xu Kuohai <xukuohai@huawei.com>

The bpf_set_retval() helper is used by cgroup BPF programs to set the
return value of the kernel hook. The argument type for this helper is
ARG_ANYTHING. This allows setting a positive value, which no cgroup
hook expects and can cause issues, such as the kernel panic reported
in [1].

This series adds validation for the argument of the bpf_set_retval()
helper.

For BPF_LSM_CGROUP, the same validation as BPF_LSM_MAC is enforced,
i.e. validate the argument against the LSM hook specific range, which
is returned by bpf_lsm_get_retval_range().

For all other cgroup program types, restrict the argument to
[-MAX_ERRNO, 0], which matches the kernel convention of 0 for success
and negative errno for error.

BPF_CGROUP_GETSOCKOPT is an exception from this restriction, since valid
getsockopt implementations may return positive values (e.g. optlen), as
allowed by commit c4dcfdd406aa ("bpf: Move getsockopt retval to struct
bpf_cg_run_ctx").

[1] https://lore.kernel.org/all/567d3206-74a5-44e5-99c6-779c425f399e@std.uestc.edu.cn

v5:
- Use resolve_prog_type(env->prog) instead of env->prog->type for prog type checks
- Target bpf-next tree

v4: https://lore.kernel.org/bpf/20260604130458.617765-1-xukuohai@huaweicloud.com
- Remove the return value limit for BPF_CGROUP_GETSOCKOPT type
- Refine the range of return value of bpf_get_retval helper

v3: https://lore.kernel.org/bpf/20260530101239.590395-1-xukuohai@huaweicloud.com/
- Mark R1 as precise to prevent validation bypass via branch pruning (sashiko)

v2: https://lore.kernel.org/bpf/20260530055557.549474-1-xukuohai@huaweicloud.com/
- Extend validation from LSM cgroup BPF type to all cgroup BPF types (sashiko)

v1: https://lore.kernel.org/bpf/20260523085806.417723-1-xukuohai@huaweicloud.com/

Xu Kuohai (3):
  selftests/bpf: Restrict bpf_set_retval argument in sk_bypass_prot_mem
  bpf: Add validation for bpf_set_retval argument
  selftests/bpf: Add tests for bpf_set_retval validation

 kernel/bpf/verifier.c                         |  55 +++++++++
 .../selftests/bpf/prog_tests/verifier.c       |   2 +
 .../selftests/bpf/progs/sk_bypass_prot_mem.c  |   2 +
 .../selftests/bpf/progs/verifier_set_retval.c | 107 ++++++++++++++++++
 4 files changed, 166 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/progs/verifier_set_retval.c

-- 
2.47.3


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

* [PATCH bpf-next v5 1/3] selftests/bpf: Restrict bpf_set_retval argument in sk_bypass_prot_mem
  2026-06-05 14:02 [PATCH bpf-next v5 0/3] Add validation for bpf_set_retval helper Xu Kuohai
@ 2026-06-05 14:02 ` Xu Kuohai
  2026-06-05 15:19   ` bot+bpf-ci
  2026-06-05 14:02 ` [PATCH bpf-next v5 2/3] bpf: Add validation for bpf_set_retval argument Xu Kuohai
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 9+ messages in thread
From: Xu Kuohai @ 2026-06-05 14:02 UTC (permalink / raw)
  To: bpf, linux-kernel
  Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Yonghong Song, Stanislav Fomichev, YiFei Zhu, Matt Bobrowski,
	Quan Sun

From: Xu Kuohai <xukuohai@huawei.com>

Test sk_bypass_prot_mem passes an unchecked value as argument to helper
bpf_set_retval(). The argument can be outside the valid range enforced
by the strict retval validation added in the next patch.

Restrict the argument to -EFAULT when it is outside the valid range, so
the test will not be rejected by the verifier when retval validation
is enforced.

Signed-off-by: Xu Kuohai <xukuohai@huawei.com>
---
 tools/testing/selftests/bpf/progs/sk_bypass_prot_mem.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tools/testing/selftests/bpf/progs/sk_bypass_prot_mem.c b/tools/testing/selftests/bpf/progs/sk_bypass_prot_mem.c
index 09a00d11ffcc..bae5283fca6b 100644
--- a/tools/testing/selftests/bpf/progs/sk_bypass_prot_mem.c
+++ b/tools/testing/selftests/bpf/progs/sk_bypass_prot_mem.c
@@ -5,6 +5,7 @@
 #include <bpf/bpf_helpers.h>
 #include <bpf/bpf_tracing.h>
 #include <errno.h>
+#include "err.h"
 
 extern int tcp_memory_per_cpu_fw_alloc __ksym;
 extern int udp_memory_per_cpu_fw_alloc __ksym;
@@ -97,6 +98,7 @@ int sock_create(struct bpf_sock *ctx)
 	return 1;
 
 err:
+	set_if_not_errno_or_zero(err, -EFAULT);
 	bpf_set_retval(err);
 	return 0;
 }
-- 
2.47.3


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

* [PATCH bpf-next v5 2/3] bpf: Add validation for bpf_set_retval argument
  2026-06-05 14:02 [PATCH bpf-next v5 0/3] Add validation for bpf_set_retval helper Xu Kuohai
  2026-06-05 14:02 ` [PATCH bpf-next v5 1/3] selftests/bpf: Restrict bpf_set_retval argument in sk_bypass_prot_mem Xu Kuohai
@ 2026-06-05 14:02 ` Xu Kuohai
  2026-06-05 14:42   ` sashiko-bot
  2026-06-05 15:19   ` bot+bpf-ci
  2026-06-05 14:02 ` [PATCH bpf-next v5 3/3] selftests/bpf: Add tests for bpf_set_retval validation Xu Kuohai
  2026-06-05 23:00 ` [PATCH bpf-next v5 0/3] Add validation for bpf_set_retval helper patchwork-bot+netdevbpf
  3 siblings, 2 replies; 9+ messages in thread
From: Xu Kuohai @ 2026-06-05 14:02 UTC (permalink / raw)
  To: bpf, linux-kernel
  Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Yonghong Song, Stanislav Fomichev, YiFei Zhu, Matt Bobrowski,
	Quan Sun

From: Xu Kuohai <xukuohai@huawei.com>

The bpf_set_retval() helper is used by cgroup BPF programs to set the
return value of the target hook. The argument type for this helper is
ARG_ANYTHING. This allows setting a positive value, which no cgroup
hook expects and can cause issues, such as:

- BPF_LSM_CGROUP: a positive value from bpf_lsm_socket_create bypasses
  the err < 0 check in __sock_create(), leaving the socket object
  unallocated. The positive return value is then propagated to the
  syscall entry __sys_socket(), which also bypasses the IS_ERR() guard
  and ultimately causes a NULL pointer dereference.

- BPF_CGROUP_DEVICE: a positive value can be returned through cgroup
  device bpf prog -> devcgroup_check_permission() -> bdev_permission()
  -> bdev_file_open_by_dev(), where ERR_PTR(positive) produces a pointer
  that IS_ERR() does not catch, leading to a wild pointer dereference.

- BPF_CGROUP_SOCK: a positive value can be returned through cgroup sock
  bpf prog -> __cgroup_bpf_run_filter_sk() -> inet_create() ->
  __sock_create(), where inet_create() frees the newly allocated sk
  via sk_common_release() and sets sock->sk = NULL on the non-zero
  return, but __sock_create() only checks err < 0 for cleanup, so a
  positive retval bypasses cleanup and returns a socket with NULL sk
  to userspace, triggering a NULL pointer dereference on subsequent
  socket operations.

- BPF_CGROUP_SYSCTL: a positive value can be returned through the cgroup
  bpf prog -> __cgroup_bpf_run_filter_sysctl() -> proc_sys_call_handler(),
  where a non-zero return bypasses the normal sysctl proc_handler and is
  returned directly to userspace as return value of read() or write()
  syscall.

So add validation for the argument of the bpf_set_retval() helper.

For BPF_LSM_CGROUP, enforce the LSM hook specific range returned by
bpf_lsm_get_retval_range().

For all other cgroup program types, restrict the argument to
[-MAX_ERRNO, 0], which matches the kernel convention of 0 for success
and negative errno for error.

BPF_CGROUP_GETSOCKOPT is an exception, since valid getsockopt
implementations may return positive values, as allowed by commit
c4dcfdd406aa ("bpf: Move getsockopt retval to struct bpf_cg_run_ctx").

Also refine the return value range of bpf_get_retval() so that
values returned by bpf_get_retval() can be passed directly to
bpf_set_retval() without extra manual bounds checking.

Fixes: b44123b4a3dc ("bpf: Add cgroup helpers bpf_{get,set}_retval to get/set syscall return value")
Fixes: 69fd337a975c ("bpf: per-cgroup lsm flavor")
Reported-by: Quan Sun <2022090917019@std.uestc.edu.cn>
Closes: https://lore.kernel.org/all/567d3206-74a5-44e5-99c6-779c425f399e@std.uestc.edu.cn
Signed-off-by: Xu Kuohai <xukuohai@huawei.com>
---
 kernel/bpf/verifier.c | 55 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8ed484cb1a8a..a50280bb0d45 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9770,7 +9770,9 @@ static int do_refine_retval_range(struct bpf_verifier_env *env,
 				  int func_id,
 				  struct bpf_call_arg_meta *meta)
 {
+	struct bpf_retval_range range;
 	struct bpf_reg_state *ret_reg = &regs[BPF_REG_0];
+	enum bpf_prog_type prog_type = resolve_prog_type(env->prog);
 
 	if (ret_type != RET_INTEGER)
 		return 0;
@@ -9790,6 +9792,29 @@ static int do_refine_retval_range(struct bpf_verifier_env *env,
 		reg_set_urange32(ret_reg, 0, nr_cpu_ids - 1);
 		reg_bounds_sync(ret_reg);
 		break;
+	case BPF_FUNC_get_retval:
+		/*
+		 * bpf_get_retval may see arbitrary value passed by bpf_prog_run_array_cg for
+		 * CGROUP_GETSOCKOPT type.
+		 */
+		if (prog_type == BPF_PROG_TYPE_CGROUP_SOCKOPT &&
+		    env->prog->expected_attach_type == BPF_CGROUP_GETSOCKOPT)
+			break;
+
+		if (prog_type == BPF_PROG_TYPE_LSM &&
+		    env->prog->expected_attach_type == BPF_LSM_CGROUP) {
+			if (!env->prog->aux->attach_func_proto->type)
+				break;
+			bpf_lsm_get_retval_range(env->prog, &range);
+		} else {
+			range.minval = -MAX_ERRNO;
+			range.maxval = 0;
+		}
+
+		reg_set_srange64(ret_reg, range.minval, range.maxval);
+		reg_set_srange32(ret_reg, range.minval, range.maxval);
+		reg_bounds_sync(ret_reg);
+		break;
 	}
 
 	return reg_bounds_sanity_check(env, ret_reg, "retval");
@@ -10259,6 +10284,24 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
 		}
 		break;
 	case BPF_FUNC_set_retval:
+	{
+		struct bpf_retval_range range = {
+			.minval = -MAX_ERRNO,
+			.maxval = 0,
+			.return_32bit = true
+		};
+		struct bpf_reg_state *r1 = &regs[BPF_REG_1];
+
+		if (r1->type != SCALAR_VALUE) {
+			verbose(env, "R1 is not a scalar\n");
+			return -EINVAL;
+		}
+
+		/* CGROUP_GETSOCKOPT is allowed to return arbitrary value */
+		if (prog_type == BPF_PROG_TYPE_CGROUP_SOCKOPT &&
+		    env->prog->expected_attach_type == BPF_CGROUP_GETSOCKOPT)
+			break;
+
 		if (prog_type == BPF_PROG_TYPE_LSM &&
 		    env->prog->expected_attach_type == BPF_LSM_CGROUP) {
 			if (!env->prog->aux->attach_func_proto->type) {
@@ -10268,8 +10311,20 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
 				verbose(env, "BPF_LSM_CGROUP that attach to void LSM hooks can't modify return value!\n");
 				return -EINVAL;
 			}
+			bpf_lsm_get_retval_range(env->prog, &range);
 		}
+
+		err = mark_chain_precision(env, BPF_REG_1);
+		if (err)
+			return err;
+
+		if (!retval_range_within(range, r1)) {
+			verbose_invalid_scalar(env, r1, range, "At bpf_set_retval", "R1");
+			return -EINVAL;
+		}
+
 		break;
+	}
 	case BPF_FUNC_dynptr_write:
 	{
 		enum bpf_dynptr_type dynptr_type = meta.dynptr.type;
-- 
2.47.3


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

* [PATCH bpf-next v5 3/3] selftests/bpf: Add tests for bpf_set_retval validation
  2026-06-05 14:02 [PATCH bpf-next v5 0/3] Add validation for bpf_set_retval helper Xu Kuohai
  2026-06-05 14:02 ` [PATCH bpf-next v5 1/3] selftests/bpf: Restrict bpf_set_retval argument in sk_bypass_prot_mem Xu Kuohai
  2026-06-05 14:02 ` [PATCH bpf-next v5 2/3] bpf: Add validation for bpf_set_retval argument Xu Kuohai
@ 2026-06-05 14:02 ` Xu Kuohai
  2026-06-05 23:00 ` [PATCH bpf-next v5 0/3] Add validation for bpf_set_retval helper patchwork-bot+netdevbpf
  3 siblings, 0 replies; 9+ messages in thread
From: Xu Kuohai @ 2026-06-05 14:02 UTC (permalink / raw)
  To: bpf, linux-kernel
  Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Yonghong Song, Stanislav Fomichev, YiFei Zhu, Matt Bobrowski,
	Quan Sun

From: Xu Kuohai <xukuohai@huawei.com>

Add verifier tests to validate bpf_set_retval argument for cgroup
program types.

Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com> #v1
Signed-off-by: Xu Kuohai <xukuohai@huawei.com>
---
 .../selftests/bpf/prog_tests/verifier.c       |   2 +
 .../selftests/bpf/progs/verifier_set_retval.c | 107 ++++++++++++++++++
 2 files changed, 109 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/progs/verifier_set_retval.c

diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index 219ff2969868..89779d897aba 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -117,6 +117,7 @@
 #include "verifier_xdp.skel.h"
 #include "verifier_xdp_direct_packet_access.skel.h"
 #include "verifier_bits_iter.skel.h"
+#include "verifier_set_retval.skel.h"
 #include "verifier_lsm.skel.h"
 #include "verifier_jit_inline.skel.h"
 #include "irq.skel.h"
@@ -266,6 +267,7 @@ void test_verifier_xadd(void)                 { RUN(verifier_xadd); }
 void test_verifier_xdp(void)                  { RUN(verifier_xdp); }
 void test_verifier_xdp_direct_packet_access(void) { RUN(verifier_xdp_direct_packet_access); }
 void test_verifier_bits_iter(void) { RUN(verifier_bits_iter); }
+void test_verifier_set_retval(void)            { RUN(verifier_set_retval); }
 void test_verifier_lsm(void)                  { RUN(verifier_lsm); }
 void test_irq(void)			      { RUN(irq); }
 void test_verifier_mtu(void)		      { RUN(verifier_mtu); }
diff --git a/tools/testing/selftests/bpf/progs/verifier_set_retval.c b/tools/testing/selftests/bpf/progs/verifier_set_retval.c
new file mode 100644
index 000000000000..1415cd15cede
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_set_retval.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include "bpf_misc.h"
+
+SEC("lsm_cgroup/socket_create")
+__description("lsm_cgroup bpf_set_retval success")
+__success
+int BPF_PROG(lsm_cgroup_set_retval_zero_valid, int family, int type, int protocol, int kern)
+{
+	bpf_set_retval(0);
+	return 0;
+}
+
+SEC("lsm_cgroup/socket_create")
+__description("lsm_cgroup bpf_set_retval valid errno")
+__success
+int BPF_PROG(lsm_cgroup_set_retval_negative_valid, int family, int type, int protocol, int kern)
+{
+	bpf_set_retval(-12);
+	return 0;
+}
+
+SEC("lsm_cgroup/socket_create")
+__description("lsm_cgroup bpf_set_retval invalid negative value")
+__failure __msg("should have been in [-4095, 0]")
+int BPF_PROG(lsm_cgroup_set_retval_negative_invalid, int family, int type, int protocol, int kern)
+{
+	bpf_set_retval(-4096);
+	return 0;
+}
+
+SEC("lsm_cgroup/socket_create")
+__description("lsm_cgroup bpf_set_retval invalid positive value")
+__failure __msg("should have been in [-4095, 0]")
+int BPF_PROG(lsm_cgroup_set_retval_positive_invalid, int family, int type, int protocol, int kern)
+{
+	bpf_set_retval(1);
+	return 0;
+}
+
+SEC("cgroup/dev")
+__description("cgroup_device bpf_set_retval success")
+__success
+int cgroup_dev_set_retval_0(struct bpf_cgroup_dev_ctx *ctx)
+{
+	bpf_set_retval(0);
+	return 1;
+}
+
+SEC("cgroup/dev")
+__description("cgroup_device bpf_set_retval valid errno")
+__success
+int cgroup_dev_set_retval_neg_maxerrno(struct bpf_cgroup_dev_ctx *ctx)
+{
+	bpf_set_retval(-4095);
+	return 1;
+}
+
+SEC("cgroup/dev")
+__description("cgroup_device bpf_set_retval invalid positive value")
+__failure __msg("should have been in [-4095, 0]")
+int cgroup_dev_set_retval_1(struct bpf_cgroup_dev_ctx *ctx)
+{
+	bpf_set_retval(1);
+	return 1;
+}
+
+SEC("cgroup/dev")
+__description("cgroup_device bpf_set_retval invalid negative value")
+__failure __msg("should have been in [-4095, 0]")
+int cgroup_dev_set_retval_neg_4096(struct bpf_cgroup_dev_ctx *ctx)
+{
+	bpf_set_retval(-4096);
+	return 1;
+}
+
+SEC("cgroup/dev")
+__description("bpf_set_retval bounds check survives state pruning")
+__failure __msg("should have been in [-4095, 0]")
+__naked int cgroup_dev_set_retval_pruning_bypass(struct bpf_cgroup_dev_ctx *ctx)
+{
+	asm volatile (
+		"call %[bpf_get_prandom_u32];"
+		"if r0 != 0 goto 1f;"
+		"r0 = r0;"
+		"r0 = r0;"
+		"r0 = r0;"
+		"r0 = r0;"
+		"goto 2f;"
+	"1:"
+		"call %[bpf_get_prandom_u32];"
+	"2:"
+		"r1 = r0;"
+		"call %[bpf_set_retval];"
+		"r0 = 1;"
+		"exit;"
+		:
+		: __imm(bpf_get_prandom_u32),
+		  __imm(bpf_set_retval)
+		: __clobber_common
+	);
+}
+
+char _license[] SEC("license") = "GPL";
-- 
2.47.3


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

* Re: [PATCH bpf-next v5 2/3] bpf: Add validation for bpf_set_retval argument
  2026-06-05 14:02 ` [PATCH bpf-next v5 2/3] bpf: Add validation for bpf_set_retval argument Xu Kuohai
@ 2026-06-05 14:42   ` sashiko-bot
  2026-06-05 15:19   ` bot+bpf-ci
  1 sibling, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-06-05 14:42 UTC (permalink / raw)
  To: Xu Kuohai; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] The verifier incorrectly refines the bounds of `bpf_get_retval()` for boolean LSM hooks to [0, 1], but at runtime the returned value can be -EPERM (-1), leading to a register bounds violation and arbitrary out-of-bounds memory access.
--

commit 2c0b94558327f28e9ce1f11e3518238475fc93d6
Author: Xu Kuohai <xukuohai@huawei.com>

bpf: Add validation for bpf_set_retval argument

This commit adds argument validation for the bpf_set_retval() helper to
prevent positive return values in cgroup hooks that expect negative error
codes. It also refines the return value bounds of bpf_get_retval() so it
can be passed directly to bpf_set_retval() without extra manual bounds
checking.

Link: https://lore.kernel.org/all/567d3206-74a5-44e5-99c6-779c425f399e@std.uestc.edu.cn

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 8ed484cb1a8a4..a50280bb0d453 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -9790,6 +9792,29 @@ static int do_refine_retval_range(struct bpf_verifier_env *env,
>  		reg_set_urange32(ret_reg, 0, nr_cpu_ids - 1);
>  		reg_bounds_sync(ret_reg);
>  		break;
> +	case BPF_FUNC_get_retval:
> +		/*
> +		 * bpf_get_retval may see arbitrary value passed by bpf_prog_run_array_cg for
> +		 * CGROUP_GETSOCKOPT type.
> +		 */
> +		if (prog_type == BPF_PROG_TYPE_CGROUP_SOCKOPT &&
> +		    env->prog->expected_attach_type == BPF_CGROUP_GETSOCKOPT)
> +			break;
> +
> +		if (prog_type == BPF_PROG_TYPE_LSM &&
> +		    env->prog->expected_attach_type == BPF_LSM_CGROUP) {
> +			if (!env->prog->aux->attach_func_proto->type)
> +				break;
> +			bpf_lsm_get_retval_range(env->prog, &range);

[Severity: Critical]
Does this cause a verifier bounds violation when multiple programs are
attached to a boolean LSM hook?

When do_refine_retval_range() calls bpf_lsm_get_retval_range() for a boolean
LSM hook, it strictly refines the register bounds to [0, 1].

However, if multiple programs are attached to the same cgroup hook via
BPF_F_ALLOW_MULTI, they run sequentially in bpf_prog_run_array_cg().
If the first program returns 0 (which is within the [0, 1] bounds),
bpf_prog_run_array_cg() translates this into an error code:

kernel/bpf/cgroup.c:bpf_prog_run_array_cg() {
    ...
    if (!func_ret && !IS_ERR_VALUE((long)run_ctx.retval))
        run_ctx.retval = -EPERM;
    ...
}

When the next BPF program in the array executes and calls bpf_get_retval(),
it receives the current run_ctx.retval, which is now -EPERM (-1).

Since the verifier statically assumes bpf_get_retval() will only return
0 or 1, will receiving -1 at runtime allow an attacker to bypass verifier
checks and perform out-of-bounds memory accesses?

> +		} else {
> +			range.minval = -MAX_ERRNO;
> +			range.maxval = 0;
> +		}
> +
> +		reg_set_srange64(ret_reg, range.minval, range.maxval);
> +		reg_set_srange32(ret_reg, range.minval, range.maxval);
> +		reg_bounds_sync(ret_reg);
> +		break;
>  	}
>  
>  	return reg_bounds_sanity_check(env, ret_reg, "retval");

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260605140243.664590-1-xukuohai@huaweicloud.com?part=2

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

* Re: [PATCH bpf-next v5 2/3] bpf: Add validation for bpf_set_retval argument
  2026-06-05 14:02 ` [PATCH bpf-next v5 2/3] bpf: Add validation for bpf_set_retval argument Xu Kuohai
  2026-06-05 14:42   ` sashiko-bot
@ 2026-06-05 15:19   ` bot+bpf-ci
  2026-06-06 10:02     ` Xu Kuohai
  1 sibling, 1 reply; 9+ messages in thread
From: bot+bpf-ci @ 2026-06-05 15:19 UTC (permalink / raw)
  To: xukuohai, bpf, linux-kernel
  Cc: ast, daniel, andrii, martin.lau, eddyz87, memxor, yonghong.song,
	sdf, zhuyifei, mattbobrowski, 2022090917019, ast, andrii, daniel,
	martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai

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

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 8ed484cb1a8a..a50280bb0d45 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -9790,6 +9792,29 @@ static int do_refine_retval_range(struct bpf_verifier_env *env,
>  		reg_set_urange32(ret_reg, 0, nr_cpu_ids - 1);
>  		reg_bounds_sync(ret_reg);
>  		break;
> +	case BPF_FUNC_get_retval:
> +		/*
> +		 * bpf_get_retval may see arbitrary value passed by bpf_prog_run_array_cg for
> +		 * CGROUP_GETSOCKOPT type.
> +		 */
> +		if (prog_type == BPF_PROG_TYPE_CGROUP_SOCKOPT &&
> +		    env->prog->expected_attach_type == BPF_CGROUP_GETSOCKOPT)
> +			break;
> +
> +		if (prog_type == BPF_PROG_TYPE_LSM &&
> +		    env->prog->expected_attach_type == BPF_LSM_CGROUP) {
> +			if (!env->prog->aux->attach_func_proto->type)
> +				break;
> +			bpf_lsm_get_retval_range(env->prog, &range);

For a boolean LSM hook, bpf_lsm_get_retval_range() returns the range
[0, 1], and this code refines the bpf_get_retval() return register to
that range.

Can that static range be violated at runtime when multiple programs are
attached to the same cgroup hook with BPF_F_ALLOW_MULTI?

In bpf_prog_run_array_cg(), if an earlier program returns 0 the retval
is translated to an error:

	if (!func_ret && !IS_ERR_VALUE((long)run_ctx.retval))
		run_ctx.retval = -EPERM;

A later program that calls bpf_get_retval() would then receive -EPERM
(-1) at runtime, which is outside the [0, 1] range the verifier assumed
here.

Does this static-versus-runtime mismatch let the verifier reason about
bounds that the value can actually fall outside of?

This was raised on the v5 posting and does not appear to have a reply
yet:

  https://lore.kernel.org/bpf/20260605144232.95A141F00893@smtp.kernel.org/

> +		} else {
> +			range.minval = -MAX_ERRNO;
> +			range.maxval = 0;
> +		}
> +
> +		reg_set_srange64(ret_reg, range.minval, range.maxval);
> +		reg_set_srange32(ret_reg, range.minval, range.maxval);
> +		reg_bounds_sync(ret_reg);
> +		break;
>  	}
>
>  	return reg_bounds_sanity_check(env, ret_reg, "retval");


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

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

* Re: [PATCH bpf-next v5 1/3] selftests/bpf: Restrict bpf_set_retval argument in sk_bypass_prot_mem
  2026-06-05 14:02 ` [PATCH bpf-next v5 1/3] selftests/bpf: Restrict bpf_set_retval argument in sk_bypass_prot_mem Xu Kuohai
@ 2026-06-05 15:19   ` bot+bpf-ci
  0 siblings, 0 replies; 9+ messages in thread
From: bot+bpf-ci @ 2026-06-05 15:19 UTC (permalink / raw)
  To: xukuohai, bpf, linux-kernel
  Cc: ast, daniel, andrii, martin.lau, eddyz87, memxor, yonghong.song,
	sdf, zhuyifei, mattbobrowski, 2022090917019, ast, andrii, daniel,
	martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai

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

> diff --git a/tools/testing/selftests/bpf/progs/sk_bypass_prot_mem.c b/tools/testing/selftests/bpf/progs/sk_bypass_prot_mem.c
> index 09a00d11ffcc..bae5283fca6b 100644
> --- a/tools/testing/selftests/bpf/progs/sk_bypass_prot_mem.c
> +++ b/tools/testing/selftests/bpf/progs/sk_bypass_prot_mem.c

[ ... ]

> @@ -97,6 +98,7 @@ int sock_create(struct bpf_sock *ctx)
>  	return 1;
>
>  err:
> +	set_if_not_errno_or_zero(err, -EFAULT);
>  	bpf_set_retval(err);
>  	return 0;
>  }

Could this cause a verifier bounds violation when multiple programs are
attached to a boolean LSM hook?

When do_refine_retval_range() calls bpf_lsm_get_retval_range() for a
boolean LSM hook, it strictly refines the register bounds to [0, 1].

However, if multiple programs are attached to the same cgroup hook via
BPF_F_ALLOW_MULTI, they run sequentially in bpf_prog_run_array_cg().
If the first program returns 0 (within the [0, 1] bounds),
bpf_prog_run_array_cg() translates this into an error code:

kernel/bpf/cgroup.c:bpf_prog_run_array_cg() {
    ...
    if (!func_ret && !IS_ERR_VALUE((long)run_ctx.retval))
        run_ctx.retval = -EPERM;
    ...
}

When the next BPF program in the array executes and calls bpf_get_retval(),
it receives the current run_ctx.retval, which is now -EPERM (-1).

Since the verifier statically assumes bpf_get_retval() will only return
0 or 1, could receiving -1 at runtime allow bypassing verifier checks and
performing out-of-bounds memory accesses?

This concern was raised by sashiko-bot on lore in v5:
https://lore.kernel.org/bpf/20260605144232.95A141F00893@smtp.kernel.org


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

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

* Re: [PATCH bpf-next v5 0/3] Add validation for bpf_set_retval helper
  2026-06-05 14:02 [PATCH bpf-next v5 0/3] Add validation for bpf_set_retval helper Xu Kuohai
                   ` (2 preceding siblings ...)
  2026-06-05 14:02 ` [PATCH bpf-next v5 3/3] selftests/bpf: Add tests for bpf_set_retval validation Xu Kuohai
@ 2026-06-05 23:00 ` patchwork-bot+netdevbpf
  3 siblings, 0 replies; 9+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-06-05 23:00 UTC (permalink / raw)
  To: Xu Kuohai
  Cc: bpf, linux-kernel, ast, daniel, andrii, martin.lau, eddyz87,
	memxor, yonghong.song, sdf, zhuyifei, mattbobrowski,
	2022090917019

Hello:

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

On Fri,  5 Jun 2026 14:02:40 +0000 you wrote:
> From: Xu Kuohai <xukuohai@huawei.com>
> 
> The bpf_set_retval() helper is used by cgroup BPF programs to set the
> return value of the kernel hook. The argument type for this helper is
> ARG_ANYTHING. This allows setting a positive value, which no cgroup
> hook expects and can cause issues, such as the kernel panic reported
> in [1].
> 
> [...]

Here is the summary with links:
  - [bpf-next,v5,1/3] selftests/bpf: Restrict bpf_set_retval argument in sk_bypass_prot_mem
    https://git.kernel.org/bpf/bpf-next/c/6fa2839893e3
  - [bpf-next,v5,2/3] bpf: Add validation for bpf_set_retval argument
    https://git.kernel.org/bpf/bpf-next/c/b1f7f67b74c2
  - [bpf-next,v5,3/3] selftests/bpf: Add tests for bpf_set_retval validation
    https://git.kernel.org/bpf/bpf-next/c/7913cdb54ee3

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

* Re: [PATCH bpf-next v5 2/3] bpf: Add validation for bpf_set_retval argument
  2026-06-05 15:19   ` bot+bpf-ci
@ 2026-06-06 10:02     ` Xu Kuohai
  0 siblings, 0 replies; 9+ messages in thread
From: Xu Kuohai @ 2026-06-06 10:02 UTC (permalink / raw)
  To: bot+bpf-ci, xukuohai, bpf, linux-kernel
  Cc: ast, daniel, andrii, martin.lau, eddyz87, memxor, yonghong.song,
	sdf, zhuyifei, mattbobrowski, 2022090917019, martin.lau, clm,
	ihor.solodrai

On 6/5/2026 11:19 PM, bot+bpf-ci@kernel.org wrote:
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>> index 8ed484cb1a8a..a50280bb0d45 100644
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
>> @@ -9790,6 +9792,29 @@ static int do_refine_retval_range(struct bpf_verifier_env *env,
>>   		reg_set_urange32(ret_reg, 0, nr_cpu_ids - 1);
>>   		reg_bounds_sync(ret_reg);
>>   		break;
>> +	case BPF_FUNC_get_retval:
>> +		/*
>> +		 * bpf_get_retval may see arbitrary value passed by bpf_prog_run_array_cg for
>> +		 * CGROUP_GETSOCKOPT type.
>> +		 */
>> +		if (prog_type == BPF_PROG_TYPE_CGROUP_SOCKOPT &&
>> +		    env->prog->expected_attach_type == BPF_CGROUP_GETSOCKOPT)
>> +			break;
>> +
>> +		if (prog_type == BPF_PROG_TYPE_LSM &&
>> +		    env->prog->expected_attach_type == BPF_LSM_CGROUP) {
>> +			if (!env->prog->aux->attach_func_proto->type)
>> +				break;
>> +			bpf_lsm_get_retval_range(env->prog, &range);
> 
> For a boolean LSM hook, bpf_lsm_get_retval_range() returns the range
> [0, 1], and this code refines the bpf_get_retval() return register to
> that range.
> 
> Can that static range be violated at runtime when multiple programs are
> attached to the same cgroup hook with BPF_F_ALLOW_MULTI?
> 
> In bpf_prog_run_array_cg(), if an earlier program returns 0 the retval
> is translated to an error:
> 
> 	if (!func_ret && !IS_ERR_VALUE((long)run_ctx.retval))
> 		run_ctx.retval = -EPERM;
> 
> A later program that calls bpf_get_retval() would then receive -EPERM
> (-1) at runtime, which is outside the [0, 1] range the verifier assumed
> here.
> 
> Does this static-versus-runtime mismatch let the verifier reason about
> bounds that the value can actually fall outside of?
> 
> This was raised on the v5 posting and does not appear to have a reply
> yet:
> 
>    https://lore.kernel.org/bpf/20260605144232.95A141F00893@smtp.kernel.org/
> 

Yes, returning -EPERM for a boolean hook breaks the verifier assumption.
I will create a follow-up patch to fix it, as the current patchset has
already been applied.

>> +		} else {
>> +			range.minval = -MAX_ERRNO;
>> +			range.maxval = 0;
>> +		}
>> +
>> +		reg_set_srange64(ret_reg, range.minval, range.maxval);
>> +		reg_set_srange32(ret_reg, range.minval, range.maxval);
>> +		reg_bounds_sync(ret_reg);
>> +		break;
>>   	}
>>
>>   	return reg_bounds_sanity_check(env, ret_reg, "retval");
> 
> 
> ---
> 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/27021279696


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

end of thread, other threads:[~2026-06-06 10:02 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-05 14:02 [PATCH bpf-next v5 0/3] Add validation for bpf_set_retval helper Xu Kuohai
2026-06-05 14:02 ` [PATCH bpf-next v5 1/3] selftests/bpf: Restrict bpf_set_retval argument in sk_bypass_prot_mem Xu Kuohai
2026-06-05 15:19   ` bot+bpf-ci
2026-06-05 14:02 ` [PATCH bpf-next v5 2/3] bpf: Add validation for bpf_set_retval argument Xu Kuohai
2026-06-05 14:42   ` sashiko-bot
2026-06-05 15:19   ` bot+bpf-ci
2026-06-06 10:02     ` Xu Kuohai
2026-06-05 14:02 ` [PATCH bpf-next v5 3/3] selftests/bpf: Add tests for bpf_set_retval validation Xu Kuohai
2026-06-05 23:00 ` [PATCH bpf-next v5 0/3] Add validation for bpf_set_retval helper patchwork-bot+netdevbpf

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.