1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * This is for all the tests related to refcount bugs (e.g. overflow, |
4 | * underflow, reaching zero untested, etc). |
5 | */ |
6 | #include "lkdtm.h" |
7 | #include <linux/refcount.h> |
8 | |
9 | static void overflow_check(refcount_t *ref) |
10 | { |
11 | switch (refcount_read(r: ref)) { |
12 | case REFCOUNT_SATURATED: |
13 | pr_info("Overflow detected: saturated\n" ); |
14 | break; |
15 | case REFCOUNT_MAX: |
16 | pr_warn("Overflow detected: unsafely reset to max\n" ); |
17 | break; |
18 | default: |
19 | pr_err("Fail: refcount wrapped to %d\n" , refcount_read(ref)); |
20 | } |
21 | } |
22 | |
23 | /* |
24 | * A refcount_inc() above the maximum value of the refcount implementation, |
25 | * should at least saturate, and at most also WARN. |
26 | */ |
27 | static void lkdtm_REFCOUNT_INC_OVERFLOW(void) |
28 | { |
29 | refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX - 1); |
30 | |
31 | pr_info("attempting good refcount_inc() without overflow\n" ); |
32 | refcount_dec(r: &over); |
33 | refcount_inc(r: &over); |
34 | |
35 | pr_info("attempting bad refcount_inc() overflow\n" ); |
36 | refcount_inc(r: &over); |
37 | refcount_inc(r: &over); |
38 | |
39 | overflow_check(ref: &over); |
40 | } |
41 | |
42 | /* refcount_add() should behave just like refcount_inc() above. */ |
43 | static void lkdtm_REFCOUNT_ADD_OVERFLOW(void) |
44 | { |
45 | refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX - 1); |
46 | |
47 | pr_info("attempting good refcount_add() without overflow\n" ); |
48 | refcount_dec(r: &over); |
49 | refcount_dec(r: &over); |
50 | refcount_dec(r: &over); |
51 | refcount_dec(r: &over); |
52 | refcount_add(i: 4, r: &over); |
53 | |
54 | pr_info("attempting bad refcount_add() overflow\n" ); |
55 | refcount_add(i: 4, r: &over); |
56 | |
57 | overflow_check(ref: &over); |
58 | } |
59 | |
60 | /* refcount_inc_not_zero() should behave just like refcount_inc() above. */ |
61 | static void lkdtm_REFCOUNT_INC_NOT_ZERO_OVERFLOW(void) |
62 | { |
63 | refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX); |
64 | |
65 | pr_info("attempting bad refcount_inc_not_zero() overflow\n" ); |
66 | if (!refcount_inc_not_zero(r: &over)) |
67 | pr_warn("Weird: refcount_inc_not_zero() reported zero\n" ); |
68 | |
69 | overflow_check(ref: &over); |
70 | } |
71 | |
72 | /* refcount_add_not_zero() should behave just like refcount_inc() above. */ |
73 | static void lkdtm_REFCOUNT_ADD_NOT_ZERO_OVERFLOW(void) |
74 | { |
75 | refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX); |
76 | |
77 | pr_info("attempting bad refcount_add_not_zero() overflow\n" ); |
78 | if (!refcount_add_not_zero(i: 6, r: &over)) |
79 | pr_warn("Weird: refcount_add_not_zero() reported zero\n" ); |
80 | |
81 | overflow_check(ref: &over); |
82 | } |
83 | |
84 | static void check_zero(refcount_t *ref) |
85 | { |
86 | switch (refcount_read(r: ref)) { |
87 | case REFCOUNT_SATURATED: |
88 | pr_info("Zero detected: saturated\n" ); |
89 | break; |
90 | case REFCOUNT_MAX: |
91 | pr_warn("Zero detected: unsafely reset to max\n" ); |
92 | break; |
93 | case 0: |
94 | pr_warn("Still at zero: refcount_inc/add() must not inc-from-0\n" ); |
95 | break; |
96 | default: |
97 | pr_err("Fail: refcount went crazy: %d\n" , refcount_read(ref)); |
98 | } |
99 | } |
100 | |
101 | /* |
102 | * A refcount_dec(), as opposed to a refcount_dec_and_test(), when it hits |
103 | * zero it should either saturate (when inc-from-zero isn't protected) |
104 | * or stay at zero (when inc-from-zero is protected) and should WARN for both. |
105 | */ |
106 | static void lkdtm_REFCOUNT_DEC_ZERO(void) |
107 | { |
108 | refcount_t zero = REFCOUNT_INIT(2); |
109 | |
110 | pr_info("attempting good refcount_dec()\n" ); |
111 | refcount_dec(r: &zero); |
112 | |
113 | pr_info("attempting bad refcount_dec() to zero\n" ); |
114 | refcount_dec(r: &zero); |
115 | |
116 | check_zero(ref: &zero); |
117 | } |
118 | |
119 | static void check_negative(refcount_t *ref, int start) |
120 | { |
121 | /* |
122 | * refcount_t refuses to move a refcount at all on an |
123 | * over-sub, so we have to track our starting position instead of |
124 | * looking only at zero-pinning. |
125 | */ |
126 | if (refcount_read(r: ref) == start) { |
127 | pr_warn("Still at %d: refcount_inc/add() must not inc-from-0\n" , |
128 | start); |
129 | return; |
130 | } |
131 | |
132 | switch (refcount_read(r: ref)) { |
133 | case REFCOUNT_SATURATED: |
134 | pr_info("Negative detected: saturated\n" ); |
135 | break; |
136 | case REFCOUNT_MAX: |
137 | pr_warn("Negative detected: unsafely reset to max\n" ); |
138 | break; |
139 | default: |
140 | pr_err("Fail: refcount went crazy: %d\n" , refcount_read(ref)); |
141 | } |
142 | } |
143 | |
144 | /* A refcount_dec() going negative should saturate and may WARN. */ |
145 | static void lkdtm_REFCOUNT_DEC_NEGATIVE(void) |
146 | { |
147 | refcount_t neg = REFCOUNT_INIT(0); |
148 | |
149 | pr_info("attempting bad refcount_dec() below zero\n" ); |
150 | refcount_dec(r: &neg); |
151 | |
152 | check_negative(ref: &neg, start: 0); |
153 | } |
154 | |
155 | /* |
156 | * A refcount_dec_and_test() should act like refcount_dec() above when |
157 | * going negative. |
158 | */ |
159 | static void lkdtm_REFCOUNT_DEC_AND_TEST_NEGATIVE(void) |
160 | { |
161 | refcount_t neg = REFCOUNT_INIT(0); |
162 | |
163 | pr_info("attempting bad refcount_dec_and_test() below zero\n" ); |
164 | if (refcount_dec_and_test(r: &neg)) |
165 | pr_warn("Weird: refcount_dec_and_test() reported zero\n" ); |
166 | |
167 | check_negative(ref: &neg, start: 0); |
168 | } |
169 | |
170 | /* |
171 | * A refcount_sub_and_test() should act like refcount_dec_and_test() |
172 | * above when going negative. |
173 | */ |
174 | static void lkdtm_REFCOUNT_SUB_AND_TEST_NEGATIVE(void) |
175 | { |
176 | refcount_t neg = REFCOUNT_INIT(3); |
177 | |
178 | pr_info("attempting bad refcount_sub_and_test() below zero\n" ); |
179 | if (refcount_sub_and_test(i: 5, r: &neg)) |
180 | pr_warn("Weird: refcount_sub_and_test() reported zero\n" ); |
181 | |
182 | check_negative(ref: &neg, start: 3); |
183 | } |
184 | |
185 | static void check_from_zero(refcount_t *ref) |
186 | { |
187 | switch (refcount_read(r: ref)) { |
188 | case 0: |
189 | pr_info("Zero detected: stayed at zero\n" ); |
190 | break; |
191 | case REFCOUNT_SATURATED: |
192 | pr_info("Zero detected: saturated\n" ); |
193 | break; |
194 | case REFCOUNT_MAX: |
195 | pr_warn("Zero detected: unsafely reset to max\n" ); |
196 | break; |
197 | default: |
198 | pr_info("Fail: zero not detected, incremented to %d\n" , |
199 | refcount_read(ref)); |
200 | } |
201 | } |
202 | |
203 | /* |
204 | * A refcount_inc() from zero should pin to zero or saturate and may WARN. |
205 | */ |
206 | static void lkdtm_REFCOUNT_INC_ZERO(void) |
207 | { |
208 | refcount_t zero = REFCOUNT_INIT(0); |
209 | |
210 | pr_info("attempting safe refcount_inc_not_zero() from zero\n" ); |
211 | if (!refcount_inc_not_zero(r: &zero)) { |
212 | pr_info("Good: zero detected\n" ); |
213 | if (refcount_read(r: &zero) == 0) |
214 | pr_info("Correctly stayed at zero\n" ); |
215 | else |
216 | pr_err("Fail: refcount went past zero!\n" ); |
217 | } else { |
218 | pr_err("Fail: Zero not detected!?\n" ); |
219 | } |
220 | |
221 | pr_info("attempting bad refcount_inc() from zero\n" ); |
222 | refcount_inc(r: &zero); |
223 | |
224 | check_from_zero(ref: &zero); |
225 | } |
226 | |
227 | /* |
228 | * A refcount_add() should act like refcount_inc() above when starting |
229 | * at zero. |
230 | */ |
231 | static void lkdtm_REFCOUNT_ADD_ZERO(void) |
232 | { |
233 | refcount_t zero = REFCOUNT_INIT(0); |
234 | |
235 | pr_info("attempting safe refcount_add_not_zero() from zero\n" ); |
236 | if (!refcount_add_not_zero(i: 3, r: &zero)) { |
237 | pr_info("Good: zero detected\n" ); |
238 | if (refcount_read(r: &zero) == 0) |
239 | pr_info("Correctly stayed at zero\n" ); |
240 | else |
241 | pr_err("Fail: refcount went past zero\n" ); |
242 | } else { |
243 | pr_err("Fail: Zero not detected!?\n" ); |
244 | } |
245 | |
246 | pr_info("attempting bad refcount_add() from zero\n" ); |
247 | refcount_add(i: 3, r: &zero); |
248 | |
249 | check_from_zero(ref: &zero); |
250 | } |
251 | |
252 | static void check_saturated(refcount_t *ref) |
253 | { |
254 | switch (refcount_read(r: ref)) { |
255 | case REFCOUNT_SATURATED: |
256 | pr_info("Saturation detected: still saturated\n" ); |
257 | break; |
258 | case REFCOUNT_MAX: |
259 | pr_warn("Saturation detected: unsafely reset to max\n" ); |
260 | break; |
261 | default: |
262 | pr_err("Fail: refcount went crazy: %d\n" , refcount_read(ref)); |
263 | } |
264 | } |
265 | |
266 | /* |
267 | * A refcount_inc() from a saturated value should at most warn about |
268 | * being saturated already. |
269 | */ |
270 | static void lkdtm_REFCOUNT_INC_SATURATED(void) |
271 | { |
272 | refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); |
273 | |
274 | pr_info("attempting bad refcount_inc() from saturated\n" ); |
275 | refcount_inc(r: &sat); |
276 | |
277 | check_saturated(ref: &sat); |
278 | } |
279 | |
280 | /* Should act like refcount_inc() above from saturated. */ |
281 | static void lkdtm_REFCOUNT_DEC_SATURATED(void) |
282 | { |
283 | refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); |
284 | |
285 | pr_info("attempting bad refcount_dec() from saturated\n" ); |
286 | refcount_dec(r: &sat); |
287 | |
288 | check_saturated(ref: &sat); |
289 | } |
290 | |
291 | /* Should act like refcount_inc() above from saturated. */ |
292 | static void lkdtm_REFCOUNT_ADD_SATURATED(void) |
293 | { |
294 | refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); |
295 | |
296 | pr_info("attempting bad refcount_dec() from saturated\n" ); |
297 | refcount_add(i: 8, r: &sat); |
298 | |
299 | check_saturated(ref: &sat); |
300 | } |
301 | |
302 | /* Should act like refcount_inc() above from saturated. */ |
303 | static void lkdtm_REFCOUNT_INC_NOT_ZERO_SATURATED(void) |
304 | { |
305 | refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); |
306 | |
307 | pr_info("attempting bad refcount_inc_not_zero() from saturated\n" ); |
308 | if (!refcount_inc_not_zero(r: &sat)) |
309 | pr_warn("Weird: refcount_inc_not_zero() reported zero\n" ); |
310 | |
311 | check_saturated(ref: &sat); |
312 | } |
313 | |
314 | /* Should act like refcount_inc() above from saturated. */ |
315 | static void lkdtm_REFCOUNT_ADD_NOT_ZERO_SATURATED(void) |
316 | { |
317 | refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); |
318 | |
319 | pr_info("attempting bad refcount_add_not_zero() from saturated\n" ); |
320 | if (!refcount_add_not_zero(i: 7, r: &sat)) |
321 | pr_warn("Weird: refcount_add_not_zero() reported zero\n" ); |
322 | |
323 | check_saturated(ref: &sat); |
324 | } |
325 | |
326 | /* Should act like refcount_inc() above from saturated. */ |
327 | static void lkdtm_REFCOUNT_DEC_AND_TEST_SATURATED(void) |
328 | { |
329 | refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); |
330 | |
331 | pr_info("attempting bad refcount_dec_and_test() from saturated\n" ); |
332 | if (refcount_dec_and_test(r: &sat)) |
333 | pr_warn("Weird: refcount_dec_and_test() reported zero\n" ); |
334 | |
335 | check_saturated(ref: &sat); |
336 | } |
337 | |
338 | /* Should act like refcount_inc() above from saturated. */ |
339 | static void lkdtm_REFCOUNT_SUB_AND_TEST_SATURATED(void) |
340 | { |
341 | refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); |
342 | |
343 | pr_info("attempting bad refcount_sub_and_test() from saturated\n" ); |
344 | if (refcount_sub_and_test(i: 8, r: &sat)) |
345 | pr_warn("Weird: refcount_sub_and_test() reported zero\n" ); |
346 | |
347 | check_saturated(ref: &sat); |
348 | } |
349 | |
350 | /* Used to time the existing atomic_t when used for reference counting */ |
351 | static void lkdtm_ATOMIC_TIMING(void) |
352 | { |
353 | unsigned int i; |
354 | atomic_t count = ATOMIC_INIT(1); |
355 | |
356 | for (i = 0; i < INT_MAX - 1; i++) |
357 | atomic_inc(v: &count); |
358 | |
359 | for (i = INT_MAX; i > 0; i--) |
360 | if (atomic_dec_and_test(v: &count)) |
361 | break; |
362 | |
363 | if (i != 1) |
364 | pr_err("atomic timing: out of sync up/down cycle: %u\n" , i - 1); |
365 | else |
366 | pr_info("atomic timing: done\n" ); |
367 | } |
368 | |
369 | /* |
370 | * This can be compared to ATOMIC_TIMING when implementing fast refcount |
371 | * protections. Looking at the number of CPU cycles tells the real story |
372 | * about performance. For example: |
373 | * cd /sys/kernel/debug/provoke-crash |
374 | * perf stat -B -- cat <(echo REFCOUNT_TIMING) > DIRECT |
375 | */ |
376 | static void lkdtm_REFCOUNT_TIMING(void) |
377 | { |
378 | unsigned int i; |
379 | refcount_t count = REFCOUNT_INIT(1); |
380 | |
381 | for (i = 0; i < INT_MAX - 1; i++) |
382 | refcount_inc(r: &count); |
383 | |
384 | for (i = INT_MAX; i > 0; i--) |
385 | if (refcount_dec_and_test(r: &count)) |
386 | break; |
387 | |
388 | if (i != 1) |
389 | pr_err("refcount: out of sync up/down cycle: %u\n" , i - 1); |
390 | else |
391 | pr_info("refcount timing: done\n" ); |
392 | } |
393 | |
394 | static struct crashtype crashtypes[] = { |
395 | CRASHTYPE(REFCOUNT_INC_OVERFLOW), |
396 | CRASHTYPE(REFCOUNT_ADD_OVERFLOW), |
397 | CRASHTYPE(REFCOUNT_INC_NOT_ZERO_OVERFLOW), |
398 | CRASHTYPE(REFCOUNT_ADD_NOT_ZERO_OVERFLOW), |
399 | CRASHTYPE(REFCOUNT_DEC_ZERO), |
400 | CRASHTYPE(REFCOUNT_DEC_NEGATIVE), |
401 | CRASHTYPE(REFCOUNT_DEC_AND_TEST_NEGATIVE), |
402 | CRASHTYPE(REFCOUNT_SUB_AND_TEST_NEGATIVE), |
403 | CRASHTYPE(REFCOUNT_INC_ZERO), |
404 | CRASHTYPE(REFCOUNT_ADD_ZERO), |
405 | CRASHTYPE(REFCOUNT_INC_SATURATED), |
406 | CRASHTYPE(REFCOUNT_DEC_SATURATED), |
407 | CRASHTYPE(REFCOUNT_ADD_SATURATED), |
408 | CRASHTYPE(REFCOUNT_INC_NOT_ZERO_SATURATED), |
409 | CRASHTYPE(REFCOUNT_ADD_NOT_ZERO_SATURATED), |
410 | CRASHTYPE(REFCOUNT_DEC_AND_TEST_SATURATED), |
411 | CRASHTYPE(REFCOUNT_SUB_AND_TEST_SATURATED), |
412 | CRASHTYPE(ATOMIC_TIMING), |
413 | CRASHTYPE(REFCOUNT_TIMING), |
414 | }; |
415 | |
416 | struct crashtype_category refcount_crashtypes = { |
417 | .crashtypes = crashtypes, |
418 | .len = ARRAY_SIZE(crashtypes), |
419 | }; |
420 | |