mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-16 18:08:20 +00:00
bpf: Unify resource leak checks
There are similar checks for covering locks, references, RCU read sections and preempt_disable sections in 3 places in the verifer, i.e. for tail calls, bpf_ld_[abs, ind], and exit path (for BPF_EXIT and bpf_throw). Unify all of these into a common check_resource_leak function to avoid code duplication. Also update the error strings in selftests to the new ones in the same change to ensure clean bisection. Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> Link: https://lore.kernel.org/r/20241103225940.1408302-3-memxor@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
46f7ed32f7
commit
d402755ced
@ -10352,6 +10352,34 @@ static int check_reference_leak(struct bpf_verifier_env *env, bool exception_exi
|
||||
return refs_lingering ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
static int check_resource_leak(struct bpf_verifier_env *env, bool exception_exit, bool check_lock, const char *prefix)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (check_lock && env->cur_state->active_lock.ptr) {
|
||||
verbose(env, "%s cannot be used inside bpf_spin_lock-ed region\n", prefix);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = check_reference_leak(env, exception_exit);
|
||||
if (err) {
|
||||
verbose(env, "%s would lead to reference leak\n", prefix);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (check_lock && env->cur_state->active_rcu_lock) {
|
||||
verbose(env, "%s cannot be used inside bpf_rcu_read_lock-ed region\n", prefix);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (check_lock && env->cur_state->active_preempt_lock) {
|
||||
verbose(env, "%s cannot be used inside bpf_preempt_disable-ed region\n", prefix);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_bpf_snprintf_call(struct bpf_verifier_env *env,
|
||||
struct bpf_reg_state *regs)
|
||||
{
|
||||
@ -10620,26 +10648,9 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
|
||||
|
||||
switch (func_id) {
|
||||
case BPF_FUNC_tail_call:
|
||||
if (env->cur_state->active_lock.ptr) {
|
||||
verbose(env, "tail_call cannot be used inside bpf_spin_lock-ed region\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = check_reference_leak(env, false);
|
||||
if (err) {
|
||||
verbose(env, "tail_call would lead to reference leak\n");
|
||||
err = check_resource_leak(env, false, true, "tail_call");
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (env->cur_state->active_rcu_lock) {
|
||||
verbose(env, "tail_call cannot be used inside bpf_rcu_read_lock-ed region\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (env->cur_state->active_preempt_lock) {
|
||||
verbose(env, "tail_call cannot be used inside bpf_preempt_disable-ed region\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case BPF_FUNC_get_local_storage:
|
||||
/* check that flags argument in get_local_storage(map, flags) is 0,
|
||||
@ -15801,26 +15812,9 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
|
||||
* gen_ld_abs() may terminate the program at runtime, leading to
|
||||
* reference leak.
|
||||
*/
|
||||
err = check_reference_leak(env, false);
|
||||
if (err) {
|
||||
verbose(env, "BPF_LD_[ABS|IND] cannot be mixed with socket references\n");
|
||||
err = check_resource_leak(env, false, true, "BPF_LD_[ABS|IND]");
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (env->cur_state->active_lock.ptr) {
|
||||
verbose(env, "BPF_LD_[ABS|IND] cannot be used inside bpf_spin_lock-ed region\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (env->cur_state->active_rcu_lock) {
|
||||
verbose(env, "BPF_LD_[ABS|IND] cannot be used inside bpf_rcu_read_lock-ed region\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (env->cur_state->active_preempt_lock) {
|
||||
verbose(env, "BPF_LD_[ABS|IND] cannot be used inside bpf_preempt_disable-ed region\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (regs[ctx_reg].type != PTR_TO_CTX) {
|
||||
verbose(env,
|
||||
@ -18606,30 +18600,14 @@ static int do_check(struct bpf_verifier_env *env)
|
||||
return -EINVAL;
|
||||
}
|
||||
process_bpf_exit_full:
|
||||
if (env->cur_state->active_lock.ptr && !env->cur_state->curframe) {
|
||||
verbose(env, "bpf_spin_unlock is missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (env->cur_state->active_rcu_lock && !env->cur_state->curframe) {
|
||||
verbose(env, "bpf_rcu_read_unlock is missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (env->cur_state->active_preempt_lock && !env->cur_state->curframe) {
|
||||
verbose(env, "%d bpf_preempt_enable%s missing\n",
|
||||
env->cur_state->active_preempt_lock,
|
||||
env->cur_state->active_preempt_lock == 1 ? " is" : "(s) are");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 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, for which reference_state must
|
||||
* match caller reference state when it exits.
|
||||
*/
|
||||
err = check_reference_leak(env, exception_exit);
|
||||
err = check_resource_leak(env, exception_exit, !env->cur_state->curframe,
|
||||
"BPF_EXIT instruction");
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -131,7 +131,7 @@ int reject_subprog_with_lock(void *ctx)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("bpf_rcu_read_unlock is missing")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_rcu_read_lock-ed region")
|
||||
int reject_with_rcu_read_lock(void *ctx)
|
||||
{
|
||||
bpf_rcu_read_lock();
|
||||
@ -147,7 +147,7 @@ __noinline static int throwing_subprog(struct __sk_buff *ctx)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("bpf_rcu_read_unlock is missing")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_rcu_read_lock-ed region")
|
||||
int reject_subprog_with_rcu_read_lock(void *ctx)
|
||||
{
|
||||
bpf_rcu_read_lock();
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "bpf_experimental.h"
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("1 bpf_preempt_enable is missing")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
|
||||
int preempt_lock_missing_1(struct __sk_buff *ctx)
|
||||
{
|
||||
bpf_preempt_disable();
|
||||
@ -14,7 +14,7 @@ int preempt_lock_missing_1(struct __sk_buff *ctx)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("2 bpf_preempt_enable(s) are missing")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
|
||||
int preempt_lock_missing_2(struct __sk_buff *ctx)
|
||||
{
|
||||
bpf_preempt_disable();
|
||||
@ -23,7 +23,7 @@ int preempt_lock_missing_2(struct __sk_buff *ctx)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("3 bpf_preempt_enable(s) are missing")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
|
||||
int preempt_lock_missing_3(struct __sk_buff *ctx)
|
||||
{
|
||||
bpf_preempt_disable();
|
||||
@ -33,7 +33,7 @@ int preempt_lock_missing_3(struct __sk_buff *ctx)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("1 bpf_preempt_enable is missing")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
|
||||
int preempt_lock_missing_3_minus_2(struct __sk_buff *ctx)
|
||||
{
|
||||
bpf_preempt_disable();
|
||||
@ -55,7 +55,7 @@ static __noinline void preempt_enable(void)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("1 bpf_preempt_enable is missing")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
|
||||
int preempt_lock_missing_1_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
preempt_disable();
|
||||
@ -63,7 +63,7 @@ int preempt_lock_missing_1_subprog(struct __sk_buff *ctx)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("2 bpf_preempt_enable(s) are missing")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
|
||||
int preempt_lock_missing_2_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
preempt_disable();
|
||||
@ -72,7 +72,7 @@ int preempt_lock_missing_2_subprog(struct __sk_buff *ctx)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("1 bpf_preempt_enable is missing")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
|
||||
int preempt_lock_missing_2_minus_1_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
preempt_disable();
|
||||
|
@ -791,7 +791,7 @@ l0_%=: r0 = *(u8*)skb[0]; \
|
||||
|
||||
SEC("tc")
|
||||
__description("reference tracking: forbid LD_ABS while holding reference")
|
||||
__failure __msg("BPF_LD_[ABS|IND] cannot be mixed with socket references")
|
||||
__failure __msg("BPF_LD_[ABS|IND] would lead to reference leak")
|
||||
__naked void ld_abs_while_holding_reference(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
@ -836,7 +836,7 @@ l0_%=: r7 = 1; \
|
||||
|
||||
SEC("tc")
|
||||
__description("reference tracking: forbid LD_IND while holding reference")
|
||||
__failure __msg("BPF_LD_[ABS|IND] cannot be mixed with socket references")
|
||||
__failure __msg("BPF_LD_[ABS|IND] would lead to reference leak")
|
||||
__naked void ld_ind_while_holding_reference(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
|
@ -187,7 +187,7 @@ l0_%=: r6 = r0; \
|
||||
|
||||
SEC("cgroup/skb")
|
||||
__description("spin_lock: test6 missing unlock")
|
||||
__failure __msg("unlock is missing")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_spin_lock-ed region")
|
||||
__failure_unpriv __msg_unpriv("")
|
||||
__naked void spin_lock_test6_missing_unlock(void)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user