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 
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 
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 
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 
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 
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 
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);
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 
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 
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 
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