Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

lib/tests: randstruct: Add deep function pointer layout test

The recent fix in commit c2ea09b193d2 ("randstruct: gcc-plugin: Remove
bogus void member") has fixed another issue: it was not always detecting
composite structures made only of function pointers and structures of
function pointers. Add a test for this case, and break out the layout
tests since this issue is actually a problem for Clang as well[1].

Link: https://github.com/llvm/llvm-project/issues/138355 [1]
Link: https://lore.kernel.org/r/20250502224116.work.591-kees@kernel.org
Signed-off-by: Kees Cook <kees@kernel.org>

Kees Cook f55aef7e b370f7ea

+65 -14
+65 -14
lib/tests/randstruct_kunit.c
··· 56 56 struct randstruct_funcs_shuffled { 57 57 DO_MANY_MEMBERS(func_member) 58 58 }; 59 - #undef func_member 60 59 61 60 #define func_body(x, ignored) \ 62 61 static noinline size_t func_##x(int arg) \ ··· 102 103 int after; 103 104 }; 104 105 105 - static void randstruct_layout(struct kunit *test) 106 - { 107 - int mismatches; 106 + struct contains_func_untouched { 107 + struct randstruct_funcs_shuffled inner; 108 + DO_MANY_MEMBERS(func_member) 109 + } __no_randomize_layout; 110 + 111 + struct contains_func_shuffled { 112 + struct randstruct_funcs_shuffled inner; 113 + DO_MANY_MEMBERS(func_member) 114 + }; 115 + #undef func_member 108 116 109 117 #define check_mismatch(x, untouched, shuffled) \ 110 118 if (offsetof(untouched, x) != offsetof(shuffled, x)) \ ··· 120 114 offsetof(shuffled, x), \ 121 115 offsetof(untouched, x)); \ 122 116 123 - #define check_pair(outcome, untouched, shuffled) \ 117 + #define check_pair(outcome, untouched, shuffled, checker...) \ 124 118 mismatches = 0; \ 125 - DO_MANY_MEMBERS(check_mismatch, untouched, shuffled) \ 119 + DO_MANY_MEMBERS(checker, untouched, shuffled) \ 126 120 kunit_info(test, "Differing " #untouched " vs " #shuffled " member positions: %d\n", \ 127 121 mismatches); \ 128 122 KUNIT_##outcome##_MSG(test, mismatches, 0, \ 129 123 #untouched " vs " #shuffled " layouts: unlucky or broken?\n"); 130 124 131 - check_pair(EXPECT_EQ, struct randstruct_untouched, struct randstruct_untouched) 132 - check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_shuffled) 133 - check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_funcs_shuffled) 134 - check_pair(EXPECT_GT, struct randstruct_funcs_untouched, struct randstruct_funcs_shuffled) 135 - check_pair(EXPECT_GT, struct randstruct_mixed_untouched, struct randstruct_mixed_shuffled) 136 - #undef check_pair 125 + static void randstruct_layout_same(struct kunit *test) 126 + { 127 + int mismatches; 137 128 138 - #undef check_mismatch 129 + check_pair(EXPECT_EQ, struct randstruct_untouched, struct randstruct_untouched, 130 + check_mismatch) 131 + check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_shuffled, 132 + check_mismatch) 139 133 } 134 + 135 + static void randstruct_layout_mixed(struct kunit *test) 136 + { 137 + int mismatches; 138 + 139 + check_pair(EXPECT_EQ, struct randstruct_mixed_untouched, struct randstruct_mixed_untouched, 140 + check_mismatch) 141 + check_pair(EXPECT_GT, struct randstruct_mixed_untouched, struct randstruct_mixed_shuffled, 142 + check_mismatch) 143 + } 144 + 145 + static void randstruct_layout_fptr(struct kunit *test) 146 + { 147 + int mismatches; 148 + 149 + check_pair(EXPECT_EQ, struct randstruct_untouched, struct randstruct_untouched, 150 + check_mismatch) 151 + check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_funcs_shuffled, 152 + check_mismatch) 153 + check_pair(EXPECT_GT, struct randstruct_funcs_untouched, struct randstruct_funcs_shuffled, 154 + check_mismatch) 155 + } 156 + 157 + #define check_mismatch_prefixed(x, prefix, untouched, shuffled) \ 158 + check_mismatch(prefix.x, untouched, shuffled) 159 + 160 + static void randstruct_layout_fptr_deep(struct kunit *test) 161 + { 162 + int mismatches; 163 + 164 + if (IS_ENABLED(CONFIG_CC_IS_CLANG)) 165 + kunit_skip(test, "Clang randstruct misses inner functions: https://github.com/llvm/llvm-project/issues/138355"); 166 + 167 + check_pair(EXPECT_EQ, struct contains_func_untouched, struct contains_func_untouched, 168 + check_mismatch_prefixed, inner) 169 + 170 + check_pair(EXPECT_GT, struct contains_func_untouched, struct contains_func_shuffled, 171 + check_mismatch_prefixed, inner) 172 + } 173 + 174 + #undef check_pair 175 + #undef check_mismatch 140 176 141 177 #define check_mismatch(x, ignore) \ 142 178 KUNIT_EXPECT_EQ_MSG(test, untouched->x, shuffled->x, \ ··· 314 266 } 315 267 316 268 static struct kunit_case randstruct_test_cases[] = { 317 - KUNIT_CASE(randstruct_layout), 269 + KUNIT_CASE(randstruct_layout_same), 270 + KUNIT_CASE(randstruct_layout_mixed), 271 + KUNIT_CASE(randstruct_layout_fptr), 272 + KUNIT_CASE(randstruct_layout_fptr_deep), 318 273 KUNIT_CASE(randstruct_initializers), 319 274 {} 320 275 };