at v5.16-rc2 16 kB view raw
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Test cases for compiler-based stack variable zeroing via 4 * -ftrivial-auto-var-init={zero,pattern} or CONFIG_GCC_PLUGIN_STRUCTLEAK*. 5 * 6 * External build example: 7 * clang -O2 -Wall -ftrivial-auto-var-init=pattern \ 8 * -o test_stackinit test_stackinit.c 9 */ 10#ifdef __KERNEL__ 11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 13#include <linux/init.h> 14#include <linux/kernel.h> 15#include <linux/module.h> 16#include <linux/string.h> 17 18#else 19 20/* Userspace headers. */ 21#include <stdio.h> 22#include <stdint.h> 23#include <string.h> 24#include <stdbool.h> 25#include <errno.h> 26#include <sys/types.h> 27 28/* Linux kernel-ism stubs for stand-alone userspace build. */ 29#define KBUILD_MODNAME "stackinit" 30#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 31#define pr_err(fmt, ...) fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__) 32#define pr_warn(fmt, ...) fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__) 33#define pr_info(fmt, ...) fprintf(stdout, pr_fmt(fmt), ##__VA_ARGS__) 34#define __init /**/ 35#define __exit /**/ 36#define __user /**/ 37#define noinline __attribute__((__noinline__)) 38#define __aligned(x) __attribute__((__aligned__(x))) 39#ifdef __clang__ 40# define __compiletime_error(message) /**/ 41#else 42# define __compiletime_error(message) __attribute__((__error__(message))) 43#endif 44#define __compiletime_assert(condition, msg, prefix, suffix) \ 45 do { \ 46 extern void prefix ## suffix(void) __compiletime_error(msg); \ 47 if (!(condition)) \ 48 prefix ## suffix(); \ 49 } while (0) 50#define _compiletime_assert(condition, msg, prefix, suffix) \ 51 __compiletime_assert(condition, msg, prefix, suffix) 52#define compiletime_assert(condition, msg) \ 53 _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__) 54#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg) 55#define BUILD_BUG_ON(condition) \ 56 BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition) 57typedef uint8_t u8; 58typedef uint16_t u16; 59typedef uint32_t u32; 60typedef uint64_t u64; 61 62#define module_init(func) static int (*do_init)(void) = func 63#define module_exit(func) static void (*do_exit)(void) = func 64#define MODULE_LICENSE(str) int main(void) { \ 65 int rc; \ 66 /* License: str */ \ 67 rc = do_init(); \ 68 if (rc == 0) \ 69 do_exit(); \ 70 return rc; \ 71 } 72 73#endif /* __KERNEL__ */ 74 75/* Exfiltration buffer. */ 76#define MAX_VAR_SIZE 128 77static u8 check_buf[MAX_VAR_SIZE]; 78 79/* Character array to trigger stack protector in all functions. */ 80#define VAR_BUFFER 32 81 82/* Volatile mask to convince compiler to copy memory with 0xff. */ 83static volatile u8 forced_mask = 0xff; 84 85/* Location and size tracking to validate fill and test are colocated. */ 86static void *fill_start, *target_start; 87static size_t fill_size, target_size; 88 89static bool range_contains(char *haystack_start, size_t haystack_size, 90 char *needle_start, size_t needle_size) 91{ 92 if (needle_start >= haystack_start && 93 needle_start + needle_size <= haystack_start + haystack_size) 94 return true; 95 return false; 96} 97 98/* Whether the test is expected to fail. */ 99#define WANT_SUCCESS 0 100#define XFAIL 1 101 102#define DO_NOTHING_TYPE_SCALAR(var_type) var_type 103#define DO_NOTHING_TYPE_STRING(var_type) void 104#define DO_NOTHING_TYPE_STRUCT(var_type) void 105 106#define DO_NOTHING_RETURN_SCALAR(ptr) *(ptr) 107#define DO_NOTHING_RETURN_STRING(ptr) /**/ 108#define DO_NOTHING_RETURN_STRUCT(ptr) /**/ 109 110#define DO_NOTHING_CALL_SCALAR(var, name) \ 111 (var) = do_nothing_ ## name(&(var)) 112#define DO_NOTHING_CALL_STRING(var, name) \ 113 do_nothing_ ## name(var) 114#define DO_NOTHING_CALL_STRUCT(var, name) \ 115 do_nothing_ ## name(&(var)) 116 117#define FETCH_ARG_SCALAR(var) &var 118#define FETCH_ARG_STRING(var) var 119#define FETCH_ARG_STRUCT(var) &var 120 121#define FILL_SIZE_STRING 16 122 123#define INIT_CLONE_SCALAR /**/ 124#define INIT_CLONE_STRING [FILL_SIZE_STRING] 125#define INIT_CLONE_STRUCT /**/ 126 127#define ZERO_CLONE_SCALAR(zero) memset(&(zero), 0x00, sizeof(zero)) 128#define ZERO_CLONE_STRING(zero) memset(&(zero), 0x00, sizeof(zero)) 129/* 130 * For the struct, intentionally poison padding to see if it gets 131 * copied out in direct assignments. 132 * */ 133#define ZERO_CLONE_STRUCT(zero) \ 134 do { \ 135 memset(&(zero), 0xFF, sizeof(zero)); \ 136 zero.one = 0; \ 137 zero.two = 0; \ 138 zero.three = 0; \ 139 zero.four = 0; \ 140 } while (0) 141 142#define INIT_SCALAR_none(var_type) /**/ 143#define INIT_SCALAR_zero(var_type) = 0 144 145#define INIT_STRING_none(var_type) [FILL_SIZE_STRING] /**/ 146#define INIT_STRING_zero(var_type) [FILL_SIZE_STRING] = { } 147 148#define INIT_STRUCT_none(var_type) /**/ 149#define INIT_STRUCT_zero(var_type) = { } 150 151 152#define __static_partial { .two = 0, } 153#define __static_all { .one = 0, \ 154 .two = 0, \ 155 .three = 0, \ 156 .four = 0, \ 157 } 158#define __dynamic_partial { .two = arg->two, } 159#define __dynamic_all { .one = arg->one, \ 160 .two = arg->two, \ 161 .three = arg->three, \ 162 .four = arg->four, \ 163 } 164#define __runtime_partial var.two = 0 165#define __runtime_all var.one = 0; \ 166 var.two = 0; \ 167 var.three = 0; \ 168 var.four = 0 169 170#define INIT_STRUCT_static_partial(var_type) \ 171 = __static_partial 172#define INIT_STRUCT_static_all(var_type) \ 173 = __static_all 174#define INIT_STRUCT_dynamic_partial(var_type) \ 175 = __dynamic_partial 176#define INIT_STRUCT_dynamic_all(var_type) \ 177 = __dynamic_all 178#define INIT_STRUCT_runtime_partial(var_type) \ 179 ; __runtime_partial 180#define INIT_STRUCT_runtime_all(var_type) \ 181 ; __runtime_all 182 183#define INIT_STRUCT_assigned_static_partial(var_type) \ 184 ; var = (var_type)__static_partial 185#define INIT_STRUCT_assigned_static_all(var_type) \ 186 ; var = (var_type)__static_all 187#define INIT_STRUCT_assigned_dynamic_partial(var_type) \ 188 ; var = (var_type)__dynamic_partial 189#define INIT_STRUCT_assigned_dynamic_all(var_type) \ 190 ; var = (var_type)__dynamic_all 191 192#define INIT_STRUCT_assigned_copy(var_type) \ 193 ; var = *(arg) 194 195/* 196 * @name: unique string name for the test 197 * @var_type: type to be tested for zeroing initialization 198 * @which: is this a SCALAR, STRING, or STRUCT type? 199 * @init_level: what kind of initialization is performed 200 * @xfail: is this test expected to fail? 201 */ 202#define DEFINE_TEST_DRIVER(name, var_type, which, xfail) \ 203/* Returns 0 on success, 1 on failure. */ \ 204static noinline __init int test_ ## name (void) \ 205{ \ 206 var_type zero INIT_CLONE_ ## which; \ 207 int ignored; \ 208 u8 sum = 0, i; \ 209 \ 210 /* Notice when a new test is larger than expected. */ \ 211 BUILD_BUG_ON(sizeof(zero) > MAX_VAR_SIZE); \ 212 \ 213 /* Fill clone type with zero for per-field init. */ \ 214 ZERO_CLONE_ ## which(zero); \ 215 /* Clear entire check buffer for 0xFF overlap test. */ \ 216 memset(check_buf, 0x00, sizeof(check_buf)); \ 217 /* Fill stack with 0xFF. */ \ 218 ignored = leaf_ ##name((unsigned long)&ignored, 1, \ 219 FETCH_ARG_ ## which(zero)); \ 220 /* Verify all bytes overwritten with 0xFF. */ \ 221 for (sum = 0, i = 0; i < target_size; i++) \ 222 sum += (check_buf[i] != 0xFF); \ 223 if (sum) { \ 224 pr_err(#name ": leaf fill was not 0xFF!?\n"); \ 225 return 1; \ 226 } \ 227 /* Clear entire check buffer for later bit tests. */ \ 228 memset(check_buf, 0x00, sizeof(check_buf)); \ 229 /* Extract stack-defined variable contents. */ \ 230 ignored = leaf_ ##name((unsigned long)&ignored, 0, \ 231 FETCH_ARG_ ## which(zero)); \ 232 \ 233 /* Validate that compiler lined up fill and target. */ \ 234 if (!range_contains(fill_start, fill_size, \ 235 target_start, target_size)) { \ 236 pr_err(#name ": stack fill missed target!?\n"); \ 237 pr_err(#name ": fill %zu wide\n", fill_size); \ 238 pr_err(#name ": target offset by %d\n", \ 239 (int)((ssize_t)(uintptr_t)fill_start - \ 240 (ssize_t)(uintptr_t)target_start)); \ 241 return 1; \ 242 } \ 243 \ 244 /* Look for any bytes still 0xFF in check region. */ \ 245 for (sum = 0, i = 0; i < target_size; i++) \ 246 sum += (check_buf[i] == 0xFF); \ 247 \ 248 if (sum == 0) { \ 249 pr_info(#name " ok\n"); \ 250 return 0; \ 251 } else { \ 252 pr_warn(#name " %sFAIL (uninit bytes: %d)\n", \ 253 (xfail) ? "X" : "", sum); \ 254 return (xfail) ? 0 : 1; \ 255 } \ 256} 257#define DEFINE_TEST(name, var_type, which, init_level, xfail) \ 258/* no-op to force compiler into ignoring "uninitialized" vars */\ 259static noinline __init DO_NOTHING_TYPE_ ## which(var_type) \ 260do_nothing_ ## name(var_type *ptr) \ 261{ \ 262 /* Will always be true, but compiler doesn't know. */ \ 263 if ((unsigned long)ptr > 0x2) \ 264 return DO_NOTHING_RETURN_ ## which(ptr); \ 265 else \ 266 return DO_NOTHING_RETURN_ ## which(ptr + 1); \ 267} \ 268static noinline __init int leaf_ ## name(unsigned long sp, \ 269 bool fill, \ 270 var_type *arg) \ 271{ \ 272 char buf[VAR_BUFFER]; \ 273 var_type var \ 274 INIT_ ## which ## _ ## init_level(var_type); \ 275 \ 276 target_start = &var; \ 277 target_size = sizeof(var); \ 278 /* \ 279 * Keep this buffer around to make sure we've got a \ 280 * stack frame of SOME kind... \ 281 */ \ 282 memset(buf, (char)(sp & 0xff), sizeof(buf)); \ 283 /* Fill variable with 0xFF. */ \ 284 if (fill) { \ 285 fill_start = &var; \ 286 fill_size = sizeof(var); \ 287 memset(fill_start, \ 288 (char)((sp & 0xff) | forced_mask), \ 289 fill_size); \ 290 } \ 291 \ 292 /* Silence "never initialized" warnings. */ \ 293 DO_NOTHING_CALL_ ## which(var, name); \ 294 \ 295 /* Exfiltrate "var". */ \ 296 memcpy(check_buf, target_start, target_size); \ 297 \ 298 return (int)buf[0] | (int)buf[sizeof(buf) - 1]; \ 299} \ 300DEFINE_TEST_DRIVER(name, var_type, which, xfail) 301 302/* Structure with no padding. */ 303struct test_packed { 304 unsigned long one; 305 unsigned long two; 306 unsigned long three; 307 unsigned long four; 308}; 309 310/* Simple structure with padding likely to be covered by compiler. */ 311struct test_small_hole { 312 size_t one; 313 char two; 314 /* 3 byte padding hole here. */ 315 int three; 316 unsigned long four; 317}; 318 319/* Trigger unhandled padding in a structure. */ 320struct test_big_hole { 321 u8 one; 322 u8 two; 323 u8 three; 324 /* 61 byte padding hole here. */ 325 u8 four __aligned(64); 326} __aligned(64); 327 328struct test_trailing_hole { 329 char *one; 330 char *two; 331 char *three; 332 char four; 333 /* "sizeof(unsigned long) - 1" byte padding hole here. */ 334}; 335 336/* Test if STRUCTLEAK is clearing structs with __user fields. */ 337struct test_user { 338 u8 one; 339 unsigned long two; 340 char __user *three; 341 unsigned long four; 342}; 343 344#define DEFINE_SCALAR_TEST(name, init, xfail) \ 345 DEFINE_TEST(name ## _ ## init, name, SCALAR, \ 346 init, xfail) 347 348#define DEFINE_SCALAR_TESTS(init, xfail) \ 349 DEFINE_SCALAR_TEST(u8, init, xfail); \ 350 DEFINE_SCALAR_TEST(u16, init, xfail); \ 351 DEFINE_SCALAR_TEST(u32, init, xfail); \ 352 DEFINE_SCALAR_TEST(u64, init, xfail); \ 353 DEFINE_TEST(char_array_ ## init, unsigned char, \ 354 STRING, init, xfail) 355 356#define DEFINE_STRUCT_TEST(name, init, xfail) \ 357 DEFINE_TEST(name ## _ ## init, \ 358 struct test_ ## name, STRUCT, init, \ 359 xfail) 360 361#define DEFINE_STRUCT_TESTS(init, xfail) \ 362 DEFINE_STRUCT_TEST(small_hole, init, xfail); \ 363 DEFINE_STRUCT_TEST(big_hole, init, xfail); \ 364 DEFINE_STRUCT_TEST(trailing_hole, init, xfail); \ 365 DEFINE_STRUCT_TEST(packed, init, xfail) 366 367#define DEFINE_STRUCT_INITIALIZER_TESTS(base) \ 368 DEFINE_STRUCT_TESTS(base ## _ ## partial, \ 369 WANT_SUCCESS); \ 370 DEFINE_STRUCT_TESTS(base ## _ ## all, \ 371 WANT_SUCCESS) 372 373/* These should be fully initialized all the time! */ 374DEFINE_SCALAR_TESTS(zero, WANT_SUCCESS); 375DEFINE_STRUCT_TESTS(zero, WANT_SUCCESS); 376/* Struct initializers: padding may be left uninitialized. */ 377DEFINE_STRUCT_INITIALIZER_TESTS(static); 378DEFINE_STRUCT_INITIALIZER_TESTS(dynamic); 379DEFINE_STRUCT_INITIALIZER_TESTS(runtime); 380DEFINE_STRUCT_INITIALIZER_TESTS(assigned_static); 381DEFINE_STRUCT_INITIALIZER_TESTS(assigned_dynamic); 382DEFINE_STRUCT_TESTS(assigned_copy, XFAIL); 383/* No initialization without compiler instrumentation. */ 384DEFINE_SCALAR_TESTS(none, WANT_SUCCESS); 385DEFINE_STRUCT_TESTS(none, WANT_SUCCESS); 386/* Initialization of members with __user attribute. */ 387DEFINE_TEST(user, struct test_user, STRUCT, none, WANT_SUCCESS); 388 389/* 390 * Check two uses through a variable declaration outside either path, 391 * which was noticed as a special case in porting earlier stack init 392 * compiler logic. 393 */ 394static int noinline __leaf_switch_none(int path, bool fill) 395{ 396 switch (path) { 397 /* 398 * This is intentionally unreachable. To silence the 399 * warning, build with -Wno-switch-unreachable 400 */ 401 uint64_t var; 402 403 case 1: 404 target_start = &var; 405 target_size = sizeof(var); 406 if (fill) { 407 fill_start = &var; 408 fill_size = sizeof(var); 409 410 memset(fill_start, forced_mask | 0x55, fill_size); 411 } 412 memcpy(check_buf, target_start, target_size); 413 break; 414 case 2: 415 target_start = &var; 416 target_size = sizeof(var); 417 if (fill) { 418 fill_start = &var; 419 fill_size = sizeof(var); 420 421 memset(fill_start, forced_mask | 0xaa, fill_size); 422 } 423 memcpy(check_buf, target_start, target_size); 424 break; 425 default: 426 var = 5; 427 return var & forced_mask; 428 } 429 return 0; 430} 431 432static noinline __init int leaf_switch_1_none(unsigned long sp, bool fill, 433 uint64_t *arg) 434{ 435 return __leaf_switch_none(1, fill); 436} 437 438static noinline __init int leaf_switch_2_none(unsigned long sp, bool fill, 439 uint64_t *arg) 440{ 441 return __leaf_switch_none(2, fill); 442} 443 444/* 445 * These are expected to fail for most configurations because neither 446 * GCC nor Clang have a way to perform initialization of variables in 447 * non-code areas (i.e. in a switch statement before the first "case"). 448 * https://bugs.llvm.org/show_bug.cgi?id=44916 449 */ 450DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, XFAIL); 451DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, XFAIL); 452 453static int __init test_stackinit_init(void) 454{ 455 unsigned int failures = 0; 456 457#define test_scalars(init) do { \ 458 failures += test_u8_ ## init (); \ 459 failures += test_u16_ ## init (); \ 460 failures += test_u32_ ## init (); \ 461 failures += test_u64_ ## init (); \ 462 failures += test_char_array_ ## init (); \ 463 } while (0) 464 465#define test_structs(init) do { \ 466 failures += test_small_hole_ ## init (); \ 467 failures += test_big_hole_ ## init (); \ 468 failures += test_trailing_hole_ ## init (); \ 469 failures += test_packed_ ## init (); \ 470 } while (0) 471 472 /* These are explicitly initialized and should always pass. */ 473 test_scalars(zero); 474 test_structs(zero); 475 /* Padding here appears to be accidentally always initialized? */ 476 test_structs(dynamic_partial); 477 test_structs(assigned_dynamic_partial); 478 /* Padding initialization depends on compiler behaviors. */ 479 test_structs(static_partial); 480 test_structs(static_all); 481 test_structs(dynamic_all); 482 test_structs(runtime_partial); 483 test_structs(runtime_all); 484 test_structs(assigned_static_partial); 485 test_structs(assigned_static_all); 486 test_structs(assigned_dynamic_all); 487 /* Everything fails this since it effectively performs a memcpy(). */ 488 test_structs(assigned_copy); 489 490 /* STRUCTLEAK_BYREF_ALL should cover everything from here down. */ 491 test_scalars(none); 492 failures += test_switch_1_none(); 493 failures += test_switch_2_none(); 494 495 /* STRUCTLEAK_BYREF should cover from here down. */ 496 test_structs(none); 497 498 /* STRUCTLEAK will only cover this. */ 499 failures += test_user(); 500 501 if (failures == 0) 502 pr_info("all tests passed!\n"); 503 else 504 pr_err("failures: %u\n", failures); 505 506 return failures ? -EINVAL : 0; 507} 508module_init(test_stackinit_init); 509 510static void __exit test_stackinit_exit(void) 511{ } 512module_exit(test_stackinit_exit); 513 514MODULE_LICENSE("GPL");