|
| 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 | +#undef func_member |
| 60 | + |
| 61 | +#define func_body(x, ignored) \ |
| 62 | +static noinline size_t func_##x(int arg) \ |
| 63 | +{ \ |
| 64 | + return offsetof(struct randstruct_funcs_untouched, x); \ |
| 65 | +} |
| 66 | +DO_MANY_MEMBERS(func_body) |
| 67 | + |
| 68 | +/* Various mixed types. */ |
| 69 | +#define mixed_members \ |
| 70 | + bool a; \ |
| 71 | + short b; \ |
| 72 | + unsigned int c __aligned(16); \ |
| 73 | + size_t d; \ |
| 74 | + char e; \ |
| 75 | + u64 f; \ |
| 76 | + union { \ |
| 77 | + struct randstruct_shuffled shuffled; \ |
| 78 | + uintptr_t g; \ |
| 79 | + }; \ |
| 80 | + union { \ |
| 81 | + void *ptr; \ |
| 82 | + char h; \ |
| 83 | + }; |
| 84 | + |
| 85 | +struct randstruct_mixed_untouched { |
| 86 | + mixed_members |
| 87 | +}; |
| 88 | + |
| 89 | +struct randstruct_mixed_shuffled { |
| 90 | + mixed_members |
| 91 | +} __randomize_layout; |
| 92 | +#undef mixed_members |
| 93 | + |
| 94 | +struct contains_randstruct_untouched { |
| 95 | + int before; |
| 96 | + struct randstruct_untouched untouched; |
| 97 | + int after; |
| 98 | +}; |
| 99 | + |
| 100 | +struct contains_randstruct_shuffled { |
| 101 | + int before; |
| 102 | + struct randstruct_shuffled shuffled; |
| 103 | + int after; |
| 104 | +}; |
| 105 | + |
| 106 | +static void randstruct_layout(struct kunit *test) |
| 107 | +{ |
| 108 | + int mismatches; |
| 109 | + |
| 110 | +#define check_mismatch(x, untouched, shuffled) \ |
| 111 | + if (offsetof(untouched, x) != offsetof(shuffled, x)) \ |
| 112 | + mismatches++; \ |
| 113 | + kunit_info(test, #shuffled "::" #x " @ %zu (vs %zu)\n", \ |
| 114 | + offsetof(shuffled, x), \ |
| 115 | + offsetof(untouched, x)); \ |
| 116 | + |
| 117 | +#define check_pair(outcome, untouched, shuffled) \ |
| 118 | + mismatches = 0; \ |
| 119 | + DO_MANY_MEMBERS(check_mismatch, untouched, shuffled) \ |
| 120 | + kunit_info(test, "Differing " #untouched " vs " #shuffled " member positions: %d\n", \ |
| 121 | + mismatches); \ |
| 122 | + KUNIT_##outcome##_MSG(test, mismatches, 0, \ |
| 123 | + #untouched " vs " #shuffled " layouts: unlucky or broken?\n"); |
| 124 | + |
| 125 | + check_pair(EXPECT_EQ, struct randstruct_untouched, struct randstruct_untouched) |
| 126 | + check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_shuffled) |
| 127 | + check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_funcs_shuffled) |
| 128 | + check_pair(EXPECT_GT, struct randstruct_funcs_untouched, struct randstruct_funcs_shuffled) |
| 129 | + check_pair(EXPECT_GT, struct randstruct_mixed_untouched, struct randstruct_mixed_shuffled) |
| 130 | +#undef check_pair |
| 131 | + |
| 132 | +#undef check_mismatch |
| 133 | +} |
| 134 | + |
| 135 | +#define check_mismatch(x, ignore) \ |
| 136 | + KUNIT_EXPECT_EQ_MSG(test, untouched->x, shuffled->x, \ |
| 137 | + "Mismatched member value in %s initializer\n", \ |
| 138 | + name); |
| 139 | + |
| 140 | +static void test_check_init(struct kunit *test, const char *name, |
| 141 | + struct randstruct_untouched *untouched, |
| 142 | + struct randstruct_shuffled *shuffled) |
| 143 | +{ |
| 144 | + DO_MANY_MEMBERS(check_mismatch) |
| 145 | +} |
| 146 | + |
| 147 | +static void test_check_mixed_init(struct kunit *test, const char *name, |
| 148 | + struct randstruct_mixed_untouched *untouched, |
| 149 | + struct randstruct_mixed_shuffled *shuffled) |
| 150 | +{ |
| 151 | + DO_MANY_MEMBERS(check_mismatch) |
| 152 | +} |
| 153 | +#undef check_mismatch |
| 154 | + |
| 155 | +#define check_mismatch(x, ignore) \ |
| 156 | + KUNIT_EXPECT_EQ_MSG(test, untouched->untouched.x, \ |
| 157 | + shuffled->shuffled.x, \ |
| 158 | + "Mismatched member value in %s initializer\n", \ |
| 159 | + name); |
| 160 | +static void test_check_contained_init(struct kunit *test, const char *name, |
| 161 | + struct contains_randstruct_untouched *untouched, |
| 162 | + struct contains_randstruct_shuffled *shuffled) |
| 163 | +{ |
| 164 | + DO_MANY_MEMBERS(check_mismatch) |
| 165 | +} |
| 166 | +#undef check_mismatch |
| 167 | + |
| 168 | +#define check_mismatch(x, ignore) \ |
| 169 | + KUNIT_EXPECT_PTR_EQ_MSG(test, untouched->x, shuffled->x, \ |
| 170 | + "Mismatched member value in %s initializer\n", \ |
| 171 | + name); |
| 172 | + |
| 173 | +static void test_check_funcs_init(struct kunit *test, const char *name, |
| 174 | + struct randstruct_funcs_untouched *untouched, |
| 175 | + struct randstruct_funcs_shuffled *shuffled) |
| 176 | +{ |
| 177 | + DO_MANY_MEMBERS(check_mismatch) |
| 178 | +} |
| 179 | +#undef check_mismatch |
| 180 | + |
| 181 | +static void randstruct_initializers(struct kunit *test) |
| 182 | +{ |
| 183 | +#define init_members \ |
| 184 | + .a = 1, \ |
| 185 | + .b = 3, \ |
| 186 | + .c = 5, \ |
| 187 | + .d = 7, \ |
| 188 | + .e = 11, \ |
| 189 | + .f = 13, \ |
| 190 | + .g = 17, \ |
| 191 | + .h = 19, |
| 192 | + struct randstruct_untouched untouched = { |
| 193 | + init_members |
| 194 | + }; |
| 195 | + struct randstruct_shuffled shuffled = { |
| 196 | + init_members |
| 197 | + }; |
| 198 | + struct randstruct_mixed_untouched mixed_untouched = { |
| 199 | + init_members |
| 200 | + }; |
| 201 | + struct randstruct_mixed_shuffled mixed_shuffled = { |
| 202 | + init_members |
| 203 | + }; |
| 204 | + struct contains_randstruct_untouched contains_untouched = { |
| 205 | + .untouched = { |
| 206 | + init_members |
| 207 | + }, |
| 208 | + }; |
| 209 | + struct contains_randstruct_shuffled contains_shuffled = { |
| 210 | + .shuffled = { |
| 211 | + init_members |
| 212 | + }, |
| 213 | + }; |
| 214 | +#define func_member(x, ignored) \ |
| 215 | + .x = func_##x, |
| 216 | + struct randstruct_funcs_untouched funcs_untouched = { |
| 217 | + DO_MANY_MEMBERS(func_member) |
| 218 | + }; |
| 219 | + struct randstruct_funcs_shuffled funcs_shuffled = { |
| 220 | + DO_MANY_MEMBERS(func_member) |
| 221 | + }; |
| 222 | + |
| 223 | + test_check_init(test, "named", &untouched, &shuffled); |
| 224 | + test_check_init(test, "unnamed", &untouched, |
| 225 | + &(struct randstruct_shuffled){ |
| 226 | + init_members |
| 227 | + }); |
| 228 | + |
| 229 | + test_check_contained_init(test, "named", &contains_untouched, &contains_shuffled); |
| 230 | + test_check_contained_init(test, "unnamed", &contains_untouched, |
| 231 | + &(struct contains_randstruct_shuffled){ |
| 232 | + .shuffled = (struct randstruct_shuffled){ |
| 233 | + init_members |
| 234 | + }, |
| 235 | + }); |
| 236 | + |
| 237 | + test_check_contained_init(test, "named", &contains_untouched, &contains_shuffled); |
| 238 | + test_check_contained_init(test, "unnamed copy", &contains_untouched, |
| 239 | + &(struct contains_randstruct_shuffled){ |
| 240 | + /* full struct copy initializer */ |
| 241 | + .shuffled = shuffled, |
| 242 | + }); |
| 243 | + |
| 244 | + test_check_mixed_init(test, "named", &mixed_untouched, &mixed_shuffled); |
| 245 | + test_check_mixed_init(test, "unnamed", &mixed_untouched, |
| 246 | + &(struct randstruct_mixed_shuffled){ |
| 247 | + init_members |
| 248 | + }); |
| 249 | + |
| 250 | + test_check_funcs_init(test, "named", &funcs_untouched, &funcs_shuffled); |
| 251 | + test_check_funcs_init(test, "unnamed", &funcs_untouched, |
| 252 | + &(struct randstruct_funcs_shuffled){ |
| 253 | + DO_MANY_MEMBERS(func_member) |
| 254 | + }); |
| 255 | + |
| 256 | +#undef func_member |
| 257 | +#undef init_members |
| 258 | +} |
| 259 | + |
| 260 | +static int randstruct_test_init(struct kunit *test) |
| 261 | +{ |
| 262 | + if (!IS_ENABLED(CONFIG_RANDSTRUCT)) |
| 263 | + kunit_skip(test, "Not built with CONFIG_RANDSTRUCT=y"); |
| 264 | + |
| 265 | + return 0; |
| 266 | +} |
| 267 | + |
| 268 | +static struct kunit_case randstruct_test_cases[] = { |
| 269 | + KUNIT_CASE(randstruct_layout), |
| 270 | + KUNIT_CASE(randstruct_initializers), |
| 271 | + {} |
| 272 | +}; |
| 273 | + |
| 274 | +static struct kunit_suite randstruct_test_suite = { |
| 275 | + .name = "randstruct", |
| 276 | + .init = randstruct_test_init, |
| 277 | + .test_cases = randstruct_test_cases, |
| 278 | +}; |
| 279 | + |
| 280 | +kunit_test_suites(&randstruct_test_suite); |
| 281 | + |
| 282 | +MODULE_DESCRIPTION("Test cases for struct randomization"); |
| 283 | +MODULE_LICENSE("GPL"); |
0 commit comments