mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 07:51:31 -04:00
bpf: extract check_global_subprog_return_code() for clarity
From: Eduard Zingerman <eddyz87@gmail.com> Both main progs and subprogs use the same function in the verifier, check_return_code, to verify the type and value range of the register being returned. However, subprogs only need a subset of the logic in check_return_code. this also goes the way - check_return_code explicitly checks whether it is handling a subprogram in multiple places, complicating the logic. Separate the handling of the two into separate functions. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com> Link: https://lore.kernel.org/r/20260228184759.108145-4-emil@etsalapatis.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
committed by
Alexei Starovoitov
parent
63ec296239
commit
69ca55e631
@@ -17984,17 +17984,14 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
|
||||
struct bpf_retval_range range = retval_range(0, 1);
|
||||
enum bpf_prog_type prog_type = resolve_prog_type(env->prog);
|
||||
struct bpf_func_state *frame = env->cur_state->frame[0];
|
||||
const bool is_subprog = frame->subprogno;
|
||||
const struct btf_type *reg_type, *ret_type = NULL;
|
||||
int err;
|
||||
|
||||
/* LSM and struct_ops func-ptr's return type could be "void" */
|
||||
if (!is_subprog || frame->in_exception_callback_fn) {
|
||||
if (program_returns_void(env))
|
||||
return 0;
|
||||
}
|
||||
if (!frame->in_async_callback_fn && program_returns_void(env))
|
||||
return 0;
|
||||
|
||||
if (!is_subprog && prog_type == BPF_PROG_TYPE_STRUCT_OPS) {
|
||||
if (prog_type == BPF_PROG_TYPE_STRUCT_OPS) {
|
||||
/* Allow a struct_ops program to return a referenced kptr if it
|
||||
* matches the operator's return type and is in its unmodified
|
||||
* form. A scalar zero (i.e., a null pointer) is also allowed.
|
||||
@@ -18028,15 +18025,6 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
|
||||
goto enforce_retval;
|
||||
}
|
||||
|
||||
if (is_subprog && !frame->in_exception_callback_fn) {
|
||||
if (reg->type != SCALAR_VALUE) {
|
||||
verbose(env, "At subprogram exit the register R%d is not a scalar value (%s)\n",
|
||||
regno, reg_type_str(env, reg->type));
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (prog_type == BPF_PROG_TYPE_STRUCT_OPS && !ret_type)
|
||||
return 0;
|
||||
|
||||
@@ -18059,8 +18047,7 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
|
||||
|
||||
if (!retval_range_within(range, reg)) {
|
||||
verbose_invalid_scalar(env, reg, range, exit_ctx, reg_name);
|
||||
if (!is_subprog &&
|
||||
prog->expected_attach_type == BPF_LSM_CGROUP &&
|
||||
if (prog->expected_attach_type == BPF_LSM_CGROUP &&
|
||||
prog_type == BPF_PROG_TYPE_LSM &&
|
||||
!prog->aux->attach_func_proto->type)
|
||||
verbose(env, "Note, BPF_LSM_CGROUP that attach to void LSM hooks can't modify return value!\n");
|
||||
@@ -18073,6 +18060,29 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_global_subprog_return_code(struct bpf_verifier_env *env)
|
||||
{
|
||||
struct bpf_reg_state *reg = reg_state(env, BPF_REG_0);
|
||||
int err;
|
||||
|
||||
err = check_reg_arg(env, BPF_REG_0, SRC_OP);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (is_pointer_value(env, BPF_REG_0)) {
|
||||
verbose(env, "R%d leaks addr as return value\n", BPF_REG_0);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (reg->type != SCALAR_VALUE) {
|
||||
verbose(env, "At subprogram exit the register R0 is not a scalar value (%s)\n",
|
||||
reg_type_str(env, reg->type));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mark_subprog_changes_pkt_data(struct bpf_verifier_env *env, int off)
|
||||
{
|
||||
struct bpf_subprog_info *subprog;
|
||||
@@ -20866,6 +20876,8 @@ static int process_bpf_exit_full(struct bpf_verifier_env *env,
|
||||
bool *do_print_state,
|
||||
bool exception_exit)
|
||||
{
|
||||
struct bpf_func_state *cur_frame = cur_func(env);
|
||||
|
||||
/* We must do check_reference_leak here before
|
||||
* prepare_func_exit to handle the case when
|
||||
* state->curframe > 0, it may be a callback function,
|
||||
@@ -20899,7 +20911,21 @@ static int process_bpf_exit_full(struct bpf_verifier_env *env,
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = check_return_code(env, BPF_REG_0, "R0");
|
||||
/*
|
||||
* Return from a regular global subprogram differs from return
|
||||
* from the main program or async/exception callback.
|
||||
* Main program exit implies return code restrictions
|
||||
* that depend on program type.
|
||||
* Exit from exception callback is equivalent to main program exit.
|
||||
* Exit from async callback implies return code restrictions
|
||||
* that depend on async scheduling mechanism.
|
||||
*/
|
||||
if (cur_frame->subprogno &&
|
||||
!cur_frame->in_async_callback_fn &&
|
||||
!cur_frame->in_exception_callback_fn)
|
||||
err = check_global_subprog_return_code(env);
|
||||
else
|
||||
err = check_return_code(env, BPF_REG_0, "R0");
|
||||
if (err)
|
||||
return err;
|
||||
return PROCESS_BPF_EXIT;
|
||||
|
||||
Reference in New Issue
Block a user