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