162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Test cases for compiler-based stack variable zeroing via 462306a36Sopenharmony_ci * -ftrivial-auto-var-init={zero,pattern} or CONFIG_GCC_PLUGIN_STRUCTLEAK*. 562306a36Sopenharmony_ci * For example, see: 662306a36Sopenharmony_ci * "Running tests with kunit_tool" at Documentation/dev-tools/kunit/start.rst 762306a36Sopenharmony_ci * ./tools/testing/kunit/kunit.py run stackinit [--raw_output] \ 862306a36Sopenharmony_ci * --make_option LLVM=1 \ 962306a36Sopenharmony_ci * --kconfig_add CONFIG_INIT_STACK_ALL_ZERO=y 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <kunit/test.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/string.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* Exfiltration buffer. */ 2162306a36Sopenharmony_ci#define MAX_VAR_SIZE 128 2262306a36Sopenharmony_cistatic u8 check_buf[MAX_VAR_SIZE]; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Character array to trigger stack protector in all functions. */ 2562306a36Sopenharmony_ci#define VAR_BUFFER 32 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Volatile mask to convince compiler to copy memory with 0xff. */ 2862306a36Sopenharmony_cistatic volatile u8 forced_mask = 0xff; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* Location and size tracking to validate fill and test are colocated. */ 3162306a36Sopenharmony_cistatic void *fill_start, *target_start; 3262306a36Sopenharmony_cistatic size_t fill_size, target_size; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic bool stackinit_range_contains(char *haystack_start, size_t haystack_size, 3562306a36Sopenharmony_ci char *needle_start, size_t needle_size) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci if (needle_start >= haystack_start && 3862306a36Sopenharmony_ci needle_start + needle_size <= haystack_start + haystack_size) 3962306a36Sopenharmony_ci return true; 4062306a36Sopenharmony_ci return false; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Whether the test is expected to fail. */ 4462306a36Sopenharmony_ci#define WANT_SUCCESS 0 4562306a36Sopenharmony_ci#define XFAIL 1 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define DO_NOTHING_TYPE_SCALAR(var_type) var_type 4862306a36Sopenharmony_ci#define DO_NOTHING_TYPE_STRING(var_type) void 4962306a36Sopenharmony_ci#define DO_NOTHING_TYPE_STRUCT(var_type) void 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define DO_NOTHING_RETURN_SCALAR(ptr) *(ptr) 5262306a36Sopenharmony_ci#define DO_NOTHING_RETURN_STRING(ptr) /**/ 5362306a36Sopenharmony_ci#define DO_NOTHING_RETURN_STRUCT(ptr) /**/ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define DO_NOTHING_CALL_SCALAR(var, name) \ 5662306a36Sopenharmony_ci (var) = do_nothing_ ## name(&(var)) 5762306a36Sopenharmony_ci#define DO_NOTHING_CALL_STRING(var, name) \ 5862306a36Sopenharmony_ci do_nothing_ ## name(var) 5962306a36Sopenharmony_ci#define DO_NOTHING_CALL_STRUCT(var, name) \ 6062306a36Sopenharmony_ci do_nothing_ ## name(&(var)) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define FETCH_ARG_SCALAR(var) &var 6362306a36Sopenharmony_ci#define FETCH_ARG_STRING(var) var 6462306a36Sopenharmony_ci#define FETCH_ARG_STRUCT(var) &var 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define FILL_SIZE_STRING 16 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define INIT_CLONE_SCALAR /**/ 6962306a36Sopenharmony_ci#define INIT_CLONE_STRING [FILL_SIZE_STRING] 7062306a36Sopenharmony_ci#define INIT_CLONE_STRUCT /**/ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define ZERO_CLONE_SCALAR(zero) memset(&(zero), 0x00, sizeof(zero)) 7362306a36Sopenharmony_ci#define ZERO_CLONE_STRING(zero) memset(&(zero), 0x00, sizeof(zero)) 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * For the struct, intentionally poison padding to see if it gets 7662306a36Sopenharmony_ci * copied out in direct assignments. 7762306a36Sopenharmony_ci * */ 7862306a36Sopenharmony_ci#define ZERO_CLONE_STRUCT(zero) \ 7962306a36Sopenharmony_ci do { \ 8062306a36Sopenharmony_ci memset(&(zero), 0xFF, sizeof(zero)); \ 8162306a36Sopenharmony_ci zero.one = 0; \ 8262306a36Sopenharmony_ci zero.two = 0; \ 8362306a36Sopenharmony_ci zero.three = 0; \ 8462306a36Sopenharmony_ci zero.four = 0; \ 8562306a36Sopenharmony_ci } while (0) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define INIT_SCALAR_none(var_type) /**/ 8862306a36Sopenharmony_ci#define INIT_SCALAR_zero(var_type) = 0 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define INIT_STRING_none(var_type) [FILL_SIZE_STRING] /**/ 9162306a36Sopenharmony_ci#define INIT_STRING_zero(var_type) [FILL_SIZE_STRING] = { } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define INIT_STRUCT_none(var_type) /**/ 9462306a36Sopenharmony_ci#define INIT_STRUCT_zero(var_type) = { } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define __static_partial { .two = 0, } 9862306a36Sopenharmony_ci#define __static_all { .one = 0, \ 9962306a36Sopenharmony_ci .two = 0, \ 10062306a36Sopenharmony_ci .three = 0, \ 10162306a36Sopenharmony_ci .four = 0, \ 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci#define __dynamic_partial { .two = arg->two, } 10462306a36Sopenharmony_ci#define __dynamic_all { .one = arg->one, \ 10562306a36Sopenharmony_ci .two = arg->two, \ 10662306a36Sopenharmony_ci .three = arg->three, \ 10762306a36Sopenharmony_ci .four = arg->four, \ 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci#define __runtime_partial var.two = 0 11062306a36Sopenharmony_ci#define __runtime_all var.one = 0; \ 11162306a36Sopenharmony_ci var.two = 0; \ 11262306a36Sopenharmony_ci var.three = 0; \ 11362306a36Sopenharmony_ci var.four = 0 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define INIT_STRUCT_static_partial(var_type) \ 11662306a36Sopenharmony_ci = __static_partial 11762306a36Sopenharmony_ci#define INIT_STRUCT_static_all(var_type) \ 11862306a36Sopenharmony_ci = __static_all 11962306a36Sopenharmony_ci#define INIT_STRUCT_dynamic_partial(var_type) \ 12062306a36Sopenharmony_ci = __dynamic_partial 12162306a36Sopenharmony_ci#define INIT_STRUCT_dynamic_all(var_type) \ 12262306a36Sopenharmony_ci = __dynamic_all 12362306a36Sopenharmony_ci#define INIT_STRUCT_runtime_partial(var_type) \ 12462306a36Sopenharmony_ci ; __runtime_partial 12562306a36Sopenharmony_ci#define INIT_STRUCT_runtime_all(var_type) \ 12662306a36Sopenharmony_ci ; __runtime_all 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci#define INIT_STRUCT_assigned_static_partial(var_type) \ 12962306a36Sopenharmony_ci ; var = (var_type)__static_partial 13062306a36Sopenharmony_ci#define INIT_STRUCT_assigned_static_all(var_type) \ 13162306a36Sopenharmony_ci ; var = (var_type)__static_all 13262306a36Sopenharmony_ci#define INIT_STRUCT_assigned_dynamic_partial(var_type) \ 13362306a36Sopenharmony_ci ; var = (var_type)__dynamic_partial 13462306a36Sopenharmony_ci#define INIT_STRUCT_assigned_dynamic_all(var_type) \ 13562306a36Sopenharmony_ci ; var = (var_type)__dynamic_all 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci#define INIT_STRUCT_assigned_copy(var_type) \ 13862306a36Sopenharmony_ci ; var = *(arg) 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* 14162306a36Sopenharmony_ci * @name: unique string name for the test 14262306a36Sopenharmony_ci * @var_type: type to be tested for zeroing initialization 14362306a36Sopenharmony_ci * @which: is this a SCALAR, STRING, or STRUCT type? 14462306a36Sopenharmony_ci * @init_level: what kind of initialization is performed 14562306a36Sopenharmony_ci * @xfail: is this test expected to fail? 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci#define DEFINE_TEST_DRIVER(name, var_type, which, xfail) \ 14862306a36Sopenharmony_ci/* Returns 0 on success, 1 on failure. */ \ 14962306a36Sopenharmony_cistatic noinline void test_ ## name (struct kunit *test) \ 15062306a36Sopenharmony_ci{ \ 15162306a36Sopenharmony_ci var_type zero INIT_CLONE_ ## which; \ 15262306a36Sopenharmony_ci int ignored; \ 15362306a36Sopenharmony_ci u8 sum = 0, i; \ 15462306a36Sopenharmony_ci \ 15562306a36Sopenharmony_ci /* Notice when a new test is larger than expected. */ \ 15662306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(zero) > MAX_VAR_SIZE); \ 15762306a36Sopenharmony_ci \ 15862306a36Sopenharmony_ci /* Fill clone type with zero for per-field init. */ \ 15962306a36Sopenharmony_ci ZERO_CLONE_ ## which(zero); \ 16062306a36Sopenharmony_ci /* Clear entire check buffer for 0xFF overlap test. */ \ 16162306a36Sopenharmony_ci memset(check_buf, 0x00, sizeof(check_buf)); \ 16262306a36Sopenharmony_ci /* Fill stack with 0xFF. */ \ 16362306a36Sopenharmony_ci ignored = leaf_ ##name((unsigned long)&ignored, 1, \ 16462306a36Sopenharmony_ci FETCH_ARG_ ## which(zero)); \ 16562306a36Sopenharmony_ci /* Verify all bytes overwritten with 0xFF. */ \ 16662306a36Sopenharmony_ci for (sum = 0, i = 0; i < target_size; i++) \ 16762306a36Sopenharmony_ci sum += (check_buf[i] != 0xFF); \ 16862306a36Sopenharmony_ci KUNIT_ASSERT_EQ_MSG(test, sum, 0, \ 16962306a36Sopenharmony_ci "leaf fill was not 0xFF!?\n"); \ 17062306a36Sopenharmony_ci /* Clear entire check buffer for later bit tests. */ \ 17162306a36Sopenharmony_ci memset(check_buf, 0x00, sizeof(check_buf)); \ 17262306a36Sopenharmony_ci /* Extract stack-defined variable contents. */ \ 17362306a36Sopenharmony_ci ignored = leaf_ ##name((unsigned long)&ignored, 0, \ 17462306a36Sopenharmony_ci FETCH_ARG_ ## which(zero)); \ 17562306a36Sopenharmony_ci \ 17662306a36Sopenharmony_ci /* Validate that compiler lined up fill and target. */ \ 17762306a36Sopenharmony_ci KUNIT_ASSERT_TRUE_MSG(test, \ 17862306a36Sopenharmony_ci stackinit_range_contains(fill_start, fill_size, \ 17962306a36Sopenharmony_ci target_start, target_size), \ 18062306a36Sopenharmony_ci "stack fill missed target!? " \ 18162306a36Sopenharmony_ci "(fill %zu wide, target offset by %d)\n", \ 18262306a36Sopenharmony_ci fill_size, \ 18362306a36Sopenharmony_ci (int)((ssize_t)(uintptr_t)fill_start - \ 18462306a36Sopenharmony_ci (ssize_t)(uintptr_t)target_start)); \ 18562306a36Sopenharmony_ci \ 18662306a36Sopenharmony_ci /* Look for any bytes still 0xFF in check region. */ \ 18762306a36Sopenharmony_ci for (sum = 0, i = 0; i < target_size; i++) \ 18862306a36Sopenharmony_ci sum += (check_buf[i] == 0xFF); \ 18962306a36Sopenharmony_ci \ 19062306a36Sopenharmony_ci if (sum != 0 && xfail) \ 19162306a36Sopenharmony_ci kunit_skip(test, \ 19262306a36Sopenharmony_ci "XFAIL uninit bytes: %d\n", \ 19362306a36Sopenharmony_ci sum); \ 19462306a36Sopenharmony_ci KUNIT_ASSERT_EQ_MSG(test, sum, 0, \ 19562306a36Sopenharmony_ci "uninit bytes: %d\n", sum); \ 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci#define DEFINE_TEST(name, var_type, which, init_level, xfail) \ 19862306a36Sopenharmony_ci/* no-op to force compiler into ignoring "uninitialized" vars */\ 19962306a36Sopenharmony_cistatic noinline DO_NOTHING_TYPE_ ## which(var_type) \ 20062306a36Sopenharmony_cido_nothing_ ## name(var_type *ptr) \ 20162306a36Sopenharmony_ci{ \ 20262306a36Sopenharmony_ci /* Will always be true, but compiler doesn't know. */ \ 20362306a36Sopenharmony_ci if ((unsigned long)ptr > 0x2) \ 20462306a36Sopenharmony_ci return DO_NOTHING_RETURN_ ## which(ptr); \ 20562306a36Sopenharmony_ci else \ 20662306a36Sopenharmony_ci return DO_NOTHING_RETURN_ ## which(ptr + 1); \ 20762306a36Sopenharmony_ci} \ 20862306a36Sopenharmony_cistatic noinline int leaf_ ## name(unsigned long sp, bool fill, \ 20962306a36Sopenharmony_ci var_type *arg) \ 21062306a36Sopenharmony_ci{ \ 21162306a36Sopenharmony_ci char buf[VAR_BUFFER]; \ 21262306a36Sopenharmony_ci var_type var \ 21362306a36Sopenharmony_ci INIT_ ## which ## _ ## init_level(var_type); \ 21462306a36Sopenharmony_ci \ 21562306a36Sopenharmony_ci target_start = &var; \ 21662306a36Sopenharmony_ci target_size = sizeof(var); \ 21762306a36Sopenharmony_ci /* \ 21862306a36Sopenharmony_ci * Keep this buffer around to make sure we've got a \ 21962306a36Sopenharmony_ci * stack frame of SOME kind... \ 22062306a36Sopenharmony_ci */ \ 22162306a36Sopenharmony_ci memset(buf, (char)(sp & 0xff), sizeof(buf)); \ 22262306a36Sopenharmony_ci /* Fill variable with 0xFF. */ \ 22362306a36Sopenharmony_ci if (fill) { \ 22462306a36Sopenharmony_ci fill_start = &var; \ 22562306a36Sopenharmony_ci fill_size = sizeof(var); \ 22662306a36Sopenharmony_ci memset(fill_start, \ 22762306a36Sopenharmony_ci (char)((sp & 0xff) | forced_mask), \ 22862306a36Sopenharmony_ci fill_size); \ 22962306a36Sopenharmony_ci } \ 23062306a36Sopenharmony_ci \ 23162306a36Sopenharmony_ci /* Silence "never initialized" warnings. */ \ 23262306a36Sopenharmony_ci DO_NOTHING_CALL_ ## which(var, name); \ 23362306a36Sopenharmony_ci \ 23462306a36Sopenharmony_ci /* Exfiltrate "var". */ \ 23562306a36Sopenharmony_ci memcpy(check_buf, target_start, target_size); \ 23662306a36Sopenharmony_ci \ 23762306a36Sopenharmony_ci return (int)buf[0] | (int)buf[sizeof(buf) - 1]; \ 23862306a36Sopenharmony_ci} \ 23962306a36Sopenharmony_ciDEFINE_TEST_DRIVER(name, var_type, which, xfail) 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* Structure with no padding. */ 24262306a36Sopenharmony_cistruct test_packed { 24362306a36Sopenharmony_ci unsigned long one; 24462306a36Sopenharmony_ci unsigned long two; 24562306a36Sopenharmony_ci unsigned long three; 24662306a36Sopenharmony_ci unsigned long four; 24762306a36Sopenharmony_ci}; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/* Simple structure with padding likely to be covered by compiler. */ 25062306a36Sopenharmony_cistruct test_small_hole { 25162306a36Sopenharmony_ci size_t one; 25262306a36Sopenharmony_ci char two; 25362306a36Sopenharmony_ci /* 3 byte padding hole here. */ 25462306a36Sopenharmony_ci int three; 25562306a36Sopenharmony_ci unsigned long four; 25662306a36Sopenharmony_ci}; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/* Trigger unhandled padding in a structure. */ 25962306a36Sopenharmony_cistruct test_big_hole { 26062306a36Sopenharmony_ci u8 one; 26162306a36Sopenharmony_ci u8 two; 26262306a36Sopenharmony_ci u8 three; 26362306a36Sopenharmony_ci /* 61 byte padding hole here. */ 26462306a36Sopenharmony_ci u8 four __aligned(64); 26562306a36Sopenharmony_ci} __aligned(64); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistruct test_trailing_hole { 26862306a36Sopenharmony_ci char *one; 26962306a36Sopenharmony_ci char *two; 27062306a36Sopenharmony_ci char *three; 27162306a36Sopenharmony_ci char four; 27262306a36Sopenharmony_ci /* "sizeof(unsigned long) - 1" byte padding hole here. */ 27362306a36Sopenharmony_ci}; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci/* Test if STRUCTLEAK is clearing structs with __user fields. */ 27662306a36Sopenharmony_cistruct test_user { 27762306a36Sopenharmony_ci u8 one; 27862306a36Sopenharmony_ci unsigned long two; 27962306a36Sopenharmony_ci char __user *three; 28062306a36Sopenharmony_ci unsigned long four; 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci#define ALWAYS_PASS WANT_SUCCESS 28462306a36Sopenharmony_ci#define ALWAYS_FAIL XFAIL 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci#ifdef CONFIG_INIT_STACK_NONE 28762306a36Sopenharmony_ci# define USER_PASS XFAIL 28862306a36Sopenharmony_ci# define BYREF_PASS XFAIL 28962306a36Sopenharmony_ci# define STRONG_PASS XFAIL 29062306a36Sopenharmony_ci#elif defined(CONFIG_GCC_PLUGIN_STRUCTLEAK_USER) 29162306a36Sopenharmony_ci# define USER_PASS WANT_SUCCESS 29262306a36Sopenharmony_ci# define BYREF_PASS XFAIL 29362306a36Sopenharmony_ci# define STRONG_PASS XFAIL 29462306a36Sopenharmony_ci#elif defined(CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF) 29562306a36Sopenharmony_ci# define USER_PASS WANT_SUCCESS 29662306a36Sopenharmony_ci# define BYREF_PASS WANT_SUCCESS 29762306a36Sopenharmony_ci# define STRONG_PASS XFAIL 29862306a36Sopenharmony_ci#else 29962306a36Sopenharmony_ci# define USER_PASS WANT_SUCCESS 30062306a36Sopenharmony_ci# define BYREF_PASS WANT_SUCCESS 30162306a36Sopenharmony_ci# define STRONG_PASS WANT_SUCCESS 30262306a36Sopenharmony_ci#endif 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci#define DEFINE_SCALAR_TEST(name, init, xfail) \ 30562306a36Sopenharmony_ci DEFINE_TEST(name ## _ ## init, name, SCALAR, \ 30662306a36Sopenharmony_ci init, xfail) 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci#define DEFINE_SCALAR_TESTS(init, xfail) \ 30962306a36Sopenharmony_ci DEFINE_SCALAR_TEST(u8, init, xfail); \ 31062306a36Sopenharmony_ci DEFINE_SCALAR_TEST(u16, init, xfail); \ 31162306a36Sopenharmony_ci DEFINE_SCALAR_TEST(u32, init, xfail); \ 31262306a36Sopenharmony_ci DEFINE_SCALAR_TEST(u64, init, xfail); \ 31362306a36Sopenharmony_ci DEFINE_TEST(char_array_ ## init, unsigned char, \ 31462306a36Sopenharmony_ci STRING, init, xfail) 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci#define DEFINE_STRUCT_TEST(name, init, xfail) \ 31762306a36Sopenharmony_ci DEFINE_TEST(name ## _ ## init, \ 31862306a36Sopenharmony_ci struct test_ ## name, STRUCT, init, \ 31962306a36Sopenharmony_ci xfail) 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci#define DEFINE_STRUCT_TESTS(init, xfail) \ 32262306a36Sopenharmony_ci DEFINE_STRUCT_TEST(small_hole, init, xfail); \ 32362306a36Sopenharmony_ci DEFINE_STRUCT_TEST(big_hole, init, xfail); \ 32462306a36Sopenharmony_ci DEFINE_STRUCT_TEST(trailing_hole, init, xfail); \ 32562306a36Sopenharmony_ci DEFINE_STRUCT_TEST(packed, init, xfail) 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci#define DEFINE_STRUCT_INITIALIZER_TESTS(base, xfail) \ 32862306a36Sopenharmony_ci DEFINE_STRUCT_TESTS(base ## _ ## partial, \ 32962306a36Sopenharmony_ci xfail); \ 33062306a36Sopenharmony_ci DEFINE_STRUCT_TESTS(base ## _ ## all, xfail) 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci/* These should be fully initialized all the time! */ 33362306a36Sopenharmony_ciDEFINE_SCALAR_TESTS(zero, ALWAYS_PASS); 33462306a36Sopenharmony_ciDEFINE_STRUCT_TESTS(zero, ALWAYS_PASS); 33562306a36Sopenharmony_ci/* Struct initializers: padding may be left uninitialized. */ 33662306a36Sopenharmony_ciDEFINE_STRUCT_INITIALIZER_TESTS(static, STRONG_PASS); 33762306a36Sopenharmony_ciDEFINE_STRUCT_INITIALIZER_TESTS(dynamic, STRONG_PASS); 33862306a36Sopenharmony_ciDEFINE_STRUCT_INITIALIZER_TESTS(runtime, STRONG_PASS); 33962306a36Sopenharmony_ciDEFINE_STRUCT_INITIALIZER_TESTS(assigned_static, STRONG_PASS); 34062306a36Sopenharmony_ciDEFINE_STRUCT_INITIALIZER_TESTS(assigned_dynamic, STRONG_PASS); 34162306a36Sopenharmony_ciDEFINE_STRUCT_TESTS(assigned_copy, ALWAYS_FAIL); 34262306a36Sopenharmony_ci/* No initialization without compiler instrumentation. */ 34362306a36Sopenharmony_ciDEFINE_SCALAR_TESTS(none, STRONG_PASS); 34462306a36Sopenharmony_ciDEFINE_STRUCT_TESTS(none, BYREF_PASS); 34562306a36Sopenharmony_ci/* Initialization of members with __user attribute. */ 34662306a36Sopenharmony_ciDEFINE_TEST(user, struct test_user, STRUCT, none, USER_PASS); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/* 34962306a36Sopenharmony_ci * Check two uses through a variable declaration outside either path, 35062306a36Sopenharmony_ci * which was noticed as a special case in porting earlier stack init 35162306a36Sopenharmony_ci * compiler logic. 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_cistatic int noinline __leaf_switch_none(int path, bool fill) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci switch (path) { 35662306a36Sopenharmony_ci /* 35762306a36Sopenharmony_ci * This is intentionally unreachable. To silence the 35862306a36Sopenharmony_ci * warning, build with -Wno-switch-unreachable 35962306a36Sopenharmony_ci */ 36062306a36Sopenharmony_ci uint64_t var[10]; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci case 1: 36362306a36Sopenharmony_ci target_start = &var; 36462306a36Sopenharmony_ci target_size = sizeof(var); 36562306a36Sopenharmony_ci if (fill) { 36662306a36Sopenharmony_ci fill_start = &var; 36762306a36Sopenharmony_ci fill_size = sizeof(var); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci memset(fill_start, forced_mask | 0x55, fill_size); 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci memcpy(check_buf, target_start, target_size); 37262306a36Sopenharmony_ci break; 37362306a36Sopenharmony_ci case 2: 37462306a36Sopenharmony_ci target_start = &var; 37562306a36Sopenharmony_ci target_size = sizeof(var); 37662306a36Sopenharmony_ci if (fill) { 37762306a36Sopenharmony_ci fill_start = &var; 37862306a36Sopenharmony_ci fill_size = sizeof(var); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci memset(fill_start, forced_mask | 0xaa, fill_size); 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci memcpy(check_buf, target_start, target_size); 38362306a36Sopenharmony_ci break; 38462306a36Sopenharmony_ci default: 38562306a36Sopenharmony_ci var[1] = 5; 38662306a36Sopenharmony_ci return var[1] & forced_mask; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci return 0; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic noinline int leaf_switch_1_none(unsigned long sp, bool fill, 39262306a36Sopenharmony_ci uint64_t *arg) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci return __leaf_switch_none(1, fill); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic noinline int leaf_switch_2_none(unsigned long sp, bool fill, 39862306a36Sopenharmony_ci uint64_t *arg) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci return __leaf_switch_none(2, fill); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/* 40462306a36Sopenharmony_ci * These are expected to fail for most configurations because neither 40562306a36Sopenharmony_ci * GCC nor Clang have a way to perform initialization of variables in 40662306a36Sopenharmony_ci * non-code areas (i.e. in a switch statement before the first "case"). 40762306a36Sopenharmony_ci * https://bugs.llvm.org/show_bug.cgi?id=44916 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ciDEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, ALWAYS_FAIL); 41062306a36Sopenharmony_ciDEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, ALWAYS_FAIL); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci#define KUNIT_test_scalars(init) \ 41362306a36Sopenharmony_ci KUNIT_CASE(test_u8_ ## init), \ 41462306a36Sopenharmony_ci KUNIT_CASE(test_u16_ ## init), \ 41562306a36Sopenharmony_ci KUNIT_CASE(test_u32_ ## init), \ 41662306a36Sopenharmony_ci KUNIT_CASE(test_u64_ ## init), \ 41762306a36Sopenharmony_ci KUNIT_CASE(test_char_array_ ## init) 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci#define KUNIT_test_structs(init) \ 42062306a36Sopenharmony_ci KUNIT_CASE(test_small_hole_ ## init), \ 42162306a36Sopenharmony_ci KUNIT_CASE(test_big_hole_ ## init), \ 42262306a36Sopenharmony_ci KUNIT_CASE(test_trailing_hole_ ## init),\ 42362306a36Sopenharmony_ci KUNIT_CASE(test_packed_ ## init) \ 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic struct kunit_case stackinit_test_cases[] = { 42662306a36Sopenharmony_ci /* These are explicitly initialized and should always pass. */ 42762306a36Sopenharmony_ci KUNIT_test_scalars(zero), 42862306a36Sopenharmony_ci KUNIT_test_structs(zero), 42962306a36Sopenharmony_ci /* Padding here appears to be accidentally always initialized? */ 43062306a36Sopenharmony_ci KUNIT_test_structs(dynamic_partial), 43162306a36Sopenharmony_ci KUNIT_test_structs(assigned_dynamic_partial), 43262306a36Sopenharmony_ci /* Padding initialization depends on compiler behaviors. */ 43362306a36Sopenharmony_ci KUNIT_test_structs(static_partial), 43462306a36Sopenharmony_ci KUNIT_test_structs(static_all), 43562306a36Sopenharmony_ci KUNIT_test_structs(dynamic_all), 43662306a36Sopenharmony_ci KUNIT_test_structs(runtime_partial), 43762306a36Sopenharmony_ci KUNIT_test_structs(runtime_all), 43862306a36Sopenharmony_ci KUNIT_test_structs(assigned_static_partial), 43962306a36Sopenharmony_ci KUNIT_test_structs(assigned_static_all), 44062306a36Sopenharmony_ci KUNIT_test_structs(assigned_dynamic_all), 44162306a36Sopenharmony_ci /* Everything fails this since it effectively performs a memcpy(). */ 44262306a36Sopenharmony_ci KUNIT_test_structs(assigned_copy), 44362306a36Sopenharmony_ci /* STRUCTLEAK_BYREF_ALL should cover everything from here down. */ 44462306a36Sopenharmony_ci KUNIT_test_scalars(none), 44562306a36Sopenharmony_ci KUNIT_CASE(test_switch_1_none), 44662306a36Sopenharmony_ci KUNIT_CASE(test_switch_2_none), 44762306a36Sopenharmony_ci /* STRUCTLEAK_BYREF should cover from here down. */ 44862306a36Sopenharmony_ci KUNIT_test_structs(none), 44962306a36Sopenharmony_ci /* STRUCTLEAK will only cover this. */ 45062306a36Sopenharmony_ci KUNIT_CASE(test_user), 45162306a36Sopenharmony_ci {} 45262306a36Sopenharmony_ci}; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic struct kunit_suite stackinit_test_suite = { 45562306a36Sopenharmony_ci .name = "stackinit", 45662306a36Sopenharmony_ci .test_cases = stackinit_test_cases, 45762306a36Sopenharmony_ci}; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cikunit_test_suites(&stackinit_test_suite); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 462