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