mirror of
https://github.com/torvalds/linux.git
synced 2026-09-26 18:12:03 +02:00
selftests/bpf: Check callback map value lock identity
Add a verifier test which retains a map value from an outer callback and then acquires a lock through an inner callback value before attempting to release the outer callback value. Both values can denote different elements, so the verifier must reject the mismatched unlock. Also exercise callbacks reached through two inner-map lookups. The lookup results share inner_map_meta but may refer to different one-element arrays, so their callback values must retain distinct lock identities. Extend the existing spin_lock failure table and reuse its array and inner-map fixtures to keep these cases alongside the other lock identity tests. Update the nested callback reference-leak expectation for the extra callback value ID. Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> Link: https://patch.msgid.link/20260917233222.2542500-10-memxor@gmail.com Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
This commit is contained in:
parent
71919742c8
commit
04ae4ffc57
|
|
@ -13,7 +13,7 @@ struct {
|
|||
} cb_refs_tests[] = {
|
||||
{ "underflow_prog", "release kfunc bpf_kfunc_call_test_release expects referenced PTR_TO_BTF_ID passed to R1" },
|
||||
{ "leak_prog", "Possibly NULL pointer passed to helper R2" },
|
||||
{ "nested_cb", "Unreleased reference id=4 alloc_insn=2" }, /* alloc_insn=2{4,5} */
|
||||
{ "nested_cb", "Unreleased reference id=5 alloc_insn=2" }, /* alloc_insn=2{4,5} */
|
||||
{ "non_cb_transfer_ref", "Unreleased reference id=4 alloc_insn=1" }, /* alloc_insn=1{1,2} */
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ static struct {
|
|||
{ "lock_global_sleepable_helper_subprog", "global function calls are not allowed while holding a lock" },
|
||||
{ "lock_global_sleepable_kfunc_subprog", "global function calls are not allowed while holding a lock" },
|
||||
{ "lock_global_sleepable_subprog_indirect", "global function calls are not allowed while holding a lock" },
|
||||
{ "callback_value_lock_identity", "bpf_spin_unlock of different lock" },
|
||||
{ "callback_inner_map_value_lock_identity", "bpf_spin_unlock of different lock" },
|
||||
};
|
||||
|
||||
static int match_regex(const char *pattern, const char *string)
|
||||
|
|
|
|||
|
|
@ -14,17 +14,18 @@ struct array_map {
|
|||
__type(key, int);
|
||||
__type(value, struct foo);
|
||||
__uint(max_entries, 1);
|
||||
} array_map SEC(".maps");
|
||||
} array_map SEC(".maps"), array_map_b SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
|
||||
__uint(max_entries, 1);
|
||||
__uint(max_entries, 2);
|
||||
__type(key, int);
|
||||
__type(value, int);
|
||||
__array(values, struct array_map);
|
||||
} map_of_maps SEC(".maps") = {
|
||||
.values = {
|
||||
[0] = &array_map,
|
||||
[1] = &array_map_b,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -314,4 +315,66 @@ int lock_global_sleepable_subprog_indirect(struct __sk_buff *ctx)
|
|||
return ret;
|
||||
}
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 2);
|
||||
__type(key, int);
|
||||
__type(value, struct foo);
|
||||
} callback_array_map SEC(".maps");
|
||||
|
||||
struct callback_ctx {
|
||||
struct foo *value;
|
||||
};
|
||||
|
||||
static long lock_different_value(struct bpf_map *map, int *key,
|
||||
struct foo *value, struct callback_ctx *ctx)
|
||||
{
|
||||
bpf_spin_lock(&value->lock);
|
||||
bpf_spin_unlock(&ctx->value->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long nest_lock_different_value(struct bpf_map *map, int *key,
|
||||
struct foo *value, void *data)
|
||||
{
|
||||
struct callback_ctx ctx = { .value = value };
|
||||
|
||||
bpf_for_each_map_elem(&callback_array_map, lock_different_value, &ctx, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
int callback_value_lock_identity(void *ctx)
|
||||
{
|
||||
bpf_for_each_map_elem(&callback_array_map, nest_lock_different_value, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long nest_lock_different_inner_value(struct bpf_map *map, int *key,
|
||||
struct foo *value, void *data)
|
||||
{
|
||||
struct callback_ctx ctx = { .value = value };
|
||||
int inner_key = 1;
|
||||
void *inner_map;
|
||||
|
||||
inner_map = bpf_map_lookup_elem(&map_of_maps, &inner_key);
|
||||
if (!inner_map)
|
||||
return 0;
|
||||
bpf_for_each_map_elem(inner_map, lock_different_value, &ctx, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
int callback_inner_map_value_lock_identity(void *ctx)
|
||||
{
|
||||
int inner_key = 0;
|
||||
void *inner_map;
|
||||
|
||||
inner_map = bpf_map_lookup_elem(&map_of_maps, &inner_key);
|
||||
if (!inner_map)
|
||||
return 0;
|
||||
bpf_for_each_map_elem(inner_map, nest_lock_different_inner_value, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user