• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Test cases for compiler-based stack variable zeroing via
4  * -ftrivial-auto-var-init={zero,pattern} or CONFIG_GCC_PLUGIN_STRUCTLEAK*.
5  *
6  * External build example:
7  *	clang -O2 -Wall -ftrivial-auto-var-init=pattern \
8  *		-o test_stackinit test_stackinit.c
9  */
10 #ifdef __KERNEL__
11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12 
13 #include <linux/init.h>
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/string.h>
17 
18 #else
19 
20 /* Userspace headers. */
21 #include <stdio.h>
22 #include <stdint.h>
23 #include <string.h>
24 #include <stdbool.h>
25 #include <errno.h>
26 #include <sys/types.h>
27 
28 /* Linux kernel-ism stubs for stand-alone userspace build. */
29 #define KBUILD_MODNAME		"stackinit"
30 #define pr_fmt(fmt)		KBUILD_MODNAME ": " fmt
31 #define pr_err(fmt, ...)	fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__)
32 #define pr_warn(fmt, ...)	fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__)
33 #define pr_info(fmt, ...)	fprintf(stdout, pr_fmt(fmt), ##__VA_ARGS__)
34 #define __init			/**/
35 #define __exit			/**/
36 #define __user			/**/
37 #define noinline		__attribute__((__noinline__))
38 #define __aligned(x)		__attribute__((__aligned__(x)))
39 #ifdef __clang__
40 # define __compiletime_error(message) /**/
41 #else
42 # define __compiletime_error(message) __attribute__((__error__(message)))
43 #endif
44 #define __compiletime_assert(condition, msg, prefix, suffix)		\
45 	do {								\
46 		extern void prefix ## suffix(void) __compiletime_error(msg); \
47 		if (!(condition))					\
48 			prefix ## suffix();				\
49 	} while (0)
50 #define _compiletime_assert(condition, msg, prefix, suffix) \
51 	__compiletime_assert(condition, msg, prefix, suffix)
52 #define compiletime_assert(condition, msg) \
53 	_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
54 #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
55 #define BUILD_BUG_ON(condition) \
56 	BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition)
57 typedef uint8_t			u8;
58 typedef uint16_t		u16;
59 typedef uint32_t		u32;
60 typedef uint64_t		u64;
61 
62 #define module_init(func)	static int (*do_init)(void) = func
63 #define module_exit(func)	static void (*do_exit)(void) = func
64 #define MODULE_LICENSE(str)	int main(void) {		\
65 					int rc;			\
66 					/* License: str */	\
67 					rc = do_init();		\
68 					if (rc == 0)		\
69 						do_exit();	\
70 					return rc;		\
71 				}
72 
73 #endif /* __KERNEL__ */
74 
75 /* Exfiltration buffer. */
76 #define MAX_VAR_SIZE	128
77 static u8 check_buf[MAX_VAR_SIZE];
78 
79 /* Character array to trigger stack protector in all functions. */
80 #define VAR_BUFFER	 32
81 
82 /* Volatile mask to convince compiler to copy memory with 0xff. */
83 static volatile u8 forced_mask = 0xff;
84 
85 /* Location and size tracking to validate fill and test are colocated. */
86 static void *fill_start, *target_start;
87 static size_t fill_size, target_size;
88 
range_contains(char * haystack_start,size_t haystack_size,char * needle_start,size_t needle_size)89 static bool range_contains(char *haystack_start, size_t haystack_size,
90 			   char *needle_start, size_t needle_size)
91 {
92 	if (needle_start >= haystack_start &&
93 	    needle_start + needle_size <= haystack_start + haystack_size)
94 		return true;
95 	return false;
96 }
97 
98 /* Whether the test is expected to fail. */
99 #define WANT_SUCCESS				0
100 #define XFAIL					1
101 
102 #define DO_NOTHING_TYPE_SCALAR(var_type)	var_type
103 #define DO_NOTHING_TYPE_STRING(var_type)	void
104 #define DO_NOTHING_TYPE_STRUCT(var_type)	void
105 
106 #define DO_NOTHING_RETURN_SCALAR(ptr)		*(ptr)
107 #define DO_NOTHING_RETURN_STRING(ptr)		/**/
108 #define DO_NOTHING_RETURN_STRUCT(ptr)		/**/
109 
110 #define DO_NOTHING_CALL_SCALAR(var, name)			\
111 		(var) = do_nothing_ ## name(&(var))
112 #define DO_NOTHING_CALL_STRING(var, name)			\
113 		do_nothing_ ## name(var)
114 #define DO_NOTHING_CALL_STRUCT(var, name)			\
115 		do_nothing_ ## name(&(var))
116 
117 #define FETCH_ARG_SCALAR(var)		&var
118 #define FETCH_ARG_STRING(var)		var
119 #define FETCH_ARG_STRUCT(var)		&var
120 
121 #define FILL_SIZE_STRING		16
122 
123 #define INIT_CLONE_SCALAR		/**/
124 #define INIT_CLONE_STRING		[FILL_SIZE_STRING]
125 #define INIT_CLONE_STRUCT		/**/
126 
127 #define ZERO_CLONE_SCALAR(zero)		memset(&(zero), 0x00, sizeof(zero))
128 #define ZERO_CLONE_STRING(zero)		memset(&(zero), 0x00, sizeof(zero))
129 /*
130  * For the struct, intentionally poison padding to see if it gets
131  * copied out in direct assignments.
132  * */
133 #define ZERO_CLONE_STRUCT(zero)				\
134 	do {						\
135 		memset(&(zero), 0xFF, sizeof(zero));	\
136 		zero.one = 0;				\
137 		zero.two = 0;				\
138 		zero.three = 0;				\
139 		zero.four = 0;				\
140 	} while (0)
141 
142 #define INIT_SCALAR_none(var_type)	/**/
143 #define INIT_SCALAR_zero(var_type)	= 0
144 
145 #define INIT_STRING_none(var_type)	[FILL_SIZE_STRING] /**/
146 #define INIT_STRING_zero(var_type)	[FILL_SIZE_STRING] = { }
147 
148 #define INIT_STRUCT_none(var_type)	/**/
149 #define INIT_STRUCT_zero(var_type)	= { }
150 
151 
152 #define __static_partial		{ .two = 0, }
153 #define __static_all			{ .one = 0,			\
154 					  .two = 0,			\
155 					  .three = 0,			\
156 					  .four = 0,			\
157 					}
158 #define __dynamic_partial		{ .two = arg->two, }
159 #define __dynamic_all			{ .one = arg->one,		\
160 					  .two = arg->two,		\
161 					  .three = arg->three,		\
162 					  .four = arg->four,		\
163 					}
164 #define __runtime_partial		var.two = 0
165 #define __runtime_all			var.one = 0;			\
166 					var.two = 0;			\
167 					var.three = 0;			\
168 					var.four = 0
169 
170 #define INIT_STRUCT_static_partial(var_type)				\
171 					= __static_partial
172 #define INIT_STRUCT_static_all(var_type)				\
173 					= __static_all
174 #define INIT_STRUCT_dynamic_partial(var_type)				\
175 					= __dynamic_partial
176 #define INIT_STRUCT_dynamic_all(var_type)				\
177 					= __dynamic_all
178 #define INIT_STRUCT_runtime_partial(var_type)				\
179 					; __runtime_partial
180 #define INIT_STRUCT_runtime_all(var_type)				\
181 					; __runtime_all
182 
183 #define INIT_STRUCT_assigned_static_partial(var_type)			\
184 					; var = (var_type)__static_partial
185 #define INIT_STRUCT_assigned_static_all(var_type)			\
186 					; var = (var_type)__static_all
187 #define INIT_STRUCT_assigned_dynamic_partial(var_type)			\
188 					; var = (var_type)__dynamic_partial
189 #define INIT_STRUCT_assigned_dynamic_all(var_type)			\
190 					; var = (var_type)__dynamic_all
191 
192 #define INIT_STRUCT_assigned_copy(var_type)				\
193 					; var = *(arg)
194 
195 /*
196  * @name: unique string name for the test
197  * @var_type: type to be tested for zeroing initialization
198  * @which: is this a SCALAR, STRING, or STRUCT type?
199  * @init_level: what kind of initialization is performed
200  * @xfail: is this test expected to fail?
201  */
202 #define DEFINE_TEST_DRIVER(name, var_type, which, xfail)	\
203 /* Returns 0 on success, 1 on failure. */			\
204 static noinline __init int test_ ## name (void)			\
205 {								\
206 	var_type zero INIT_CLONE_ ## which;			\
207 	int ignored;						\
208 	u8 sum = 0, i;						\
209 								\
210 	/* Notice when a new test is larger than expected. */	\
211 	BUILD_BUG_ON(sizeof(zero) > MAX_VAR_SIZE);		\
212 								\
213 	/* Fill clone type with zero for per-field init. */	\
214 	ZERO_CLONE_ ## which(zero);				\
215 	/* Clear entire check buffer for 0xFF overlap test. */	\
216 	memset(check_buf, 0x00, sizeof(check_buf));		\
217 	/* Fill stack with 0xFF. */				\
218 	ignored = leaf_ ##name((unsigned long)&ignored, 1,	\
219 				FETCH_ARG_ ## which(zero));	\
220 	/* Verify all bytes overwritten with 0xFF. */		\
221 	for (sum = 0, i = 0; i < target_size; i++)		\
222 		sum += (check_buf[i] != 0xFF);			\
223 	if (sum) {						\
224 		pr_err(#name ": leaf fill was not 0xFF!?\n");	\
225 		return 1;					\
226 	}							\
227 	/* Clear entire check buffer for later bit tests. */	\
228 	memset(check_buf, 0x00, sizeof(check_buf));		\
229 	/* Extract stack-defined variable contents. */		\
230 	ignored = leaf_ ##name((unsigned long)&ignored, 0,	\
231 				FETCH_ARG_ ## which(zero));	\
232 								\
233 	/* Validate that compiler lined up fill and target. */	\
234 	if (!range_contains(fill_start, fill_size,		\
235 			    target_start, target_size)) {	\
236 		pr_err(#name ": stack fill missed target!?\n");	\
237 		pr_err(#name ": fill %zu wide\n", fill_size);	\
238 		pr_err(#name ": target offset by %d\n",	\
239 			(int)((ssize_t)(uintptr_t)fill_start -	\
240 			(ssize_t)(uintptr_t)target_start));	\
241 		return 1;					\
242 	}							\
243 								\
244 	/* Look for any bytes still 0xFF in check region. */	\
245 	for (sum = 0, i = 0; i < target_size; i++)		\
246 		sum += (check_buf[i] == 0xFF);			\
247 								\
248 	if (sum == 0) {						\
249 		pr_info(#name " ok\n");				\
250 		return 0;					\
251 	} else {						\
252 		pr_warn(#name " %sFAIL (uninit bytes: %d)\n",	\
253 			(xfail) ? "X" : "", sum);		\
254 		return (xfail) ? 0 : 1;				\
255 	}							\
256 }
257 #define DEFINE_TEST(name, var_type, which, init_level, xfail)	\
258 /* no-op to force compiler into ignoring "uninitialized" vars */\
259 static noinline __init DO_NOTHING_TYPE_ ## which(var_type)	\
260 do_nothing_ ## name(var_type *ptr)				\
261 {								\
262 	/* Will always be true, but compiler doesn't know. */	\
263 	if ((unsigned long)ptr > 0x2)				\
264 		return DO_NOTHING_RETURN_ ## which(ptr);	\
265 	else							\
266 		return DO_NOTHING_RETURN_ ## which(ptr + 1);	\
267 }								\
268 static noinline __init int leaf_ ## name(unsigned long sp,	\
269 					 bool fill,		\
270 					 var_type *arg)		\
271 {								\
272 	char buf[VAR_BUFFER];					\
273 	var_type var						\
274 		INIT_ ## which ## _ ## init_level(var_type);	\
275 								\
276 	target_start = &var;					\
277 	target_size = sizeof(var);				\
278 	/*							\
279 	 * Keep this buffer around to make sure we've got a	\
280 	 * stack frame of SOME kind...				\
281 	 */							\
282 	memset(buf, (char)(sp & 0xff), sizeof(buf));		\
283 	/* Fill variable with 0xFF. */				\
284 	if (fill) {						\
285 		fill_start = &var;				\
286 		fill_size = sizeof(var);			\
287 		memset(fill_start,				\
288 		       (char)((sp & 0xff) | forced_mask),	\
289 		       fill_size);				\
290 	}							\
291 								\
292 	/* Silence "never initialized" warnings. */		\
293 	DO_NOTHING_CALL_ ## which(var, name);			\
294 								\
295 	/* Exfiltrate "var". */					\
296 	memcpy(check_buf, target_start, target_size);		\
297 								\
298 	return (int)buf[0] | (int)buf[sizeof(buf) - 1];		\
299 }								\
300 DEFINE_TEST_DRIVER(name, var_type, which, xfail)
301 
302 /* Structure with no padding. */
303 struct test_packed {
304 	unsigned long one;
305 	unsigned long two;
306 	unsigned long three;
307 	unsigned long four;
308 };
309 
310 /* Simple structure with padding likely to be covered by compiler. */
311 struct test_small_hole {
312 	size_t one;
313 	char two;
314 	/* 3 byte padding hole here. */
315 	int three;
316 	unsigned long four;
317 };
318 
319 /* Trigger unhandled padding in a structure. */
320 struct test_big_hole {
321 	u8 one;
322 	u8 two;
323 	u8 three;
324 	/* 61 byte padding hole here. */
325 	u8 four __aligned(64);
326 } __aligned(64);
327 
328 struct test_trailing_hole {
329 	char *one;
330 	char *two;
331 	char *three;
332 	char four;
333 	/* "sizeof(unsigned long) - 1" byte padding hole here. */
334 };
335 
336 /* Test if STRUCTLEAK is clearing structs with __user fields. */
337 struct test_user {
338 	u8 one;
339 	unsigned long two;
340 	char __user *three;
341 	unsigned long four;
342 };
343 
344 #define DEFINE_SCALAR_TEST(name, init, xfail)			\
345 		DEFINE_TEST(name ## _ ## init, name, SCALAR,	\
346 			    init, xfail)
347 
348 #define DEFINE_SCALAR_TESTS(init, xfail)			\
349 		DEFINE_SCALAR_TEST(u8, init, xfail);		\
350 		DEFINE_SCALAR_TEST(u16, init, xfail);		\
351 		DEFINE_SCALAR_TEST(u32, init, xfail);		\
352 		DEFINE_SCALAR_TEST(u64, init, xfail);		\
353 		DEFINE_TEST(char_array_ ## init, unsigned char,	\
354 			    STRING, init, xfail)
355 
356 #define DEFINE_STRUCT_TEST(name, init, xfail)			\
357 		DEFINE_TEST(name ## _ ## init,			\
358 			    struct test_ ## name, STRUCT, init, \
359 			    xfail)
360 
361 #define DEFINE_STRUCT_TESTS(init, xfail)			\
362 		DEFINE_STRUCT_TEST(small_hole, init, xfail);	\
363 		DEFINE_STRUCT_TEST(big_hole, init, xfail);	\
364 		DEFINE_STRUCT_TEST(trailing_hole, init, xfail);	\
365 		DEFINE_STRUCT_TEST(packed, init, xfail)
366 
367 #define DEFINE_STRUCT_INITIALIZER_TESTS(base)			\
368 		DEFINE_STRUCT_TESTS(base ## _ ## partial,	\
369 				    WANT_SUCCESS);		\
370 		DEFINE_STRUCT_TESTS(base ## _ ## all,		\
371 				    WANT_SUCCESS)
372 
373 /* These should be fully initialized all the time! */
374 DEFINE_SCALAR_TESTS(zero, WANT_SUCCESS);
375 DEFINE_STRUCT_TESTS(zero, WANT_SUCCESS);
376 /* Struct initializers: padding may be left uninitialized. */
377 DEFINE_STRUCT_INITIALIZER_TESTS(static);
378 DEFINE_STRUCT_INITIALIZER_TESTS(dynamic);
379 DEFINE_STRUCT_INITIALIZER_TESTS(runtime);
380 DEFINE_STRUCT_INITIALIZER_TESTS(assigned_static);
381 DEFINE_STRUCT_INITIALIZER_TESTS(assigned_dynamic);
382 DEFINE_STRUCT_TESTS(assigned_copy, XFAIL);
383 /* No initialization without compiler instrumentation. */
384 DEFINE_SCALAR_TESTS(none, WANT_SUCCESS);
385 DEFINE_STRUCT_TESTS(none, WANT_SUCCESS);
386 /* Initialization of members with __user attribute. */
387 DEFINE_TEST(user, struct test_user, STRUCT, none, WANT_SUCCESS);
388 
389 /*
390  * Check two uses through a variable declaration outside either path,
391  * which was noticed as a special case in porting earlier stack init
392  * compiler logic.
393  */
__leaf_switch_none(int path,bool fill)394 static int noinline __leaf_switch_none(int path, bool fill)
395 {
396 	switch (path) {
397 		/*
398 		 * This is intentionally unreachable. To silence the
399 		 * warning, build with -Wno-switch-unreachable
400 		 */
401 		uint64_t var;
402 
403 	case 1:
404 		target_start = &var;
405 		target_size = sizeof(var);
406 		if (fill) {
407 			fill_start = &var;
408 			fill_size = sizeof(var);
409 
410 			memset(fill_start, forced_mask | 0x55, fill_size);
411 		}
412 		memcpy(check_buf, target_start, target_size);
413 		break;
414 	case 2:
415 		target_start = &var;
416 		target_size = sizeof(var);
417 		if (fill) {
418 			fill_start = &var;
419 			fill_size = sizeof(var);
420 
421 			memset(fill_start, forced_mask | 0xaa, fill_size);
422 		}
423 		memcpy(check_buf, target_start, target_size);
424 		break;
425 	default:
426 		var = 5;
427 		return var & forced_mask;
428 	}
429 	return 0;
430 }
431 
leaf_switch_1_none(unsigned long sp,bool fill,uint64_t * arg)432 static noinline __init int leaf_switch_1_none(unsigned long sp, bool fill,
433 					      uint64_t *arg)
434 {
435 	return __leaf_switch_none(1, fill);
436 }
437 
leaf_switch_2_none(unsigned long sp,bool fill,uint64_t * arg)438 static noinline __init int leaf_switch_2_none(unsigned long sp, bool fill,
439 					      uint64_t *arg)
440 {
441 	return __leaf_switch_none(2, fill);
442 }
443 
444 /*
445  * These are expected to fail for most configurations because neither
446  * GCC nor Clang have a way to perform initialization of variables in
447  * non-code areas (i.e. in a switch statement before the first "case").
448  * https://bugs.llvm.org/show_bug.cgi?id=44916
449  */
450 DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, XFAIL);
451 DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, XFAIL);
452 
test_stackinit_init(void)453 static int __init test_stackinit_init(void)
454 {
455 	unsigned int failures = 0;
456 
457 #define test_scalars(init)	do {				\
458 		failures += test_u8_ ## init ();		\
459 		failures += test_u16_ ## init ();		\
460 		failures += test_u32_ ## init ();		\
461 		failures += test_u64_ ## init ();		\
462 		failures += test_char_array_ ## init ();	\
463 	} while (0)
464 
465 #define test_structs(init)	do {				\
466 		failures += test_small_hole_ ## init ();	\
467 		failures += test_big_hole_ ## init ();		\
468 		failures += test_trailing_hole_ ## init ();	\
469 		failures += test_packed_ ## init ();		\
470 	} while (0)
471 
472 	/* These are explicitly initialized and should always pass. */
473 	test_scalars(zero);
474 	test_structs(zero);
475 	/* Padding here appears to be accidentally always initialized? */
476 	test_structs(dynamic_partial);
477 	test_structs(assigned_dynamic_partial);
478 	/* Padding initialization depends on compiler behaviors. */
479 	test_structs(static_partial);
480 	test_structs(static_all);
481 	test_structs(dynamic_all);
482 	test_structs(runtime_partial);
483 	test_structs(runtime_all);
484 	test_structs(assigned_static_partial);
485 	test_structs(assigned_static_all);
486 	test_structs(assigned_dynamic_all);
487 	/* Everything fails this since it effectively performs a memcpy(). */
488 	test_structs(assigned_copy);
489 
490 	/* STRUCTLEAK_BYREF_ALL should cover everything from here down. */
491 	test_scalars(none);
492 	failures += test_switch_1_none();
493 	failures += test_switch_2_none();
494 
495 	/* STRUCTLEAK_BYREF should cover from here down. */
496 	test_structs(none);
497 
498 	/* STRUCTLEAK will only cover this. */
499 	failures += test_user();
500 
501 	if (failures == 0)
502 		pr_info("all tests passed!\n");
503 	else
504 		pr_err("failures: %u\n", failures);
505 
506 	return failures ? -EINVAL : 0;
507 }
508 module_init(test_stackinit_init);
509 
test_stackinit_exit(void)510 static void __exit test_stackinit_exit(void)
511 { }
512 module_exit(test_stackinit_exit);
513 
514 MODULE_LICENSE("GPL");
515