Michal Koutný 3a695c
From: Peter Zijlstra <peterz@infradead.org>
Michal Koutný 3a695c
Date: Thu, 27 Oct 2022 14:54:41 -0700
Michal Koutný 3a695c
Subject: x86/mm: Randomize per-cpu entry area
Michal Koutný 3a695c
Git-commit: 97e3d26b5e5f371b3ee223d94dd123e6c442ba80
Michal Koutný 3a695c
Patch-mainline: v6.2-rc1
Michal Koutný 3a695c
References: bsc#1207845 CVE-2023-0597
Michal Koutný 3a695c
Michal Koutný 3a695c
Seth found that the CPU-entry-area; the piece of per-cpu data that is
Michal Koutný 3a695c
mapped into the userspace page-tables for kPTI is not subject to any
Michal Koutný 3a695c
randomization -- irrespective of kASLR settings.
Michal Koutný 3a695c
Michal Koutný 3a695c
On x86_64 a whole P4D (512 GB) of virtual address space is reserved for
Michal Koutný 3a695c
this structure, which is plenty large enough to randomize things a
Michal Koutný 3a695c
little.
Michal Koutný 3a695c
Michal Koutný 3a695c
As such, use a straight forward randomization scheme that avoids
Michal Koutný 3a695c
duplicates to spread the existing CPUs over the available space.
Michal Koutný 3a695c
Michal Koutný 3a695c
  [ bp: Fix le build. ]
