Merge tag 'powerpc-5.15-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc...
[linux-2.6-microblaze.git] / drivers / misc / lkdtm / refcount.c
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(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 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(&over);
33         refcount_inc(&over);
34
35         pr_info("attempting bad refcount_inc() overflow\n");
36         refcount_inc(&over);
37         refcount_inc(&over);
38
39         overflow_check(&over);
40 }
41
42 /* refcount_add() should behave just like refcount_inc() above. */
43 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(&over);
49         refcount_dec(&over);
50         refcount_dec(&over);
51         refcount_dec(&over);
52         refcount_add(4, &over);
53
54         pr_info("attempting bad refcount_add() overflow\n");
55         refcount_add(4, &over);
56
57         overflow_check(&over);
58 }
59
60 /* refcount_inc_not_zero() should behave just like refcount_inc() above. */
61 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(&over))
67                 pr_warn("Weird: refcount_inc_not_zero() reported zero\n");
68
69         overflow_check(&over);
70 }
71
72 /* refcount_add_not_zero() should behave just like refcount_inc() above. */
73 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(6, &over))
79                 pr_warn("Weird: refcount_add_not_zero() reported zero\n");
80
81         overflow_check(&over);
82 }
83
84 static void check_zero(refcount_t *ref)
85 {
86         switch (refcount_read(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 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(&zero);
112
113         pr_info("attempting bad refcount_dec() to zero\n");
114         refcount_dec(&zero);
115
116         check_zero(&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(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(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 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(&neg);
151
152         check_negative(&neg, 0);
153 }
154
155 /*
156  * A refcount_dec_and_test() should act like refcount_dec() above when
157  * going negative.
158  */
159 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(&neg))
165                 pr_warn("Weird: refcount_dec_and_test() reported zero\n");
166
167         check_negative(&neg, 0);
168 }
169
170 /*
171  * A refcount_sub_and_test() should act like refcount_dec_and_test()
172  * above when going negative.
173  */
174 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(5, &neg))
180                 pr_warn("Weird: refcount_sub_and_test() reported zero\n");
181
182         check_negative(&neg, 3);
183 }
184
185 static void check_from_zero(refcount_t *ref)
186 {
187         switch (refcount_read(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 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(&zero)) {
212                 pr_info("Good: zero detected\n");
213                 if (refcount_read(&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(&zero);
223
224         check_from_zero(&zero);
225 }
226
227 /*
228  * A refcount_add() should act like refcount_inc() above when starting
229  * at zero.
230  */
231 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(3, &zero)) {
237                 pr_info("Good: zero detected\n");
238                 if (refcount_read(&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(3, &zero);
248
249         check_from_zero(&zero);
250 }
251
252 static void check_saturated(refcount_t *ref)
253 {
254         switch (refcount_read(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 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(&sat);
276
277         check_saturated(&sat);
278 }
279
280 /* Should act like refcount_inc() above from saturated. */
281 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(&sat);
287
288         check_saturated(&sat);
289 }
290
291 /* Should act like refcount_inc() above from saturated. */
292 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(8, &sat);
298
299         check_saturated(&sat);
300 }
301
302 /* Should act like refcount_inc() above from saturated. */
303 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(&sat))
309                 pr_warn("Weird: refcount_inc_not_zero() reported zero\n");
310
311         check_saturated(&sat);
312 }
313
314 /* Should act like refcount_inc() above from saturated. */
315 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(7, &sat))
321                 pr_warn("Weird: refcount_add_not_zero() reported zero\n");
322
323         check_saturated(&sat);
324 }
325
326 /* Should act like refcount_inc() above from saturated. */
327 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(&sat))
333                 pr_warn("Weird: refcount_dec_and_test() reported zero\n");
334
335         check_saturated(&sat);
336 }
337
338 /* Should act like refcount_inc() above from saturated. */
339 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(8, &sat))
345                 pr_warn("Weird: refcount_sub_and_test() reported zero\n");
346
347         check_saturated(&sat);
348 }
349
350 /* Used to time the existing atomic_t when used for reference counting */
351 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(&count);
358
359         for (i = INT_MAX; i > 0; i--)
360                 if (atomic_dec_and_test(&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 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(&count);
383
384         for (i = INT_MAX; i > 0; i--)
385                 if (refcount_dec_and_test(&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 }