From 9025f3bf73f0565eaa271ecc15f468b1d1e4cbaa Mon Sep 17 00:00:00 2001 From: Bill Roberts Date: Tue, 7 Jul 2026 13:45:42 -0500 Subject: [PATCH 1/7] x86/shstk: Return the correct error value when user shadow stacks are disabled The WRSS instruction (the special instruction that writes to shadow stacks) cannot be used in userspace unless IA32_U_CET.SH_STK_EN=1 (user shadow stack is enabled). So the kernel *should* return -EINVAL to userspace if it tries to enable it when shadow stack is disabled. However, currently, it will return -EPERM. But, that error code doesn't fit the condition as the failure is due to an invalid state change request not a permission issue. Investigating userspace call sites, like glibc and criu (checkpoint code), they do not rely on this specific error message, nor could a userspace effectively utilize this specific return error code to indicate a difference in "I cannot enable write because of invalid permissions" versus "I cannot enable write because the shadow stack is disabled". [ bp: Fixup commit title. ] Signed-off-by: Bill Roberts Signed-off-by: Dave Hansen Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Rick Edgecombe Link: https://patch.msgid.link/20260707184542.1721085-1-bill.roberts@arm.com --- arch/x86/kernel/shstk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/shstk.c b/arch/x86/kernel/shstk.c index 0ca64900192f..eb690ba90180 100644 --- a/arch/x86/kernel/shstk.c +++ b/arch/x86/kernel/shstk.c @@ -490,7 +490,7 @@ static int wrss_control(bool enable) * when disabling. */ if (!features_enabled(ARCH_SHSTK_SHSTK)) - return -EPERM; + return -EINVAL; /* Already enabled/disabled? */ if (features_enabled(ARCH_SHSTK_WRSS) == enable) From 228200f695c0f92c5fd06ff0bdcf94bb0d2ca7f5 Mon Sep 17 00:00:00 2001 From: "Borislav Petkov (AMD)" Date: Thu, 20 Aug 2026 17:02:10 -0700 Subject: [PATCH 2/7] x86/CPU/AMD: Fix Zen5 TLB sizes reporting Starting with Zen5, TLB sizes in CPUID_Fn80000006_E[AB]X are reported as multiples of 32. There's a CPUID bit which determines that: CPUID_Fn80000021_EAX [Extended Feature 2 EAX] (Core::X86::Cpuid::FeatureExt2Eax) ... 14: L2TlbSizeX32. Read-only. Reset: 1. Indicates that L2TLB sizes are encoded as multiples of 32. Update the places which report that information. With it, the numbers look correct now: -Last level iTLB entries: 4KB 64, 2MB 64, 4MB 32 -Last level dTLB entries: 4KB 128, 2MB 128, 4MB 64, 1GB 0 +Last level iTLB entries: 4KB 2048, 2MB 2048, 4MB 1024 +Last level dTLB entries: 4KB 4096, 2MB 4096, 4MB 2048, 1GB 0 /proc/cpuinfo -TLB size : 192 4K pages +TLB size : 6144 4K pages Signed-off-by: Borislav Petkov (AMD) Link: https://lore.kernel.org/r/20260821022031.946311-1-bp@kernel.org --- arch/x86/include/asm/cpufeatures.h | 2 ++ arch/x86/kernel/cpu/amd.c | 21 +++++++++++++-------- arch/x86/kernel/cpu/common.c | 4 +++- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index f70ee74b5f92..a7689e606b16 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -483,6 +483,8 @@ #define X86_FEATURE_AUTOIBRS (20*32+ 8) /* Automatic IBRS */ #define X86_FEATURE_NO_SMM_CTL_MSR (20*32+ 9) /* SMM_CTL MSR is not present */ +#define X86_FEATURE_L2_TLB_SIZE_X32 (20*32+14) /* L2 TLB sizes are encoded as multiples of 32 */ + #define X86_FEATURE_GP_ON_USER_CPUID (20*32+17) /* User CPUID faulting */ #define X86_FEATURE_PREFETCHI (20*32+20) /* Prefetch Data/Instruction to Cache Level */ diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 54e14ed276b5..e5279bc648d3 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -1192,7 +1192,7 @@ static unsigned int amd_size_cache(struct cpuinfo_x86 *c, unsigned int size) static void cpu_detect_tlb_amd(struct cpuinfo_x86 *c) { - u32 ebx, eax, ecx, edx; + u32 ebx, eax, ecx, edx, shift, tmp; u16 mask = 0xfff; if (c->x86 < 0xf) @@ -1201,10 +1201,12 @@ static void cpu_detect_tlb_amd(struct cpuinfo_x86 *c) if (c->extended_cpuid_level < 0x80000006) return; + shift = !!cpu_has(c, X86_FEATURE_L2_TLB_SIZE_X32) * 5; + cpuid(0x80000006, &eax, &ebx, &ecx, &edx); - tlb_lld_4k = (ebx >> 16) & mask; - tlb_lli_4k = ebx & mask; + tlb_lld_4k = ((ebx >> 16) & mask) << shift; + tlb_lli_4k = (ebx & mask) << shift; /* * K8 doesn't have 2M/4M entries in the L2 TLB so read out the L1 TLB @@ -1216,16 +1218,18 @@ static void cpu_detect_tlb_amd(struct cpuinfo_x86 *c) } /* Handle DTLB 2M and 4M sizes, fall back to L1 if L2 is disabled */ - if (!((eax >> 16) & mask)) + tmp = ((eax >> 16) & mask) << shift; + if (!tmp) tlb_lld_2m = (cpuid_eax(0x80000005) >> 16) & 0xff; else - tlb_lld_2m = (eax >> 16) & mask; + tlb_lld_2m = tmp; /* a 4M entry uses two 2M entries */ tlb_lld_4m = tlb_lld_2m >> 1; /* Handle ITLB 2M and 4M sizes, fall back to L1 if L2 is disabled */ - if (!(eax & mask)) { + tmp = (eax & mask) << shift; + if (!tmp) { /* Erratum 658 */ if (c->x86 == 0x15 && c->x86_model <= 0x1f) { tlb_lli_2m = 1024; @@ -1233,8 +1237,9 @@ static void cpu_detect_tlb_amd(struct cpuinfo_x86 *c) cpuid(0x80000005, &eax, &ebx, &ecx, &edx); tlb_lli_2m = eax & 0xff; } - } else - tlb_lli_2m = eax & mask; + } else { + tlb_lli_2m = tmp; + } tlb_lli_4m = tlb_lli_2m >> 1; diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index c7352827f491..3716a6af13cb 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -857,7 +857,7 @@ static void get_model_name(struct cpuinfo_x86 *c) void cpu_detect_cache_sizes(struct cpuinfo_x86 *c) { - unsigned int n, dummy, ebx, ecx, edx, l2size; + unsigned int n, dummy, ebx, ecx, edx, l2size, shift __maybe_unused; n = c->extended_cpuid_level; @@ -877,7 +877,9 @@ void cpu_detect_cache_sizes(struct cpuinfo_x86 *c) l2size = ecx >> 16; #ifdef CONFIG_X86_64 + shift = !!cpu_has(c, X86_FEATURE_L2_TLB_SIZE_X32) * 5; c->x86_tlbsize += ((ebx >> 16) & 0xfff) + (ebx & 0xfff); + c->x86_tlbsize <<= shift; #else /* do processor-specific cache resizing */ if (this_cpu->legacy_cache_size) From 54a2882ab9f75ab8a77a4f113145d4d709d2387c Mon Sep 17 00:00:00 2001 From: Ihor Solodrai Date: Wed, 16 Sep 2026 12:51:59 -0700 Subject: [PATCH 3/7] x86/cpu: Factor init_cpu_info() out of identify_cpu() identify_cpu() unconditionally resets the struct cpuinfo_x86 fields to their default values and clears the capability arrays with memset() before rescanning the CPU to fill it in again. However the boot CPU capabilities have already been scanned by early_identify_cpu(), with interrupts disabled. Introduce init_cpu_info() helper in preparation for letting the callers decide whether the reset is needed. No functional changes. Signed-off-by: Ihor Solodrai Signed-off-by: Borislav Petkov (AMD) Link: https://patch.msgid.link/20260916195203.1099646-2-ihor.solodrai@linux.dev --- arch/x86/kernel/cpu/common.c | 51 ++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 3716a6af13cb..173748a77877 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1784,6 +1784,32 @@ static void __init cpu_parse_early_param(void) } } +static void init_cpu_info(struct cpuinfo_x86 *c) +{ + c->x86_cache_size = 0; + c->x86_vendor = X86_VENDOR_UNKNOWN; + c->x86_model = c->x86_stepping = 0; /* So far unknown... */ + c->x86_vendor_id[0] = '\0'; /* Unset */ + c->x86_model_id[0] = '\0'; /* Unset */ +#ifdef CONFIG_X86_64 + c->x86_clflush_size = 64; + c->x86_phys_bits = 36; + c->x86_virt_bits = 48; +#else + c->cpuid_level = -1; /* CPUID not detected */ + c->x86_clflush_size = 32; + c->x86_phys_bits = 32; + c->x86_virt_bits = 32; +#endif + c->x86_cache_alignment = c->x86_clflush_size; + memset(&c->x86_capability, 0, sizeof(c->x86_capability)); + memset(&c->cpuid, 0, sizeof(c->cpuid)); +#ifdef CONFIG_X86_VMX_FEATURE_NAMES + memset(&c->vmx_capability, 0, sizeof(c->vmx_capability)); +#endif + c->extended_cpuid_level = 0; +} + /* * Do minimum CPU detection early. * Fields really needed: vendor, cpuid_level, family, model, mask, @@ -1968,8 +1994,6 @@ void check_null_seg_clears_base(struct cpuinfo_x86 *c) static void generic_identify(struct cpuinfo_x86 *c) { - c->extended_cpuid_level = 0; - if (!cpuid_feature()) identify_cpu_without_cpuid(c); @@ -2013,27 +2037,8 @@ static void identify_cpu(struct cpuinfo_x86 *c) int i; c->loops_per_jiffy = loops_per_jiffy; - c->x86_cache_size = 0; - c->x86_vendor = X86_VENDOR_UNKNOWN; - c->x86_model = c->x86_stepping = 0; /* So far unknown... */ - c->x86_vendor_id[0] = '\0'; /* Unset */ - c->x86_model_id[0] = '\0'; /* Unset */ -#ifdef CONFIG_X86_64 - c->x86_clflush_size = 64; - c->x86_phys_bits = 36; - c->x86_virt_bits = 48; -#else - c->cpuid_level = -1; /* CPUID not detected */ - c->x86_clflush_size = 32; - c->x86_phys_bits = 32; - c->x86_virt_bits = 32; -#endif - c->x86_cache_alignment = c->x86_clflush_size; - memset(&c->x86_capability, 0, sizeof(c->x86_capability)); - memset(&c->cpuid, 0, sizeof(c->cpuid)); -#ifdef CONFIG_X86_VMX_FEATURE_NAMES - memset(&c->vmx_capability, 0, sizeof(c->vmx_capability)); -#endif + + init_cpu_info(c); generic_identify(c); From 7a762b51a149b7bfe431a7571bc7df4beea52e5c Mon Sep 17 00:00:00 2001 From: Ihor Solodrai Date: Wed, 16 Sep 2026 12:52:00 -0700 Subject: [PATCH 4/7] x86/cpu: Initialize boot CPU cpuinfo defaults early early_identify_cpu() clears the capability array, the CPUID table and extended_cpuid_level, but the architectural defaults for the rest of struct cpuinfo_x86 are set only later, in identify_cpu(). Use the same defaults from the start, so that the boot CPU does not depend on a later reset to end up with the right ones. Signed-off-by: Ihor Solodrai Signed-off-by: Borislav Petkov (AMD) Link: https://patch.msgid.link/20260916195203.1099646-3-ihor.solodrai@linux.dev --- arch/x86/kernel/cpu/common.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 173748a77877..d1a949ad25bd 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1814,16 +1814,15 @@ static void init_cpu_info(struct cpuinfo_x86 *c) * Do minimum CPU detection early. * Fields really needed: vendor, cpuid_level, family, model, mask, * cache alignment. - * The others are not touched to avoid unwanted side effects. + * The others are reset to their defaults here and only filled in later, + * by identify_cpu(). * * WARNING: this function is only called on the boot CPU. Don't add code * here that is supposed to run on all CPUs. */ static void __init early_identify_cpu(struct cpuinfo_x86 *c) { - memset(&c->x86_capability, 0, sizeof(c->x86_capability)); - memset(&c->cpuid, 0, sizeof(c->cpuid)); - c->extended_cpuid_level = 0; + init_cpu_info(c); if (!cpuid_feature()) identify_cpu_without_cpuid(c); From ad86fe2134ccdc97a967693d2723a731c99fb7bc Mon Sep 17 00:00:00 2001 From: Ihor Solodrai Date: Wed, 16 Sep 2026 12:52:01 -0700 Subject: [PATCH 5/7] x86/cpu: Inline generic_identify() into identify_cpu() generic_identify() has exactly one call site: at the top of identify_cpu(). Fold it into identify_cpu() so that a single function does the job for both the boot CPU and the secondary CPUs. While at it, fix up both copies of the Cyrix comment. No functional changes. Signed-off-by: Ihor Solodrai Signed-off-by: Borislav Petkov (AMD) Link: https://patch.msgid.link/20260916195203.1099646-4-ihor.solodrai@linux.dev --- arch/x86/kernel/cpu/common.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index d1a949ad25bd..79a126085374 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1827,7 +1827,7 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c) if (!cpuid_feature()) identify_cpu_without_cpuid(c); - /* cyrix could have cpuid enabled via c_identify()*/ + /* Cyrix could have CPUID enabled via c_identify(). */ if (cpuid_feature()) { cpuid_scan_cpu(c); cpu_detect(c); @@ -1991,14 +1991,23 @@ void check_null_seg_clears_base(struct cpuinfo_x86 *c) set_cpu_bug(c, X86_BUG_NULL_SEG); } -static void generic_identify(struct cpuinfo_x86 *c) +/* + * This does the hard work of actually picking apart the CPU stuff... + */ +static void identify_cpu(struct cpuinfo_x86 *c) { + int i; + + c->loops_per_jiffy = loops_per_jiffy; + + init_cpu_info(c); + if (!cpuid_feature()) identify_cpu_without_cpuid(c); - /* cyrix could have cpuid enabled via c_identify()*/ + /* Cyrix could have CPUID enabled via c_identify(). */ if (!cpuid_feature()) - return; + goto no_cpuid; cpuid_scan_cpu(c); cpu_detect(c); @@ -2026,21 +2035,8 @@ static void generic_identify(struct cpuinfo_x86 *c) #ifdef CONFIG_X86_32 set_cpu_bug(c, X86_BUG_ESPFIX); #endif -} - -/* - * This does the hard work of actually picking apart the CPU stuff... - */ -static void identify_cpu(struct cpuinfo_x86 *c) -{ - int i; - - c->loops_per_jiffy = loops_per_jiffy; - - init_cpu_info(c); - - generic_identify(c); +no_cpuid: cpu_parse_topology(c); if (this_cpu->c_identify) From db349783ca7aa99345f3809523bff2db8f8714de Mon Sep 17 00:00:00 2001 From: Ihor Solodrai Date: Wed, 16 Sep 2026 12:52:02 -0700 Subject: [PATCH 6/7] x86/cpu: Move 32-bit SEP setup into identify_cpu() identify_boot_cpu() and identify_secondary_cpu() both call enable_sep_cpu() under CONFIG_X86_32 immediately after identify_cpu(). Do it once and drop the ifdefs while at it. No functional changes. Signed-off-by: Ihor Solodrai Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Nikolay Borisov Link: https://patch.msgid.link/20260916195203.1099646-5-ihor.solodrai@linux.dev --- arch/x86/kernel/cpu/common.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 79a126085374..156a7b8ec1c1 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -2128,6 +2128,9 @@ static void identify_cpu(struct cpuinfo_x86 *c) mcheck_cpu_init(c); numa_add_cpu(smp_processor_id()); + + if (IS_ENABLED(CONFIG_X86_32)) + enable_sep_cpu(); } /* @@ -2165,9 +2168,6 @@ static __init void identify_boot_cpu(void) identify_cpu(&boot_cpu_data); if (HAS_KERNEL_IBT && cpu_feature_enabled(X86_FEATURE_IBT)) pr_info("CET detected: Indirect Branch Tracking enabled\n"); -#ifdef CONFIG_X86_32 - enable_sep_cpu(); -#endif cpu_detect_tlb(&boot_cpu_data); setup_cr_pinning(); @@ -2187,9 +2187,6 @@ void identify_secondary_cpu(unsigned int cpu) c->cpu_index = cpu; identify_cpu(c); -#ifdef CONFIG_X86_32 - enable_sep_cpu(); -#endif x86_spec_ctrl_setup_ap(); update_srbds_msr(); if (boot_cpu_has_bug(X86_BUG_GDS)) From 2711d67bc3a776abcfc651ad87be5d29ada19166 Mon Sep 17 00:00:00 2001 From: Ihor Solodrai Date: Wed, 16 Sep 2026 12:52:03 -0700 Subject: [PATCH 7/7] x86/cpu: Don't transiently clear the boot CPU's capabilities On the boot CPU, identify_cpu() runs from arch_cpu_finalize_init(), with interrupts enabled and before alternatives are patched. So cpu_feature_enabled() still evaluates against boot_cpu_data. identify_cpu() rebuilds c->x86_capability from scratch: the reset zeroes the array and the CPUID rescan fills it in again. An interrupt delivered in that window finds X86_FEATURE_LA57 clear in boot_cpu_data, so pgtable_l5_enabled() is false and KASAN checks a 5-level address against the 4-level addressability limit. The result is a bogus "wild-memory-access" report, and under kasan_multi_shot a report storm that wedges the boot. The boot CPU has already been scanned by early_identify_cpu(), with interrupts disabled, and its capabilities cannot have changed since. Reset only the CPUs which have not been scanned yet. The window is as old as identify_cpu() rebuilding the capabilities. Commit 39b9552281ab ("x86/mm: Optimize boot-time paging mode switching cost") merely let KASAN notice it by making pgtable_l5_enabled() read the feature bit. So no Fixes: tag. Closes: https://lore.kernel.org/bpf/20260610175651.647515-1-ihor.solodrai@linux.dev/ Signed-off-by: Ihor Solodrai Signed-off-by: Borislav Petkov (AMD) Link: https://patch.msgid.link/20260916195203.1099646-6-ihor.solodrai@linux.dev --- arch/x86/kernel/cpu/common.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 156a7b8ec1c1..7d4ff292bdf5 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -2000,8 +2000,6 @@ static void identify_cpu(struct cpuinfo_x86 *c) c->loops_per_jiffy = loops_per_jiffy; - init_cpu_info(c); - if (!cpuid_feature()) identify_cpu_without_cpuid(c); @@ -2186,6 +2184,7 @@ void identify_secondary_cpu(unsigned int cpu) *c = boot_cpu_data; c->cpu_index = cpu; + init_cpu_info(c); identify_cpu(c); x86_spec_ctrl_setup_ap(); update_srbds_msr();