1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2024-2025 Meta Platforms, Inc. and affiliates. */ 3 #include <vmlinux.h> 4 #include <bpf/bpf_tracing.h> 5 #include <bpf/bpf_helpers.h> 6 #include <bpf/bpf_core_read.h> 7 #include "bpf_misc.h" 8 #include "bpf_experimental.h" 9 10 struct arr_elem { 11 struct bpf_res_spin_lock lock; 12 }; 13 14 struct { 15 __uint(type, BPF_MAP_TYPE_ARRAY); 16 __uint(max_entries, 1); 17 __type(key, int); 18 __type(value, struct arr_elem); 19 } arrmap SEC(".maps"); 20 21 long value; 22 23 struct bpf_spin_lock lock __hidden SEC(".data.A"); 24 struct bpf_res_spin_lock res_lock __hidden SEC(".data.B"); 25 26 SEC("?tc") 27 __failure __msg("point to map value or allocated object") 28 int res_spin_lock_arg(struct __sk_buff *ctx) 29 { 30 struct arr_elem *elem; 31 32 elem = bpf_map_lookup_elem(&arrmap, &(int){0}); 33 if (!elem) 34 return 0; 35 bpf_res_spin_lock((struct bpf_res_spin_lock *)bpf_core_cast(&elem->lock, struct __sk_buff)); 36 bpf_res_spin_lock(&elem->lock); 37 return 0; 38 } 39 40 SEC("?tc") 41 __failure __msg("AA deadlock detected") 42 int res_spin_lock_AA(struct __sk_buff *ctx) 43 { 44 struct arr_elem *elem; 45 46 elem = bpf_map_lookup_elem(&arrmap, &(int){0}); 47 if (!elem) 48 return 0; 49 bpf_res_spin_lock(&elem->lock); 50 bpf_res_spin_lock(&elem->lock); 51 return 0; 52 } 53 54 SEC("?tc") 55 __failure __msg("AA deadlock detected") 56 int res_spin_lock_cond_AA(struct __sk_buff *ctx) 57 { 58 struct arr_elem *elem; 59 60 elem = bpf_map_lookup_elem(&arrmap, &(int){0}); 61 if (!elem) 62 return 0; 63 if (bpf_res_spin_lock(&elem->lock)) 64 return 0; 65 bpf_res_spin_lock(&elem->lock); 66 return 0; 67 } 68 69 SEC("?tc") 70 __failure __msg("unlock of different lock") 71 int res_spin_lock_mismatch_1(struct __sk_buff *ctx) 72 { 73 struct arr_elem *elem; 74 75 elem = bpf_map_lookup_elem(&arrmap, &(int){0}); 76 if (!elem) 77 return 0; 78 if (bpf_res_spin_lock(&elem->lock)) 79 return 0; 80 bpf_res_spin_unlock(&res_lock); 81 return 0; 82 } 83 84 SEC("?tc") 85 __failure __msg("unlock of different lock") 86 int res_spin_lock_mismatch_2(struct __sk_buff *ctx) 87 { 88 struct arr_elem *elem; 89 90 elem = bpf_map_lookup_elem(&arrmap, &(int){0}); 91 if (!elem) 92 return 0; 93 if (bpf_res_spin_lock(&res_lock)) 94 return 0; 95 bpf_res_spin_unlock(&elem->lock); 96 return 0; 97 } 98 99 SEC("?tc") 100 __failure __msg("unlock of different lock") 101 int res_spin_lock_irq_mismatch_1(struct __sk_buff *ctx) 102 { 103 struct arr_elem *elem; 104 unsigned long f1; 105 106 elem = bpf_map_lookup_elem(&arrmap, &(int){0}); 107 if (!elem) 108 return 0; 109 bpf_local_irq_save(&f1); 110 if (bpf_res_spin_lock(&res_lock)) 111 return 0; 112 bpf_res_spin_unlock_irqrestore(&res_lock, &f1); 113 return 0; 114 } 115 116 SEC("?tc") 117 __failure __msg("unlock of different lock") 118 int res_spin_lock_irq_mismatch_2(struct __sk_buff *ctx) 119 { 120 struct arr_elem *elem; 121 unsigned long f1; 122 123 elem = bpf_map_lookup_elem(&arrmap, &(int){0}); 124 if (!elem) 125 return 0; 126 if (bpf_res_spin_lock_irqsave(&res_lock, &f1)) 127 return 0; 128 bpf_res_spin_unlock(&res_lock); 129 return 0; 130 } 131 132 SEC("?tc") 133 __success 134 int res_spin_lock_ooo(struct __sk_buff *ctx) 135 { 136 struct arr_elem *elem; 137 138 elem = bpf_map_lookup_elem(&arrmap, &(int){0}); 139 if (!elem) 140 return 0; 141 if (bpf_res_spin_lock(&res_lock)) 142 return 0; 143 if (bpf_res_spin_lock(&elem->lock)) { 144 bpf_res_spin_unlock(&res_lock); 145 return 0; 146 } 147 bpf_res_spin_unlock(&elem->lock); 148 bpf_res_spin_unlock(&res_lock); 149 return 0; 150 } 151 152 SEC("?tc") 153 __success 154 int res_spin_lock_ooo_irq(struct __sk_buff *ctx) 155 { 156 struct arr_elem *elem; 157 unsigned long f1, f2; 158 159 elem = bpf_map_lookup_elem(&arrmap, &(int){0}); 160 if (!elem) 161 return 0; 162 if (bpf_res_spin_lock_irqsave(&res_lock, &f1)) 163 return 0; 164 if (bpf_res_spin_lock_irqsave(&elem->lock, &f2)) { 165 bpf_res_spin_unlock_irqrestore(&res_lock, &f1); 166 /* We won't have a unreleased IRQ flag error here. */ 167 return 0; 168 } 169 bpf_res_spin_unlock_irqrestore(&elem->lock, &f2); 170 bpf_res_spin_unlock_irqrestore(&res_lock, &f1); 171 return 0; 172 } 173 174 struct bpf_res_spin_lock lock1 __hidden SEC(".data.OO1"); 175 struct bpf_res_spin_lock lock2 __hidden SEC(".data.OO2"); 176 177 SEC("?tc") 178 __failure __msg("bpf_res_spin_unlock cannot be out of order") 179 int res_spin_lock_ooo_unlock(struct __sk_buff *ctx) 180 { 181 if (bpf_res_spin_lock(&lock1)) 182 return 0; 183 if (bpf_res_spin_lock(&lock2)) { 184 bpf_res_spin_unlock(&lock1); 185 return 0; 186 } 187 bpf_res_spin_unlock(&lock1); 188 bpf_res_spin_unlock(&lock2); 189 return 0; 190 } 191 192 SEC("?tc") 193 __failure __msg("off 1 doesn't point to 'struct bpf_res_spin_lock' that is at 0") 194 int res_spin_lock_bad_off(struct __sk_buff *ctx) 195 { 196 struct arr_elem *elem; 197 198 elem = bpf_map_lookup_elem(&arrmap, &(int){0}); 199 if (!elem) 200 return 0; 201 bpf_res_spin_lock((void *)&elem->lock + 1); 202 return 0; 203 } 204 205 SEC("?tc") 206 __failure __msg("R1 doesn't have constant offset. bpf_res_spin_lock has to be at the constant offset") 207 int res_spin_lock_var_off(struct __sk_buff *ctx) 208 { 209 struct arr_elem *elem; 210 u64 val = value; 211 212 elem = bpf_map_lookup_elem(&arrmap, &(int){0}); 213 if (!elem) { 214 // FIXME: Only inline assembly use in assert macro doesn't emit 215 // BTF definition. 216 bpf_throw(0); 217 return 0; 218 } 219 bpf_assert_range(val, 0, 40); 220 bpf_res_spin_lock((void *)&value + val); 221 return 0; 222 } 223 224 SEC("?tc") 225 __failure __msg("map 'res_spin.bss' has no valid bpf_res_spin_lock") 226 int res_spin_lock_no_lock_map(struct __sk_buff *ctx) 227 { 228 bpf_res_spin_lock((void *)&value + 1); 229 return 0; 230 } 231 232 SEC("?tc") 233 __failure __msg("local 'kptr' has no valid bpf_res_spin_lock") 234 int res_spin_lock_no_lock_kptr(struct __sk_buff *ctx) 235 { 236 struct { int i; } *p = bpf_obj_new(typeof(*p)); 237 238 if (!p) 239 return 0; 240 bpf_res_spin_lock((void *)p); 241 return 0; 242 } 243 244 char _license[] SEC("license") = "GPL"; 245