1b370f7eaSKees Cook // SPDX-License-Identifier: GPL-2.0-or-later 2b370f7eaSKees Cook /* 3b370f7eaSKees Cook * Test cases for struct randomization, i.e. CONFIG_RANDSTRUCT=y. 4b370f7eaSKees Cook * 5b370f7eaSKees Cook * For example, see: 6b370f7eaSKees Cook * "Running tests with kunit_tool" at Documentation/dev-tools/kunit/start.rst 7b370f7eaSKees Cook * ./tools/testing/kunit/kunit.py run randstruct [--raw_output] \ 8b370f7eaSKees Cook * [--make_option LLVM=1] \ 9b370f7eaSKees Cook * --kconfig_add CONFIG_RANDSTRUCT_FULL=y 10b370f7eaSKees Cook * 11b370f7eaSKees Cook */ 12b370f7eaSKees Cook #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13b370f7eaSKees Cook 14b370f7eaSKees Cook #include <kunit/test.h> 15b370f7eaSKees Cook #include <linux/init.h> 16b370f7eaSKees Cook #include <linux/kernel.h> 17b370f7eaSKees Cook #include <linux/module.h> 18b370f7eaSKees Cook #include <linux/string.h> 19b370f7eaSKees Cook 20b370f7eaSKees Cook #define DO_MANY_MEMBERS(macro, args...) \ 21b370f7eaSKees Cook macro(a, args) \ 22b370f7eaSKees Cook macro(b, args) \ 23b370f7eaSKees Cook macro(c, args) \ 24b370f7eaSKees Cook macro(d, args) \ 25b370f7eaSKees Cook macro(e, args) \ 26b370f7eaSKees Cook macro(f, args) \ 27b370f7eaSKees Cook macro(g, args) \ 28b370f7eaSKees Cook macro(h, args) 29b370f7eaSKees Cook 30b370f7eaSKees Cook #define do_enum(x, ignored) MEMBER_NAME_ ## x, 31b370f7eaSKees Cook enum randstruct_member_names { 32b370f7eaSKees Cook DO_MANY_MEMBERS(do_enum) 33b370f7eaSKees Cook MEMBER_NAME_MAX, 34b370f7eaSKees Cook }; 35b370f7eaSKees Cook /* Make sure the macros are working: want 8 test members. */ 36b370f7eaSKees Cook _Static_assert(MEMBER_NAME_MAX == 8, "Number of test members changed?!"); 37b370f7eaSKees Cook 38b370f7eaSKees Cook /* This is an unsigned long member to match the function pointer size */ 39b370f7eaSKees Cook #define unsigned_long_member(x, ignored) unsigned long x; 40b370f7eaSKees Cook struct randstruct_untouched { 41b370f7eaSKees Cook DO_MANY_MEMBERS(unsigned_long_member) 42b370f7eaSKees Cook }; 43b370f7eaSKees Cook 44b370f7eaSKees Cook /* Struct explicitly marked with __randomize_layout. */ 45b370f7eaSKees Cook struct randstruct_shuffled { 46b370f7eaSKees Cook DO_MANY_MEMBERS(unsigned_long_member) 47b370f7eaSKees Cook } __randomize_layout; 48b370f7eaSKees Cook #undef unsigned_long_member 49b370f7eaSKees Cook 50b370f7eaSKees Cook /* Struct implicitly randomized from being all func ptrs. */ 51b370f7eaSKees Cook #define func_member(x, ignored) size_t (*x)(int); 52b370f7eaSKees Cook struct randstruct_funcs_untouched { 53b370f7eaSKees Cook DO_MANY_MEMBERS(func_member) 54b370f7eaSKees Cook } __no_randomize_layout; 55b370f7eaSKees Cook 56b370f7eaSKees Cook struct randstruct_funcs_shuffled { 57b370f7eaSKees Cook DO_MANY_MEMBERS(func_member) 58b370f7eaSKees Cook }; 59b370f7eaSKees Cook 60b370f7eaSKees Cook #define func_body(x, ignored) \ 61b370f7eaSKees Cook static noinline size_t func_##x(int arg) \ 62b370f7eaSKees Cook { \ 63b370f7eaSKees Cook return offsetof(struct randstruct_funcs_untouched, x); \ 64b370f7eaSKees Cook } 65b370f7eaSKees Cook DO_MANY_MEMBERS(func_body) 66b370f7eaSKees Cook 67b370f7eaSKees Cook /* Various mixed types. */ 68b370f7eaSKees Cook #define mixed_members \ 69b370f7eaSKees Cook bool a; \ 70b370f7eaSKees Cook short b; \ 71b370f7eaSKees Cook unsigned int c __aligned(16); \ 72b370f7eaSKees Cook size_t d; \ 73b370f7eaSKees Cook char e; \ 74b370f7eaSKees Cook u64 f; \ 75b370f7eaSKees Cook union { \ 76b370f7eaSKees Cook struct randstruct_shuffled shuffled; \ 77b370f7eaSKees Cook uintptr_t g; \ 78b370f7eaSKees Cook }; \ 79b370f7eaSKees Cook union { \ 80b370f7eaSKees Cook void *ptr; \ 81b370f7eaSKees Cook char h; \ 82b370f7eaSKees Cook }; 83b370f7eaSKees Cook 84b370f7eaSKees Cook struct randstruct_mixed_untouched { 85b370f7eaSKees Cook mixed_members 86b370f7eaSKees Cook }; 87b370f7eaSKees Cook 88b370f7eaSKees Cook struct randstruct_mixed_shuffled { 89b370f7eaSKees Cook mixed_members 90b370f7eaSKees Cook } __randomize_layout; 91b370f7eaSKees Cook #undef mixed_members 92b370f7eaSKees Cook 93b370f7eaSKees Cook struct contains_randstruct_untouched { 94b370f7eaSKees Cook int before; 95b370f7eaSKees Cook struct randstruct_untouched untouched; 96b370f7eaSKees Cook int after; 97b370f7eaSKees Cook }; 98b370f7eaSKees Cook 99b370f7eaSKees Cook struct contains_randstruct_shuffled { 100b370f7eaSKees Cook int before; 101b370f7eaSKees Cook struct randstruct_shuffled shuffled; 102b370f7eaSKees Cook int after; 103b370f7eaSKees Cook }; 104b370f7eaSKees Cook 105*f55aef7eSKees Cook struct contains_func_untouched { 106*f55aef7eSKees Cook struct randstruct_funcs_shuffled inner; 107*f55aef7eSKees Cook DO_MANY_MEMBERS(func_member) 108*f55aef7eSKees Cook } __no_randomize_layout; 109*f55aef7eSKees Cook 110*f55aef7eSKees Cook struct contains_func_shuffled { 111*f55aef7eSKees Cook struct randstruct_funcs_shuffled inner; 112*f55aef7eSKees Cook DO_MANY_MEMBERS(func_member) 113*f55aef7eSKees Cook }; 114*f55aef7eSKees Cook #undef func_member 115b370f7eaSKees Cook 116b370f7eaSKees Cook #define check_mismatch(x, untouched, shuffled) \ 117b370f7eaSKees Cook if (offsetof(untouched, x) != offsetof(shuffled, x)) \ 118b370f7eaSKees Cook mismatches++; \ 119b370f7eaSKees Cook kunit_info(test, #shuffled "::" #x " @ %zu (vs %zu)\n", \ 120b370f7eaSKees Cook offsetof(shuffled, x), \ 121b370f7eaSKees Cook offsetof(untouched, x)); \ 122b370f7eaSKees Cook 123*f55aef7eSKees Cook #define check_pair(outcome, untouched, shuffled, checker...) \ 124b370f7eaSKees Cook mismatches = 0; \ 125*f55aef7eSKees Cook DO_MANY_MEMBERS(checker, untouched, shuffled) \ 126b370f7eaSKees Cook kunit_info(test, "Differing " #untouched " vs " #shuffled " member positions: %d\n", \ 127b370f7eaSKees Cook mismatches); \ 128b370f7eaSKees Cook KUNIT_##outcome##_MSG(test, mismatches, 0, \ 129b370f7eaSKees Cook #untouched " vs " #shuffled " layouts: unlucky or broken?\n"); 130b370f7eaSKees Cook 131*f55aef7eSKees Cook static void randstruct_layout_same(struct kunit *test) 132*f55aef7eSKees Cook { 133*f55aef7eSKees Cook int mismatches; 134b370f7eaSKees Cook 135*f55aef7eSKees Cook check_pair(EXPECT_EQ, struct randstruct_untouched, struct randstruct_untouched, 136*f55aef7eSKees Cook check_mismatch) 137*f55aef7eSKees Cook check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_shuffled, 138*f55aef7eSKees Cook check_mismatch) 139b370f7eaSKees Cook } 140b370f7eaSKees Cook 141*f55aef7eSKees Cook static void randstruct_layout_mixed(struct kunit *test) 142*f55aef7eSKees Cook { 143*f55aef7eSKees Cook int mismatches; 144*f55aef7eSKees Cook 145*f55aef7eSKees Cook check_pair(EXPECT_EQ, struct randstruct_mixed_untouched, struct randstruct_mixed_untouched, 146*f55aef7eSKees Cook check_mismatch) 147*f55aef7eSKees Cook check_pair(EXPECT_GT, struct randstruct_mixed_untouched, struct randstruct_mixed_shuffled, 148*f55aef7eSKees Cook check_mismatch) 149*f55aef7eSKees Cook } 150*f55aef7eSKees Cook 151*f55aef7eSKees Cook static void randstruct_layout_fptr(struct kunit *test) 152*f55aef7eSKees Cook { 153*f55aef7eSKees Cook int mismatches; 154*f55aef7eSKees Cook 155*f55aef7eSKees Cook check_pair(EXPECT_EQ, struct randstruct_untouched, struct randstruct_untouched, 156*f55aef7eSKees Cook check_mismatch) 157*f55aef7eSKees Cook check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_funcs_shuffled, 158*f55aef7eSKees Cook check_mismatch) 159*f55aef7eSKees Cook check_pair(EXPECT_GT, struct randstruct_funcs_untouched, struct randstruct_funcs_shuffled, 160*f55aef7eSKees Cook check_mismatch) 161*f55aef7eSKees Cook } 162*f55aef7eSKees Cook 163*f55aef7eSKees Cook #define check_mismatch_prefixed(x, prefix, untouched, shuffled) \ 164*f55aef7eSKees Cook check_mismatch(prefix.x, untouched, shuffled) 165*f55aef7eSKees Cook 166*f55aef7eSKees Cook static void randstruct_layout_fptr_deep(struct kunit *test) 167*f55aef7eSKees Cook { 168*f55aef7eSKees Cook int mismatches; 169*f55aef7eSKees Cook 170*f55aef7eSKees Cook if (IS_ENABLED(CONFIG_CC_IS_CLANG)) 171*f55aef7eSKees Cook kunit_skip(test, "Clang randstruct misses inner functions: https://github.com/llvm/llvm-project/issues/138355"); 172*f55aef7eSKees Cook 173*f55aef7eSKees Cook check_pair(EXPECT_EQ, struct contains_func_untouched, struct contains_func_untouched, 174*f55aef7eSKees Cook check_mismatch_prefixed, inner) 175*f55aef7eSKees Cook 176*f55aef7eSKees Cook check_pair(EXPECT_GT, struct contains_func_untouched, struct contains_func_shuffled, 177*f55aef7eSKees Cook check_mismatch_prefixed, inner) 178*f55aef7eSKees Cook } 179*f55aef7eSKees Cook 180*f55aef7eSKees Cook #undef check_pair 181*f55aef7eSKees Cook #undef check_mismatch 182*f55aef7eSKees Cook 183b370f7eaSKees Cook #define check_mismatch(x, ignore) \ 184b370f7eaSKees Cook KUNIT_EXPECT_EQ_MSG(test, untouched->x, shuffled->x, \ 185b370f7eaSKees Cook "Mismatched member value in %s initializer\n", \ 186b370f7eaSKees Cook name); 187b370f7eaSKees Cook 188b370f7eaSKees Cook static void test_check_init(struct kunit *test, const char *name, 189b370f7eaSKees Cook struct randstruct_untouched *untouched, 190b370f7eaSKees Cook struct randstruct_shuffled *shuffled) 191b370f7eaSKees Cook { 192b370f7eaSKees Cook DO_MANY_MEMBERS(check_mismatch) 193b370f7eaSKees Cook } 194b370f7eaSKees Cook 195b370f7eaSKees Cook static void test_check_mixed_init(struct kunit *test, const char *name, 196b370f7eaSKees Cook struct randstruct_mixed_untouched *untouched, 197b370f7eaSKees Cook struct randstruct_mixed_shuffled *shuffled) 198b370f7eaSKees Cook { 199b370f7eaSKees Cook DO_MANY_MEMBERS(check_mismatch) 200b370f7eaSKees Cook } 201b370f7eaSKees Cook #undef check_mismatch 202b370f7eaSKees Cook 203b370f7eaSKees Cook #define check_mismatch(x, ignore) \ 204b370f7eaSKees Cook KUNIT_EXPECT_EQ_MSG(test, untouched->untouched.x, \ 205b370f7eaSKees Cook shuffled->shuffled.x, \ 206b370f7eaSKees Cook "Mismatched member value in %s initializer\n", \ 207b370f7eaSKees Cook name); 208b370f7eaSKees Cook static void test_check_contained_init(struct kunit *test, const char *name, 209b370f7eaSKees Cook struct contains_randstruct_untouched *untouched, 210b370f7eaSKees Cook struct contains_randstruct_shuffled *shuffled) 211b370f7eaSKees Cook { 212b370f7eaSKees Cook DO_MANY_MEMBERS(check_mismatch) 213b370f7eaSKees Cook } 214b370f7eaSKees Cook #undef check_mismatch 215b370f7eaSKees Cook 216b370f7eaSKees Cook #define check_mismatch(x, ignore) \ 217b370f7eaSKees Cook KUNIT_EXPECT_PTR_EQ_MSG(test, untouched->x, shuffled->x, \ 218b370f7eaSKees Cook "Mismatched member value in %s initializer\n", \ 219b370f7eaSKees Cook name); 220b370f7eaSKees Cook 221b370f7eaSKees Cook static void test_check_funcs_init(struct kunit *test, const char *name, 222b370f7eaSKees Cook struct randstruct_funcs_untouched *untouched, 223b370f7eaSKees Cook struct randstruct_funcs_shuffled *shuffled) 224b370f7eaSKees Cook { 225b370f7eaSKees Cook DO_MANY_MEMBERS(check_mismatch) 226b370f7eaSKees Cook } 227b370f7eaSKees Cook #undef check_mismatch 228b370f7eaSKees Cook 229b370f7eaSKees Cook static void randstruct_initializers(struct kunit *test) 230b370f7eaSKees Cook { 231b370f7eaSKees Cook #define init_members \ 232b370f7eaSKees Cook .a = 1, \ 233b370f7eaSKees Cook .b = 3, \ 234b370f7eaSKees Cook .c = 5, \ 235b370f7eaSKees Cook .d = 7, \ 236b370f7eaSKees Cook .e = 11, \ 237b370f7eaSKees Cook .f = 13, \ 238b370f7eaSKees Cook .g = 17, \ 239b370f7eaSKees Cook .h = 19, 240b370f7eaSKees Cook struct randstruct_untouched untouched = { 241b370f7eaSKees Cook init_members 242b370f7eaSKees Cook }; 243b370f7eaSKees Cook struct randstruct_shuffled shuffled = { 244b370f7eaSKees Cook init_members 245b370f7eaSKees Cook }; 246b370f7eaSKees Cook struct randstruct_mixed_untouched mixed_untouched = { 247b370f7eaSKees Cook init_members 248b370f7eaSKees Cook }; 249b370f7eaSKees Cook struct randstruct_mixed_shuffled mixed_shuffled = { 250b370f7eaSKees Cook init_members 251b370f7eaSKees Cook }; 252b370f7eaSKees Cook struct contains_randstruct_untouched contains_untouched = { 253b370f7eaSKees Cook .untouched = { 254b370f7eaSKees Cook init_members 255b370f7eaSKees Cook }, 256b370f7eaSKees Cook }; 257b370f7eaSKees Cook struct contains_randstruct_shuffled contains_shuffled = { 258b370f7eaSKees Cook .shuffled = { 259b370f7eaSKees Cook init_members 260b370f7eaSKees Cook }, 261b370f7eaSKees Cook }; 262b370f7eaSKees Cook #define func_member(x, ignored) \ 263b370f7eaSKees Cook .x = func_##x, 264b370f7eaSKees Cook struct randstruct_funcs_untouched funcs_untouched = { 265b370f7eaSKees Cook DO_MANY_MEMBERS(func_member) 266b370f7eaSKees Cook }; 267b370f7eaSKees Cook struct randstruct_funcs_shuffled funcs_shuffled = { 268b370f7eaSKees Cook DO_MANY_MEMBERS(func_member) 269b370f7eaSKees Cook }; 270b370f7eaSKees Cook 271b370f7eaSKees Cook test_check_init(test, "named", &untouched, &shuffled); 272b370f7eaSKees Cook test_check_init(test, "unnamed", &untouched, 273b370f7eaSKees Cook &(struct randstruct_shuffled){ 274b370f7eaSKees Cook init_members 275b370f7eaSKees Cook }); 276b370f7eaSKees Cook 277b370f7eaSKees Cook test_check_contained_init(test, "named", &contains_untouched, &contains_shuffled); 278b370f7eaSKees Cook test_check_contained_init(test, "unnamed", &contains_untouched, 279b370f7eaSKees Cook &(struct contains_randstruct_shuffled){ 280b370f7eaSKees Cook .shuffled = (struct randstruct_shuffled){ 281b370f7eaSKees Cook init_members 282b370f7eaSKees Cook }, 283b370f7eaSKees Cook }); 284b370f7eaSKees Cook 285b370f7eaSKees Cook test_check_contained_init(test, "named", &contains_untouched, &contains_shuffled); 286b370f7eaSKees Cook test_check_contained_init(test, "unnamed copy", &contains_untouched, 287b370f7eaSKees Cook &(struct contains_randstruct_shuffled){ 288b370f7eaSKees Cook /* full struct copy initializer */ 289b370f7eaSKees Cook .shuffled = shuffled, 290b370f7eaSKees Cook }); 291b370f7eaSKees Cook 292b370f7eaSKees Cook test_check_mixed_init(test, "named", &mixed_untouched, &mixed_shuffled); 293b370f7eaSKees Cook test_check_mixed_init(test, "unnamed", &mixed_untouched, 294b370f7eaSKees Cook &(struct randstruct_mixed_shuffled){ 295b370f7eaSKees Cook init_members 296b370f7eaSKees Cook }); 297b370f7eaSKees Cook 298b370f7eaSKees Cook test_check_funcs_init(test, "named", &funcs_untouched, &funcs_shuffled); 299b370f7eaSKees Cook test_check_funcs_init(test, "unnamed", &funcs_untouched, 300b370f7eaSKees Cook &(struct randstruct_funcs_shuffled){ 301b370f7eaSKees Cook DO_MANY_MEMBERS(func_member) 302b370f7eaSKees Cook }); 303b370f7eaSKees Cook 304b370f7eaSKees Cook #undef func_member 305b370f7eaSKees Cook #undef init_members 306b370f7eaSKees Cook } 307b370f7eaSKees Cook 308b370f7eaSKees Cook static int randstruct_test_init(struct kunit *test) 309b370f7eaSKees Cook { 310b370f7eaSKees Cook if (!IS_ENABLED(CONFIG_RANDSTRUCT)) 311b370f7eaSKees Cook kunit_skip(test, "Not built with CONFIG_RANDSTRUCT=y"); 312b370f7eaSKees Cook 313b370f7eaSKees Cook return 0; 314b370f7eaSKees Cook } 315b370f7eaSKees Cook 316b370f7eaSKees Cook static struct kunit_case randstruct_test_cases[] = { 317*f55aef7eSKees Cook KUNIT_CASE(randstruct_layout_same), 318*f55aef7eSKees Cook KUNIT_CASE(randstruct_layout_mixed), 319*f55aef7eSKees Cook KUNIT_CASE(randstruct_layout_fptr), 320*f55aef7eSKees Cook KUNIT_CASE(randstruct_layout_fptr_deep), 321b370f7eaSKees Cook KUNIT_CASE(randstruct_initializers), 322b370f7eaSKees Cook {} 323b370f7eaSKees Cook }; 324b370f7eaSKees Cook 325b370f7eaSKees Cook static struct kunit_suite randstruct_test_suite = { 326b370f7eaSKees Cook .name = "randstruct", 327b370f7eaSKees Cook .init = randstruct_test_init, 328b370f7eaSKees Cook .test_cases = randstruct_test_cases, 329b370f7eaSKees Cook }; 330b370f7eaSKees Cook 331b370f7eaSKees Cook kunit_test_suites(&randstruct_test_suite); 332b370f7eaSKees Cook 333b370f7eaSKees Cook MODULE_DESCRIPTION("Test cases for struct randomization"); 334b370f7eaSKees Cook MODULE_LICENSE("GPL"); 335