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