mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 18:21:24 +02:00
Merge branch 'add-validation-for-bpf_set_retval-helper'
Xu Kuohai says:
====================
Add validation for bpf_set_retval helper
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 c4dcfdd406 ("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/
====================
Link: https://patch.msgid.link/20260605140243.664590-1-xukuohai@huaweicloud.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
39a23eee83
|
|
@ -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 = ®s[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 = ®s[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;
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
107
tools/testing/selftests/bpf/progs/verifier_set_retval.c
Normal file
107
tools/testing/selftests/bpf/progs/verifier_set_retval.c
Normal file
|
|
@ -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";
|
||||
Loading…
Reference in New Issue
Block a user