18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Test cases for compiler-based stack variable zeroing via future 48c2ecf20Sopenharmony_ci * compiler flags or CONFIG_GCC_PLUGIN_STRUCTLEAK*. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/string.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* Exfiltration buffer. */ 148c2ecf20Sopenharmony_ci#define MAX_VAR_SIZE 128 158c2ecf20Sopenharmony_cistatic u8 check_buf[MAX_VAR_SIZE]; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* Character array to trigger stack protector in all functions. */ 188c2ecf20Sopenharmony_ci#define VAR_BUFFER 32 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* Volatile mask to convince compiler to copy memory with 0xff. */ 218c2ecf20Sopenharmony_cistatic volatile u8 forced_mask = 0xff; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* Location and size tracking to validate fill and test are colocated. */ 248c2ecf20Sopenharmony_cistatic void *fill_start, *target_start; 258c2ecf20Sopenharmony_cistatic size_t fill_size, target_size; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic bool range_contains(char *haystack_start, size_t haystack_size, 288c2ecf20Sopenharmony_ci char *needle_start, size_t needle_size) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci if (needle_start >= haystack_start && 318c2ecf20Sopenharmony_ci needle_start + needle_size <= haystack_start + haystack_size) 328c2ecf20Sopenharmony_ci return true; 338c2ecf20Sopenharmony_ci return false; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define DO_NOTHING_TYPE_SCALAR(var_type) var_type 378c2ecf20Sopenharmony_ci#define DO_NOTHING_TYPE_STRING(var_type) void 388c2ecf20Sopenharmony_ci#define DO_NOTHING_TYPE_STRUCT(var_type) void 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define DO_NOTHING_RETURN_SCALAR(ptr) *(ptr) 418c2ecf20Sopenharmony_ci#define DO_NOTHING_RETURN_STRING(ptr) /**/ 428c2ecf20Sopenharmony_ci#define DO_NOTHING_RETURN_STRUCT(ptr) /**/ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define DO_NOTHING_CALL_SCALAR(var, name) \ 458c2ecf20Sopenharmony_ci (var) = do_nothing_ ## name(&(var)) 468c2ecf20Sopenharmony_ci#define DO_NOTHING_CALL_STRING(var, name) \ 478c2ecf20Sopenharmony_ci do_nothing_ ## name(var) 488c2ecf20Sopenharmony_ci#define DO_NOTHING_CALL_STRUCT(var, name) \ 498c2ecf20Sopenharmony_ci do_nothing_ ## name(&(var)) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define FETCH_ARG_SCALAR(var) &var 528c2ecf20Sopenharmony_ci#define FETCH_ARG_STRING(var) var 538c2ecf20Sopenharmony_ci#define FETCH_ARG_STRUCT(var) &var 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define FILL_SIZE_STRING 16 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define INIT_CLONE_SCALAR /**/ 588c2ecf20Sopenharmony_ci#define INIT_CLONE_STRING [FILL_SIZE_STRING] 598c2ecf20Sopenharmony_ci#define INIT_CLONE_STRUCT /**/ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define INIT_SCALAR_none /**/ 628c2ecf20Sopenharmony_ci#define INIT_SCALAR_zero = 0 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define INIT_STRING_none [FILL_SIZE_STRING] /**/ 658c2ecf20Sopenharmony_ci#define INIT_STRING_zero [FILL_SIZE_STRING] = { } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define INIT_STRUCT_none /**/ 688c2ecf20Sopenharmony_ci#define INIT_STRUCT_zero = { } 698c2ecf20Sopenharmony_ci#define INIT_STRUCT_static_partial = { .two = 0, } 708c2ecf20Sopenharmony_ci#define INIT_STRUCT_static_all = { .one = 0, \ 718c2ecf20Sopenharmony_ci .two = 0, \ 728c2ecf20Sopenharmony_ci .three = 0, \ 738c2ecf20Sopenharmony_ci .four = 0, \ 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci#define INIT_STRUCT_dynamic_partial = { .two = arg->two, } 768c2ecf20Sopenharmony_ci#define INIT_STRUCT_dynamic_all = { .one = arg->one, \ 778c2ecf20Sopenharmony_ci .two = arg->two, \ 788c2ecf20Sopenharmony_ci .three = arg->three, \ 798c2ecf20Sopenharmony_ci .four = arg->four, \ 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci#define INIT_STRUCT_runtime_partial ; \ 828c2ecf20Sopenharmony_ci var.two = 0 838c2ecf20Sopenharmony_ci#define INIT_STRUCT_runtime_all ; \ 848c2ecf20Sopenharmony_ci var.one = 0; \ 858c2ecf20Sopenharmony_ci var.two = 0; \ 868c2ecf20Sopenharmony_ci var.three = 0; \ 878c2ecf20Sopenharmony_ci var.four = 0 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* 908c2ecf20Sopenharmony_ci * @name: unique string name for the test 918c2ecf20Sopenharmony_ci * @var_type: type to be tested for zeroing initialization 928c2ecf20Sopenharmony_ci * @which: is this a SCALAR, STRING, or STRUCT type? 938c2ecf20Sopenharmony_ci * @init_level: what kind of initialization is performed 948c2ecf20Sopenharmony_ci * @xfail: is this test expected to fail? 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci#define DEFINE_TEST_DRIVER(name, var_type, which, xfail) \ 978c2ecf20Sopenharmony_ci/* Returns 0 on success, 1 on failure. */ \ 988c2ecf20Sopenharmony_cistatic noinline __init int test_ ## name (void) \ 998c2ecf20Sopenharmony_ci{ \ 1008c2ecf20Sopenharmony_ci var_type zero INIT_CLONE_ ## which; \ 1018c2ecf20Sopenharmony_ci int ignored; \ 1028c2ecf20Sopenharmony_ci u8 sum = 0, i; \ 1038c2ecf20Sopenharmony_ci \ 1048c2ecf20Sopenharmony_ci /* Notice when a new test is larger than expected. */ \ 1058c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(zero) > MAX_VAR_SIZE); \ 1068c2ecf20Sopenharmony_ci \ 1078c2ecf20Sopenharmony_ci /* Fill clone type with zero for per-field init. */ \ 1088c2ecf20Sopenharmony_ci memset(&zero, 0x00, sizeof(zero)); \ 1098c2ecf20Sopenharmony_ci /* Clear entire check buffer for 0xFF overlap test. */ \ 1108c2ecf20Sopenharmony_ci memset(check_buf, 0x00, sizeof(check_buf)); \ 1118c2ecf20Sopenharmony_ci /* Fill stack with 0xFF. */ \ 1128c2ecf20Sopenharmony_ci ignored = leaf_ ##name((unsigned long)&ignored, 1, \ 1138c2ecf20Sopenharmony_ci FETCH_ARG_ ## which(zero)); \ 1148c2ecf20Sopenharmony_ci /* Verify all bytes overwritten with 0xFF. */ \ 1158c2ecf20Sopenharmony_ci for (sum = 0, i = 0; i < target_size; i++) \ 1168c2ecf20Sopenharmony_ci sum += (check_buf[i] != 0xFF); \ 1178c2ecf20Sopenharmony_ci if (sum) { \ 1188c2ecf20Sopenharmony_ci pr_err(#name ": leaf fill was not 0xFF!?\n"); \ 1198c2ecf20Sopenharmony_ci return 1; \ 1208c2ecf20Sopenharmony_ci } \ 1218c2ecf20Sopenharmony_ci /* Clear entire check buffer for later bit tests. */ \ 1228c2ecf20Sopenharmony_ci memset(check_buf, 0x00, sizeof(check_buf)); \ 1238c2ecf20Sopenharmony_ci /* Extract stack-defined variable contents. */ \ 1248c2ecf20Sopenharmony_ci ignored = leaf_ ##name((unsigned long)&ignored, 0, \ 1258c2ecf20Sopenharmony_ci FETCH_ARG_ ## which(zero)); \ 1268c2ecf20Sopenharmony_ci \ 1278c2ecf20Sopenharmony_ci /* Validate that compiler lined up fill and target. */ \ 1288c2ecf20Sopenharmony_ci if (!range_contains(fill_start, fill_size, \ 1298c2ecf20Sopenharmony_ci target_start, target_size)) { \ 1308c2ecf20Sopenharmony_ci pr_err(#name ": stack fill missed target!?\n"); \ 1318c2ecf20Sopenharmony_ci pr_err(#name ": fill %zu wide\n", fill_size); \ 1328c2ecf20Sopenharmony_ci pr_err(#name ": target offset by %d\n", \ 1338c2ecf20Sopenharmony_ci (int)((ssize_t)(uintptr_t)fill_start - \ 1348c2ecf20Sopenharmony_ci (ssize_t)(uintptr_t)target_start)); \ 1358c2ecf20Sopenharmony_ci return 1; \ 1368c2ecf20Sopenharmony_ci } \ 1378c2ecf20Sopenharmony_ci \ 1388c2ecf20Sopenharmony_ci /* Look for any bytes still 0xFF in check region. */ \ 1398c2ecf20Sopenharmony_ci for (sum = 0, i = 0; i < target_size; i++) \ 1408c2ecf20Sopenharmony_ci sum += (check_buf[i] == 0xFF); \ 1418c2ecf20Sopenharmony_ci \ 1428c2ecf20Sopenharmony_ci if (sum == 0) { \ 1438c2ecf20Sopenharmony_ci pr_info(#name " ok\n"); \ 1448c2ecf20Sopenharmony_ci return 0; \ 1458c2ecf20Sopenharmony_ci } else { \ 1468c2ecf20Sopenharmony_ci pr_warn(#name " %sFAIL (uninit bytes: %d)\n", \ 1478c2ecf20Sopenharmony_ci (xfail) ? "X" : "", sum); \ 1488c2ecf20Sopenharmony_ci return (xfail) ? 0 : 1; \ 1498c2ecf20Sopenharmony_ci } \ 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci#define DEFINE_TEST(name, var_type, which, init_level) \ 1528c2ecf20Sopenharmony_ci/* no-op to force compiler into ignoring "uninitialized" vars */\ 1538c2ecf20Sopenharmony_cistatic noinline __init DO_NOTHING_TYPE_ ## which(var_type) \ 1548c2ecf20Sopenharmony_cido_nothing_ ## name(var_type *ptr) \ 1558c2ecf20Sopenharmony_ci{ \ 1568c2ecf20Sopenharmony_ci /* Will always be true, but compiler doesn't know. */ \ 1578c2ecf20Sopenharmony_ci if ((unsigned long)ptr > 0x2) \ 1588c2ecf20Sopenharmony_ci return DO_NOTHING_RETURN_ ## which(ptr); \ 1598c2ecf20Sopenharmony_ci else \ 1608c2ecf20Sopenharmony_ci return DO_NOTHING_RETURN_ ## which(ptr + 1); \ 1618c2ecf20Sopenharmony_ci} \ 1628c2ecf20Sopenharmony_cistatic noinline __init int leaf_ ## name(unsigned long sp, \ 1638c2ecf20Sopenharmony_ci bool fill, \ 1648c2ecf20Sopenharmony_ci var_type *arg) \ 1658c2ecf20Sopenharmony_ci{ \ 1668c2ecf20Sopenharmony_ci char buf[VAR_BUFFER]; \ 1678c2ecf20Sopenharmony_ci var_type var INIT_ ## which ## _ ## init_level; \ 1688c2ecf20Sopenharmony_ci \ 1698c2ecf20Sopenharmony_ci target_start = &var; \ 1708c2ecf20Sopenharmony_ci target_size = sizeof(var); \ 1718c2ecf20Sopenharmony_ci /* \ 1728c2ecf20Sopenharmony_ci * Keep this buffer around to make sure we've got a \ 1738c2ecf20Sopenharmony_ci * stack frame of SOME kind... \ 1748c2ecf20Sopenharmony_ci */ \ 1758c2ecf20Sopenharmony_ci memset(buf, (char)(sp & 0xff), sizeof(buf)); \ 1768c2ecf20Sopenharmony_ci /* Fill variable with 0xFF. */ \ 1778c2ecf20Sopenharmony_ci if (fill) { \ 1788c2ecf20Sopenharmony_ci fill_start = &var; \ 1798c2ecf20Sopenharmony_ci fill_size = sizeof(var); \ 1808c2ecf20Sopenharmony_ci memset(fill_start, \ 1818c2ecf20Sopenharmony_ci (char)((sp & 0xff) | forced_mask), \ 1828c2ecf20Sopenharmony_ci fill_size); \ 1838c2ecf20Sopenharmony_ci } \ 1848c2ecf20Sopenharmony_ci \ 1858c2ecf20Sopenharmony_ci /* Silence "never initialized" warnings. */ \ 1868c2ecf20Sopenharmony_ci DO_NOTHING_CALL_ ## which(var, name); \ 1878c2ecf20Sopenharmony_ci \ 1888c2ecf20Sopenharmony_ci /* Exfiltrate "var". */ \ 1898c2ecf20Sopenharmony_ci memcpy(check_buf, target_start, target_size); \ 1908c2ecf20Sopenharmony_ci \ 1918c2ecf20Sopenharmony_ci return (int)buf[0] | (int)buf[sizeof(buf) - 1]; \ 1928c2ecf20Sopenharmony_ci} \ 1938c2ecf20Sopenharmony_ciDEFINE_TEST_DRIVER(name, var_type, which, 0) 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* Structure with no padding. */ 1968c2ecf20Sopenharmony_cistruct test_packed { 1978c2ecf20Sopenharmony_ci unsigned long one; 1988c2ecf20Sopenharmony_ci unsigned long two; 1998c2ecf20Sopenharmony_ci unsigned long three; 2008c2ecf20Sopenharmony_ci unsigned long four; 2018c2ecf20Sopenharmony_ci}; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/* Simple structure with padding likely to be covered by compiler. */ 2048c2ecf20Sopenharmony_cistruct test_small_hole { 2058c2ecf20Sopenharmony_ci size_t one; 2068c2ecf20Sopenharmony_ci char two; 2078c2ecf20Sopenharmony_ci /* 3 byte padding hole here. */ 2088c2ecf20Sopenharmony_ci int three; 2098c2ecf20Sopenharmony_ci unsigned long four; 2108c2ecf20Sopenharmony_ci}; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/* Trigger unhandled padding in a structure. */ 2138c2ecf20Sopenharmony_cistruct test_big_hole { 2148c2ecf20Sopenharmony_ci u8 one; 2158c2ecf20Sopenharmony_ci u8 two; 2168c2ecf20Sopenharmony_ci u8 three; 2178c2ecf20Sopenharmony_ci /* 61 byte padding hole here. */ 2188c2ecf20Sopenharmony_ci u8 four __aligned(64); 2198c2ecf20Sopenharmony_ci} __aligned(64); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistruct test_trailing_hole { 2228c2ecf20Sopenharmony_ci char *one; 2238c2ecf20Sopenharmony_ci char *two; 2248c2ecf20Sopenharmony_ci char *three; 2258c2ecf20Sopenharmony_ci char four; 2268c2ecf20Sopenharmony_ci /* "sizeof(unsigned long) - 1" byte padding hole here. */ 2278c2ecf20Sopenharmony_ci}; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/* Test if STRUCTLEAK is clearing structs with __user fields. */ 2308c2ecf20Sopenharmony_cistruct test_user { 2318c2ecf20Sopenharmony_ci u8 one; 2328c2ecf20Sopenharmony_ci unsigned long two; 2338c2ecf20Sopenharmony_ci char __user *three; 2348c2ecf20Sopenharmony_ci unsigned long four; 2358c2ecf20Sopenharmony_ci}; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci#define DEFINE_SCALAR_TEST(name, init) \ 2388c2ecf20Sopenharmony_ci DEFINE_TEST(name ## _ ## init, name, SCALAR, init) 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci#define DEFINE_SCALAR_TESTS(init) \ 2418c2ecf20Sopenharmony_ci DEFINE_SCALAR_TEST(u8, init); \ 2428c2ecf20Sopenharmony_ci DEFINE_SCALAR_TEST(u16, init); \ 2438c2ecf20Sopenharmony_ci DEFINE_SCALAR_TEST(u32, init); \ 2448c2ecf20Sopenharmony_ci DEFINE_SCALAR_TEST(u64, init); \ 2458c2ecf20Sopenharmony_ci DEFINE_TEST(char_array_ ## init, unsigned char, STRING, init) 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci#define DEFINE_STRUCT_TEST(name, init) \ 2488c2ecf20Sopenharmony_ci DEFINE_TEST(name ## _ ## init, \ 2498c2ecf20Sopenharmony_ci struct test_ ## name, STRUCT, init) 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci#define DEFINE_STRUCT_TESTS(init) \ 2528c2ecf20Sopenharmony_ci DEFINE_STRUCT_TEST(small_hole, init); \ 2538c2ecf20Sopenharmony_ci DEFINE_STRUCT_TEST(big_hole, init); \ 2548c2ecf20Sopenharmony_ci DEFINE_STRUCT_TEST(trailing_hole, init); \ 2558c2ecf20Sopenharmony_ci DEFINE_STRUCT_TEST(packed, init) 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/* These should be fully initialized all the time! */ 2588c2ecf20Sopenharmony_ciDEFINE_SCALAR_TESTS(zero); 2598c2ecf20Sopenharmony_ciDEFINE_STRUCT_TESTS(zero); 2608c2ecf20Sopenharmony_ci/* Static initialization: padding may be left uninitialized. */ 2618c2ecf20Sopenharmony_ciDEFINE_STRUCT_TESTS(static_partial); 2628c2ecf20Sopenharmony_ciDEFINE_STRUCT_TESTS(static_all); 2638c2ecf20Sopenharmony_ci/* Dynamic initialization: padding may be left uninitialized. */ 2648c2ecf20Sopenharmony_ciDEFINE_STRUCT_TESTS(dynamic_partial); 2658c2ecf20Sopenharmony_ciDEFINE_STRUCT_TESTS(dynamic_all); 2668c2ecf20Sopenharmony_ci/* Runtime initialization: padding may be left uninitialized. */ 2678c2ecf20Sopenharmony_ciDEFINE_STRUCT_TESTS(runtime_partial); 2688c2ecf20Sopenharmony_ciDEFINE_STRUCT_TESTS(runtime_all); 2698c2ecf20Sopenharmony_ci/* No initialization without compiler instrumentation. */ 2708c2ecf20Sopenharmony_ciDEFINE_SCALAR_TESTS(none); 2718c2ecf20Sopenharmony_ciDEFINE_STRUCT_TESTS(none); 2728c2ecf20Sopenharmony_ciDEFINE_TEST(user, struct test_user, STRUCT, none); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci/* 2758c2ecf20Sopenharmony_ci * Check two uses through a variable declaration outside either path, 2768c2ecf20Sopenharmony_ci * which was noticed as a special case in porting earlier stack init 2778c2ecf20Sopenharmony_ci * compiler logic. 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_cistatic int noinline __leaf_switch_none(int path, bool fill) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci switch (path) { 2828c2ecf20Sopenharmony_ci uint64_t var; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci case 1: 2858c2ecf20Sopenharmony_ci target_start = &var; 2868c2ecf20Sopenharmony_ci target_size = sizeof(var); 2878c2ecf20Sopenharmony_ci if (fill) { 2888c2ecf20Sopenharmony_ci fill_start = &var; 2898c2ecf20Sopenharmony_ci fill_size = sizeof(var); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci memset(fill_start, forced_mask | 0x55, fill_size); 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci memcpy(check_buf, target_start, target_size); 2948c2ecf20Sopenharmony_ci break; 2958c2ecf20Sopenharmony_ci case 2: 2968c2ecf20Sopenharmony_ci target_start = &var; 2978c2ecf20Sopenharmony_ci target_size = sizeof(var); 2988c2ecf20Sopenharmony_ci if (fill) { 2998c2ecf20Sopenharmony_ci fill_start = &var; 3008c2ecf20Sopenharmony_ci fill_size = sizeof(var); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci memset(fill_start, forced_mask | 0xaa, fill_size); 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci memcpy(check_buf, target_start, target_size); 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci default: 3078c2ecf20Sopenharmony_ci var = 5; 3088c2ecf20Sopenharmony_ci return var & forced_mask; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic noinline __init int leaf_switch_1_none(unsigned long sp, bool fill, 3148c2ecf20Sopenharmony_ci uint64_t *arg) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci return __leaf_switch_none(1, fill); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic noinline __init int leaf_switch_2_none(unsigned long sp, bool fill, 3208c2ecf20Sopenharmony_ci uint64_t *arg) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci return __leaf_switch_none(2, fill); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/* 3268c2ecf20Sopenharmony_ci * These are expected to fail for most configurations because neither 3278c2ecf20Sopenharmony_ci * GCC nor Clang have a way to perform initialization of variables in 3288c2ecf20Sopenharmony_ci * non-code areas (i.e. in a switch statement before the first "case"). 3298c2ecf20Sopenharmony_ci * https://bugs.llvm.org/show_bug.cgi?id=44916 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ciDEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, 1); 3328c2ecf20Sopenharmony_ciDEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, 1); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic int __init test_stackinit_init(void) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci unsigned int failures = 0; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci#define test_scalars(init) do { \ 3398c2ecf20Sopenharmony_ci failures += test_u8_ ## init (); \ 3408c2ecf20Sopenharmony_ci failures += test_u16_ ## init (); \ 3418c2ecf20Sopenharmony_ci failures += test_u32_ ## init (); \ 3428c2ecf20Sopenharmony_ci failures += test_u64_ ## init (); \ 3438c2ecf20Sopenharmony_ci failures += test_char_array_ ## init (); \ 3448c2ecf20Sopenharmony_ci } while (0) 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci#define test_structs(init) do { \ 3478c2ecf20Sopenharmony_ci failures += test_small_hole_ ## init (); \ 3488c2ecf20Sopenharmony_ci failures += test_big_hole_ ## init (); \ 3498c2ecf20Sopenharmony_ci failures += test_trailing_hole_ ## init (); \ 3508c2ecf20Sopenharmony_ci failures += test_packed_ ## init (); \ 3518c2ecf20Sopenharmony_ci } while (0) 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* These are explicitly initialized and should always pass. */ 3548c2ecf20Sopenharmony_ci test_scalars(zero); 3558c2ecf20Sopenharmony_ci test_structs(zero); 3568c2ecf20Sopenharmony_ci /* Padding here appears to be accidentally always initialized? */ 3578c2ecf20Sopenharmony_ci test_structs(dynamic_partial); 3588c2ecf20Sopenharmony_ci /* Padding initialization depends on compiler behaviors. */ 3598c2ecf20Sopenharmony_ci test_structs(static_partial); 3608c2ecf20Sopenharmony_ci test_structs(static_all); 3618c2ecf20Sopenharmony_ci test_structs(dynamic_all); 3628c2ecf20Sopenharmony_ci test_structs(runtime_partial); 3638c2ecf20Sopenharmony_ci test_structs(runtime_all); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* STRUCTLEAK_BYREF_ALL should cover everything from here down. */ 3668c2ecf20Sopenharmony_ci test_scalars(none); 3678c2ecf20Sopenharmony_ci failures += test_switch_1_none(); 3688c2ecf20Sopenharmony_ci failures += test_switch_2_none(); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* STRUCTLEAK_BYREF should cover from here down. */ 3718c2ecf20Sopenharmony_ci test_structs(none); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* STRUCTLEAK will only cover this. */ 3748c2ecf20Sopenharmony_ci failures += test_user(); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (failures == 0) 3778c2ecf20Sopenharmony_ci pr_info("all tests passed!\n"); 3788c2ecf20Sopenharmony_ci else 3798c2ecf20Sopenharmony_ci pr_err("failures: %u\n", failures); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return failures ? -EINVAL : 0; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_cimodule_init(test_stackinit_init); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic void __exit test_stackinit_exit(void) 3868c2ecf20Sopenharmony_ci{ } 3878c2ecf20Sopenharmony_cimodule_exit(test_stackinit_exit); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 390