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