From a82adfd5c7cb4b8bb37ef439aed954f9972bb618 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 12 Apr 2021 19:56:54 -0700 Subject: [PATCH 1/5] hardening: Introduce CONFIG_ZERO_CALL_USED_REGS When CONFIG_ZERO_CALL_USED_REGS is enabled, build the kernel with "-fzero-call-used-regs=used-gpr" (in GCC 11). This option will zero any caller-used register contents just before returning from a function, ensuring that temporary values are not leaked beyond the function boundary. This means that register contents are less likely to be available for side channel attacks and information exposures. Additionally this helps reduce the number of useful ROP gadgets in the kernel image by about 20%: $ ROPgadget.py --nosys --nojop --binary vmlinux.stock | tail -n1 Unique gadgets found: 337245 $ ROPgadget.py --nosys --nojop --binary vmlinux.zero-call-regs | tail -n1 Unique gadgets found: 267175 and more notably removes simple "write-what-where" gadgets: $ ROPgadget.py --ropchain --binary vmlinux.stock | sed -n '/Step 1/,/Step 2/p' - Step 1 -- Write-what-where gadgets [+] Gadget found: 0xffffffff8102d76c mov qword ptr [rsi], rdx ; ret [+] Gadget found: 0xffffffff81000cf5 pop rsi ; ret [+] Gadget found: 0xffffffff8104d7c8 pop rdx ; ret [-] Can't find the 'xor rdx, rdx' gadget. Try with another 'mov [reg], reg' [+] Gadget found: 0xffffffff814c2b4c mov qword ptr [rsi], rdi ; ret [+] Gadget found: 0xffffffff81000cf5 pop rsi ; ret [+] Gadget found: 0xffffffff81001e51 pop rdi ; ret [-] Can't find the 'xor rdi, rdi' gadget. Try with another 'mov [reg], reg' [+] Gadget found: 0xffffffff81540d61 mov qword ptr [rsi], rdi ; pop rbx ; pop rbp ; ret [+] Gadget found: 0xffffffff81000cf5 pop rsi ; ret [+] Gadget found: 0xffffffff81001e51 pop rdi ; ret [-] Can't find the 'xor rdi, rdi' gadget. Try with another 'mov [reg], reg' [+] Gadget found: 0xffffffff8105341e mov qword ptr [rsi], rax ; ret [+] Gadget found: 0xffffffff81000cf5 pop rsi ; ret [+] Gadget found: 0xffffffff81029a11 pop rax ; ret [+] Gadget found: 0xffffffff811f1c3b xor rax, rax ; ret - Step 2 -- Init syscall number gadgets $ ROPgadget.py --ropchain --binary vmlinux.zero* | sed -n '/Step 1/,/Step 2/p' - Step 1 -- Write-what-where gadgets [-] Can't find the 'mov qword ptr [r64], r64' gadget For an x86_64 parallel build tests, this has a less than 1% performance impact, and grows the image size less than 1%: $ size vmlinux.stock vmlinux.zero-call-regs text data bss dec hex filename 22437676 8559152 14127340 45124168 2b08a48 vmlinux.stock 22453184 8563248 14110956 45127388 2b096dc vmlinux.zero-call-regs Impact for other architectures may vary. For example, arm64 sees a 5.5% image size growth, mainly due to needing to always clear x16 and x17: https://lore.kernel.org/lkml/20210510134503.GA88495@C02TD0UTHF1T.local/ Signed-off-by: Kees Cook --- Makefile | 5 +++++ security/Kconfig.hardening | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Makefile b/Makefile index e4f5895badb5..5def36c7882a 100644 --- a/Makefile +++ b/Makefile @@ -842,6 +842,11 @@ endif # for the randomize_kstack_offset feature. Disable it for all compilers. KBUILD_CFLAGS += $(call cc-option, -fno-stack-clash-protection) +# Clear used registers at func exit (to reduce data lifetime and ROP gadgets). +ifdef CONFIG_ZERO_CALL_USED_REGS +KBUILD_CFLAGS += -fzero-call-used-regs=used-gpr +endif + DEBUG_CFLAGS := # Workaround for GCC versions < 5.0 diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening index a56c36470cb1..023aea5e117c 100644 --- a/security/Kconfig.hardening +++ b/security/Kconfig.hardening @@ -217,6 +217,25 @@ config INIT_ON_FREE_DEFAULT_ON touching "cold" memory areas. Most cases see 3-5% impact. Some synthetic workloads have measured as high as 8%. +config CC_HAS_ZERO_CALL_USED_REGS + def_bool $(cc-option,-fzero-call-used-regs=used-gpr) + +config ZERO_CALL_USED_REGS + bool "Enable register zeroing on function exit" + depends on CC_HAS_ZERO_CALL_USED_REGS + help + At the end of functions, always zero any caller-used register + contents. This helps ensure that temporary values are not + leaked beyond the function boundary. This means that register + contents are less likely to be available for side channels + and information exposures. Additionally, this helps reduce the + number of useful ROP gadgets by about 20% (and removes compiler + generated "write-what-where" gadgets) in the resulting kernel + image. This has a less than 1% performance impact on most + workloads. Image size growth depends on architecture, and should + be evaluated for suitability. For example, x86_64 grows by less + than 1%, and arm64 grows by about 5%. + endmenu endmenu From dcb7c0b9461c2a30f6616262736daac6f01ecb09 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 20 Jul 2021 14:54:17 -0700 Subject: [PATCH 2/5] hardening: Clarify Kconfig text for auto-var-init Clarify the details around the automatic variable initialization modes available. Specifically this details the values used for pattern init and expands on the rationale for zero init safety. Additionally makes zero init the default when available. Cc: glider@google.com Cc: Nathan Chancellor Cc: Nick Desaulniers Cc: linux-security-module@vger.kernel.org Cc: clang-built-linux@googlegroups.com Signed-off-by: Kees Cook Acked-by: Gustavo A. R. Silva --- security/Kconfig.hardening | 50 +++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening index 023aea5e117c..90cbaff86e13 100644 --- a/security/Kconfig.hardening +++ b/security/Kconfig.hardening @@ -29,6 +29,7 @@ choice prompt "Initialize kernel stack variables at function entry" default GCC_PLUGIN_STRUCTLEAK_BYREF_ALL if COMPILE_TEST && GCC_PLUGINS default INIT_STACK_ALL_PATTERN if COMPILE_TEST && CC_HAS_AUTO_VAR_INIT_PATTERN + default INIT_STACK_ALL_ZERO if CC_HAS_AUTO_VAR_INIT_PATTERN default INIT_STACK_NONE help This option enables initialization of stack variables at @@ -39,11 +40,11 @@ choice syscalls. This chooses the level of coverage over classes of potentially - uninitialized variables. The selected class will be + uninitialized variables. The selected class of variable will be initialized before use in a function. config INIT_STACK_NONE - bool "no automatic initialization (weakest)" + bool "no automatic stack variable initialization (weakest)" help Disable automatic stack variable initialization. This leaves the kernel vulnerable to the standard @@ -80,7 +81,7 @@ choice and is disallowed. config GCC_PLUGIN_STRUCTLEAK_BYREF_ALL - bool "zero-init anything passed by reference (very strong)" + bool "zero-init everything passed by reference (very strong)" depends on GCC_PLUGINS depends on !(KASAN && KASAN_STACK) select GCC_PLUGIN_STRUCTLEAK @@ -91,33 +92,44 @@ choice of uninitialized stack variable exploits and information exposures. + As a side-effect, this keeps a lot of variables on the + stack that can otherwise be optimized out, so combining + this with CONFIG_KASAN_STACK can lead to a stack overflow + and is disallowed. + config INIT_STACK_ALL_PATTERN - bool "0xAA-init everything on the stack (strongest)" + bool "pattern-init everything (strongest)" depends on CC_HAS_AUTO_VAR_INIT_PATTERN help - Initializes everything on the stack with a 0xAA - pattern. This is intended to eliminate all classes - of uninitialized stack variable exploits and information - exposures, even variables that were warned to have been - left uninitialized. + Initializes everything on the stack (including padding) + with a specific debug value. This is intended to eliminate + all classes of uninitialized stack variable exploits and + information exposures, even variables that were warned about + having been left uninitialized. Pattern initialization is known to provoke many existing bugs related to uninitialized locals, e.g. pointers receive - non-NULL values, buffer sizes and indices are very big. + non-NULL values, buffer sizes and indices are very big. The + pattern is situation-specific; Clang on 64-bit uses 0xAA + repeating for all types and padding except float and double + which use 0xFF repeating (-NaN). Clang on 32-bit uses 0xFF + repeating for all types and padding. config INIT_STACK_ALL_ZERO - bool "zero-init everything on the stack (strongest and safest)" + bool "zero-init everything (strongest and safest)" depends on CC_HAS_AUTO_VAR_INIT_ZERO help - Initializes everything on the stack with a zero - value. This is intended to eliminate all classes - of uninitialized stack variable exploits and information - exposures, even variables that were warned to have been - left uninitialized. + Initializes everything on the stack (including padding) + with a zero value. This is intended to eliminate all + classes of uninitialized stack variable exploits and + information exposures, even variables that were warned + about having been left uninitialized. - Zero initialization provides safe defaults for strings, - pointers, indices and sizes, and is therefore - more suitable as a security mitigation measure. + Zero initialization provides safe defaults for strings + (immediately NUL-terminated), pointers (NULL), indices + (index 0), and sizes (0 length), so it is therefore more + suitable as a production security mitigation than pattern + initialization. endchoice From f9398f15605a50110bf570aaa361163a85113dd1 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 23 Jul 2021 15:19:31 -0700 Subject: [PATCH 3/5] lib/test_stackinit: Fix static initializer test The static initializer test got accidentally converted to a dynamic initializer. Fix this and retain the giant padding hole without using an aligned struct member. Fixes: 50ceaa95ea09 ("lib: Introduce test_stackinit module") Cc: Ard Biesheuvel Cc: stable@vger.kernel.org Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20210723221933.3431999-2-keescook@chromium.org --- lib/test_stackinit.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/lib/test_stackinit.c b/lib/test_stackinit.c index f93b1e145ada..16b1d3a3a497 100644 --- a/lib/test_stackinit.c +++ b/lib/test_stackinit.c @@ -67,10 +67,10 @@ static bool range_contains(char *haystack_start, size_t haystack_size, #define INIT_STRUCT_none /**/ #define INIT_STRUCT_zero = { } #define INIT_STRUCT_static_partial = { .two = 0, } -#define INIT_STRUCT_static_all = { .one = arg->one, \ - .two = arg->two, \ - .three = arg->three, \ - .four = arg->four, \ +#define INIT_STRUCT_static_all = { .one = 0, \ + .two = 0, \ + .three = 0, \ + .four = 0, \ } #define INIT_STRUCT_dynamic_partial = { .two = arg->two, } #define INIT_STRUCT_dynamic_all = { .one = arg->one, \ @@ -84,8 +84,7 @@ static bool range_contains(char *haystack_start, size_t haystack_size, var.one = 0; \ var.two = 0; \ var.three = 0; \ - memset(&var.four, 0, \ - sizeof(var.four)) + var.four = 0 /* * @name: unique string name for the test @@ -210,18 +209,13 @@ struct test_small_hole { unsigned long four; }; -/* Try to trigger unhandled padding in a structure. */ -struct test_aligned { - u32 internal1; - u64 internal2; -} __aligned(64); - +/* Trigger unhandled padding in a structure. */ struct test_big_hole { u8 one; u8 two; u8 three; /* 61 byte padding hole here. */ - struct test_aligned four; + u8 four __aligned(64); } __aligned(64); struct test_trailing_hole { From 1e2cd3084fff19e12bdf3c83ac1a8d64ef11aa63 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 23 Jul 2021 15:19:32 -0700 Subject: [PATCH 4/5] lib/test_stackinit: Allow building stand-alone Especially now that GCC is developing the -ftrivial-auto-var-init option[1], it's helpful to have a stand-alone userspace test for stack variable initialization. Relicense to GPLv2+ (I am the only author), provide stand-alone kernel macro stubs, and update comments for clarity. [1] https://gcc.gnu.org/pipermail/gcc-patches/2021-July/575198.html Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20210723221933.3431999-3-keescook@chromium.org --- lib/test_stackinit.c | 72 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/lib/test_stackinit.c b/lib/test_stackinit.c index 16b1d3a3a497..70bcc1e5112c 100644 --- a/lib/test_stackinit.c +++ b/lib/test_stackinit.c @@ -1,8 +1,13 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Test cases for compiler-based stack variable zeroing via future - * compiler flags or CONFIG_GCC_PLUGIN_STRUCTLEAK*. + * Test cases for compiler-based stack variable zeroing via + * -ftrivial-auto-var-init={zero,pattern} or CONFIG_GCC_PLUGIN_STRUCTLEAK*. + * + * External build example: + * clang -O2 -Wall -ftrivial-auto-var-init=pattern \ + * -o test_stackinit test_stackinit.c */ +#ifdef __KERNEL__ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include @@ -10,6 +15,63 @@ #include #include +#else + +/* Userspace headers. */ +#include +#include +#include +#include +#include +#include + +/* Linux kernel-ism stubs for stand-alone userspace build. */ +#define KBUILD_MODNAME "stackinit" +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define pr_err(fmt, ...) fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__) +#define pr_warn(fmt, ...) fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__) +#define pr_info(fmt, ...) fprintf(stdout, pr_fmt(fmt), ##__VA_ARGS__) +#define __init /**/ +#define __exit /**/ +#define __user /**/ +#define noinline __attribute__((__noinline__)) +#define __aligned(x) __attribute__((__aligned__(x))) +#ifdef __clang__ +# define __compiletime_error(message) /**/ +#else +# define __compiletime_error(message) __attribute__((__error__(message))) +#endif +#define __compiletime_assert(condition, msg, prefix, suffix) \ + do { \ + extern void prefix ## suffix(void) __compiletime_error(msg); \ + if (!(condition)) \ + prefix ## suffix(); \ + } while (0) +#define _compiletime_assert(condition, msg, prefix, suffix) \ + __compiletime_assert(condition, msg, prefix, suffix) +#define compiletime_assert(condition, msg) \ + _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__) +#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg) +#define BUILD_BUG_ON(condition) \ + BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition) +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +#define module_init(func) static int (*do_init)(void) = func +#define module_exit(func) static void (*do_exit)(void) = func +#define MODULE_LICENSE(str) int main(void) { \ + int rc; \ + /* License: str */ \ + rc = do_init(); \ + if (rc == 0) \ + do_exit(); \ + return rc; \ + } + +#endif /* __KERNEL__ */ + /* Exfiltration buffer. */ #define MAX_VAR_SIZE 128 static u8 check_buf[MAX_VAR_SIZE]; @@ -279,6 +341,10 @@ DEFINE_TEST(user, struct test_user, STRUCT, none); static int noinline __leaf_switch_none(int path, bool fill) { switch (path) { + /* + * This is intentionally unreachable. To silence the + * warning, build with -Wno-switch-unreachable + */ uint64_t var; case 1: From a8fc576d4af2f23a87a586424252df97f0ad0b06 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 23 Jul 2021 15:19:33 -0700 Subject: [PATCH 5/5] lib/test_stackinit: Add assigned initializers Add whole-variable assignments of cast static initializers. These appear to currently behave like the direct initializers, but best to check them too. For example: struct test_big_hole var; var = (struct test_big_hole){ .one = arg->one, .two= arg->two, .three = arg->three, .four = arg->four }; Additionally adds a test for whole-object assignment, which is expected to fail since it usually falls back to a memcpy(): var = *arg; Suggested-by: Arnd Bergmann Link: https://lore.kernel.org/lkml/CAK8P3a20SEoYCrp3jOK32oZc9OkiPv+1KTjNZ2GxLbHpY4WexQ@mail.gmail.com Cc: Ard Biesheuvel Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20210723221933.3431999-4-keescook@chromium.org --- lib/test_stackinit.c | 169 +++++++++++++++++++++++++++++-------------- 1 file changed, 114 insertions(+), 55 deletions(-) diff --git a/lib/test_stackinit.c b/lib/test_stackinit.c index 70bcc1e5112c..a3c74e6a21ff 100644 --- a/lib/test_stackinit.c +++ b/lib/test_stackinit.c @@ -95,6 +95,10 @@ static bool range_contains(char *haystack_start, size_t haystack_size, return false; } +/* Whether the test is expected to fail. */ +#define WANT_SUCCESS 0 +#define XFAIL 1 + #define DO_NOTHING_TYPE_SCALAR(var_type) var_type #define DO_NOTHING_TYPE_STRING(var_type) void #define DO_NOTHING_TYPE_STRUCT(var_type) void @@ -120,34 +124,74 @@ static bool range_contains(char *haystack_start, size_t haystack_size, #define INIT_CLONE_STRING [FILL_SIZE_STRING] #define INIT_CLONE_STRUCT /**/ -#define INIT_SCALAR_none /**/ -#define INIT_SCALAR_zero = 0 +#define ZERO_CLONE_SCALAR(zero) memset(&(zero), 0x00, sizeof(zero)) +#define ZERO_CLONE_STRING(zero) memset(&(zero), 0x00, sizeof(zero)) +/* + * For the struct, intentionally poison padding to see if it gets + * copied out in direct assignments. + * */ +#define ZERO_CLONE_STRUCT(zero) \ + do { \ + memset(&(zero), 0xFF, sizeof(zero)); \ + zero.one = 0; \ + zero.two = 0; \ + zero.three = 0; \ + zero.four = 0; \ + } while (0) -#define INIT_STRING_none [FILL_SIZE_STRING] /**/ -#define INIT_STRING_zero [FILL_SIZE_STRING] = { } +#define INIT_SCALAR_none(var_type) /**/ +#define INIT_SCALAR_zero(var_type) = 0 -#define INIT_STRUCT_none /**/ -#define INIT_STRUCT_zero = { } -#define INIT_STRUCT_static_partial = { .two = 0, } -#define INIT_STRUCT_static_all = { .one = 0, \ - .two = 0, \ - .three = 0, \ - .four = 0, \ +#define INIT_STRING_none(var_type) [FILL_SIZE_STRING] /**/ +#define INIT_STRING_zero(var_type) [FILL_SIZE_STRING] = { } + +#define INIT_STRUCT_none(var_type) /**/ +#define INIT_STRUCT_zero(var_type) = { } + + +#define __static_partial { .two = 0, } +#define __static_all { .one = 0, \ + .two = 0, \ + .three = 0, \ + .four = 0, \ } -#define INIT_STRUCT_dynamic_partial = { .two = arg->two, } -#define INIT_STRUCT_dynamic_all = { .one = arg->one, \ - .two = arg->two, \ - .three = arg->three, \ - .four = arg->four, \ +#define __dynamic_partial { .two = arg->two, } +#define __dynamic_all { .one = arg->one, \ + .two = arg->two, \ + .three = arg->three, \ + .four = arg->four, \ } -#define INIT_STRUCT_runtime_partial ; \ - var.two = 0 -#define INIT_STRUCT_runtime_all ; \ - var.one = 0; \ +#define __runtime_partial var.two = 0 +#define __runtime_all var.one = 0; \ var.two = 0; \ var.three = 0; \ var.four = 0 +#define INIT_STRUCT_static_partial(var_type) \ + = __static_partial +#define INIT_STRUCT_static_all(var_type) \ + = __static_all +#define INIT_STRUCT_dynamic_partial(var_type) \ + = __dynamic_partial +#define INIT_STRUCT_dynamic_all(var_type) \ + = __dynamic_all +#define INIT_STRUCT_runtime_partial(var_type) \ + ; __runtime_partial +#define INIT_STRUCT_runtime_all(var_type) \ + ; __runtime_all + +#define INIT_STRUCT_assigned_static_partial(var_type) \ + ; var = (var_type)__static_partial +#define INIT_STRUCT_assigned_static_all(var_type) \ + ; var = (var_type)__static_all +#define INIT_STRUCT_assigned_dynamic_partial(var_type) \ + ; var = (var_type)__dynamic_partial +#define INIT_STRUCT_assigned_dynamic_all(var_type) \ + ; var = (var_type)__dynamic_all + +#define INIT_STRUCT_assigned_copy(var_type) \ + ; var = *(arg) + /* * @name: unique string name for the test * @var_type: type to be tested for zeroing initialization @@ -167,7 +211,7 @@ static noinline __init int test_ ## name (void) \ BUILD_BUG_ON(sizeof(zero) > MAX_VAR_SIZE); \ \ /* Fill clone type with zero for per-field init. */ \ - memset(&zero, 0x00, sizeof(zero)); \ + ZERO_CLONE_ ## which(zero); \ /* Clear entire check buffer for 0xFF overlap test. */ \ memset(check_buf, 0x00, sizeof(check_buf)); \ /* Fill stack with 0xFF. */ \ @@ -210,7 +254,7 @@ static noinline __init int test_ ## name (void) \ return (xfail) ? 0 : 1; \ } \ } -#define DEFINE_TEST(name, var_type, which, init_level) \ +#define DEFINE_TEST(name, var_type, which, init_level, xfail) \ /* no-op to force compiler into ignoring "uninitialized" vars */\ static noinline __init DO_NOTHING_TYPE_ ## which(var_type) \ do_nothing_ ## name(var_type *ptr) \ @@ -226,7 +270,8 @@ static noinline __init int leaf_ ## name(unsigned long sp, \ var_type *arg) \ { \ char buf[VAR_BUFFER]; \ - var_type var INIT_ ## which ## _ ## init_level; \ + var_type var \ + INIT_ ## which ## _ ## init_level(var_type); \ \ target_start = &var; \ target_size = sizeof(var); \ @@ -252,7 +297,7 @@ static noinline __init int leaf_ ## name(unsigned long sp, \ \ return (int)buf[0] | (int)buf[sizeof(buf) - 1]; \ } \ -DEFINE_TEST_DRIVER(name, var_type, which, 0) +DEFINE_TEST_DRIVER(name, var_type, which, xfail) /* Structure with no padding. */ struct test_packed { @@ -296,42 +341,50 @@ struct test_user { unsigned long four; }; -#define DEFINE_SCALAR_TEST(name, init) \ - DEFINE_TEST(name ## _ ## init, name, SCALAR, init) +#define DEFINE_SCALAR_TEST(name, init, xfail) \ + DEFINE_TEST(name ## _ ## init, name, SCALAR, \ + init, xfail) -#define DEFINE_SCALAR_TESTS(init) \ - DEFINE_SCALAR_TEST(u8, init); \ - DEFINE_SCALAR_TEST(u16, init); \ - DEFINE_SCALAR_TEST(u32, init); \ - DEFINE_SCALAR_TEST(u64, init); \ - DEFINE_TEST(char_array_ ## init, unsigned char, STRING, init) +#define DEFINE_SCALAR_TESTS(init, xfail) \ + DEFINE_SCALAR_TEST(u8, init, xfail); \ + DEFINE_SCALAR_TEST(u16, init, xfail); \ + DEFINE_SCALAR_TEST(u32, init, xfail); \ + DEFINE_SCALAR_TEST(u64, init, xfail); \ + DEFINE_TEST(char_array_ ## init, unsigned char, \ + STRING, init, xfail) -#define DEFINE_STRUCT_TEST(name, init) \ +#define DEFINE_STRUCT_TEST(name, init, xfail) \ DEFINE_TEST(name ## _ ## init, \ - struct test_ ## name, STRUCT, init) + struct test_ ## name, STRUCT, init, \ + xfail) -#define DEFINE_STRUCT_TESTS(init) \ - DEFINE_STRUCT_TEST(small_hole, init); \ - DEFINE_STRUCT_TEST(big_hole, init); \ - DEFINE_STRUCT_TEST(trailing_hole, init); \ - DEFINE_STRUCT_TEST(packed, init) +#define DEFINE_STRUCT_TESTS(init, xfail) \ + DEFINE_STRUCT_TEST(small_hole, init, xfail); \ + DEFINE_STRUCT_TEST(big_hole, init, xfail); \ + DEFINE_STRUCT_TEST(trailing_hole, init, xfail); \ + DEFINE_STRUCT_TEST(packed, init, xfail) + +#define DEFINE_STRUCT_INITIALIZER_TESTS(base) \ + DEFINE_STRUCT_TESTS(base ## _ ## partial, \ + WANT_SUCCESS); \ + DEFINE_STRUCT_TESTS(base ## _ ## all, \ + WANT_SUCCESS) /* These should be fully initialized all the time! */ -DEFINE_SCALAR_TESTS(zero); -DEFINE_STRUCT_TESTS(zero); -/* Static initialization: padding may be left uninitialized. */ -DEFINE_STRUCT_TESTS(static_partial); -DEFINE_STRUCT_TESTS(static_all); -/* Dynamic initialization: padding may be left uninitialized. */ -DEFINE_STRUCT_TESTS(dynamic_partial); -DEFINE_STRUCT_TESTS(dynamic_all); -/* Runtime initialization: padding may be left uninitialized. */ -DEFINE_STRUCT_TESTS(runtime_partial); -DEFINE_STRUCT_TESTS(runtime_all); +DEFINE_SCALAR_TESTS(zero, WANT_SUCCESS); +DEFINE_STRUCT_TESTS(zero, WANT_SUCCESS); +/* Struct initializers: padding may be left uninitialized. */ +DEFINE_STRUCT_INITIALIZER_TESTS(static); +DEFINE_STRUCT_INITIALIZER_TESTS(dynamic); +DEFINE_STRUCT_INITIALIZER_TESTS(runtime); +DEFINE_STRUCT_INITIALIZER_TESTS(assigned_static); +DEFINE_STRUCT_INITIALIZER_TESTS(assigned_dynamic); +DEFINE_STRUCT_TESTS(assigned_copy, XFAIL); /* No initialization without compiler instrumentation. */ -DEFINE_SCALAR_TESTS(none); -DEFINE_STRUCT_TESTS(none); -DEFINE_TEST(user, struct test_user, STRUCT, none); +DEFINE_SCALAR_TESTS(none, WANT_SUCCESS); +DEFINE_STRUCT_TESTS(none, WANT_SUCCESS); +/* Initialization of members with __user attribute. */ +DEFINE_TEST(user, struct test_user, STRUCT, none, WANT_SUCCESS); /* * Check two uses through a variable declaration outside either path, @@ -394,8 +447,8 @@ static noinline __init int leaf_switch_2_none(unsigned long sp, bool fill, * non-code areas (i.e. in a switch statement before the first "case"). * https://bugs.llvm.org/show_bug.cgi?id=44916 */ -DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, 1); -DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, 1); +DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, XFAIL); +DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, XFAIL); static int __init test_stackinit_init(void) { @@ -421,12 +474,18 @@ static int __init test_stackinit_init(void) test_structs(zero); /* Padding here appears to be accidentally always initialized? */ test_structs(dynamic_partial); + test_structs(assigned_dynamic_partial); /* Padding initialization depends on compiler behaviors. */ test_structs(static_partial); test_structs(static_all); test_structs(dynamic_all); test_structs(runtime_partial); test_structs(runtime_all); + test_structs(assigned_static_partial); + test_structs(assigned_static_all); + test_structs(assigned_dynamic_all); + /* Everything fails this since it effectively performs a memcpy(). */ + test_structs(assigned_copy); /* STRUCTLEAK_BYREF_ALL should cover everything from here down. */ test_scalars(none);