mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-11 17:16:11 -04:00
selftests/bpf: Test borrowed refcount acquisition nullability
Add verifier coverage for the distinction between owning and borrowed arguments to bpf_refcount_acquire(). An owning pointer returned by bpf_obj_new() must continue producing a non-NULL result without an extra check. An RCU-loaded local kptr is only borrowed, so a checked result must load successfully while passing an unchecked result to bpf_obj_drop() must be rejected as possibly NULL. Use a sleepable syscall program for the borrowed cases so the explicit RCU critical section is what permits the local kptr load. Without the verifier fix, the unchecked case is incorrectly accepted. With it, the verifier rejects the possibly NULL argument. Signed-off-by: Ning Ding <dingning04@gmail.com> [ kkd: Rewrote commit log ] Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> Link: https://lore.kernel.org/r/20260904084325.52250-5-memxor@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
committed by
Alexei Starovoitov
parent
dc36739e5c
commit
2edd833946
@@ -23,6 +23,15 @@ struct map_value {
|
||||
struct node_data __kptr *node;
|
||||
};
|
||||
|
||||
struct node_refcount_only {
|
||||
long key;
|
||||
struct bpf_refcount refcount;
|
||||
};
|
||||
|
||||
struct map_value_refcount_only {
|
||||
struct node_refcount_only __kptr *node;
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__type(key, int);
|
||||
@@ -30,6 +39,13 @@ struct {
|
||||
__uint(max_entries, 2);
|
||||
} stashed_nodes SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__type(key, int);
|
||||
__type(value, struct map_value_refcount_only);
|
||||
__uint(max_entries, 1);
|
||||
} stashed_refcount_only SEC(".maps");
|
||||
|
||||
struct node_acquire {
|
||||
long key;
|
||||
long data;
|
||||
@@ -832,6 +848,51 @@ long rbtree_refcounted_node_ref_escapes_owning_input(void *ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("tc")
|
||||
__success
|
||||
long refcount_acquire_owning_input_no_null_check(void *ctx)
|
||||
{
|
||||
struct node_refcount_only *n, *m;
|
||||
|
||||
n = bpf_obj_new(typeof(*n));
|
||||
if (!n)
|
||||
return 1;
|
||||
|
||||
m = bpf_refcount_acquire(n);
|
||||
bpf_obj_drop(m);
|
||||
bpf_obj_drop(n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?syscall")
|
||||
__success
|
||||
long refcount_acquire_rcu_map_kptr_null_checked(void *ctx)
|
||||
{
|
||||
struct map_value_refcount_only *mapval;
|
||||
struct node_refcount_only *n, *m;
|
||||
int idx = 0;
|
||||
|
||||
mapval = bpf_map_lookup_elem(&stashed_refcount_only, &idx);
|
||||
if (!mapval)
|
||||
return 1;
|
||||
|
||||
bpf_rcu_read_lock();
|
||||
n = mapval->node;
|
||||
if (!n) {
|
||||
bpf_rcu_read_unlock();
|
||||
return 2;
|
||||
}
|
||||
m = bpf_refcount_acquire(n);
|
||||
bpf_rcu_read_unlock();
|
||||
|
||||
if (!m)
|
||||
return 3;
|
||||
bpf_obj_drop(m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long __stash_map_empty_xchg(struct node_data *n, int idx)
|
||||
{
|
||||
struct map_value *mapval = bpf_map_lookup_elem(&stashed_nodes, &idx);
|
||||
|
||||
@@ -19,6 +19,15 @@ struct node_refcounted {
|
||||
struct bpf_refcount refcount;
|
||||
};
|
||||
|
||||
struct node_refcount_only {
|
||||
long key;
|
||||
struct bpf_refcount refcount;
|
||||
};
|
||||
|
||||
struct map_value_refcount_only {
|
||||
struct node_refcount_only __kptr *node;
|
||||
};
|
||||
|
||||
extern void bpf_rcu_read_lock(void) __ksym;
|
||||
extern void bpf_rcu_read_unlock(void) __ksym;
|
||||
|
||||
@@ -28,6 +37,13 @@ private(A) struct bpf_rb_root groot __contains(node_acquire, node);
|
||||
private(B) struct bpf_spin_lock lock;
|
||||
private(B) struct bpf_list_head head __contains(node_refcounted, list);
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__type(key, int);
|
||||
__type(value, struct map_value_refcount_only);
|
||||
__uint(max_entries, 1);
|
||||
} stashed_refcount_only SEC(".maps");
|
||||
|
||||
static bool less(struct bpf_rb_node *a, const struct bpf_rb_node *b)
|
||||
{
|
||||
struct node_acquire *node_a;
|
||||
@@ -89,6 +105,38 @@ long refcount_acquire_non_object(void *ctx)
|
||||
return bpf_refcount_acquire(ctx) != NULL;
|
||||
}
|
||||
|
||||
SEC("?syscall")
|
||||
__failure __msg("Possibly NULL pointer passed to trusted R1")
|
||||
long refcount_acquire_rcu_map_kptr_unchecked_drop(void *ctx)
|
||||
{
|
||||
struct map_value_refcount_only *mapval;
|
||||
struct node_refcount_only *tmp, *n, *m;
|
||||
int idx = 0;
|
||||
|
||||
/* Force Clang to emit complete BTF for struct node_refcount_only. */
|
||||
tmp = bpf_obj_new(typeof(*tmp));
|
||||
if (!tmp)
|
||||
return 3;
|
||||
bpf_obj_drop(tmp);
|
||||
|
||||
mapval = bpf_map_lookup_elem(&stashed_refcount_only, &idx);
|
||||
if (!mapval)
|
||||
return 1;
|
||||
|
||||
bpf_rcu_read_lock();
|
||||
n = mapval->node;
|
||||
if (!n) {
|
||||
bpf_rcu_read_unlock();
|
||||
return 2;
|
||||
}
|
||||
m = bpf_refcount_acquire(n);
|
||||
bpf_rcu_read_unlock();
|
||||
|
||||
bpf_obj_drop(m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("Unreleased reference id=3 alloc_insn={{[0-9]+}}")
|
||||
long rbtree_refcounted_node_ref_escapes_owning_input(void *ctx)
|
||||
|
||||
Reference in New Issue
Block a user