diff options
author | Kees Cook <keescook@chromium.org> | 2017-04-24 16:23:21 -0400 |
---|---|---|
committer | Kees Cook <keescook@chromium.org> | 2017-07-26 17:38:03 -0400 |
commit | 95925c99b9043d52db626645e6ef5ee5f62c97e4 (patch) | |
tree | c7aca11aba82dbf05d5b75e5655be29f0b92bc90 /drivers | |
parent | 520eccdfe187591a51ea9ab4c1a024ae4d0f68d9 (diff) |
lkdtm: Provide more complete coverage for REFCOUNT tests
The existing REFCOUNT_* LKDTM tests were designed only for testing a narrow
portion of CONFIG_REFCOUNT_FULL. This moves the tests to their own file and
expands their testing to poke each boundary condition.
Since the protections (CONFIG_REFCOUNT_FULL and x86-fast) use different
saturation values and reach-zero behavior, those have to be build-time
set so the tests can actually validate things are happening at the
right places.
Notably, the x86-fast protection will fail REFCOUNT_INC_ZERO and
REFCOUNT_ADD_ZERO since those conditions are not checked (only overflow
is critical to protecting refcount_t). CONFIG_REFCOUNT_FULL will warn for
each REFCOUNT_*_NEGATIVE test since it provides zero-pinning behaviors
(which allows it to pass REFCOUNT_INC_ZERO and REFCOUNT_ADD_ZERO).
Signed-off-by: Kees Cook <keescook@chromium.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/lkdtm.h | 25 | ||||
-rw-r--r-- | drivers/misc/lkdtm_bugs.c | 83 | ||||
-rw-r--r-- | drivers/misc/lkdtm_core.c | 23 | ||||
-rw-r--r-- | drivers/misc/lkdtm_refcount.c | 356 |
5 files changed, 393 insertions, 95 deletions
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index b0b766416306..d84819dc2468 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile | |||
@@ -60,6 +60,7 @@ lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o | |||
60 | lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o | 60 | lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o |
61 | lkdtm-$(CONFIG_LKDTM) += lkdtm_heap.o | 61 | lkdtm-$(CONFIG_LKDTM) += lkdtm_heap.o |
62 | lkdtm-$(CONFIG_LKDTM) += lkdtm_perms.o | 62 | lkdtm-$(CONFIG_LKDTM) += lkdtm_perms.o |
63 | lkdtm-$(CONFIG_LKDTM) += lkdtm_refcount.o | ||
63 | lkdtm-$(CONFIG_LKDTM) += lkdtm_rodata_objcopy.o | 64 | lkdtm-$(CONFIG_LKDTM) += lkdtm_rodata_objcopy.o |
64 | lkdtm-$(CONFIG_LKDTM) += lkdtm_usercopy.o | 65 | lkdtm-$(CONFIG_LKDTM) += lkdtm_usercopy.o |
65 | 66 | ||
diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm.h index 3b4976396ec4..04ff8b23b3b0 100644 --- a/drivers/misc/lkdtm.h +++ b/drivers/misc/lkdtm.h | |||
@@ -19,12 +19,6 @@ void lkdtm_SOFTLOCKUP(void); | |||
19 | void lkdtm_HARDLOCKUP(void); | 19 | void lkdtm_HARDLOCKUP(void); |
20 | void lkdtm_SPINLOCKUP(void); | 20 | void lkdtm_SPINLOCKUP(void); |
21 | void lkdtm_HUNG_TASK(void); | 21 | void lkdtm_HUNG_TASK(void); |
22 | void lkdtm_REFCOUNT_SATURATE_INC(void); | ||
23 | void lkdtm_REFCOUNT_SATURATE_ADD(void); | ||
24 | void lkdtm_REFCOUNT_ZERO_DEC(void); | ||
25 | void lkdtm_REFCOUNT_ZERO_INC(void); | ||
26 | void lkdtm_REFCOUNT_ZERO_SUB(void); | ||
27 | void lkdtm_REFCOUNT_ZERO_ADD(void); | ||
28 | void lkdtm_CORRUPT_LIST_ADD(void); | 22 | void lkdtm_CORRUPT_LIST_ADD(void); |
29 | void lkdtm_CORRUPT_LIST_DEL(void); | 23 | void lkdtm_CORRUPT_LIST_DEL(void); |
30 | void lkdtm_CORRUPT_USER_DS(void); | 24 | void lkdtm_CORRUPT_USER_DS(void); |
@@ -49,6 +43,25 @@ void lkdtm_EXEC_RODATA(void); | |||
49 | void lkdtm_EXEC_USERSPACE(void); | 43 | void lkdtm_EXEC_USERSPACE(void); |
50 | void lkdtm_ACCESS_USERSPACE(void); | 44 | void lkdtm_ACCESS_USERSPACE(void); |
51 | 45 | ||
46 | /* lkdtm_refcount.c */ | ||
47 | void lkdtm_REFCOUNT_INC_OVERFLOW(void); | ||
48 | void lkdtm_REFCOUNT_ADD_OVERFLOW(void); | ||
49 | void lkdtm_REFCOUNT_INC_NOT_ZERO_OVERFLOW(void); | ||
50 | void lkdtm_REFCOUNT_ADD_NOT_ZERO_OVERFLOW(void); | ||
51 | void lkdtm_REFCOUNT_DEC_ZERO(void); | ||
52 | void lkdtm_REFCOUNT_DEC_NEGATIVE(void); | ||
53 | void lkdtm_REFCOUNT_DEC_AND_TEST_NEGATIVE(void); | ||
54 | void lkdtm_REFCOUNT_SUB_AND_TEST_NEGATIVE(void); | ||
55 | void lkdtm_REFCOUNT_INC_ZERO(void); | ||
56 | void lkdtm_REFCOUNT_ADD_ZERO(void); | ||
57 | void lkdtm_REFCOUNT_INC_SATURATED(void); | ||
58 | void lkdtm_REFCOUNT_DEC_SATURATED(void); | ||
59 | void lkdtm_REFCOUNT_ADD_SATURATED(void); | ||
60 | void lkdtm_REFCOUNT_INC_NOT_ZERO_SATURATED(void); | ||
61 | void lkdtm_REFCOUNT_ADD_NOT_ZERO_SATURATED(void); | ||
62 | void lkdtm_REFCOUNT_DEC_AND_TEST_SATURATED(void); | ||
63 | void lkdtm_REFCOUNT_SUB_AND_TEST_SATURATED(void); | ||
64 | |||
52 | /* lkdtm_rodata.c */ | 65 | /* lkdtm_rodata.c */ |
53 | void lkdtm_rodata_do_nothing(void); | 66 | void lkdtm_rodata_do_nothing(void); |
54 | 67 | ||
diff --git a/drivers/misc/lkdtm_bugs.c b/drivers/misc/lkdtm_bugs.c index d9028ef50fbe..ef3d06f901fc 100644 --- a/drivers/misc/lkdtm_bugs.c +++ b/drivers/misc/lkdtm_bugs.c | |||
@@ -6,7 +6,6 @@ | |||
6 | */ | 6 | */ |
7 | #include "lkdtm.h" | 7 | #include "lkdtm.h" |
8 | #include <linux/list.h> | 8 | #include <linux/list.h> |
9 | #include <linux/refcount.h> | ||
10 | #include <linux/sched.h> | 9 | #include <linux/sched.h> |
11 | #include <linux/sched/signal.h> | 10 | #include <linux/sched/signal.h> |
12 | #include <linux/uaccess.h> | 11 | #include <linux/uaccess.h> |
@@ -137,88 +136,6 @@ void lkdtm_HUNG_TASK(void) | |||
137 | schedule(); | 136 | schedule(); |
138 | } | 137 | } |
139 | 138 | ||
140 | void lkdtm_REFCOUNT_SATURATE_INC(void) | ||
141 | { | ||
142 | refcount_t over = REFCOUNT_INIT(UINT_MAX - 1); | ||
143 | |||
144 | pr_info("attempting good refcount decrement\n"); | ||
145 | refcount_dec(&over); | ||
146 | refcount_inc(&over); | ||
147 | |||
148 | pr_info("attempting bad refcount inc overflow\n"); | ||
149 | refcount_inc(&over); | ||
150 | refcount_inc(&over); | ||
151 | if (refcount_read(&over) == UINT_MAX) | ||
152 | pr_err("Correctly stayed saturated, but no BUG?!\n"); | ||
153 | else | ||
154 | pr_err("Fail: refcount wrapped\n"); | ||
155 | } | ||
156 | |||
157 | void lkdtm_REFCOUNT_SATURATE_ADD(void) | ||
158 | { | ||
159 | refcount_t over = REFCOUNT_INIT(UINT_MAX - 1); | ||
160 | |||
161 | pr_info("attempting good refcount decrement\n"); | ||
162 | refcount_dec(&over); | ||
163 | refcount_inc(&over); | ||
164 | |||
165 | pr_info("attempting bad refcount add overflow\n"); | ||
166 | refcount_add(2, &over); | ||
167 | if (refcount_read(&over) == UINT_MAX) | ||
168 | pr_err("Correctly stayed saturated, but no BUG?!\n"); | ||
169 | else | ||
170 | pr_err("Fail: refcount wrapped\n"); | ||
171 | } | ||
172 | |||
173 | void lkdtm_REFCOUNT_ZERO_DEC(void) | ||
174 | { | ||
175 | refcount_t zero = REFCOUNT_INIT(1); | ||
176 | |||
177 | pr_info("attempting bad refcount decrement to zero\n"); | ||
178 | refcount_dec(&zero); | ||
179 | if (refcount_read(&zero) == 0) | ||
180 | pr_err("Stayed at zero, but no BUG?!\n"); | ||
181 | else | ||
182 | pr_err("Fail: refcount went crazy\n"); | ||
183 | } | ||
184 | |||
185 | void lkdtm_REFCOUNT_ZERO_SUB(void) | ||
186 | { | ||
187 | refcount_t zero = REFCOUNT_INIT(1); | ||
188 | |||
189 | pr_info("attempting bad refcount subtract past zero\n"); | ||
190 | if (!refcount_sub_and_test(2, &zero)) | ||
191 | pr_info("wrap attempt was noticed\n"); | ||
192 | if (refcount_read(&zero) == 1) | ||
193 | pr_err("Correctly stayed above 0, but no BUG?!\n"); | ||
194 | else | ||
195 | pr_err("Fail: refcount wrapped\n"); | ||
196 | } | ||
197 | |||
198 | void lkdtm_REFCOUNT_ZERO_INC(void) | ||
199 | { | ||
200 | refcount_t zero = REFCOUNT_INIT(0); | ||
201 | |||
202 | pr_info("attempting bad refcount increment from zero\n"); | ||
203 | refcount_inc(&zero); | ||
204 | if (refcount_read(&zero) == 0) | ||
205 | pr_err("Stayed at zero, but no BUG?!\n"); | ||
206 | else | ||
207 | pr_err("Fail: refcount went past zero\n"); | ||
208 | } | ||
209 | |||
210 | void lkdtm_REFCOUNT_ZERO_ADD(void) | ||
211 | { | ||
212 | refcount_t zero = REFCOUNT_INIT(0); | ||
213 | |||
214 | pr_info("attempting bad refcount addition from zero\n"); | ||
215 | refcount_add(2, &zero); | ||
216 | if (refcount_read(&zero) == 0) | ||
217 | pr_err("Stayed at zero, but no BUG?!\n"); | ||
218 | else | ||
219 | pr_err("Fail: refcount went past zero\n"); | ||
220 | } | ||
221 | |||
222 | void lkdtm_CORRUPT_LIST_ADD(void) | 139 | void lkdtm_CORRUPT_LIST_ADD(void) |
223 | { | 140 | { |
224 | /* | 141 | /* |
diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c index 42d2b8e31e6b..156a1a07c3db 100644 --- a/drivers/misc/lkdtm_core.c +++ b/drivers/misc/lkdtm_core.c | |||
@@ -221,12 +221,23 @@ struct crashtype crashtypes[] = { | |||
221 | CRASHTYPE(WRITE_RO), | 221 | CRASHTYPE(WRITE_RO), |
222 | CRASHTYPE(WRITE_RO_AFTER_INIT), | 222 | CRASHTYPE(WRITE_RO_AFTER_INIT), |
223 | CRASHTYPE(WRITE_KERN), | 223 | CRASHTYPE(WRITE_KERN), |
224 | CRASHTYPE(REFCOUNT_SATURATE_INC), | 224 | CRASHTYPE(REFCOUNT_INC_OVERFLOW), |
225 | CRASHTYPE(REFCOUNT_SATURATE_ADD), | 225 | CRASHTYPE(REFCOUNT_ADD_OVERFLOW), |
226 | CRASHTYPE(REFCOUNT_ZERO_DEC), | 226 | CRASHTYPE(REFCOUNT_INC_NOT_ZERO_OVERFLOW), |
227 | CRASHTYPE(REFCOUNT_ZERO_INC), | 227 | CRASHTYPE(REFCOUNT_ADD_NOT_ZERO_OVERFLOW), |
228 | CRASHTYPE(REFCOUNT_ZERO_SUB), | 228 | CRASHTYPE(REFCOUNT_DEC_ZERO), |
229 | CRASHTYPE(REFCOUNT_ZERO_ADD), | 229 | CRASHTYPE(REFCOUNT_DEC_NEGATIVE), |
230 | CRASHTYPE(REFCOUNT_DEC_AND_TEST_NEGATIVE), | ||
231 | CRASHTYPE(REFCOUNT_SUB_AND_TEST_NEGATIVE), | ||
232 | CRASHTYPE(REFCOUNT_INC_ZERO), | ||
233 | CRASHTYPE(REFCOUNT_ADD_ZERO), | ||
234 | CRASHTYPE(REFCOUNT_INC_SATURATED), | ||
235 | CRASHTYPE(REFCOUNT_DEC_SATURATED), | ||
236 | CRASHTYPE(REFCOUNT_ADD_SATURATED), | ||
237 | CRASHTYPE(REFCOUNT_INC_NOT_ZERO_SATURATED), | ||
238 | CRASHTYPE(REFCOUNT_ADD_NOT_ZERO_SATURATED), | ||
239 | CRASHTYPE(REFCOUNT_DEC_AND_TEST_SATURATED), | ||
240 | CRASHTYPE(REFCOUNT_SUB_AND_TEST_SATURATED), | ||
230 | CRASHTYPE(USERCOPY_HEAP_SIZE_TO), | 241 | CRASHTYPE(USERCOPY_HEAP_SIZE_TO), |
231 | CRASHTYPE(USERCOPY_HEAP_SIZE_FROM), | 242 | CRASHTYPE(USERCOPY_HEAP_SIZE_FROM), |
232 | CRASHTYPE(USERCOPY_HEAP_FLAG_TO), | 243 | CRASHTYPE(USERCOPY_HEAP_FLAG_TO), |
diff --git a/drivers/misc/lkdtm_refcount.c b/drivers/misc/lkdtm_refcount.c new file mode 100644 index 000000000000..313abea4bf9d --- /dev/null +++ b/drivers/misc/lkdtm_refcount.c | |||
@@ -0,0 +1,356 @@ | |||
1 | /* | ||
2 | * This is for all the tests related to refcount bugs (e.g. overflow, | ||
3 | * underflow, reaching zero untested, etc). | ||
4 | */ | ||
5 | #include "lkdtm.h" | ||
6 | #include <linux/refcount.h> | ||
7 | |||
8 | #ifdef CONFIG_REFCOUNT_FULL | ||
9 | #define REFCOUNT_MAX (UINT_MAX - 1) | ||
10 | #define REFCOUNT_SATURATED UINT_MAX | ||
11 | #else | ||
12 | #define REFCOUNT_MAX INT_MAX | ||
13 | #define REFCOUNT_SATURATED (INT_MIN / 2) | ||
14 | #endif | ||
15 | |||
16 | static void overflow_check(refcount_t *ref) | ||
17 | { | ||
18 | switch (refcount_read(ref)) { | ||
19 | case REFCOUNT_SATURATED: | ||
20 | pr_info("Overflow detected: saturated\n"); | ||
21 | break; | ||
22 | case REFCOUNT_MAX: | ||
23 | pr_warn("Overflow detected: unsafely reset to max\n"); | ||
24 | break; | ||
25 | default: | ||
26 | pr_err("Fail: refcount wrapped to %d\n", refcount_read(ref)); | ||
27 | } | ||
28 | } | ||
29 | |||
30 | /* | ||
31 | * A refcount_inc() above the maximum value of the refcount implementation, | ||
32 | * should at least saturate, and at most also WARN. | ||
33 | */ | ||
34 | void lkdtm_REFCOUNT_INC_OVERFLOW(void) | ||
35 | { | ||
36 | refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX - 1); | ||
37 | |||
38 | pr_info("attempting good refcount_inc() without overflow\n"); | ||
39 | refcount_dec(&over); | ||
40 | refcount_inc(&over); | ||
41 | |||
42 | pr_info("attempting bad refcount_inc() overflow\n"); | ||
43 | refcount_inc(&over); | ||
44 | refcount_inc(&over); | ||
45 | |||
46 | overflow_check(&over); | ||
47 | } | ||
48 | |||
49 | /* refcount_add() should behave just like refcount_inc() above. */ | ||
50 | void lkdtm_REFCOUNT_ADD_OVERFLOW(void) | ||
51 | { | ||
52 | refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX - 1); | ||
53 | |||
54 | pr_info("attempting good refcount_add() without overflow\n"); | ||
55 | refcount_dec(&over); | ||
56 | refcount_dec(&over); | ||
57 | refcount_dec(&over); | ||
58 | refcount_dec(&over); | ||
59 | refcount_add(4, &over); | ||
60 | |||
61 | pr_info("attempting bad refcount_add() overflow\n"); | ||
62 | refcount_add(4, &over); | ||
63 | |||
64 | overflow_check(&over); | ||
65 | } | ||
66 | |||
67 | /* refcount_inc_not_zero() should behave just like refcount_inc() above. */ | ||
68 | void lkdtm_REFCOUNT_INC_NOT_ZERO_OVERFLOW(void) | ||
69 | { | ||
70 | refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX); | ||
71 | |||
72 | pr_info("attempting bad refcount_inc_not_zero() overflow\n"); | ||
73 | if (!refcount_inc_not_zero(&over)) | ||
74 | pr_warn("Weird: refcount_inc_not_zero() reported zero\n"); | ||
75 | |||
76 | overflow_check(&over); | ||
77 | } | ||
78 | |||
79 | /* refcount_add_not_zero() should behave just like refcount_inc() above. */ | ||
80 | void lkdtm_REFCOUNT_ADD_NOT_ZERO_OVERFLOW(void) | ||
81 | { | ||
82 | refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX); | ||
83 | |||
84 | pr_info("attempting bad refcount_add_not_zero() overflow\n"); | ||
85 | if (!refcount_add_not_zero(6, &over)) | ||
86 | pr_warn("Weird: refcount_add_not_zero() reported zero\n"); | ||
87 | |||
88 | overflow_check(&over); | ||
89 | } | ||
90 | |||
91 | static void check_zero(refcount_t *ref) | ||
92 | { | ||
93 | switch (refcount_read(ref)) { | ||
94 | case REFCOUNT_SATURATED: | ||
95 | pr_info("Zero detected: saturated\n"); | ||
96 | break; | ||
97 | case REFCOUNT_MAX: | ||
98 | pr_warn("Zero detected: unsafely reset to max\n"); | ||
99 | break; | ||
100 | case 0: | ||
101 | pr_warn("Still at zero: refcount_inc/add() must not inc-from-0\n"); | ||
102 | break; | ||
103 | default: | ||
104 | pr_err("Fail: refcount went crazy: %d\n", refcount_read(ref)); | ||
105 | } | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * A refcount_dec(), as opposed to a refcount_dec_and_test(), when it hits | ||
110 | * zero it should either saturate (when inc-from-zero isn't protected) | ||
111 | * or stay at zero (when inc-from-zero is protected) and should WARN for both. | ||
112 | */ | ||
113 | void lkdtm_REFCOUNT_DEC_ZERO(void) | ||
114 | { | ||
115 | refcount_t zero = REFCOUNT_INIT(2); | ||
116 | |||
117 | pr_info("attempting good refcount_dec()\n"); | ||
118 | refcount_dec(&zero); | ||
119 | |||
120 | pr_info("attempting bad refcount_dec() to zero\n"); | ||
121 | refcount_dec(&zero); | ||
122 | |||
123 | check_zero(&zero); | ||
124 | } | ||
125 | |||
126 | static void check_negative(refcount_t *ref, int start) | ||
127 | { | ||
128 | /* | ||
129 | * CONFIG_REFCOUNT_FULL refuses to move a refcount at all on an | ||
130 | * over-sub, so we have to track our starting position instead of | ||
131 | * looking only at zero-pinning. | ||
132 | */ | ||
133 | if (refcount_read(ref) == start) { | ||
134 | pr_warn("Still at %d: refcount_inc/add() must not inc-from-0\n", | ||
135 | start); | ||
136 | return; | ||
137 | } | ||
138 | |||
139 | switch (refcount_read(ref)) { | ||
140 | case REFCOUNT_SATURATED: | ||
141 | pr_info("Negative detected: saturated\n"); | ||
142 | break; | ||
143 | case REFCOUNT_MAX: | ||
144 | pr_warn("Negative detected: unsafely reset to max\n"); | ||
145 | break; | ||
146 | default: | ||
147 | pr_err("Fail: refcount went crazy: %d\n", refcount_read(ref)); | ||
148 | } | ||
149 | } | ||
150 | |||
151 | /* A refcount_dec() going negative should saturate and may WARN. */ | ||
152 | void lkdtm_REFCOUNT_DEC_NEGATIVE(void) | ||
153 | { | ||
154 | refcount_t neg = REFCOUNT_INIT(0); | ||
155 | |||
156 | pr_info("attempting bad refcount_dec() below zero\n"); | ||
157 | refcount_dec(&neg); | ||
158 | |||
159 | check_negative(&neg, 0); | ||
160 | } | ||
161 | |||
162 | /* | ||
163 | * A refcount_dec_and_test() should act like refcount_dec() above when | ||
164 | * going negative. | ||
165 | */ | ||
166 | void lkdtm_REFCOUNT_DEC_AND_TEST_NEGATIVE(void) | ||
167 | { | ||
168 | refcount_t neg = REFCOUNT_INIT(0); | ||
169 | |||
170 | pr_info("attempting bad refcount_dec_and_test() below zero\n"); | ||
171 | if (refcount_dec_and_test(&neg)) | ||
172 | pr_warn("Weird: refcount_dec_and_test() reported zero\n"); | ||
173 | |||
174 | check_negative(&neg, 0); | ||
175 | } | ||
176 | |||
177 | /* | ||
178 | * A refcount_sub_and_test() should act like refcount_dec_and_test() | ||
179 | * above when going negative. | ||
180 | */ | ||
181 | void lkdtm_REFCOUNT_SUB_AND_TEST_NEGATIVE(void) | ||
182 | { | ||
183 | refcount_t neg = REFCOUNT_INIT(3); | ||
184 | |||
185 | pr_info("attempting bad refcount_sub_and_test() below zero\n"); | ||
186 | if (refcount_sub_and_test(5, &neg)) | ||
187 | pr_warn("Weird: refcount_sub_and_test() reported zero\n"); | ||
188 | |||
189 | check_negative(&neg, 3); | ||
190 | } | ||
191 | |||
192 | static void check_from_zero(refcount_t *ref) | ||
193 | { | ||
194 | switch (refcount_read(ref)) { | ||
195 | case 0: | ||
196 | pr_info("Zero detected: stayed at zero\n"); | ||
197 | break; | ||
198 | case REFCOUNT_SATURATED: | ||
199 | pr_info("Zero detected: saturated\n"); | ||
200 | break; | ||
201 | case REFCOUNT_MAX: | ||
202 | pr_warn("Zero detected: unsafely reset to max\n"); | ||
203 | break; | ||
204 | default: | ||
205 | pr_info("Fail: zero not detected, incremeted to %d\n", | ||
206 | refcount_read(ref)); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * A refcount_inc() from zero should pin to zero or saturate and may WARN. | ||
212 | * Only CONFIG_REFCOUNT_FULL provides this protection currently. | ||
213 | */ | ||
214 | void lkdtm_REFCOUNT_INC_ZERO(void) | ||
215 | { | ||
216 | refcount_t zero = REFCOUNT_INIT(0); | ||
217 | |||
218 | pr_info("attempting safe refcount_inc_not_zero() from zero\n"); | ||
219 | if (!refcount_inc_not_zero(&zero)) { | ||
220 | pr_info("Good: zero detected\n"); | ||
221 | if (refcount_read(&zero) == 0) | ||
222 | pr_info("Correctly stayed at zero\n"); | ||
223 | else | ||
224 | pr_err("Fail: refcount went past zero!\n"); | ||
225 | } else { | ||
226 | pr_err("Fail: Zero not detected!?\n"); | ||
227 | } | ||
228 | |||
229 | pr_info("attempting bad refcount_inc() from zero\n"); | ||
230 | refcount_inc(&zero); | ||
231 | |||
232 | check_from_zero(&zero); | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * A refcount_add() should act like refcount_inc() above when starting | ||
237 | * at zero. | ||
238 | */ | ||
239 | void lkdtm_REFCOUNT_ADD_ZERO(void) | ||
240 | { | ||
241 | refcount_t zero = REFCOUNT_INIT(0); | ||
242 | |||
243 | pr_info("attempting safe refcount_add_not_zero() from zero\n"); | ||
244 | if (!refcount_add_not_zero(3, &zero)) { | ||
245 | pr_info("Good: zero detected\n"); | ||
246 | if (refcount_read(&zero) == 0) | ||
247 | pr_info("Correctly stayed at zero\n"); | ||
248 | else | ||
249 | pr_err("Fail: refcount went past zero\n"); | ||
250 | } else { | ||
251 | pr_err("Fail: Zero not detected!?\n"); | ||
252 | } | ||
253 | |||
254 | pr_info("attempting bad refcount_add() from zero\n"); | ||
255 | refcount_add(3, &zero); | ||
256 | |||
257 | check_from_zero(&zero); | ||
258 | } | ||
259 | |||
260 | static void check_saturated(refcount_t *ref) | ||
261 | { | ||
262 | switch (refcount_read(ref)) { | ||
263 | case REFCOUNT_SATURATED: | ||
264 | pr_info("Saturation detected: still saturated\n"); | ||
265 | break; | ||
266 | case REFCOUNT_MAX: | ||
267 | pr_warn("Saturation detected: unsafely reset to max\n"); | ||
268 | break; | ||
269 | default: | ||
270 | pr_err("Fail: refcount went crazy: %d\n", refcount_read(ref)); | ||
271 | } | ||
272 | } | ||
273 | |||
274 | /* | ||
275 | * A refcount_inc() from a saturated value should at most warn about | ||
276 | * being saturated already. | ||
277 | */ | ||
278 | void lkdtm_REFCOUNT_INC_SATURATED(void) | ||
279 | { | ||
280 | refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); | ||
281 | |||
282 | pr_info("attempting bad refcount_inc() from saturated\n"); | ||
283 | refcount_inc(&sat); | ||
284 | |||
285 | check_saturated(&sat); | ||
286 | } | ||
287 | |||
288 | /* Should act like refcount_inc() above from saturated. */ | ||
289 | void lkdtm_REFCOUNT_DEC_SATURATED(void) | ||
290 | { | ||
291 | refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); | ||
292 | |||
293 | pr_info("attempting bad refcount_dec() from saturated\n"); | ||
294 | refcount_dec(&sat); | ||
295 | |||
296 | check_saturated(&sat); | ||
297 | } | ||
298 | |||
299 | /* Should act like refcount_inc() above from saturated. */ | ||
300 | void lkdtm_REFCOUNT_ADD_SATURATED(void) | ||
301 | { | ||
302 | refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); | ||
303 | |||
304 | pr_info("attempting bad refcount_dec() from saturated\n"); | ||
305 | refcount_add(8, &sat); | ||
306 | |||
307 | check_saturated(&sat); | ||
308 | } | ||
309 | |||
310 | /* Should act like refcount_inc() above from saturated. */ | ||
311 | void lkdtm_REFCOUNT_INC_NOT_ZERO_SATURATED(void) | ||
312 | { | ||
313 | refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); | ||
314 | |||
315 | pr_info("attempting bad refcount_inc_not_zero() from saturated\n"); | ||
316 | if (!refcount_inc_not_zero(&sat)) | ||
317 | pr_warn("Weird: refcount_inc_not_zero() reported zero\n"); | ||
318 | |||
319 | check_saturated(&sat); | ||
320 | } | ||
321 | |||
322 | /* Should act like refcount_inc() above from saturated. */ | ||
323 | void lkdtm_REFCOUNT_ADD_NOT_ZERO_SATURATED(void) | ||
324 | { | ||
325 | refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); | ||
326 | |||
327 | pr_info("attempting bad refcount_add_not_zero() from saturated\n"); | ||
328 | if (!refcount_add_not_zero(7, &sat)) | ||
329 | pr_warn("Weird: refcount_add_not_zero() reported zero\n"); | ||
330 | |||
331 | check_saturated(&sat); | ||
332 | } | ||
333 | |||
334 | /* Should act like refcount_inc() above from saturated. */ | ||
335 | void lkdtm_REFCOUNT_DEC_AND_TEST_SATURATED(void) | ||
336 | { | ||
337 | refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); | ||
338 | |||
339 | pr_info("attempting bad refcount_dec_and_test() from saturated\n"); | ||
340 | if (refcount_dec_and_test(&sat)) | ||
341 | pr_warn("Weird: refcount_dec_and_test() reported zero\n"); | ||
342 | |||
343 | check_saturated(&sat); | ||
344 | } | ||
345 | |||
346 | /* Should act like refcount_inc() above from saturated. */ | ||
347 | void lkdtm_REFCOUNT_SUB_AND_TEST_SATURATED(void) | ||
348 | { | ||
349 | refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); | ||
350 | |||
351 | pr_info("attempting bad refcount_sub_and_test() from saturated\n"); | ||
352 | if (refcount_sub_and_test(8, &sat)) | ||
353 | pr_warn("Weird: refcount_sub_and_test() reported zero\n"); | ||
354 | |||
355 | check_saturated(&sat); | ||
356 | } | ||