Skip to content

Commit 496f4f1

Browse files
author
Alexei Starovoitov
committed
Merge branch 'Don't invoke KPTR_REF destructor on NULL xchg'
David Vernet says: ==================== When a map value is being freed, we loop over all of the fields of the corresponding BPF object and issue the appropriate cleanup calls corresponding to the field's type. If the field is a referenced kptr, we atomically xchg the value out of the map, and invoke the kptr's destructor on whatever was there before. Currently, we always invoke the destructor (or bpf_obj_drop() for a local kptr) on any kptr, including if no value was xchg'd out of the map. This means that any function serving as the kptr's KF_RELEASE destructor must always treat the argument as possibly NULL, and we invoke unnecessary (and seemingly unsafe) cleanup logic for the local kptr path as well. This is an odd requirement -- KF_RELEASE kfuncs that are invoked by BPF programs do not have this restriction, and the verifier will fail to load the program if the register containing the to-be-released type has any untrusted modifiers (e.g. PTR_UNTRUSTED or PTR_MAYBE_NULL). So as to simplify the expectations required for a KF_RELEASE kfunc, this patch set updates the KPTR_REF destructor logic to only be invoked when a non-NULL value is xchg'd out of the map. Additionally, the patch removes now-unnecessary KF_RELEASE calls from several kfuncs, and finally, updates the verifier to have KF_RELEASE automatically imply KF_TRUSTED_ARGS. This restriction was already implicitly happening because of the aforementioned logic in the verifier to reject any regs with untrusted modifiers, and to enforce that KF_RELEASE args are passed with a 0 offset. This change just updates the behavior to match that of other trusted args. This patch is left to the end of the series in case it happens to be controversial, as it arguably is slightly orthogonal to the purpose of the rest of the series. ==================== Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 55fbae0 + 6c831c4 commit 496f4f1

File tree

12 files changed

+30
-33
lines changed

12 files changed

+30
-33
lines changed

Documentation/bpf/kfuncs.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,10 @@ both are orthogonal to each other.
179179
---------------------
180180

181181
The KF_RELEASE flag is used to indicate that the kfunc releases the pointer
182-
passed in to it. There can be only one referenced pointer that can be passed in.
183-
All copies of the pointer being released are invalidated as a result of invoking
184-
kfunc with this flag.
182+
passed in to it. There can be only one referenced pointer that can be passed
183+
in. All copies of the pointer being released are invalidated as a result of
184+
invoking kfunc with this flag. KF_RELEASE kfuncs automatically receive the
185+
protection afforded by the KF_TRUSTED_ARGS flag described below.
185186

186187
2.4.4 KF_KPTR_GET flag
187188
----------------------

