Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
10struct arr_elem {
11 struct bpf_res_spin_lock lock;
12};
13
14struct {
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
21long value;
22
23struct bpf_spin_lock lock __hidden SEC(".data.A");
24struct bpf_res_spin_lock res_lock __hidden SEC(".data.B");
25
26SEC("?tc")
27__failure __msg("point to map value or allocated object")
28int 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
40SEC("?tc")
41__failure __msg("AA deadlock detected")
42int 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
54SEC("?tc")
55__failure __msg("AA deadlock detected")
56int 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
69SEC("?tc")
70__failure __msg("unlock of different lock")
71int 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
84SEC("?tc")
85__failure __msg("unlock of different lock")
86int 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
99SEC("?tc")
100__failure __msg("unlock of different lock")
101int 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
116SEC("?tc")
117__failure __msg("unlock of different lock")
118int 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
132SEC("?tc")
133__success
134int 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
152SEC("?tc")
153__success
154int 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
174struct bpf_res_spin_lock lock1 __hidden SEC(".data.OO1");
175struct bpf_res_spin_lock lock2 __hidden SEC(".data.OO2");
176
177SEC("?tc")
178__failure __msg("bpf_res_spin_unlock cannot be out of order")
179int 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
192SEC("?tc")
193__failure __msg("off 1 doesn't point to 'struct bpf_res_spin_lock' that is at 0")
194int 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
205SEC("?tc")
206__failure __msg("R1 doesn't have constant offset. bpf_res_spin_lock has to be at the constant offset")
207int 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
224SEC("?tc")
225__failure __msg("map 'res_spin.bss' has no valid bpf_res_spin_lock")
226int res_spin_lock_no_lock_map(struct __sk_buff *ctx)
227{
228 bpf_res_spin_lock((void *)&value + 1);
229 return 0;
230}
231
232SEC("?tc")
233__failure __msg("local 'kptr' has no valid bpf_res_spin_lock")
234int 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
244char _license[] SEC("license") = "GPL";