mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] kunit/stackinit: Cover declarations bypassed by goto and switch
@ 2026-10-02 19:12 Justin Stitt
  2026-10-05  9:41 ` Kees Cook
  0 siblings, 1 reply; 2+ messages in thread
From: Justin Stitt @ 2026-10-02 19:12 UTC (permalink / raw)
  To: Nathan Chancellor, Kees Cook
  Cc: linux-hardening, linux-kernel, llvm, linux-kselftest, Justin Stitt

From: Justin Stitt <justinstitt@google.com>

Under -ftrivial-auto-var-init= the initialization is emitted where a
variable is declared. A switch dispatch that reaches a case label below
the declaration, or a goto that jumps past it, never reaches that point,
so the variable was left uninitialized.

Commit 9cf016e6b49b ("lib: test_stackinit.c: XFAIL switch variable init
tests") marked test_switch_1_none and test_switch_2_none as expected
failures, noting the test should be retained "so that we can evaluate
compiler fixes". Clang 24 emits the initialization on the jumps that
bypass the declaration, so make the expectation depend on the compiler
version instead of failing unconditionally.

Add test_goto_none as well. The existing cases only cover a declaration
sitting between a switch and its first case label, while the compiler
change covers plain goto too.

GCC continues to initialize only at the declaration, and reports as much
with -Wtrivial-auto-var-init, so it keeps the expected-failure path.

Link: https://github.com/llvm/llvm-project/pull/181937
Link: https://github.com/KSPP/linux/issues/125
Link: https://llvm.org/pr44916
Assisted-by: LLM
Signed-off-by: Justin Stitt <justinstitt@google.com>
---
 lib/tests/stackinit_kunit.c | 53 ++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 47 insertions(+), 6 deletions(-)

diff --git a/lib/tests/stackinit_kunit.c b/lib/tests/stackinit_kunit.c
index ff2784769772..a0029f3b894d 100644
--- a/lib/tests/stackinit_kunit.c
+++ b/lib/tests/stackinit_kunit.c
@@ -493,6 +493,36 @@ static int noinline __leaf_switch_none(int path, bool fill)
 	return 0;
 }
 
+static noinline int __leaf_goto_none(bool fill)
+{
+	goto bypass;
+	/*
+	 * This declaration is jumped over by the goto above, so it is
+	 * never reached. Compilers that initialize only at the point of
+	 * declaration leave this variable untouched.
+	 */
+	uint64_t var[10];
+
+bypass:
+	target_start = &var;
+	target_size = sizeof(var);
+	if (fill) {
+		fill_start = &var;
+		fill_size = sizeof(var);
+
+		memset(fill_start, (forced_mask | 0x55) & FILL_BYTE, fill_size);
+	}
+	memcpy(check_buf, target_start, target_size);
+
+	return 0;
+}
+
+static noinline int leaf_goto_none(unsigned long sp, bool fill,
+					  uint64_t *arg)
+{
+	return __leaf_goto_none(fill);
+}
+
 static noinline int leaf_switch_1_none(unsigned long sp, bool fill,
 					      uint64_t *arg)
 {
@@ -506,13 +536,23 @@ static noinline int leaf_switch_2_none(unsigned long sp, bool fill,
 }
 
 /*
- * These are expected to fail for most configurations because neither
- * GCC nor Clang have a way to perform initialization of variables in
- * non-code areas (i.e. in a switch statement before the first "case").
- * https://llvm.org/pr44916
+ * A declaration whose definition point is jumped over, either by a switch
+ * dispatch reaching a "case" label below it or by a goto, used to be left
+ * uninitialized: the initialization is emitted where the variable is
+ * declared, and that point is never reached.
+ *
+ * Clang 24 and later emit the initialization on the jumps that bypass the
+ * declaration instead. GCC still initializes only at the declaration, and
+ * says so with -Wtrivial-auto-var-init.
  */
-DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, ALWAYS_FAIL);
-DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, ALWAYS_FAIL);
+#if defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION >= 240000
+# define BYPASS_PASS	WANT_SUCCESS
+#else
+# define BYPASS_PASS	XFAIL
+#endif
+DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, BYPASS_PASS);
+DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, BYPASS_PASS);
+DEFINE_TEST_DRIVER(goto_none, uint64_t, SCALAR, BYPASS_PASS);
 
 #define KUNIT_test_scalars(init)			\
 		KUNIT_CASE(test_u8_ ## init),		\
@@ -568,6 +608,7 @@ static struct kunit_case stackinit_test_cases[] = {
 	KUNIT_test_scalars(none),
 	KUNIT_CASE(test_switch_1_none),
 	KUNIT_CASE(test_switch_2_none),
+	KUNIT_CASE(test_goto_none),
 	/* STRUCTLEAK_BYREF should cover from here down. */
 	KUNIT_test_structs(none),
 	/* STRUCTLEAK will only cover this. */

---
base-commit: 5e0f8396d4805a3e7f753fa58c8c55f1f3cc2160
change-id: 20261002-kspp-125-3e3ae2bb0173

Best regards,
--  
Justin Stitt <justinstitt@google.com>


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] kunit/stackinit: Cover declarations bypassed by goto and switch
  2026-10-02 19:12 [PATCH] kunit/stackinit: Cover declarations bypassed by goto and switch Justin Stitt
@ 2026-10-05  9:41 ` Kees Cook
  0 siblings, 0 replies; 2+ messages in thread
From: Kees Cook @ 2026-10-05  9:41 UTC (permalink / raw)
  To: Nathan Chancellor, Justin Stitt
  Cc: Kees Cook, linux-hardening, linux-kernel, llvm, linux-kselftest

On Fri, 02 Oct 2026 12:12:53 -0700, Justin Stitt wrote:
> Under -ftrivial-auto-var-init= the initialization is emitted where a
> variable is declared. A switch dispatch that reaches a case label below
> the declaration, or a goto that jumps past it, never reaches that point,
> so the variable was left uninitialized.
> 
> Commit 9cf016e6b49b ("lib: test_stackinit.c: XFAIL switch variable init
> tests") marked test_switch_1_none and test_switch_2_none as expected
> failures, noting the test should be retained "so that we can evaluate
> compiler fixes". Clang 24 emits the initialization on the jumps that
> bypass the declaration, so make the expectation depend on the compiler
> version instead of failing unconditionally.
> 
> [...]

Applied to for-next/hardening, thanks!

[1/1] kunit/stackinit: Cover declarations bypassed by goto and switch
      https://git.kernel.org/kees/c/6bf37b9c59dc

Take care,

-- 
Kees Cook


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-10-05  9:41 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-02 19:12 [PATCH] kunit/stackinit: Cover declarations bypassed by goto and switch Justin Stitt
2026-10-05  9:41 ` Kees Cook

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®