1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Test cases for struct randomization, i.e. CONFIG_RANDSTRUCT=y.
4 *
5 * For example, see:
6 * "Running tests with kunit_tool" at Documentation/dev-tools/kunit/start.rst
7 * ./tools/testing/kunit/kunit.py run randstruct [--raw_output] \
8 * [--make_option LLVM=1] \
9 * --kconfig_add CONFIG_RANDSTRUCT_FULL=y
10 *
11 */
12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13
14 #include <kunit/test.h>
15 #include <linux/init.h>
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/string.h>
19
20 #define DO_MANY_MEMBERS(macro, args...) \
21 macro(a, args) \
22 macro(b, args) \
23 macro(c, args) \
24 macro(d, args) \
25 macro(e, args) \
26 macro(f, args) \
27 macro(g, args) \
28 macro(h, args)
29
30 #define do_enum(x, ignored) MEMBER_NAME_ ## x,
31 enum randstruct_member_names {
32 DO_MANY_MEMBERS(do_enum)
33 MEMBER_NAME_MAX,
34 };
35 /* Make sure the macros are working: want 8 test members. */
36 _Static_assert(MEMBER_NAME_MAX == 8, "Number of test members changed?!");
37
38 /* This is an unsigned long member to match the function pointer size */
39 #define unsigned_long_member(x, ignored) unsigned long x;
40 struct randstruct_untouched {
41 DO_MANY_MEMBERS(unsigned_long_member)
42 };
43
44 /* Struct explicitly marked with __randomize_layout. */
45 struct randstruct_shuffled {
46 DO_MANY_MEMBERS(unsigned_long_member)
47 } __randomize_layout;
48 #undef unsigned_long_member
49
50 /* Struct implicitly randomized from being all func ptrs. */
51 #define func_member(x, ignored) size_t (*x)(int);
52 struct randstruct_funcs_untouched {
53 DO_MANY_MEMBERS(func_member)
54 } __no_randomize_layout;
55
56 struct randstruct_funcs_shuffled {
57 DO_MANY_MEMBERS(func_member)
58 };
59
60 #define func_body(x, ignored) \
61 static noinline size_t func_##x(int arg) \
62 { \
63 return offsetof(struct randstruct_funcs_untouched, x); \
64 }
65 DO_MANY_MEMBERS(func_body)
66
67 /* Various mixed types. */
68 #define mixed_members \
69 bool a; \
70 short b; \
71 unsigned int c __aligned(16); \
72 size_t d; \
73 char e; \
74 u64 f; \
75 union { \
76 struct randstruct_shuffled shuffled; \
77 uintptr_t g; \
78 }; \
79 union { \
80 void *ptr; \
81 char h; \
82 };
83
84 struct randstruct_mixed_untouched {
85 mixed_members
86 };
87
88 struct randstruct_mixed_shuffled {
89 mixed_members
90 } __randomize_layout;
91 #undef mixed_members
92
93 struct contains_randstruct_untouched {
94 int before;
95 struct randstruct_untouched untouched;
96 int after;
97 };
98
99 struct contains_randstruct_shuffled {
100 int before;
101 struct randstruct_shuffled shuffled;
102 int after;
103 };
104
105 struct contains_func_untouched {
106 struct randstruct_funcs_shuffled inner;
107 DO_MANY_MEMBERS(func_member)
108 } __no_randomize_layout;
109
110 struct contains_func_shuffled {
111 struct randstruct_funcs_shuffled inner;
112 DO_MANY_MEMBERS(func_member)
113 };
114 #undef func_member
115
116 #define check_mismatch(x, untouched, shuffled) \
117 if (offsetof(untouched, x) != offsetof(shuffled, x)) \
118 mismatches++; \
119 kunit_info(test, #shuffled "::" #x " @ %zu (vs %zu)\n", \
120 offsetof(shuffled, x), \
121 offsetof(untouched, x)); \
122
123 #define check_pair(outcome, untouched, shuffled, checker...) \
124 mismatches = 0; \
125 DO_MANY_MEMBERS(checker, untouched, shuffled) \
126 kunit_info(test, "Differing " #untouched " vs " #shuffled " member positions: %d\n", \
127 mismatches); \
128 KUNIT_##outcome##_MSG(test, mismatches, 0, \
129 #untouched " vs " #shuffled " layouts: unlucky or broken?\n");
130
randstruct_layout_same(struct kunit * test)131 static void randstruct_layout_same(struct kunit *test)
132 {
133 int mismatches;
134
135 check_pair(EXPECT_EQ, struct randstruct_untouched, struct randstruct_untouched,
136 check_mismatch)
137 check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_shuffled,
138 check_mismatch)
139 }
140
randstruct_layout_mixed(struct kunit * test)141 static void randstruct_layout_mixed(struct kunit *test)
142 {
143 int mismatches;
144
145 check_pair(EXPECT_EQ, struct randstruct_mixed_untouched, struct randstruct_mixed_untouched,
146 check_mismatch)
147 check_pair(EXPECT_GT, struct randstruct_mixed_untouched, struct randstruct_mixed_shuffled,
148 check_mismatch)
149 }
150
randstruct_layout_fptr(struct kunit * test)151 static void randstruct_layout_fptr(struct kunit *test)
152 {
153 int mismatches;
154
155 check_pair(EXPECT_EQ, struct randstruct_untouched, struct randstruct_untouched,
156 check_mismatch)
157 check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_funcs_shuffled,
158 check_mismatch)
159 check_pair(EXPECT_GT, struct randstruct_funcs_untouched, struct randstruct_funcs_shuffled,
160 check_mismatch)
161 }
162
163 #define check_mismatch_prefixed(x, prefix, untouched, shuffled) \
164 check_mismatch(prefix.x, untouched, shuffled)
165
randstruct_layout_fptr_deep(struct kunit * test)166 static void randstruct_layout_fptr_deep(struct kunit *test)
167 {
168 int mismatches;
169
170 if (IS_ENABLED(CONFIG_CC_IS_CLANG))
171 kunit_skip(test, "Clang randstruct misses inner functions: https://github.com/llvm/llvm-project/issues/138355");
172
173 check_pair(EXPECT_EQ, struct contains_func_untouched, struct contains_func_untouched,
174 check_mismatch_prefixed, inner)
175
176 check_pair(EXPECT_GT, struct contains_func_untouched, struct contains_func_shuffled,
177 check_mismatch_prefixed, inner)
178 }
179
180 #undef check_pair
181 #undef check_mismatch
182
183 #define check_mismatch(x, ignore) \
184 KUNIT_EXPECT_EQ_MSG(test, untouched->x, shuffled->x, \
185 "Mismatched member value in %s initializer\n", \
186 name);
187
test_check_init(struct kunit * test,const char * name,struct randstruct_untouched * untouched,struct randstruct_shuffled * shuffled)188 static void test_check_init(struct kunit *test, const char *name,
189 struct randstruct_untouched *untouched,
190 struct randstruct_shuffled *shuffled)
191 {
192 DO_MANY_MEMBERS(check_mismatch)
193 }
194
test_check_mixed_init(struct kunit * test,const char * name,struct randstruct_mixed_untouched * untouched,struct randstruct_mixed_shuffled * shuffled)195 static void test_check_mixed_init(struct kunit *test, const char *name,
196 struct randstruct_mixed_untouched *untouched,
197 struct randstruct_mixed_shuffled *shuffled)
198 {
199 DO_MANY_MEMBERS(check_mismatch)
200 }
201 #undef check_mismatch
202
203 #define check_mismatch(x, ignore) \
204 KUNIT_EXPECT_EQ_MSG(test, untouched->untouched.x, \
205 shuffled->shuffled.x, \
206 "Mismatched member value in %s initializer\n", \
207 name);
test_check_contained_init(struct kunit * test,const char * name,struct contains_randstruct_untouched * untouched,struct contains_randstruct_shuffled * shuffled)208 static void test_check_contained_init(struct kunit *test, const char *name,
209 struct contains_randstruct_untouched *untouched,
210 struct contains_randstruct_shuffled *shuffled)
211 {
212 DO_MANY_MEMBERS(check_mismatch)
213 }
214 #undef check_mismatch
215
216 #define check_mismatch(x, ignore) \
217 KUNIT_EXPECT_PTR_EQ_MSG(test, untouched->x, shuffled->x, \
218 "Mismatched member value in %s initializer\n", \
219 name);
220
test_check_funcs_init(struct kunit * test,const char * name,struct randstruct_funcs_untouched * untouched,struct randstruct_funcs_shuffled * shuffled)221 static void test_check_funcs_init(struct kunit *test, const char *name,
222 struct randstruct_funcs_untouched *untouched,
223 struct randstruct_funcs_shuffled *shuffled)
224 {
225 DO_MANY_MEMBERS(check_mismatch)
226 }
227 #undef check_mismatch
228
randstruct_initializers(struct kunit * test)229 static void randstruct_initializers(struct kunit *test)
230 {
231 #define init_members \
232 .a = 1, \
233 .b = 3, \
234 .c = 5, \
235 .d = 7, \
236 .e = 11, \
237 .f = 13, \
238 .g = 17, \
239 .h = 19,
240 struct randstruct_untouched untouched = {
241 init_members
242 };
243 struct randstruct_shuffled shuffled = {
244 init_members
245 };
246 struct randstruct_mixed_untouched mixed_untouched = {
247 init_members
248 };
249 struct randstruct_mixed_shuffled mixed_shuffled = {
250 init_members
251 };
252 struct contains_randstruct_untouched contains_untouched = {
253 .untouched = {
254 init_members
255 },
256 };
257 struct contains_randstruct_shuffled contains_shuffled = {
258 .shuffled = {
259 init_members
260 },
261 };
262 #define func_member(x, ignored) \
263 .x = func_##x,
264 struct randstruct_funcs_untouched funcs_untouched = {
265 DO_MANY_MEMBERS(func_member)
266 };
267 struct randstruct_funcs_shuffled funcs_shuffled = {
268 DO_MANY_MEMBERS(func_member)
269 };
270
271 test_check_init(test, "named", &untouched, &shuffled);
272 test_check_init(test, "unnamed", &untouched,
273 &(struct randstruct_shuffled){
274 init_members
275 });
276
277 test_check_contained_init(test, "named", &contains_untouched, &contains_shuffled);
278 test_check_contained_init(test, "unnamed", &contains_untouched,
279 &(struct contains_randstruct_shuffled){
280 .shuffled = (struct randstruct_shuffled){
281 init_members
282 },
283 });
284
285 test_check_contained_init(test, "named", &contains_untouched, &contains_shuffled);
286 test_check_contained_init(test, "unnamed copy", &contains_untouched,
287 &(struct contains_randstruct_shuffled){
288 /* full struct copy initializer */
289 .shuffled = shuffled,
290 });
291
292 test_check_mixed_init(test, "named", &mixed_untouched, &mixed_shuffled);
293 test_check_mixed_init(test, "unnamed", &mixed_untouched,
294 &(struct randstruct_mixed_shuffled){
295 init_members
296 });
297
298 test_check_funcs_init(test, "named", &funcs_untouched, &funcs_shuffled);
299 test_check_funcs_init(test, "unnamed", &funcs_untouched,
300 &(struct randstruct_funcs_shuffled){
301 DO_MANY_MEMBERS(func_member)
302 });
303
304 #undef func_member
305 #undef init_members
306 }
307
randstruct_test_init(struct kunit * test)308 static int randstruct_test_init(struct kunit *test)
309 {
310 if (!IS_ENABLED(CONFIG_RANDSTRUCT))
311 kunit_skip(test, "Not built with CONFIG_RANDSTRUCT=y");
312
313 return 0;
314 }
315
316 static struct kunit_case randstruct_test_cases[] = {
317 KUNIT_CASE(randstruct_layout_same),
318 KUNIT_CASE(randstruct_layout_mixed),
319 KUNIT_CASE(randstruct_layout_fptr),
320 KUNIT_CASE(randstruct_layout_fptr_deep),
321 KUNIT_CASE(randstruct_initializers),
322 {}
323 };
324
325 static struct kunit_suite randstruct_test_suite = {
326 .name = "randstruct",
327 .init = randstruct_test_init,
328 .test_cases = randstruct_test_cases,
329 };
330
331 kunit_test_suites(&randstruct_test_suite);
332
333 MODULE_DESCRIPTION("Test cases for struct randomization");
334 MODULE_LICENSE("GPL");
335