// SPDX-License-Identifier: GPL-2.0 #include #include #include #include #include "bpf_experimental.h" #include "linked_list.h" #define INIT \ struct map_value *v, *v2, *iv, *iv2; \ struct foo *f, *f1, *f2; \ struct bar *b; \ void *map; \ \ map = bpf_map_lookup_elem(&map_of_maps, &(int){ 0 }); \ if (!map) \ return 0; \ v = bpf_map_lookup_elem(&array_map, &(int){ 0 }); \ if (!v) \ return 0; \ v2 = bpf_map_lookup_elem(&array_map, &(int){ 0 }); \ if (!v2) \ return 0; \ iv = bpf_map_lookup_elem(map, &(int){ 0 }); \ if (!iv) \ return 0; \ iv2 = bpf_map_lookup_elem(map, &(int){ 0 }); \ if (!iv2) \ return 0; \ f = bpf_obj_new(typeof(*f)); \ if (!f) \ return 0; \ f1 = f; \ f2 = bpf_obj_new(typeof(*f2)); \ if (!f2) { \ bpf_obj_drop(f1); \ return 0; \ } \ b = bpf_obj_new(typeof(*b)); \ if (!b) { \ bpf_obj_drop(f2); \ bpf_obj_drop(f1); \ return 0; \ } #define CHECK(test, op, hexpr) \ SEC("?tc") \ int test##_missing_lock_##op(void *ctx) \ { \ INIT; \ void (*p)(void *) = (void *)&bpf_list_##op; \ p(hexpr); \ return 0; \ } CHECK(kptr, pop_front, &f->head); CHECK(kptr, pop_back, &f->head); CHECK(global, pop_front, &ghead); CHECK(global, pop_back, &ghead); CHECK(map, pop_front, &v->head); CHECK(map, pop_back, &v->head); CHECK(inner_map, pop_front, &iv->head); CHECK(inner_map, pop_back, &iv->head); #undef CHECK #define CHECK(test, op, hexpr, nexpr) \ SEC("?tc") \ int test##_missing_lock_##op(void *ctx) \ { \ INIT; \ bpf_list_##op(hexpr, nexpr); \ return 0; \ } CHECK(kptr, push_front, &f->head, &b->node); CHECK(kptr, push_back, &f->head, &b->node); CHECK(global, push_front, &ghead, &f->node2); CHECK(global, push_back, &ghead, &f->node2); CHECK(map, push_front, &v->head, &f->node2); CHECK(map, push_back, &v->head, &f->node2); CHECK(inner_map, push_front, &iv->head, &f->node2); CHECK(inner_map, push_back, &iv->head, &f->node2); #undef CHECK #define CHECK(test, op, lexpr, hexpr) \ SEC("?tc") \ int test##_incorrect_lock_##op(void *ctx) \ { \ INIT; \ void (*p)(void *) = (void *)&bpf_list_##op; \ bpf_spin_lock(lexpr); \ p(hexpr); \ return 0; \ } #define CHECK_OP(op) \ CHECK(kptr_kptr, op, &f1->lock, &f2->head); \ CHECK(kptr_global, op, &f1->lock, &ghead); \ CHECK(kptr_map, op, &f1->lock, &v->head); \ CHECK(kptr_inner_map, op, &f1->lock, &iv->head); \ \ CHECK(global_global, op, &glock2, &ghead); \ CHECK(global_kptr, op, &glock, &f1->head); \ CHECK(global_map, op, &glock, &v->head); \ CHECK(global_inner_map, op, &glock, &iv->head); \ \ CHECK(map_map, op, &v->lock, &v2->head); \ CHECK(map_kptr, op, &v->lock, &f2->head); \ CHECK(map_global, op, &v->lock, &ghead); \ CHECK(map_inner_map, op, &v->lock, &iv->head); \ \ CHECK(inner_map_inner_map, op, &iv->lock, &iv2->head); \ CHECK(inner_map_kptr, op, &iv->lock, &f2->head); \ CHECK(inner_map_global, op, &iv->lock, &ghead); \ CHECK(inner_map_map, op, &iv->lock, &v->head); CHECK_OP(pop_front); CHECK_OP(pop_back); #undef CHECK #undef CHECK_OP #define CHECK(test, op, lexpr, hexpr, nexpr) \ SEC("?tc") \ int test##_incorrect_lock_##op(void *ctx) \ { \ INIT; \ bpf_spin_lock(lexpr); \ bpf_list_##op(hexpr, nexpr); \ return 0; \ } #define CHECK_OP(op) \ CHECK(kptr_kptr, op, &f1->lock, &f2->head, &b->node); \ CHECK(kptr_global, op, &f1->lock, &ghead, &f->node2); \ CHECK(kptr_map, op, &f1->lock, &v->head, &f->node2); \ CHECK(kptr_inner_map, op, &f1->lock, &iv->head, &f->node2); \ \ CHECK(global_global, op, &glock2, &ghead, &f->node2); \ CHECK(global_kptr, op, &glock, &f1->head, &b->node); \ CHECK(global_map, op, &glock, &v->head, &f->node2); \ CHECK(global_inner_map, op, &glock, &iv->head, &f->node2); \ \ CHECK(map_map, op, &v->lock, &v2->head, &f->node2); \ CHECK(map_kptr, op, &v->lock, &f2->head, &b->node); \ CHECK(map_global, op, &v->lock, &ghead, &f->node2); \ CHECK(map_inner_map, op, &v->lock, &iv->head, &f->node2); \ \ CHECK(inner_map_inner_map, op, &iv->lock, &iv2->head, &f->node2);\ CHECK(inner_map_kptr, op, &iv->lock, &f2->head, &b->node); \ CHECK(inner_map_global, op, &iv->lock, &ghead, &f->node2); \ CHECK(inner_map_map, op, &iv->lock, &v->head, &f->node2); CHECK_OP(push_front); CHECK_OP(push_back); #undef CHECK #undef CHECK_OP #undef INIT SEC("?kprobe/xyz") int map_compat_kprobe(void *ctx) { bpf_list_push_front(&ghead, NULL); return 0; } SEC("?kretprobe/xyz") int map_compat_kretprobe(void *ctx) { bpf_list_push_front(&ghead, NULL); return 0; } SEC("?tracepoint/xyz") int map_compat_tp(void *ctx) { bpf_list_push_front(&ghead, NULL); return 0; } SEC("?perf_event") int map_compat_perf(void *ctx) { bpf_list_push_front(&ghead, NULL); return 0; } SEC("?raw_tp/xyz") int map_compat_raw_tp(void *ctx) { bpf_list_push_front(&ghead, NULL); return 0; } SEC("?raw_tp.w/xyz") int map_compat_raw_tp_w(void *ctx) { bpf_list_push_front(&ghead, NULL); return 0; } SEC("?tc") int obj_type_id_oor(void *ctx) { bpf_obj_new_impl(~0UL, NULL); return 0; } SEC("?tc") int obj_new_no_composite(void *ctx) { bpf_obj_new_impl(bpf_core_type_id_local(int), (void *)42); return 0; } SEC("?tc") int obj_new_no_struct(void *ctx) { bpf_obj_new(union { int data; unsigned udata; }); return 0; } SEC("?tc") int obj_drop_non_zero_off(void *ctx) { void *f; f = bpf_obj_new(struct foo); if (!f) return 0; bpf_obj_drop(f+1); return 0; } SEC("?tc") int new_null_ret(void *ctx) { return bpf_obj_new(struct foo)->data; } SEC("?tc") int obj_new_acq(void *ctx) { bpf_obj_new(struct foo); return 0; } SEC("?tc") int use_after_drop(void *ctx) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return 0; bpf_obj_drop(f); return f->data; } SEC("?tc") int ptr_walk_scalar(void *ctx) { struct test1 { struct test2 { struct test2 *next; } *ptr; } *p; p = bpf_obj_new(typeof(*p)); if (!p) return 0; bpf_this_cpu_ptr(p->ptr); return 0; } SEC("?tc") int direct_read_lock(void *ctx) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return 0; return *(int *)&f->lock; } SEC("?tc") int direct_write_lock(void *ctx) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return 0; *(int *)&f->lock = 0; return 0; } SEC("?tc") int direct_read_head(void *ctx) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return 0; return *(int *)&f->head; } SEC("?tc") int direct_write_head(void *ctx) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return 0; *(int *)&f->head = 0; return 0; } SEC("?tc") int direct_read_node(void *ctx) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return 0; return *(int *)&f->node2; } SEC("?tc") int direct_write_node(void *ctx) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return 0; *(int *)&f->node2 = 0; return 0; } static __always_inline int use_after_unlock(bool push_front) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return 0; bpf_spin_lock(&glock); f->data = 42; if (push_front) bpf_list_push_front(&ghead, &f->node2); else bpf_list_push_back(&ghead, &f->node2); bpf_spin_unlock(&glock); return f->data; } SEC("?tc") int use_after_unlock_push_front(void *ctx) { return use_after_unlock(true); } SEC("?tc") int use_after_unlock_push_back(void *ctx) { return use_after_unlock(false); } static __always_inline int list_double_add(bool push_front) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return 0; bpf_spin_lock(&glock); if (push_front) { bpf_list_push_front(&ghead, &f->node2); bpf_list_push_front(&ghead, &f->node2); } else { bpf_list_push_back(&ghead, &f->node2); bpf_list_push_back(&ghead, &f->node2); } bpf_spin_unlock(&glock); return 0; } SEC("?tc") int double_push_front(void *ctx) { return list_double_add(true); } SEC("?tc") int double_push_back(void *ctx) { return list_double_add(false); } SEC("?tc") int no_node_value_type(void *ctx) { void *p; p = bpf_obj_new(struct { int data; }); if (!p) return 0; bpf_spin_lock(&glock); bpf_list_push_front(&ghead, p); bpf_spin_unlock(&glock); return 0; } SEC("?tc") int incorrect_value_type(void *ctx) { struct bar *b; b = bpf_obj_new(typeof(*b)); if (!b) return 0; bpf_spin_lock(&glock); bpf_list_push_front(&ghead, &b->node); bpf_spin_unlock(&glock); return 0; } SEC("?tc") int incorrect_node_var_off(struct __sk_buff *ctx) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return 0; bpf_spin_lock(&glock); bpf_list_push_front(&ghead, (void *)&f->node2 + ctx->protocol); bpf_spin_unlock(&glock); return 0; } SEC("?tc") int incorrect_node_off1(void *ctx) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return 0; bpf_spin_lock(&glock); bpf_list_push_front(&ghead, (void *)&f->node2 + 1); bpf_spin_unlock(&glock); return 0; } SEC("?tc") int incorrect_node_off2(void *ctx) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return 0; bpf_spin_lock(&glock); bpf_list_push_front(&ghead, &f->node); bpf_spin_unlock(&glock); return 0; } SEC("?tc") int no_head_type(void *ctx) { void *p; p = bpf_obj_new(typeof(struct { int data; })); if (!p) return 0; bpf_spin_lock(&glock); bpf_list_push_front(p, NULL); bpf_spin_lock(&glock); return 0; } SEC("?tc") int incorrect_head_var_off1(struct __sk_buff *ctx) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return 0; bpf_spin_lock(&glock); bpf_list_push_front((void *)&ghead + ctx->protocol, &f->node2); bpf_spin_unlock(&glock); return 0; } SEC("?tc") int incorrect_head_var_off2(struct __sk_buff *ctx) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return 0; bpf_spin_lock(&glock); bpf_list_push_front((void *)&f->head + ctx->protocol, &f->node2); bpf_spin_unlock(&glock); return 0; } SEC("?tc") int incorrect_head_off1(void *ctx) { struct foo *f; struct bar *b; f = bpf_obj_new(typeof(*f)); if (!f) return 0; b = bpf_obj_new(typeof(*b)); if (!b) { bpf_obj_drop(f); return 0; } bpf_spin_lock(&f->lock); bpf_list_push_front((void *)&f->head + 1, &b->node); bpf_spin_unlock(&f->lock); return 0; } SEC("?tc") int incorrect_head_off2(void *ctx) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return 0; bpf_spin_lock(&glock); bpf_list_push_front((void *)&ghead + 1, &f->node2); bpf_spin_unlock(&glock); return 0; } static __always_inline int pop_ptr_off(void *(*op)(void *head)) { struct { struct bpf_list_head head __contains(foo, node2); struct bpf_spin_lock lock; } *p; struct bpf_list_node *n; p = bpf_obj_new(typeof(*p)); if (!p) return 0; bpf_spin_lock(&p->lock); n = op(&p->head); bpf_spin_unlock(&p->lock); if (!n) return 0; bpf_spin_lock((void *)n); return 0; } SEC("?tc") int pop_front_off(void *ctx) { return pop_ptr_off((void *)bpf_list_pop_front); } SEC("?tc") int pop_back_off(void *ctx) { return pop_ptr_off((void *)bpf_list_pop_back); } char _license[] SEC("license") = "GPL";