Michal Koutný 3a695c
Michal Koutný 3a695c
Reported-by: Seth Jenkins <sethjenkins@google.com>
Michal Koutný 3a695c
Reviewed-by: Kees Cook <keescook@chromium.org>
Michal Koutný 3a695c
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Michal Koutný 3a695c
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Michal Koutný 3a695c
Signed-off-by: Borislav Petkov <bp@suse.de>
Michal Koutný 3a695c
[mkoutny: v5.14 backport: init_cea_offsets() is called way before
Michal Koutný 3a695c
	  prandom_init_early() initcall, prandom_u32_max() is not properly
Michal Koutný 3a695c
	  seeded yet. Use KASLR seed and local state to generate CPU entry
Michal Koutný 3a695c
	  areas offsets, this is based on the approach in
Michal Koutný 3a695c
	  kernel_randomize_memory() and should provide same randomness
Michal Koutný 3a695c
	  guarantees -- beware we don't get cryptographically secure random
Michal Koutný 3a695c
	  offsets. This reduces effective entropy in exfiltrating *all* CPU
Michal Koutný 3a695c
	  entry areas by log2(nr_cpus) bits, entropy for *any* CPU is
Michal Koutný 3a695c
	  unaffected.
Michal Koutný 3a695c
	  This was chosen instead of backporting f62384995e4c ("random: split
Michal Koutný 3a695c
	  initialization into early step and later step") and crng related
Michal Koutný 3a695c
	  reworks.]
Michal Koutný 3a695c
[mkoutny: v5.3 backport: dropped hw_breakpoint hunk without 24ae0c91cbc5
Michal Koutný 3a695c
	  ("x86/hw_breakpoint: Prevent data breakpoints on cpu_entry_area"),
Michal Koutný 3a695c
	  adjusted context for missing doublefault_stack on 32b]
Michal Koutný 3a695c
Acked-by: Michal Koutný <mkoutny@suse.com>
Michal Koutný 3a695c
---
Michal Koutný 3a695c
 arch/x86/include/asm/cpu_entry_area.h |   13 ++++-----
Michal Koutný 3a695c
 arch/x86/mm/cpu_entry_area.c          |   48 ++++++++++++++++++++++++++++++++--
Michal Koutný 3a695c
 2 files changed, 53 insertions(+), 8 deletions(-)
Michal Koutný 3a695c
Michal Koutný 3a695c
--- a/arch/x86/include/asm/cpu_entry_area.h
Michal Koutný 3a695c
+++ b/arch/x86/include/asm/cpu_entry_area.h
Michal Koutný 3a695c
@@ -111,10 +111,6 @@ struct cpu_entry_area {
Michal Koutný 3a695c
 };
Michal Koutný 3a695c
 
Michal Koutný 3a695c
 #define CPU_ENTRY_AREA_SIZE		(sizeof(struct cpu_entry_area))
Michal Koutný 3a695c
-#define CPU_ENTRY_AREA_ARRAY_SIZE	(CPU_ENTRY_AREA_SIZE * NR_CPUS)
Michal Koutný 3a695c
-
Michal Koutný 3a695c
-/* Total size includes the readonly IDT mapping page as well: */
Michal Koutný 3a695c
-#define CPU_ENTRY_AREA_TOTAL_SIZE	(CPU_ENTRY_AREA_ARRAY_SIZE + PAGE_SIZE)
Michal Koutný 3a695c
 
Michal Koutný 3a695c
 DECLARE_PER_CPU(struct cpu_entry_area *, cpu_entry_area);
Michal Koutný 3a695c
 DECLARE_PER_CPU(struct cea_exception_stacks *, cea_exception_stacks);
Michal Koutný 3a695c
@@ -128,8 +124,13 @@ extern void cea_set_pte(void *cea_vaddr,
Michal Koutný 3a695c
 
Michal Koutný 3a695c
 #define CPU_ENTRY_AREA_RO_IDT_VADDR	((void *)CPU_ENTRY_AREA_RO_IDT)
Michal Koutný 3a695c
 
Michal Koutný 3a695c
-#define CPU_ENTRY_AREA_MAP_SIZE			\
Michal Koutný 3a695c
-	(CPU_ENTRY_AREA_PER_CPU + CPU_ENTRY_AREA_ARRAY_SIZE - CPU_ENTRY_AREA_BASE)
Michal Koutný 3a695c
+#ifdef CONFIG_X86_32
Michal Koutný 3a695c
+#define CPU_ENTRY_AREA_MAP_SIZE		(CPU_ENTRY_AREA_PER_CPU +		\
Michal Koutný 3a695c
+					 (CPU_ENTRY_AREA_SIZE * NR_CPUS) -	\
Michal Koutný 3a695c
+					 CPU_ENTRY_AREA_BASE)
Michal Koutný 3a695c
+#else
Michal Koutný 3a695c
+#define CPU_ENTRY_AREA_MAP_SIZE		P4D_SIZE
Michal Koutný 3a695c
+#endif
Michal Koutný 3a695c
 
Michal Koutný 3a695c
 extern struct cpu_entry_area *get_cpu_entry_area(int cpu);
Michal Koutný 3a695c
 
Michal Koutný 3a695c
--- a/arch/x86/mm/cpu_entry_area.c
Michal Koutný 3a695c
+++ b/arch/x86/mm/cpu_entry_area.c
Michal Koutný 3a695c
@@ -4,6 +4,7 @@
Michal Koutný 3a695c
 #include <linux/percpu.h>
Michal Koutný 3a695c
 #include <linux/kallsyms.h>
Michal Koutný 3a695c
 #include <linux/kcore.h>
Michal Koutný 3a695c
+#include <linux/prandom.h>
Michal Koutný 3a695c
 
Michal Koutný 3a695c
 #include <asm/cpu_entry_area.h>
Michal Koutný 3a695c
 #include <asm/pgtable.h>
Michal Koutný 3a695c
@@ -15,11 +16,53 @@ static DEFINE_PER_CPU_PAGE_ALIGNED(struc
Michal Koutný 3a695c
 #ifdef CONFIG_X86_64
Michal Koutný 3a695c
 static DEFINE_PER_CPU_PAGE_ALIGNED(struct exception_stacks, exception_stacks);
Michal Koutný 3a695c
 DEFINE_PER_CPU(struct cea_exception_stacks*, cea_exception_stacks);
Michal Koutný 3a695c
+
Michal Koutný 3a695c
+static DEFINE_PER_CPU_READ_MOSTLY(unsigned long, _cea_offset);
Michal Koutný 3a695c
+
Michal Koutný 3a695c
+static __always_inline unsigned int cea_offset(unsigned int cpu)
Michal Koutný 3a695c
+{
Michal Koutný 3a695c
+	return per_cpu(_cea_offset, cpu);
Michal Koutný 3a695c
+}
Michal Koutný 3a695c
+
Michal Koutný 3a695c
+static __init void init_cea_offsets(void)
Michal Koutný 3a695c
+{
Michal Koutný 3a695c
+	struct rnd_state rand_state;
Michal Koutný 3a695c
+	unsigned int max_cea, rand;
Michal Koutný 3a695c
+	unsigned int i, j;
Michal Koutný 3a695c
+
Michal Koutný 3a695c
+	max_cea = (CPU_ENTRY_AREA_MAP_SIZE - PAGE_SIZE) / CPU_ENTRY_AREA_SIZE;
Michal Koutný 3a695c
+	prandom_seed_state(&rand_state, kaslr_get_random_long("CPU entry"));
Michal Koutný 3a695c
+
Michal Koutný 3a695c
+	/* O(sodding terrible) */
Michal Koutný 3a695c
+	for_each_possible_cpu(i) {
Michal Koutný 3a695c
+		unsigned int cea;
Michal Koutný 3a695c
+
Michal Koutný 3a695c
+again:
Michal Koutný 3a695c
+		prandom_bytes_state(&rand_state, &rand, sizeof(rand));
Michal Koutný 3a695c
+		cea = rand % max_cea;
Michal Koutný 3a695c
+
Michal Koutný 3a695c
+		for_each_possible_cpu(j) {
Michal Koutný 3a695c
+			if (cea_offset(j) == cea)
Michal Koutný 3a695c
+				goto again;
Michal Koutný 3a695c
+
Michal Koutný 3a695c
+			if (i == j)
Michal Koutný 3a695c
+				break;
Michal Koutný 3a695c
+		}
Michal Koutný 3a695c
+
Michal Koutný 3a695c
+		per_cpu(_cea_offset, i) = cea;
Michal Koutný 3a695c
+	}
Michal Koutný 3a695c
+}
Michal Koutný 3a695c
+#else /* !X86_64 */
Michal Koutný 3a695c
+static __always_inline unsigned int cea_offset(unsigned int cpu)
Michal Koutný 3a695c
+{
Michal Koutný 3a695c
+	return cpu;
Michal Koutný 3a695c
+}
Michal Koutný 3a695c
+static inline void init_cea_offsets(void) { }
Michal Koutný 3a695c
 #endif
Michal Koutný 3a695c
 
Michal Koutný 3a695c
 struct cpu_entry_area *get_cpu_entry_area(int cpu)
Michal Koutný 3a695c
 {
Michal Koutný 3a695c
-	unsigned long va = CPU_ENTRY_AREA_PER_CPU + cpu * CPU_ENTRY_AREA_SIZE;
Michal Koutný 3a695c
+	unsigned long va = CPU_ENTRY_AREA_PER_CPU + cea_offset(cpu) * CPU_ENTRY_AREA_SIZE;
Michal Koutný 3a695c
 	BUILD_BUG_ON(sizeof(struct cpu_entry_area) % PAGE_SIZE != 0);
Michal Koutný 3a695c
 
Michal Koutný 3a695c
 	return (struct cpu_entry_area *) va;
Michal Koutný 3a695c
@@ -180,7 +223,6 @@ static __init void setup_cpu_entry_area_
Michal Koutný 3a695c
 
Michal Koutný 3a695c
 	/* The +1 is for the readonly IDT: */
Michal Koutný 3a695c
 	BUILD_BUG_ON((CPU_ENTRY_AREA_PAGES+1)*PAGE_SIZE != CPU_ENTRY_AREA_MAP_SIZE);
Michal Koutný 3a695c
-	BUILD_BUG_ON(CPU_ENTRY_AREA_TOTAL_SIZE != CPU_ENTRY_AREA_MAP_SIZE);
Michal Koutný 3a695c
 	BUG_ON(CPU_ENTRY_AREA_BASE & ~PMD_MASK);
Michal Koutný 3a695c
 
Michal Koutný 3a695c
 	start = CPU_ENTRY_AREA_BASE;
Michal Koutný 3a695c
@@ -196,6 +238,8 @@ void __init setup_cpu_entry_areas(void)
Michal Koutný 3a695c
 {
Michal Koutný 3a695c
 	unsigned int cpu;
Michal Koutný 3a695c
 
Michal Koutný 3a695c
+	init_cea_offsets();
Michal Koutný 3a695c
+
Michal Koutný 3a695c
 	setup_cpu_entry_area_ptes();
Michal Koutný 3a695c
 
Michal Koutný 3a695c
 	for_each_possible_cpu(cpu)