drivers/hid/bpf/hid_bpf_dispatch.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,9 +342,6 @@ hid_bpf_release_context(struct hid_bpf_ctx *ctx)
342342
{
343343
struct hid_bpf_ctx_kern *ctx_kern;
344344

345-
if (!ctx)
346-
return;
347-
348345
ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
349346

350347
kfree(ctx_kern);

kernel/bpf/cpumask.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,6 @@ static void cpumask_free_cb(struct rcu_head *head)
102102
*/
103103
__bpf_kfunc void bpf_cpumask_release(struct bpf_cpumask *cpumask)
104104
{
105-
if (!cpumask)
106-
return;
107-
108105
if (refcount_dec_and_test(&cpumask->usage))
109106
call_rcu(&cpumask->rcu, cpumask_free_cb);
110107
}
@@ -405,7 +402,7 @@ __diag_pop();
405402

406403
BTF_SET8_START(cpumask_kfunc_btf_ids)
407404
BTF_ID_FLAGS(func, bpf_cpumask_create, KF_ACQUIRE | KF_RET_NULL)
408-
BTF_ID_FLAGS(func, bpf_cpumask_release, KF_RELEASE | KF_TRUSTED_ARGS)
405+
BTF_ID_FLAGS(func, bpf_cpumask_release, KF_RELEASE)
409406
BTF_ID_FLAGS(func, bpf_cpumask_acquire, KF_ACQUIRE | KF_TRUSTED_ARGS)
410407
BTF_ID_FLAGS(func, bpf_cpumask_first, KF_RCU)
411408
BTF_ID_FLAGS(func, bpf_cpumask_first_zero, KF_RCU)

kernel/bpf/helpers.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2089,9 +2089,6 @@ __bpf_kfunc struct task_struct *bpf_task_kptr_get(struct task_struct **pp)
20892089
*/
20902090
__bpf_kfunc void bpf_task_release(struct task_struct *p)
20912091
{
2092-
if (!p)
2093-
return;
2094-
20952092
put_task_struct(p);
20962093
}
20972094

@@ -2148,9 +2145,6 @@ __bpf_kfunc struct cgroup *bpf_cgroup_kptr_get(struct cgroup **cgrpp)
21482145
*/
21492146
__bpf_kfunc void bpf_cgroup_release(struct cgroup *cgrp)
21502147
{
2151-
if (!cgrp)
2152-
return;
2153-
21542148
cgroup_put(cgrp);
21552149
}
21562150

kernel/bpf/syscall.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,9 @@ void bpf_obj_free_fields(const struct btf_record *rec, void *obj)
677677
break;
678678
case BPF_KPTR_REF:
679679
xchgd_field = (void *)xchg((unsigned long *)field_ptr, 0);
680+
if (!xchgd_field)
681+
break;
682+
680683
if (!btf_is_kernel(field->kptr.btf)) {
681684
pointee_struct_meta = btf_find_struct_meta(field->kptr.btf,
682685
field->kptr.btf_id);

kernel/bpf/verifier.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9307,7 +9307,7 @@ static bool is_kfunc_release(struct bpf_kfunc_call_arg_meta *meta)
93079307

93089308
static bool is_kfunc_trusted_args(struct bpf_kfunc_call_arg_meta *meta)
93099309
{
9310-
return meta->kfunc_flags & KF_TRUSTED_ARGS;
9310+
return (meta->kfunc_flags & KF_TRUSTED_ARGS) || is_kfunc_release(meta);
93119311
}
93129312

93139313
static bool is_kfunc_sleepable(struct bpf_kfunc_call_arg_meta *meta)

net/bpf/test_run.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,11 @@ bpf_kfunc_call_test_acquire(unsigned long *scalar_ptr)
606606
return &prog_test_struct;
607607
}
608608

609+
__bpf_kfunc void bpf_kfunc_call_test_offset(struct prog_test_ref_kfunc *p)
610+
{
611+
WARN_ON_ONCE(1);
612+
}
613+
609614
__bpf_kfunc struct prog_test_member *
610615
bpf_kfunc_call_memb_acquire(void)
611616
{
@@ -615,9 +620,6 @@ bpf_kfunc_call_memb_acquire(void)
615620

616621
__bpf_kfunc void bpf_kfunc_call_test_release(struct prog_test_ref_kfunc *p)
617622
{
618-
if (!p)
619-
return;
620-
621623
refcount_dec(&p->cnt);
622624
}
623625

@@ -803,6 +805,7 @@ BTF_ID_FLAGS(func, bpf_kfunc_call_test_mem_len_fail2)
803805
BTF_ID_FLAGS(func, bpf_kfunc_call_test_ref, KF_TRUSTED_ARGS | KF_RCU)
804806
BTF_ID_FLAGS(func, bpf_kfunc_call_test_destructive, KF_DESTRUCTIVE)
805807
BTF_ID_FLAGS(func, bpf_kfunc_call_test_static_unused_arg)
808+
BTF_ID_FLAGS(func, bpf_kfunc_call_test_offset)
806809
BTF_SET8_END(test_sk_check_kfunc_ids)
807810

808811
static void *bpf_test_init(const union bpf_attr *kattr, u32 user_size,

net/netfilter/nf_conntrack_bpf.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,8 +401,6 @@ __bpf_kfunc struct nf_conn *bpf_ct_insert_entry(struct nf_conn___init *nfct_i)
401401
*/
402402
__bpf_kfunc void bpf_ct_release(struct nf_conn *nfct)
403403
{
404-
if (!nfct)
405-
return;
406404
nf_ct_put(nfct);
407405
}
408406

tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ int BPF_PROG(cgrp_kfunc_get_unreleased, struct cgroup *cgrp, const char *path)
206206
}
207207

208208
SEC("tp_btf/cgroup_mkdir")
209-
__failure __msg("expects refcounted")
209+
__failure __msg("Possibly NULL pointer passed to trusted arg0")
210210
int BPF_PROG(cgrp_kfunc_release_untrusted, struct cgroup *cgrp, const char *path)
211211
{
212212
struct __cgrps_kfunc_map_value *v;
@@ -234,7 +234,7 @@ int BPF_PROG(cgrp_kfunc_release_fp, struct cgroup *cgrp, const char *path)
234234
}
235235

236236
SEC("tp_btf/cgroup_mkdir")
237-
__failure __msg("arg#0 is ptr_or_null_ expected ptr_ or socket")
237+
__failure __msg("Possibly NULL pointer passed to trusted arg0")
238238
int BPF_PROG(cgrp_kfunc_release_null, struct cgroup *cgrp, const char *path)
239239
{
240240
struct __cgrps_kfunc_map_value local, *v;

tools/testing/selftests/bpf/progs/task_kfunc_failure.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ int BPF_PROG(task_kfunc_get_unreleased, struct task_struct *task, u64 clone_flag
206206
}
207207

208208
SEC("tp_btf/task_newtask")
209-
__failure __msg("arg#0 is untrusted_ptr_or_null_ expected ptr_ or socket")
209+
__failure __msg("Possibly NULL pointer passed to trusted arg0")
210210
int BPF_PROG(task_kfunc_release_untrusted, struct task_struct *task, u64 clone_flags)
211211
{
212212
struct __tasks_kfunc_map_value *v;
@@ -234,7 +234,7 @@ int BPF_PROG(task_kfunc_release_fp, struct task_struct *task, u64 clone_flags)
234234
}
235235

236236
SEC("tp_btf/task_newtask")
237-
__failure __msg("arg#0 is ptr_or_null_ expected ptr_ or socket")
237+
__failure __msg("Possibly NULL pointer passed to trusted arg0")
238238
int BPF_PROG(task_kfunc_release_null, struct task_struct *task, u64 clone_flags)
239239
{
240240
struct __tasks_kfunc_map_value local, *v;
@@ -277,7 +277,7 @@ int BPF_PROG(task_kfunc_release_unacquired, struct task_struct *task, u64 clone_
277277
}
278278

279279
SEC("tp_btf/task_newtask")
280-
__failure __msg("arg#0 is ptr_or_null_ expected ptr_ or socket")
280+
__failure __msg("Possibly NULL pointer passed to trusted arg0")
281281
int BPF_PROG(task_kfunc_from_pid_no_null_check, struct task_struct *task, u64 clone_flags)
282282
{
283283
struct task_struct *acquired;

tools/testing/selftests/bpf/verifier/calls.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109
},
110110
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
111111
.result = REJECT,
112-
.errstr = "arg#0 is ptr_or_null_ expected ptr_ or socket",
112+
.errstr = "Possibly NULL pointer passed to trusted arg0",
113113
.fixup_kfunc_btf_id = {
114114
{ "bpf_kfunc_call_test_acquire", 3 },
115115
{ "bpf_kfunc_call_test_release", 5 },
@@ -165,19 +165,23 @@
165165
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8),
166166
BPF_ST_MEM(BPF_DW, BPF_REG_1, 0, 0),
167167
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0),
168+
BPF_MOV64_REG(BPF_REG_2, BPF_REG_0),
168169
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
169170
BPF_EXIT_INSN(),
170171
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
171-
BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, 16),
172172
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -4),
173173
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0),
174174
BPF_MOV64_IMM(BPF_REG_0, 0),
175+
BPF_MOV64_REG(BPF_REG_1, BPF_REG_2),
176+
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0),
177+
BPF_MOV64_IMM(BPF_REG_0, 0),
175178
BPF_EXIT_INSN(),
176179
},
177180
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
178181
.fixup_kfunc_btf_id = {
179182
{ "bpf_kfunc_call_test_acquire", 3 },
180-
{ "bpf_kfunc_call_test_release", 9 },
183+
{ "bpf_kfunc_call_test_offset", 9 },
184+
{ "bpf_kfunc_call_test_release", 12 },
181185
},
182186
.result_unpriv = REJECT,
183187
.result = REJECT,

