at master 9.2 kB view raw
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, 31enum 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; 40struct randstruct_untouched { 41 DO_MANY_MEMBERS(unsigned_long_member) 42}; 43 44/* Struct explicitly marked with __randomize_layout. */ 45struct 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); 52struct randstruct_funcs_untouched { 53 DO_MANY_MEMBERS(func_member) 54} __no_randomize_layout; 55 56struct randstruct_funcs_shuffled { 57 DO_MANY_MEMBERS(func_member) 58}; 59 60#define func_body(x, ignored) \ 61static noinline size_t func_##x(int arg) \ 62{ \ 63 return offsetof(struct randstruct_funcs_untouched, x); \ 64} 65DO_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 84struct randstruct_mixed_untouched { 85 mixed_members 86}; 87 88struct randstruct_mixed_shuffled { 89 mixed_members 90} __randomize_layout; 91#undef mixed_members 92 93struct contains_randstruct_untouched { 94 int before; 95 struct randstruct_untouched untouched; 96 int after; 97}; 98 99struct contains_randstruct_shuffled { 100 int before; 101 struct randstruct_shuffled shuffled; 102 int after; 103}; 104 105struct contains_func_untouched { 106 struct randstruct_funcs_shuffled inner; 107 DO_MANY_MEMBERS(func_member) 108} __no_randomize_layout; 109 110struct 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 131static 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 141static 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 151static 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 166static 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 188static 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 195static 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); 208static 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 221static 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 229static 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 308static 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 316static 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 325static struct kunit_suite randstruct_test_suite = { 326 .name = "randstruct", 327 .init = randstruct_test_init, 328 .test_cases = randstruct_test_cases, 329}; 330 331kunit_test_suites(&randstruct_test_suite); 332 333MODULE_DESCRIPTION("Test cases for struct randomization"); 334MODULE_LICENSE("GPL");