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