tools/testing/selftests/bpf/verifier/ref_tracking.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@
142142
.kfunc = "bpf",
143143
.expected_attach_type = BPF_LSM_MAC,
144144
.flags = BPF_F_SLEEPABLE,
145-
.errstr = "arg#0 is ptr_or_null_ expected ptr_ or socket",
145+
.errstr = "Possibly NULL pointer passed to trusted arg0",
146146
.fixup_kfunc_btf_id = {
147147
{ "bpf_lookup_user_key", 2 },
148148
{ "bpf_key_put", 4 },
@@ -163,7 +163,7 @@
163163
.kfunc = "bpf",
164164
.expected_attach_type = BPF_LSM_MAC,
165165
.flags = BPF_F_SLEEPABLE,
166-
.errstr = "arg#0 is ptr_or_null_ expected ptr_ or socket",
166+
.errstr = "Possibly NULL pointer passed to trusted arg0",
167167
.fixup_kfunc_btf_id = {
168168
{ "bpf_lookup_system_key", 1 },
169169
{ "bpf_key_put", 3 },
@@ -182,7 +182,7 @@
182182
.kfunc = "bpf",
183183
.expected_attach_type = BPF_LSM_MAC,
184184
.flags = BPF_F_SLEEPABLE,
185-
.errstr = "arg#0 pointer type STRUCT bpf_key must point to scalar, or struct with scalar",
185+
.errstr = "Possibly NULL pointer passed to trusted arg0",
186186
.fixup_kfunc_btf_id = {
187187
{ "bpf_key_put", 1 },
188188
},

0 commit comments

Comments
 (0)