Skip to content

Commit 51302c9

Browse files
Byte-LabAlexei Starovoitov
authored andcommitted
bpf: Teach verifier that trusted PTR_TO_BTF_ID pointers are non-NULL
In reg_type_not_null(), we currently assume that a pointer may be NULL if it has the PTR_MAYBE_NULL modifier, or if it doesn't belong to one of several base type of pointers that are never NULL-able. For example, PTR_TO_CTX, PTR_TO_MAP_VALUE, etc. It turns out that in some cases, PTR_TO_BTF_ID can never be NULL as well, though we currently don't specify it. For example, if you had the following program: SEC("tc") long example_refcnt_fail(void *ctx) { struct bpf_cpumask *mask1, *mask2; mask1 = bpf_cpumask_create(); mask2 = bpf_cpumask_create(); if (!mask1 || !mask2) goto error_release; bpf_cpumask_test_cpu(0, (const struct cpumask *)mask1); bpf_cpumask_test_cpu(0, (const struct cpumask *)mask2); error_release: if (mask1) bpf_cpumask_release(mask1); if (mask2) bpf_cpumask_release(mask2); return ret; } The verifier will incorrectly fail to load the program, thinking (unintuitively) that we have a possibly-unreleased reference if the mask is NULL, because we (correctly) don't issue a bpf_cpumask_release() on the NULL path. The reason the verifier gets confused is due to the fact that we don't explicitly tell the verifier that trusted PTR_TO_BTF_ID pointers can never be NULL. Basically, if we successfully get past the if check (meaning both pointers go from ptr_or_null_bpf_cpumask to ptr_bpf_cpumask), the verifier will correctly assume that the references need to be dropped on any possible branch that leads to program exit. However, it will _incorrectly_ think that the ptr == NULL branch is possible, and will erroneously detect it as a branch on which we failed to drop the reference. The solution is of course to teach the verifier that trusted PTR_TO_BTF_ID pointers can never be NULL, so that it doesn't incorrectly think it's possible for the reference to be present on the ptr == NULL branch. A follow-on patch will add a selftest that verifies this behavior. Signed-off-by: David Vernet <void@manifault.com> Link: https://lore.kernel.org/r/20230602150112.1494194-1-void@manifault.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 503e4de commit 51302c9

File tree

1 file changed

+7
-2
lines changed

1 file changed

+7
-2
lines changed

kernel/bpf/verifier.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ static int ref_set_non_owning(struct bpf_verifier_env *env,
197197
struct bpf_reg_state *reg);
198198
static void specialize_kfunc(struct bpf_verifier_env *env,
199199
u32 func_id, u16 offset, unsigned long *addr);
200+
static bool is_trusted_reg(const struct bpf_reg_state *reg);
200201

201202
static bool bpf_map_ptr_poisoned(const struct bpf_insn_aux_data *aux)
202203
{
@@ -442,8 +443,11 @@ static bool type_may_be_null(u32 type)
442443
return type & PTR_MAYBE_NULL;
443444
}
444445

445-
static bool reg_type_not_null(enum bpf_reg_type type)
446+
static bool reg_not_null(const struct bpf_reg_state *reg)
446447
{
448+
enum bpf_reg_type type;
449+
450+
type = reg->type;
447451
if (type_may_be_null(type))
448452
return false;
449453

@@ -453,6 +457,7 @@ static bool reg_type_not_null(enum bpf_reg_type type)
453457
type == PTR_TO_MAP_VALUE ||
454458
type == PTR_TO_MAP_KEY ||
455459
type == PTR_TO_SOCK_COMMON ||
460+
(type == PTR_TO_BTF_ID && is_trusted_reg(reg)) ||
456461
type == PTR_TO_MEM;
457462
}
458463

@@ -13170,7 +13175,7 @@ static int is_branch_taken(struct bpf_reg_state *reg, u64 val, u8 opcode,
1317013175
bool is_jmp32)
1317113176
{
1317213177
if (__is_pointer_value(false, reg)) {
13173-
if (!reg_type_not_null(reg->type))
13178+
if (!reg_not_null(reg))
1317413179
return -1;
1317513180

1317613181
/* If pointer is valid tests against zero will fail so we can

0 commit comments

Comments
 (0)