// 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 #include #include #include #include #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");