334 lines · 8.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Test cases for struct randomization, i.e. CONFIG_RANDSTRUCT=y. * * For example, see: * "Running tests with kunit_tool" at Documentation/dev-tools/kunit/start.rst *	./tools/testing/kunit/kunit.py run randstruct [--raw_output] \ *		[--make_option LLVM=1] \ *		--kconfig_add CONFIG_RANDSTRUCT_FULL=y * */#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <kunit/test.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/string.h> #define DO_MANY_MEMBERS(macro, args...)	\	macro(a, args)			\	macro(b, args)			\	macro(c, args)			\	macro(d, args)			\	macro(e, args)			\	macro(f, args)			\	macro(g, args)			\	macro(h, args) #define do_enum(x, ignored)	MEMBER_NAME_ ## x,enum randstruct_member_names {	DO_MANY_MEMBERS(do_enum)	MEMBER_NAME_MAX,};/* Make sure the macros are working: want 8 test members. */_Static_assert(MEMBER_NAME_MAX == 8, "Number of test members changed?!"); /* This is an unsigned long member to match the function pointer size */#define unsigned_long_member(x, ignored)	unsigned long x;struct randstruct_untouched {	DO_MANY_MEMBERS(unsigned_long_member)}; /* Struct explicitly marked with __randomize_layout. */struct randstruct_shuffled {	DO_MANY_MEMBERS(unsigned_long_member)} __randomize_layout;#undef unsigned_long_member /* Struct implicitly randomized from being all func ptrs. */#define func_member(x, ignored)	size_t (*x)(int);struct randstruct_funcs_untouched {	DO_MANY_MEMBERS(func_member)} __no_randomize_layout; struct randstruct_funcs_shuffled {	DO_MANY_MEMBERS(func_member)}; #define func_body(x, ignored)					\static noinline size_t func_##x(int arg)			\{								\	return offsetof(struct randstruct_funcs_untouched, x);	\}DO_MANY_MEMBERS(func_body) /* Various mixed types. */#define mixed_members					\	bool a;						\	short b;					\	unsigned int c __aligned(16);			\	size_t d;					\	char e;						\	u64 f;						\	union {						\		struct randstruct_shuffled shuffled;	\		uintptr_t g;				\	};						\	union {						\		void *ptr;				\		char h;					\	}; struct randstruct_mixed_untouched {	mixed_members}; struct randstruct_mixed_shuffled {	mixed_members} __randomize_layout;#undef mixed_members struct contains_randstruct_untouched {	int before;	struct randstruct_untouched untouched;	int after;}; struct contains_randstruct_shuffled {	int before;	struct randstruct_shuffled shuffled;	int after;}; struct contains_func_untouched {	struct randstruct_funcs_shuffled inner;	DO_MANY_MEMBERS(func_member)} __no_randomize_layout; struct contains_func_shuffled {	struct randstruct_funcs_shuffled inner;	DO_MANY_MEMBERS(func_member)};#undef func_member #define check_mismatch(x, untouched, shuffled)	\	if (offsetof(untouched, x) != offsetof(shuffled, x))	\		mismatches++;					\	kunit_info(test, #shuffled "::" #x " @ %zu (vs %zu)\n",	\		   offsetof(shuffled, x),			\		   offsetof(untouched, x));			\ #define check_pair(outcome, untouched, shuffled, checker...)	\	mismatches = 0;						\	DO_MANY_MEMBERS(checker, untouched, shuffled)	\	kunit_info(test, "Differing " #untouched " vs " #shuffled " member positions: %d\n", \		   mismatches);					\	KUNIT_##outcome##_MSG(test, mismatches, 0,		\			      #untouched " vs " #shuffled " layouts: unlucky or broken?\n"); static void randstruct_layout_same(struct kunit *test){	int mismatches; 	check_pair(EXPECT_EQ, struct randstruct_untouched, struct randstruct_untouched,		   check_mismatch)	check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_shuffled,		   check_mismatch)} static void randstruct_layout_mixed(struct kunit *test){	int mismatches; 	check_pair(EXPECT_EQ, struct randstruct_mixed_untouched, struct randstruct_mixed_untouched,		   check_mismatch)	check_pair(EXPECT_GT, struct randstruct_mixed_untouched, struct randstruct_mixed_shuffled,		   check_mismatch)} static void randstruct_layout_fptr(struct kunit *test){	int mismatches; 	check_pair(EXPECT_EQ, struct randstruct_untouched, struct randstruct_untouched,		   check_mismatch)	check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_funcs_shuffled,		   check_mismatch)	check_pair(EXPECT_GT, struct randstruct_funcs_untouched, struct randstruct_funcs_shuffled,		   check_mismatch)} #define check_mismatch_prefixed(x, prefix, untouched, shuffled)	\	check_mismatch(prefix.x, untouched, shuffled) static void randstruct_layout_fptr_deep(struct kunit *test){	int mismatches; 	if (IS_ENABLED(CONFIG_CC_IS_CLANG))		kunit_skip(test, "Clang randstruct misses inner functions: https://github.com/llvm/llvm-project/issues/138355"); 	check_pair(EXPECT_EQ, struct contains_func_untouched, struct contains_func_untouched,			check_mismatch_prefixed, inner) 	check_pair(EXPECT_GT, struct contains_func_untouched, struct contains_func_shuffled,			check_mismatch_prefixed, inner)} #undef check_pair#undef check_mismatch #define check_mismatch(x, ignore)				\	KUNIT_EXPECT_EQ_MSG(test, untouched->x, shuffled->x,	\			    "Mismatched member value in %s initializer\n", \			    name); static void test_check_init(struct kunit *test, const char *name,			    struct randstruct_untouched *untouched,			    struct randstruct_shuffled *shuffled){	DO_MANY_MEMBERS(check_mismatch)} static void test_check_mixed_init(struct kunit *test, const char *name,				  struct randstruct_mixed_untouched *untouched,				  struct randstruct_mixed_shuffled *shuffled){	DO_MANY_MEMBERS(check_mismatch)}#undef check_mismatch #define check_mismatch(x, ignore)				\	KUNIT_EXPECT_EQ_MSG(test, untouched->untouched.x,	\			    shuffled->shuffled.x,		\			    "Mismatched member value in %s initializer\n", \			    name);static void test_check_contained_init(struct kunit *test, const char *name,				      struct contains_randstruct_untouched *untouched,				      struct contains_randstruct_shuffled *shuffled){	DO_MANY_MEMBERS(check_mismatch)}#undef check_mismatch #define check_mismatch(x, ignore)					\	KUNIT_EXPECT_PTR_EQ_MSG(test, untouched->x, shuffled->x,	\			    "Mismatched member value in %s initializer\n", \			    name); static void test_check_funcs_init(struct kunit *test, const char *name,				  struct randstruct_funcs_untouched *untouched,				  struct randstruct_funcs_shuffled *shuffled){	DO_MANY_MEMBERS(check_mismatch)}#undef check_mismatch static void randstruct_initializers(struct kunit *test){#define init_members		\		.a = 1,		\		.b = 3,		\		.c = 5,		\		.d = 7,		\		.e = 11,	\		.f = 13,	\		.g = 17,	\		.h = 19,	struct randstruct_untouched untouched = {		init_members	};	struct randstruct_shuffled shuffled = {		init_members	};	struct randstruct_mixed_untouched mixed_untouched = {		init_members	};	struct randstruct_mixed_shuffled mixed_shuffled = {		init_members	};	struct contains_randstruct_untouched contains_untouched = {		.untouched = {			init_members		},	};	struct contains_randstruct_shuffled contains_shuffled = {		.shuffled = {			init_members		},	};#define func_member(x, ignored)	\		.x = func_##x,	struct randstruct_funcs_untouched funcs_untouched = {		DO_MANY_MEMBERS(func_member)	};	struct randstruct_funcs_shuffled funcs_shuffled = {		DO_MANY_MEMBERS(func_member)	}; 	test_check_init(test, "named", &untouched, &shuffled);	test_check_init(test, "unnamed", &untouched,		&(struct randstruct_shuffled){			init_members		}); 	test_check_contained_init(test, "named", &contains_untouched, &contains_shuffled);	test_check_contained_init(test, "unnamed", &contains_untouched,		&(struct contains_randstruct_shuffled){			.shuffled = (struct randstruct_shuffled){				init_members			},		}); 	test_check_contained_init(test, "named", &contains_untouched, &contains_shuffled);	test_check_contained_init(test, "unnamed copy", &contains_untouched,		&(struct contains_randstruct_shuffled){			/* full struct copy initializer */			.shuffled = shuffled,		}); 	test_check_mixed_init(test, "named", &mixed_untouched, &mixed_shuffled);	test_check_mixed_init(test, "unnamed", &mixed_untouched,		&(struct randstruct_mixed_shuffled){			init_members		}); 	test_check_funcs_init(test, "named", &funcs_untouched, &funcs_shuffled);	test_check_funcs_init(test, "unnamed", &funcs_untouched,		&(struct randstruct_funcs_shuffled){			DO_MANY_MEMBERS(func_member)		}); #undef func_member#undef init_members} static int randstruct_test_init(struct kunit *test){	if (!IS_ENABLED(CONFIG_RANDSTRUCT))		kunit_skip(test, "Not built with CONFIG_RANDSTRUCT=y"); 	return 0;} static struct kunit_case randstruct_test_cases[] = {	KUNIT_CASE(randstruct_layout_same),	KUNIT_CASE(randstruct_layout_mixed),	KUNIT_CASE(randstruct_layout_fptr),	KUNIT_CASE(randstruct_layout_fptr_deep),	KUNIT_CASE(randstruct_initializers),	{}}; static struct kunit_suite randstruct_test_suite = {	.name = "randstruct",	.init = randstruct_test_init,	.test_cases = randstruct_test_cases,}; kunit_test_suites(&randstruct_test_suite); MODULE_DESCRIPTION("Test cases for struct randomization");MODULE_LICENSE("GPL");