162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Test cases for compiler-based stack variable zeroing via
462306a36Sopenharmony_ci * -ftrivial-auto-var-init={zero,pattern} or CONFIG_GCC_PLUGIN_STRUCTLEAK*.
562306a36Sopenharmony_ci * For example, see:
662306a36Sopenharmony_ci * "Running tests with kunit_tool" at Documentation/dev-tools/kunit/start.rst
762306a36Sopenharmony_ci *	./tools/testing/kunit/kunit.py run stackinit [--raw_output] \
862306a36Sopenharmony_ci *		--make_option LLVM=1 \
962306a36Sopenharmony_ci *		--kconfig_add CONFIG_INIT_STACK_ALL_ZERO=y
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <kunit/test.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/string.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* Exfiltration buffer. */
2162306a36Sopenharmony_ci#define MAX_VAR_SIZE	128
2262306a36Sopenharmony_cistatic u8 check_buf[MAX_VAR_SIZE];
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* Character array to trigger stack protector in all functions. */
2562306a36Sopenharmony_ci#define VAR_BUFFER	 32
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* Volatile mask to convince compiler to copy memory with 0xff. */
2862306a36Sopenharmony_cistatic volatile u8 forced_mask = 0xff;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* Location and size tracking to validate fill and test are colocated. */
3162306a36Sopenharmony_cistatic void *fill_start, *target_start;
3262306a36Sopenharmony_cistatic size_t fill_size, target_size;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic bool stackinit_range_contains(char *haystack_start, size_t haystack_size,
3562306a36Sopenharmony_ci				     char *needle_start, size_t needle_size)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	if (needle_start >= haystack_start &&
3862306a36Sopenharmony_ci	    needle_start + needle_size <= haystack_start + haystack_size)
3962306a36Sopenharmony_ci		return true;
4062306a36Sopenharmony_ci	return false;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/* Whether the test is expected to fail. */
4462306a36Sopenharmony_ci#define WANT_SUCCESS				0
4562306a36Sopenharmony_ci#define XFAIL					1
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define DO_NOTHING_TYPE_SCALAR(var_type)	var_type
4862306a36Sopenharmony_ci#define DO_NOTHING_TYPE_STRING(var_type)	void
4962306a36Sopenharmony_ci#define DO_NOTHING_TYPE_STRUCT(var_type)	void
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define DO_NOTHING_RETURN_SCALAR(ptr)		*(ptr)
5262306a36Sopenharmony_ci#define DO_NOTHING_RETURN_STRING(ptr)		/**/
5362306a36Sopenharmony_ci#define DO_NOTHING_RETURN_STRUCT(ptr)		/**/
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define DO_NOTHING_CALL_SCALAR(var, name)			\
5662306a36Sopenharmony_ci		(var) = do_nothing_ ## name(&(var))
5762306a36Sopenharmony_ci#define DO_NOTHING_CALL_STRING(var, name)			\
5862306a36Sopenharmony_ci		do_nothing_ ## name(var)
5962306a36Sopenharmony_ci#define DO_NOTHING_CALL_STRUCT(var, name)			\
6062306a36Sopenharmony_ci		do_nothing_ ## name(&(var))
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define FETCH_ARG_SCALAR(var)		&var
6362306a36Sopenharmony_ci#define FETCH_ARG_STRING(var)		var
6462306a36Sopenharmony_ci#define FETCH_ARG_STRUCT(var)		&var
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define FILL_SIZE_STRING		16
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define INIT_CLONE_SCALAR		/**/
6962306a36Sopenharmony_ci#define INIT_CLONE_STRING		[FILL_SIZE_STRING]
7062306a36Sopenharmony_ci#define INIT_CLONE_STRUCT		/**/
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#define ZERO_CLONE_SCALAR(zero)		memset(&(zero), 0x00, sizeof(zero))
7362306a36Sopenharmony_ci#define ZERO_CLONE_STRING(zero)		memset(&(zero), 0x00, sizeof(zero))
7462306a36Sopenharmony_ci/*
7562306a36Sopenharmony_ci * For the struct, intentionally poison padding to see if it gets
7662306a36Sopenharmony_ci * copied out in direct assignments.
7762306a36Sopenharmony_ci * */
7862306a36Sopenharmony_ci#define ZERO_CLONE_STRUCT(zero)				\
7962306a36Sopenharmony_ci	do {						\
8062306a36Sopenharmony_ci		memset(&(zero), 0xFF, sizeof(zero));	\
8162306a36Sopenharmony_ci		zero.one = 0;				\
8262306a36Sopenharmony_ci		zero.two = 0;				\
8362306a36Sopenharmony_ci		zero.three = 0;				\
8462306a36Sopenharmony_ci		zero.four = 0;				\
8562306a36Sopenharmony_ci	} while (0)
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define INIT_SCALAR_none(var_type)	/**/
8862306a36Sopenharmony_ci#define INIT_SCALAR_zero(var_type)	= 0
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define INIT_STRING_none(var_type)	[FILL_SIZE_STRING] /**/
9162306a36Sopenharmony_ci#define INIT_STRING_zero(var_type)	[FILL_SIZE_STRING] = { }
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define INIT_STRUCT_none(var_type)	/**/
9462306a36Sopenharmony_ci#define INIT_STRUCT_zero(var_type)	= { }
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci#define __static_partial		{ .two = 0, }
9862306a36Sopenharmony_ci#define __static_all			{ .one = 0,			\
9962306a36Sopenharmony_ci					  .two = 0,			\
10062306a36Sopenharmony_ci					  .three = 0,			\
10162306a36Sopenharmony_ci					  .four = 0,			\
10262306a36Sopenharmony_ci					}
10362306a36Sopenharmony_ci#define __dynamic_partial		{ .two = arg->two, }
10462306a36Sopenharmony_ci#define __dynamic_all			{ .one = arg->one,		\
10562306a36Sopenharmony_ci					  .two = arg->two,		\
10662306a36Sopenharmony_ci					  .three = arg->three,		\
10762306a36Sopenharmony_ci					  .four = arg->four,		\
10862306a36Sopenharmony_ci					}
10962306a36Sopenharmony_ci#define __runtime_partial		var.two = 0
11062306a36Sopenharmony_ci#define __runtime_all			var.one = 0;			\
11162306a36Sopenharmony_ci					var.two = 0;			\
11262306a36Sopenharmony_ci					var.three = 0;			\
11362306a36Sopenharmony_ci					var.four = 0
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci#define INIT_STRUCT_static_partial(var_type)				\
11662306a36Sopenharmony_ci					= __static_partial
11762306a36Sopenharmony_ci#define INIT_STRUCT_static_all(var_type)				\
11862306a36Sopenharmony_ci					= __static_all
11962306a36Sopenharmony_ci#define INIT_STRUCT_dynamic_partial(var_type)				\
12062306a36Sopenharmony_ci					= __dynamic_partial
12162306a36Sopenharmony_ci#define INIT_STRUCT_dynamic_all(var_type)				\
12262306a36Sopenharmony_ci					= __dynamic_all
12362306a36Sopenharmony_ci#define INIT_STRUCT_runtime_partial(var_type)				\
12462306a36Sopenharmony_ci					; __runtime_partial
12562306a36Sopenharmony_ci#define INIT_STRUCT_runtime_all(var_type)				\
12662306a36Sopenharmony_ci					; __runtime_all
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci#define INIT_STRUCT_assigned_static_partial(var_type)			\
12962306a36Sopenharmony_ci					; var = (var_type)__static_partial
13062306a36Sopenharmony_ci#define INIT_STRUCT_assigned_static_all(var_type)			\
13162306a36Sopenharmony_ci					; var = (var_type)__static_all
13262306a36Sopenharmony_ci#define INIT_STRUCT_assigned_dynamic_partial(var_type)			\
13362306a36Sopenharmony_ci					; var = (var_type)__dynamic_partial
13462306a36Sopenharmony_ci#define INIT_STRUCT_assigned_dynamic_all(var_type)			\
13562306a36Sopenharmony_ci					; var = (var_type)__dynamic_all
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci#define INIT_STRUCT_assigned_copy(var_type)				\
13862306a36Sopenharmony_ci					; var = *(arg)
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/*
14162306a36Sopenharmony_ci * @name: unique string name for the test
14262306a36Sopenharmony_ci * @var_type: type to be tested for zeroing initialization
14362306a36Sopenharmony_ci * @which: is this a SCALAR, STRING, or STRUCT type?
14462306a36Sopenharmony_ci * @init_level: what kind of initialization is performed
14562306a36Sopenharmony_ci * @xfail: is this test expected to fail?
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_ci#define DEFINE_TEST_DRIVER(name, var_type, which, xfail)	\
14862306a36Sopenharmony_ci/* Returns 0 on success, 1 on failure. */			\
14962306a36Sopenharmony_cistatic noinline void test_ ## name (struct kunit *test)		\
15062306a36Sopenharmony_ci{								\
15162306a36Sopenharmony_ci	var_type zero INIT_CLONE_ ## which;			\
15262306a36Sopenharmony_ci	int ignored;						\
15362306a36Sopenharmony_ci	u8 sum = 0, i;						\
15462306a36Sopenharmony_ci								\
15562306a36Sopenharmony_ci	/* Notice when a new test is larger than expected. */	\
15662306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(zero) > MAX_VAR_SIZE);		\
15762306a36Sopenharmony_ci								\
15862306a36Sopenharmony_ci	/* Fill clone type with zero for per-field init. */	\
15962306a36Sopenharmony_ci	ZERO_CLONE_ ## which(zero);				\
16062306a36Sopenharmony_ci	/* Clear entire check buffer for 0xFF overlap test. */	\
16162306a36Sopenharmony_ci	memset(check_buf, 0x00, sizeof(check_buf));		\
16262306a36Sopenharmony_ci	/* Fill stack with 0xFF. */				\
16362306a36Sopenharmony_ci	ignored = leaf_ ##name((unsigned long)&ignored, 1,	\
16462306a36Sopenharmony_ci				FETCH_ARG_ ## which(zero));	\
16562306a36Sopenharmony_ci	/* Verify all bytes overwritten with 0xFF. */		\
16662306a36Sopenharmony_ci	for (sum = 0, i = 0; i < target_size; i++)		\
16762306a36Sopenharmony_ci		sum += (check_buf[i] != 0xFF);			\
16862306a36Sopenharmony_ci	KUNIT_ASSERT_EQ_MSG(test, sum, 0,			\
16962306a36Sopenharmony_ci			    "leaf fill was not 0xFF!?\n");	\
17062306a36Sopenharmony_ci	/* Clear entire check buffer for later bit tests. */	\
17162306a36Sopenharmony_ci	memset(check_buf, 0x00, sizeof(check_buf));		\
17262306a36Sopenharmony_ci	/* Extract stack-defined variable contents. */		\
17362306a36Sopenharmony_ci	ignored = leaf_ ##name((unsigned long)&ignored, 0,	\
17462306a36Sopenharmony_ci				FETCH_ARG_ ## which(zero));	\
17562306a36Sopenharmony_ci								\
17662306a36Sopenharmony_ci	/* Validate that compiler lined up fill and target. */	\
17762306a36Sopenharmony_ci	KUNIT_ASSERT_TRUE_MSG(test,				\
17862306a36Sopenharmony_ci		stackinit_range_contains(fill_start, fill_size,	\
17962306a36Sopenharmony_ci			    target_start, target_size),		\
18062306a36Sopenharmony_ci		"stack fill missed target!? "			\
18162306a36Sopenharmony_ci		"(fill %zu wide, target offset by %d)\n",	\
18262306a36Sopenharmony_ci		fill_size,					\
18362306a36Sopenharmony_ci		(int)((ssize_t)(uintptr_t)fill_start -		\
18462306a36Sopenharmony_ci		      (ssize_t)(uintptr_t)target_start));	\
18562306a36Sopenharmony_ci								\
18662306a36Sopenharmony_ci	/* Look for any bytes still 0xFF in check region. */	\
18762306a36Sopenharmony_ci	for (sum = 0, i = 0; i < target_size; i++)		\
18862306a36Sopenharmony_ci		sum += (check_buf[i] == 0xFF);			\
18962306a36Sopenharmony_ci								\
19062306a36Sopenharmony_ci	if (sum != 0 && xfail)					\
19162306a36Sopenharmony_ci		kunit_skip(test,				\
19262306a36Sopenharmony_ci			   "XFAIL uninit bytes: %d\n",		\
19362306a36Sopenharmony_ci			   sum);				\
19462306a36Sopenharmony_ci	KUNIT_ASSERT_EQ_MSG(test, sum, 0,			\
19562306a36Sopenharmony_ci		"uninit bytes: %d\n", sum);			\
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci#define DEFINE_TEST(name, var_type, which, init_level, xfail)	\
19862306a36Sopenharmony_ci/* no-op to force compiler into ignoring "uninitialized" vars */\
19962306a36Sopenharmony_cistatic noinline DO_NOTHING_TYPE_ ## which(var_type)		\
20062306a36Sopenharmony_cido_nothing_ ## name(var_type *ptr)				\
20162306a36Sopenharmony_ci{								\
20262306a36Sopenharmony_ci	/* Will always be true, but compiler doesn't know. */	\
20362306a36Sopenharmony_ci	if ((unsigned long)ptr > 0x2)				\
20462306a36Sopenharmony_ci		return DO_NOTHING_RETURN_ ## which(ptr);	\
20562306a36Sopenharmony_ci	else							\
20662306a36Sopenharmony_ci		return DO_NOTHING_RETURN_ ## which(ptr + 1);	\
20762306a36Sopenharmony_ci}								\
20862306a36Sopenharmony_cistatic noinline int leaf_ ## name(unsigned long sp, bool fill,	\
20962306a36Sopenharmony_ci				  var_type *arg)		\
21062306a36Sopenharmony_ci{								\
21162306a36Sopenharmony_ci	char buf[VAR_BUFFER];					\
21262306a36Sopenharmony_ci	var_type var						\
21362306a36Sopenharmony_ci		INIT_ ## which ## _ ## init_level(var_type);	\
21462306a36Sopenharmony_ci								\
21562306a36Sopenharmony_ci	target_start = &var;					\
21662306a36Sopenharmony_ci	target_size = sizeof(var);				\
21762306a36Sopenharmony_ci	/*							\
21862306a36Sopenharmony_ci	 * Keep this buffer around to make sure we've got a	\
21962306a36Sopenharmony_ci	 * stack frame of SOME kind...				\
22062306a36Sopenharmony_ci	 */							\
22162306a36Sopenharmony_ci	memset(buf, (char)(sp & 0xff), sizeof(buf));		\
22262306a36Sopenharmony_ci	/* Fill variable with 0xFF. */				\
22362306a36Sopenharmony_ci	if (fill) {						\
22462306a36Sopenharmony_ci		fill_start = &var;				\
22562306a36Sopenharmony_ci		fill_size = sizeof(var);			\
22662306a36Sopenharmony_ci		memset(fill_start,				\
22762306a36Sopenharmony_ci		       (char)((sp & 0xff) | forced_mask),	\
22862306a36Sopenharmony_ci		       fill_size);				\
22962306a36Sopenharmony_ci	}							\
23062306a36Sopenharmony_ci								\
23162306a36Sopenharmony_ci	/* Silence "never initialized" warnings. */		\
23262306a36Sopenharmony_ci	DO_NOTHING_CALL_ ## which(var, name);			\
23362306a36Sopenharmony_ci								\
23462306a36Sopenharmony_ci	/* Exfiltrate "var". */					\
23562306a36Sopenharmony_ci	memcpy(check_buf, target_start, target_size);		\
23662306a36Sopenharmony_ci								\
23762306a36Sopenharmony_ci	return (int)buf[0] | (int)buf[sizeof(buf) - 1];		\
23862306a36Sopenharmony_ci}								\
23962306a36Sopenharmony_ciDEFINE_TEST_DRIVER(name, var_type, which, xfail)
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/* Structure with no padding. */
24262306a36Sopenharmony_cistruct test_packed {
24362306a36Sopenharmony_ci	unsigned long one;
24462306a36Sopenharmony_ci	unsigned long two;
24562306a36Sopenharmony_ci	unsigned long three;
24662306a36Sopenharmony_ci	unsigned long four;
24762306a36Sopenharmony_ci};
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci/* Simple structure with padding likely to be covered by compiler. */
25062306a36Sopenharmony_cistruct test_small_hole {
25162306a36Sopenharmony_ci	size_t one;
25262306a36Sopenharmony_ci	char two;
25362306a36Sopenharmony_ci	/* 3 byte padding hole here. */
25462306a36Sopenharmony_ci	int three;
25562306a36Sopenharmony_ci	unsigned long four;
25662306a36Sopenharmony_ci};
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci/* Trigger unhandled padding in a structure. */
25962306a36Sopenharmony_cistruct test_big_hole {
26062306a36Sopenharmony_ci	u8 one;
26162306a36Sopenharmony_ci	u8 two;
26262306a36Sopenharmony_ci	u8 three;
26362306a36Sopenharmony_ci	/* 61 byte padding hole here. */
26462306a36Sopenharmony_ci	u8 four __aligned(64);
26562306a36Sopenharmony_ci} __aligned(64);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistruct test_trailing_hole {
26862306a36Sopenharmony_ci	char *one;
26962306a36Sopenharmony_ci	char *two;
27062306a36Sopenharmony_ci	char *three;
27162306a36Sopenharmony_ci	char four;
27262306a36Sopenharmony_ci	/* "sizeof(unsigned long) - 1" byte padding hole here. */
27362306a36Sopenharmony_ci};
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci/* Test if STRUCTLEAK is clearing structs with __user fields. */
27662306a36Sopenharmony_cistruct test_user {
27762306a36Sopenharmony_ci	u8 one;
27862306a36Sopenharmony_ci	unsigned long two;
27962306a36Sopenharmony_ci	char __user *three;
28062306a36Sopenharmony_ci	unsigned long four;
28162306a36Sopenharmony_ci};
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci#define ALWAYS_PASS	WANT_SUCCESS
28462306a36Sopenharmony_ci#define ALWAYS_FAIL	XFAIL
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci#ifdef CONFIG_INIT_STACK_NONE
28762306a36Sopenharmony_ci# define USER_PASS	XFAIL
28862306a36Sopenharmony_ci# define BYREF_PASS	XFAIL
28962306a36Sopenharmony_ci# define STRONG_PASS	XFAIL
29062306a36Sopenharmony_ci#elif defined(CONFIG_GCC_PLUGIN_STRUCTLEAK_USER)
29162306a36Sopenharmony_ci# define USER_PASS	WANT_SUCCESS
29262306a36Sopenharmony_ci# define BYREF_PASS	XFAIL
29362306a36Sopenharmony_ci# define STRONG_PASS	XFAIL
29462306a36Sopenharmony_ci#elif defined(CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF)
29562306a36Sopenharmony_ci# define USER_PASS	WANT_SUCCESS
29662306a36Sopenharmony_ci# define BYREF_PASS	WANT_SUCCESS
29762306a36Sopenharmony_ci# define STRONG_PASS	XFAIL
29862306a36Sopenharmony_ci#else
29962306a36Sopenharmony_ci# define USER_PASS	WANT_SUCCESS
30062306a36Sopenharmony_ci# define BYREF_PASS	WANT_SUCCESS
30162306a36Sopenharmony_ci# define STRONG_PASS	WANT_SUCCESS
30262306a36Sopenharmony_ci#endif
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci#define DEFINE_SCALAR_TEST(name, init, xfail)			\
30562306a36Sopenharmony_ci		DEFINE_TEST(name ## _ ## init, name, SCALAR,	\
30662306a36Sopenharmony_ci			    init, xfail)
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci#define DEFINE_SCALAR_TESTS(init, xfail)			\
30962306a36Sopenharmony_ci		DEFINE_SCALAR_TEST(u8, init, xfail);		\
31062306a36Sopenharmony_ci		DEFINE_SCALAR_TEST(u16, init, xfail);		\
31162306a36Sopenharmony_ci		DEFINE_SCALAR_TEST(u32, init, xfail);		\
31262306a36Sopenharmony_ci		DEFINE_SCALAR_TEST(u64, init, xfail);		\
31362306a36Sopenharmony_ci		DEFINE_TEST(char_array_ ## init, unsigned char,	\
31462306a36Sopenharmony_ci			    STRING, init, xfail)
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci#define DEFINE_STRUCT_TEST(name, init, xfail)			\
31762306a36Sopenharmony_ci		DEFINE_TEST(name ## _ ## init,			\
31862306a36Sopenharmony_ci			    struct test_ ## name, STRUCT, init, \
31962306a36Sopenharmony_ci			    xfail)
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci#define DEFINE_STRUCT_TESTS(init, xfail)			\
32262306a36Sopenharmony_ci		DEFINE_STRUCT_TEST(small_hole, init, xfail);	\
32362306a36Sopenharmony_ci		DEFINE_STRUCT_TEST(big_hole, init, xfail);	\
32462306a36Sopenharmony_ci		DEFINE_STRUCT_TEST(trailing_hole, init, xfail);	\
32562306a36Sopenharmony_ci		DEFINE_STRUCT_TEST(packed, init, xfail)
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci#define DEFINE_STRUCT_INITIALIZER_TESTS(base, xfail)		\
32862306a36Sopenharmony_ci		DEFINE_STRUCT_TESTS(base ## _ ## partial,	\
32962306a36Sopenharmony_ci				    xfail);			\
33062306a36Sopenharmony_ci		DEFINE_STRUCT_TESTS(base ## _ ## all, xfail)
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci/* These should be fully initialized all the time! */
33362306a36Sopenharmony_ciDEFINE_SCALAR_TESTS(zero, ALWAYS_PASS);
33462306a36Sopenharmony_ciDEFINE_STRUCT_TESTS(zero, ALWAYS_PASS);
33562306a36Sopenharmony_ci/* Struct initializers: padding may be left uninitialized. */
33662306a36Sopenharmony_ciDEFINE_STRUCT_INITIALIZER_TESTS(static, STRONG_PASS);
33762306a36Sopenharmony_ciDEFINE_STRUCT_INITIALIZER_TESTS(dynamic, STRONG_PASS);
33862306a36Sopenharmony_ciDEFINE_STRUCT_INITIALIZER_TESTS(runtime, STRONG_PASS);
33962306a36Sopenharmony_ciDEFINE_STRUCT_INITIALIZER_TESTS(assigned_static, STRONG_PASS);
34062306a36Sopenharmony_ciDEFINE_STRUCT_INITIALIZER_TESTS(assigned_dynamic, STRONG_PASS);
34162306a36Sopenharmony_ciDEFINE_STRUCT_TESTS(assigned_copy, ALWAYS_FAIL);
34262306a36Sopenharmony_ci/* No initialization without compiler instrumentation. */
34362306a36Sopenharmony_ciDEFINE_SCALAR_TESTS(none, STRONG_PASS);
34462306a36Sopenharmony_ciDEFINE_STRUCT_TESTS(none, BYREF_PASS);
34562306a36Sopenharmony_ci/* Initialization of members with __user attribute. */
34662306a36Sopenharmony_ciDEFINE_TEST(user, struct test_user, STRUCT, none, USER_PASS);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci/*
34962306a36Sopenharmony_ci * Check two uses through a variable declaration outside either path,
35062306a36Sopenharmony_ci * which was noticed as a special case in porting earlier stack init
35162306a36Sopenharmony_ci * compiler logic.
35262306a36Sopenharmony_ci */
35362306a36Sopenharmony_cistatic int noinline __leaf_switch_none(int path, bool fill)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	switch (path) {
35662306a36Sopenharmony_ci		/*
35762306a36Sopenharmony_ci		 * This is intentionally unreachable. To silence the
35862306a36Sopenharmony_ci		 * warning, build with -Wno-switch-unreachable
35962306a36Sopenharmony_ci		 */
36062306a36Sopenharmony_ci		uint64_t var[10];
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	case 1:
36362306a36Sopenharmony_ci		target_start = &var;
36462306a36Sopenharmony_ci		target_size = sizeof(var);
36562306a36Sopenharmony_ci		if (fill) {
36662306a36Sopenharmony_ci			fill_start = &var;
36762306a36Sopenharmony_ci			fill_size = sizeof(var);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci			memset(fill_start, forced_mask | 0x55, fill_size);
37062306a36Sopenharmony_ci		}
37162306a36Sopenharmony_ci		memcpy(check_buf, target_start, target_size);
37262306a36Sopenharmony_ci		break;
37362306a36Sopenharmony_ci	case 2:
37462306a36Sopenharmony_ci		target_start = &var;
37562306a36Sopenharmony_ci		target_size = sizeof(var);
37662306a36Sopenharmony_ci		if (fill) {
37762306a36Sopenharmony_ci			fill_start = &var;
37862306a36Sopenharmony_ci			fill_size = sizeof(var);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci			memset(fill_start, forced_mask | 0xaa, fill_size);
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci		memcpy(check_buf, target_start, target_size);
38362306a36Sopenharmony_ci		break;
38462306a36Sopenharmony_ci	default:
38562306a36Sopenharmony_ci		var[1] = 5;
38662306a36Sopenharmony_ci		return var[1] & forced_mask;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci	return 0;
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic noinline int leaf_switch_1_none(unsigned long sp, bool fill,
39262306a36Sopenharmony_ci					      uint64_t *arg)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	return __leaf_switch_none(1, fill);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic noinline int leaf_switch_2_none(unsigned long sp, bool fill,
39862306a36Sopenharmony_ci					      uint64_t *arg)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	return __leaf_switch_none(2, fill);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci/*
40462306a36Sopenharmony_ci * These are expected to fail for most configurations because neither
40562306a36Sopenharmony_ci * GCC nor Clang have a way to perform initialization of variables in
40662306a36Sopenharmony_ci * non-code areas (i.e. in a switch statement before the first "case").
40762306a36Sopenharmony_ci * https://bugs.llvm.org/show_bug.cgi?id=44916
40862306a36Sopenharmony_ci */
40962306a36Sopenharmony_ciDEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, ALWAYS_FAIL);
41062306a36Sopenharmony_ciDEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, ALWAYS_FAIL);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci#define KUNIT_test_scalars(init)			\
41362306a36Sopenharmony_ci		KUNIT_CASE(test_u8_ ## init),		\
41462306a36Sopenharmony_ci		KUNIT_CASE(test_u16_ ## init),		\
41562306a36Sopenharmony_ci		KUNIT_CASE(test_u32_ ## init),		\
41662306a36Sopenharmony_ci		KUNIT_CASE(test_u64_ ## init),		\
41762306a36Sopenharmony_ci		KUNIT_CASE(test_char_array_ ## init)
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci#define KUNIT_test_structs(init)			\
42062306a36Sopenharmony_ci		KUNIT_CASE(test_small_hole_ ## init),	\
42162306a36Sopenharmony_ci		KUNIT_CASE(test_big_hole_ ## init),	\
42262306a36Sopenharmony_ci		KUNIT_CASE(test_trailing_hole_ ## init),\
42362306a36Sopenharmony_ci		KUNIT_CASE(test_packed_ ## init)	\
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic struct kunit_case stackinit_test_cases[] = {
42662306a36Sopenharmony_ci	/* These are explicitly initialized and should always pass. */
42762306a36Sopenharmony_ci	KUNIT_test_scalars(zero),
42862306a36Sopenharmony_ci	KUNIT_test_structs(zero),
42962306a36Sopenharmony_ci	/* Padding here appears to be accidentally always initialized? */
43062306a36Sopenharmony_ci	KUNIT_test_structs(dynamic_partial),
43162306a36Sopenharmony_ci	KUNIT_test_structs(assigned_dynamic_partial),
43262306a36Sopenharmony_ci	/* Padding initialization depends on compiler behaviors. */
43362306a36Sopenharmony_ci	KUNIT_test_structs(static_partial),
43462306a36Sopenharmony_ci	KUNIT_test_structs(static_all),
43562306a36Sopenharmony_ci	KUNIT_test_structs(dynamic_all),
43662306a36Sopenharmony_ci	KUNIT_test_structs(runtime_partial),
43762306a36Sopenharmony_ci	KUNIT_test_structs(runtime_all),
43862306a36Sopenharmony_ci	KUNIT_test_structs(assigned_static_partial),
43962306a36Sopenharmony_ci	KUNIT_test_structs(assigned_static_all),
44062306a36Sopenharmony_ci	KUNIT_test_structs(assigned_dynamic_all),
44162306a36Sopenharmony_ci	/* Everything fails this since it effectively performs a memcpy(). */
44262306a36Sopenharmony_ci	KUNIT_test_structs(assigned_copy),
44362306a36Sopenharmony_ci	/* STRUCTLEAK_BYREF_ALL should cover everything from here down. */
44462306a36Sopenharmony_ci	KUNIT_test_scalars(none),
44562306a36Sopenharmony_ci	KUNIT_CASE(test_switch_1_none),
44662306a36Sopenharmony_ci	KUNIT_CASE(test_switch_2_none),
44762306a36Sopenharmony_ci	/* STRUCTLEAK_BYREF should cover from here down. */
44862306a36Sopenharmony_ci	KUNIT_test_structs(none),
44962306a36Sopenharmony_ci	/* STRUCTLEAK will only cover this. */
45062306a36Sopenharmony_ci	KUNIT_CASE(test_user),
45162306a36Sopenharmony_ci	{}
45262306a36Sopenharmony_ci};
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic struct kunit_suite stackinit_test_suite = {
45562306a36Sopenharmony_ci	.name = "stackinit",
45662306a36Sopenharmony_ci	.test_cases = stackinit_test_cases,
45762306a36Sopenharmony_ci};
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cikunit_test_suites(&stackinit_test_suite);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
462