hardening updates for v7.2-rc1

- lkdtm: Add case to provoke a crash in EFI runtime services (Ard Biesheuvel)
 
 - lkdtm: add PPC_RADIX_TLBIEL test and missed isync (Sayali Patil)
 
 - stddef: Document designated initializer semantics for __TRAILING_OVERLAP()
   (Gustavo A. R. Silva)
 
 - strarray: drop redundant allocation, add __counted_by_ptr (Thorsten Blum)
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRSPkdeREjth1dHnSE2KwveOeQkuwUCajBj8AAKCRA2KwveOeQk
 u/fjAPwJVWvocviOCGGA6Rq/BAPNJpkrahqtcNAjwvuh+SWDfQD/bHcdXLXi6tl2
 fHWCdJ2ScNBqAWVkJNl9F3k+XdSsmws=
 =GZAD
 -----END PGP SIGNATURE-----

Merge tag 'hardening-v7.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux

Pull hardening updates from Kees Cook:

 - lkdtm:
     - Add case to provoke a crash in EFI runtime services (Ard Biesheuvel)
     - add PPC_RADIX_TLBIEL test and missed isync (Sayali Patil)

 - stddef: Document designated initializer semantics for
   __TRAILING_OVERLAP() (Gustavo A. R. Silva)

 - strarray: drop redundant allocation, add __counted_by_ptr (Thorsten
   Blum)

* tag 'hardening-v7.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  lkdtm/powerpc: add PPC_RADIX_TLBIEL test for radix MCE validation
  lkdtm/powerpc: add isync after slbmte to enforce SLB update ordering
  lkdtm: Add case to provoke a crash in EFI runtime services
  lib/string_helpers: annotate struct strarray with __counted_by_ptr
  lib/string_helpers: drop redundant allocation in kasprintf_strarray
  MAINTAINERS: add kernel hardening keyword __counted_by_ptr
  stddef: Document designated initializer semantics for __TRAILING_OVERLAP()
This commit is contained in:
Linus Torvalds 2026-06-16 09:21:01 +05:30
commit 481329ec5b
8 changed files with 149 additions and 7 deletions

View File

@ -13944,7 +13944,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

View File

@ -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

View File

@ -7,6 +7,7 @@
*/
#include "lkdtm.h"
#include <linux/cpu.h>
#include <linux/efi.h>
#include <linux/list.h>
#include <linux/hrtimer.h>
#include <linux/sched.h>
@ -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 = {

View File

@ -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
};

View File

@ -5,6 +5,7 @@
#include <linux/vmalloc.h>
#include <asm/mmu.h>
#ifdef CONFIG_PPC_64S_HASH_MMU
/* Inserts new slb entries */
static void insert_slb_entry(unsigned long p, int ssize, int page_size)
{
@ -17,11 +18,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 +88,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,15 +98,42 @@ 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);
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");
/*
@ -117,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 = {

View File

@ -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

View File

@ -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;
@ -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;

View File

@ -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