From 97d272699e06bb60883333447cb628b6e846cbcf Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Sun, 10 May 2026 15:10:31 -0600 Subject: [PATCH 1/7] stddef: Document designated initializer semantics for __TRAILING_OVERLAP() Document the designated initializer behavior for overlapping storage between NAME and MEMBERS, and clarify the implications for static initialization to help avoid unintended overwrites. Signed-off-by: Gustavo A. R. Silva Link: https://patch.msgid.link/agD0R-kNbg9YMOCT@kspp Signed-off-by: Kees Cook --- include/linux/stddef.h | 65 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/include/linux/stddef.h b/include/linux/stddef.h index 80b6bfb944f0..e1851c50c89b 100644 --- a/include/linux/stddef.h +++ b/include/linux/stddef.h @@ -100,6 +100,71 @@ enum { * Creates a union between a flexible-array member (FAM) in a struct and a set * of additional members that would otherwise follow it. * + * Beware that, as this helper encloses TYPE NAME and MEMBERS in the same + * union, designated initializers for MEMBERS may overwrite portions + * previously initialized through NAME. + * + * For example:: + * + * struct flex { + * size_t count; + * u8 fam[]; + * }; + * + * struct composite { + * ... + * __TRAILING_OVERLAP(struct flex, flex, fam, __packed, + * u8 data; + * ); + * } __packed; + * + * static struct composite comp = { + * .flex = { + * .count = 1, + * }, + * .data = 2, + * }; + * + * In the example above, .flex and .data initialize different views of the same + * union storage. Since .data is initialized last, it _may_ overwrite portions + * previously initialized through .flex, leading to .flex.count being zeroed + * out. + * + * A couple of alternatives are shown below. + * + * a) Initialize only one view of the overlapped storage and assign the rest + * at runtime:: + * + * static struct composite comp = { + * .flex = { + * .count = 1, + * }, + * }; + * + * static void foo(void) + * { + * comp.data = 2; + * ... + * } + * + * b) Alternatively, replace designated initializers with runtime assignments:: + * + * static void foo(void) + * { + * struct composite comp; + * + * comp.flex.count = 1; + * comp.data = 2; + * ... + * } + * + * Compiler Explorer test code: https://godbolt.org/z/voM4E36dT + * + * For another example of the above see commit 5e54510a9389 ("acpi: nfit: + * intel: avoid multiple -Wflex-array-member-not-at-end warnings") + * + * Link: https://git.kernel.org/linus/5e54510a9389caa9 + * * @TYPE: Flexible structure type name, including "struct" keyword. * @NAME: Name for a variable to define. * @FAM: The flexible-array member within @TYPE From e0251b5e07f313619c15054de69bee10678da3a6 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 14 Apr 2026 15:09:28 +0200 Subject: [PATCH 2/7] MAINTAINERS: add kernel hardening keyword __counted_by_ptr In addition to __counted_by, __counted_by_le, and __counted_by_be, also match the keyword __counted_by_ptr. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260414130926.312094-3-thorsten.blum@linux.dev Signed-off-by: Kees Cook --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 882214b0e7db..2c486ca11cf8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13928,7 +13928,7 @@ F: scripts/Makefile.randstruct F: security/Kconfig.hardening K: \b(add|choose)_random_kstack_offset\b K: \b__check_(object_size|heap_object)\b -K: \b__counted_by(_le|_be)?\b +K: \b__counted_by(_le|_be|_ptr)?\b KERNEL JANITORS L: kernel-janitors@vger.kernel.org From 233e1ab980269394bfa78191ac12446cc6a22683 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 15 Apr 2026 14:25:43 +0200 Subject: [PATCH 3/7] lib/string_helpers: drop redundant allocation in kasprintf_strarray kasprintf_strarray() returns an array of N strings and kfree_strarray() also frees N entries. However, kasprintf_strarray() currently allocates N+1 char pointers. Allocate exactly N pointers instead of N+1. Also update the kernel-doc for @n. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260415122542.370926-4-thorsten.blum@linux.dev Signed-off-by: Kees Cook --- lib/string_helpers.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 169eaf583494..6a8db441b6fd 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -752,7 +752,7 @@ EXPORT_SYMBOL_GPL(kstrdup_and_replace); * kasprintf_strarray - allocate and fill array of sequential strings * @gfp: flags for the slab allocator * @prefix: prefix to be used - * @n: amount of lines to be allocated and filled + * @n: number of strings to be allocated and filled * * Allocates and fills @n strings using pattern "%s-%zu", where prefix * is provided by caller. The caller is responsible to free them with @@ -765,7 +765,7 @@ char **kasprintf_strarray(gfp_t gfp, const char *prefix, size_t n) char **names; size_t i; - names = kcalloc(n + 1, sizeof(char *), gfp); + names = kcalloc(n, sizeof(char *), gfp); if (!names) return NULL; From 8f64fb098a5b1dfea57989fdf732b6168f0e1c35 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 15 Apr 2026 14:25:45 +0200 Subject: [PATCH 4/7] lib/string_helpers: annotate struct strarray with __counted_by_ptr Add the __counted_by_ptr() compiler attribute to 'array' to improve bounds checking via CONFIG_UBSAN_BOUNDS and CONFIG_FORTIFY_SOURCE. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260415122542.370926-6-thorsten.blum@linux.dev Signed-off-by: Kees Cook --- lib/string_helpers.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 6a8db441b6fd..98d6ed0eaab7 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -805,7 +805,7 @@ void kfree_strarray(char **array, size_t n) EXPORT_SYMBOL_GPL(kfree_strarray); struct strarray { - char **array; + char **array __counted_by_ptr(n); size_t n; }; @@ -824,13 +824,13 @@ char **devm_kasprintf_strarray(struct device *dev, const char *prefix, size_t n) if (!ptr) return ERR_PTR(-ENOMEM); + ptr->n = n; ptr->array = kasprintf_strarray(GFP_KERNEL, prefix, n); if (!ptr->array) { devres_free(ptr); return ERR_PTR(-ENOMEM); } - ptr->n = n; devres_add(dev, ptr); return ptr->array; From a34039981e6deb0580cf3215bfda02731596eada Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 1 May 2026 19:01:56 +0200 Subject: [PATCH 5/7] lkdtm: Add case to provoke a crash in EFI runtime services Add a lkdtm test case that triggers a fault during the execution of a EFI runtime service by passing a read-only variable as a by-ref argument that the firmware is supposed to update. This is useful for testing the graceful handling of faults/exception in EFI platform firmware, which is implemented on x86 and arm64. Signed-off-by: Ard Biesheuvel Link: https://patch.msgid.link/20260501170156.2833364-2-ardb+git@google.com Signed-off-by: Kees Cook --- drivers/misc/lkdtm/bugs.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index e0098f314570..3eca2ef64aff 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -7,6 +7,7 @@ */ #include "lkdtm.h" #include +#include #include #include #include @@ -817,6 +818,29 @@ static noinline void lkdtm_CORRUPT_PAC(void) #endif } +static void __maybe_unused lkdtm_EFI_RUNTIME_CRASH(void) +{ + static unsigned long size __ro_after_init = sizeof(efi_char16_t); + efi_status_t status; + + if (!efi.get_next_variable || + !efi_enabled(EFI_RUNTIME_SERVICES) || + !efi_rt_services_supported(EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME)) { + pr_err("FAIL: EFI GetNextVariableName() is not available\n"); + return; + } + + /* + * Provoke a fault by asking the firmware to write to a read-only + * variable. + */ + status = efi.get_next_variable(&size, L"", &(efi_guid_t){}); + + if (status != EFI_ABORTED || efi_enabled(EFI_RUNTIME_SERVICES)) + pr_err("FAIL: EFI GetNextVariable() did not abort (%#lx)\n", + status); +} + static struct crashtype crashtypes[] = { CRASHTYPE(PANIC), CRASHTYPE(PANIC_STOP_IRQOFF), @@ -850,6 +874,9 @@ static struct crashtype crashtypes[] = { CRASHTYPE(UNSET_SMEP), CRASHTYPE(DOUBLE_FAULT), CRASHTYPE(CORRUPT_PAC), +#ifdef CONFIG_EFI + CRASHTYPE(EFI_RUNTIME_CRASH), +#endif }; struct crashtype_category bugs_crashtypes = { From 3f21a4426e6fc87447bda557655708c2c02cbabc Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Mon, 18 May 2026 12:26:04 +0530 Subject: [PATCH 6/7] lkdtm/powerpc: add isync after slbmte to enforce SLB update ordering The slbmte instruction modifies the Segment Lookaside Buffer, but without a context synchronizing operation the CPU is not guaranteed to observe the updated SLB state for subsequent instructions. This can result in use of stale translation state when memory is accessed immediately after SLB modifications. Add isync after each slbmte in the PPC_SLB_MULTIHIT test to ensure proper ordering of SLB updates before subsequent memory accesses. This aligns with Power ISA context synchronization requirements for changes in address translation state and improves the reliability of SLB multihit injection tests in hash MMU mode. Suggested-by: Ritesh Harjani (IBM) Signed-off-by: Sayali Patil Reviewed-by: Ritesh Harjani (IBM) Reviewed-by: Michael Ellerman Link: https://patch.msgid.link/2f8d430962a96a7498903b994f081deee4a4d97a.1778975974.git.sayalip@linux.ibm.com Signed-off-by: Kees Cook --- drivers/misc/lkdtm/powerpc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/misc/lkdtm/powerpc.c b/drivers/misc/lkdtm/powerpc.c index be385449911a..ef07e5201edf 100644 --- a/drivers/misc/lkdtm/powerpc.c +++ b/drivers/misc/lkdtm/powerpc.c @@ -17,11 +17,14 @@ static void insert_slb_entry(unsigned long p, int ssize, int page_size) : "r" (mk_vsid_data(p, ssize, flags)), "r" (mk_esid_data(p, ssize, SLB_NUM_BOLTED)) : "memory"); + isync(); asm volatile("slbmte %0,%1" : : "r" (mk_vsid_data(p, ssize, flags)), "r" (mk_esid_data(p, ssize, SLB_NUM_BOLTED + 1)) : "memory"); + isync(); + preempt_enable(); } @@ -84,6 +87,7 @@ static void insert_dup_slb_entry_0(void) : "r" (vsid), "r" (esid | SLB_NUM_BOLTED) : "memory"); + isync(); asm volatile("slbmfee %0,%1" : "=r" (esid) : "r" (i)); asm volatile("slbmfev %0,%1" : "=r" (vsid) : "r" (i)); @@ -93,6 +97,7 @@ static void insert_dup_slb_entry_0(void) : "r" (vsid), "r" (esid | (SLB_NUM_BOLTED + 1)) : "memory"); + isync(); pr_info("%s accessing test address 0x%lx: 0x%lx\n", __func__, test_address, *test_ptr); From 122b52f0bab007ebeb414c8280c1def17b9ed1f4 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Mon, 18 May 2026 12:26:05 +0530 Subject: [PATCH 7/7] lkdtm/powerpc: add PPC_RADIX_TLBIEL test for radix MCE validation Add a new LKDTM trigger (PPC_RADIX_TLBIEL) that executes a process-scoped radix TLBIEL instruction to exercise the radix MMU behaviour and associated machine check exception (MCE) handling paths. This provides a way to validate MCE handling in radix mode. Currently, there is no dedicated LKDTM test that exercises this path or allows triggering radix-specific machine check behaviour for validation. The test is only enabled on ppc64 systems with radix MMU support and If radix is not active, the trigger is skipped and reported as XFAIL. Co-developed-by: Ritesh Harjani (IBM) Signed-off-by: Ritesh Harjani (IBM) Signed-off-by: Sayali Patil Reviewed-by: Ritesh Harjani (IBM) Reviewed-by: Michael Ellerman Link: https://patch.msgid.link/85c9b59217bcecb3c7af52e9d5b175266771d7de.1778975974.git.sayalip@linux.ibm.com Signed-off-by: Kees Cook --- drivers/misc/lkdtm/Makefile | 2 +- drivers/misc/lkdtm/core.c | 2 +- drivers/misc/lkdtm/powerpc.c | 44 +++++++++++++++++++++++++ tools/testing/selftests/lkdtm/tests.txt | 1 + 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/drivers/misc/lkdtm/Makefile b/drivers/misc/lkdtm/Makefile index 03ebe33185f9..4e58d16fc01e 100644 --- a/drivers/misc/lkdtm/Makefile +++ b/drivers/misc/lkdtm/Makefile @@ -11,7 +11,7 @@ lkdtm-$(CONFIG_LKDTM) += usercopy.o lkdtm-$(CONFIG_LKDTM) += kstack_erase.o lkdtm-$(CONFIG_LKDTM) += cfi.o lkdtm-$(CONFIG_LKDTM) += fortify.o -lkdtm-$(CONFIG_PPC_64S_HASH_MMU) += powerpc.o +lkdtm-$(CONFIG_PPC_BOOK3S_64) += powerpc.o KASAN_SANITIZE_stackleak.o := n diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c index 5732fd59a227..ededa32d6744 100644 --- a/drivers/misc/lkdtm/core.c +++ b/drivers/misc/lkdtm/core.c @@ -96,7 +96,7 @@ static const struct crashtype_category *crashtype_categories[] = { &stackleak_crashtypes, &cfi_crashtypes, &fortify_crashtypes, -#ifdef CONFIG_PPC_64S_HASH_MMU +#ifdef CONFIG_PPC_BOOK3S_64 &powerpc_crashtypes, #endif }; diff --git a/drivers/misc/lkdtm/powerpc.c b/drivers/misc/lkdtm/powerpc.c index ef07e5201edf..6eaac79ea26b 100644 --- a/drivers/misc/lkdtm/powerpc.c +++ b/drivers/misc/lkdtm/powerpc.c @@ -5,6 +5,7 @@ #include #include +#ifdef CONFIG_PPC_64S_HASH_MMU /* Inserts new slb entries */ static void insert_slb_entry(unsigned long p, int ssize, int page_size) { @@ -104,9 +105,35 @@ static void insert_dup_slb_entry_0(void) preempt_enable(); } +#endif /* CONFIG_PPC_64S_HASH_MMU */ + +static __always_inline void tlbiel_va(unsigned long va, + unsigned long pid, + unsigned long ap, + unsigned long ric) +{ + unsigned long rb, rs, prs, r; + + rb = va & ~(PPC_BITMASK(52, 63)); + rb |= ap << PPC_BITLSHIFT(58); + rs = pid << PPC_BITLSHIFT(31); + + prs = 1; /* process scoped */ + r = 1; /* radix format */ + + /* + * Trigger an MCE by issuing radix tlbiel with an invalid operand combination. + * The combination of RIC = 2 with IS = 0 (Invalidation selector specified + * in the RB register) is invalid. + * This invalid combination causes hardware to raise a machine check. + */ + asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1) + : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); +} static void lkdtm_PPC_SLB_MULTIHIT(void) { +#ifdef CONFIG_PPC_64S_HASH_MMU if (!radix_enabled()) { pr_info("Injecting SLB multihit errors\n"); /* @@ -122,10 +149,27 @@ static void lkdtm_PPC_SLB_MULTIHIT(void) } else { pr_err("XFAIL: This test is for ppc64 and with hash mode MMU only\n"); } +#else + pr_err("XFAIL: This test requires CONFIG_PPC_64S_HASH_MMU\n"); +#endif +} + +static void lkdtm_PPC_RADIX_TLBIEL(void) +{ + unsigned long addr = PAGE_OFFSET; + + if (radix_enabled()) { + pr_info("Injecting Radix TLB invalidation MCE\n"); + tlbiel_va(addr, 0, 0, RIC_FLUSH_ALL); + pr_info("Recovered from radix tlbiel attempt\n"); + } else { + pr_err("XFAIL: This test is for ppc64 and with radix mode MMU only\n"); + } } static struct crashtype crashtypes[] = { CRASHTYPE(PPC_SLB_MULTIHIT), + CRASHTYPE(PPC_RADIX_TLBIEL), }; struct crashtype_category powerpc_crashtypes = { diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt index 3245032db34d..d8180bbe31e8 100644 --- a/tools/testing/selftests/lkdtm/tests.txt +++ b/tools/testing/selftests/lkdtm/tests.txt @@ -86,3 +86,4 @@ FORTIFY_STR_MEMBER detected buffer overflow FORTIFY_MEM_OBJECT detected buffer overflow FORTIFY_MEM_MEMBER detected field-spanning write PPC_SLB_MULTIHIT Recovered +#PPC_RADIX_TLBIEL Triggers unrecoverable MCE