x86/mm: backport fixes for CVE-2023-0597 and CVE-2023-3640
v2 cover x86/mm: backport fixes for CVE-2023-0597 and CVE-2023-3640
v1: https://lore.kernel.org/all/[email protected]/
v2: fix the regression causing kernel boot failures when both CONFIG_RANDOMIZE_BASE=y and CONFIG_KASAN=y are enabled, instead of backporting commit d4150779e60f ("random32: use real rng for non-deterministic randomness"), which would bring in additional fixing commits:
4051a81774d6 ("locking/lockdep: Use sched_clock() for random numbers") 327b18b7aaed ("mm/kfence: select random number before taking raw lock") f05ccf6a6ac6 ("crypto: testmgr - fix RNG performance in fuzz tests")
replaced the random number generator function (prandom -> random) with in commit dcd5ba760e89 ("x86/mm: Randomize per-cpu entry area"):
- cea = prandom_u32_max(max_cea);
+ cea = (u32)(((u64) get_random_u32() * max_cea) >> 32);
This change will replicate the behavior as if the fixing
commit d4150779e60f ("random32: use real rng for non-deterministic randomness") had been applied.
[PATCH v2 5.10/5.15/6.1 1/5] x86/kasan: Map shadow for percpu pages on demand [PATCH v2 5.10/5.15/6.1 2/5] x86/mm: Recompute physical address for every page of [PATCH v2 5.10/5.15/6.1 3/5] x86/mm: Populate KASAN shadow for entire per-CPU range of [PATCH v2 5.10/5.15/6.1 4/5] x86/mm: Randomize per-cpu entry area
[PATCH v2 5.10/5.15/6.1 5/5] x86/mm: Do not shuffle CPU entry areas without KASLR
PATCH v2 1/5 x86/kasan: Map shadow for percpu pages on demand
From: Andrey Ryabinin <[email protected]>
commit 3f148f3318140035e87decc1214795ff0755757b upstream.
KASAN maps shadow for the entire CPU-entry-area:
[CPU_ENTRY_AREA_BASE, CPU_ENTRY_AREA_BASE + CPU_ENTRY_AREA_MAP_SIZE]
This will explode once the per-cpu entry areas are randomized since it will increase CPU_ENTRY_AREA_MAP_SIZE to 512 GB and KASAN fails to allocate shadow for such big area.
Fix this by allocating KASAN shadow only for really used cpu entry area addresses mapped by cea_map_percpu_pages()
Thanks to the 0day folks for finding and reporting this to be an issue.
[ dhansen: tweak changelog since this will get committed before peterz's
actual cpu-entry-area randomization ]
--- arch/x86/include/asm/kasan.h | 3 +++ arch/x86/mm/cpu_entry_area.c | 8 +++++++- arch/x86/mm/kasan_init_64.c | 15 ++++++++++++--- 3 files changed, 22 insertions(+), 4 deletions(-)
arch/x86/include/asm/kasan.h +3 -0
@@ -28,9 +28,12 @@#ifdef CONFIG_KASAN void __init kasan_early_init (void );void __init kasan_init (void ); +void __init kasan_populate_shadow_for_vaddr (void *va,size_t size,int nid);#else static inline void kasan_early_init (void ) { }static inline void kasan_init (void ) { } +static inline void kasan_populate_shadow_for_vaddr (void *va,size_t size, +int nid) { }#endif #endif
arch/x86/mm/cpu_entry_area.c +7 -1
@@ -9,6 +9,7 @@#include <asm/cpu_entry_area.h> #include <asm/fixmap.h> #include <asm/desc.h> +#include <asm/kasan.h> static DEFINE_PER_CPU_PAGE_ALIGNED(struct entry_stack_page, entry_stack_storage);
@@ -53,8 +54,13 @@ void cea_set_pte(void *cea_vaddr, phys_addr_t pa, pgprot_t flags)static void __init cea_map_percpu_pages (void *cea_vaddr,void *ptr,int pages,pgprot_t prot) { +phys_addr_t pa =per_cpu_ptr_to_phys (ptr); + +kasan_populate_shadow_for_vaddr (cea_vaddr, pages *PAGE_SIZE , +early_pfn_to_nid (PFN_DOWN (pa))); +for ( ; pages; pages--, cea_vaddr+=PAGE_SIZE , ptr +=PAGE_SIZE ) -cea_set_pte (cea_vaddr,per_cpu_ptr_to_phys (ptr), prot); +cea_set_pte (cea_vaddr, pa, prot); }static void __init percpu_setup_debug_store (unsigned int cpu)
arch/x86/mm/kasan_init_64.c +12 -3
@@ -318,6 +318,18 @@ void __init kasan_early_init(void)kasan_map_early_shadow (init_top_pgt); } +void __init kasan_populate_shadow_for_vaddr (void *va,size_t size,int nid) +{ +unsigned long shadow_start, shadow_end; + + shadow_start = (unsigned long )kasan_mem_to_shadow (va); + shadow_start =round_down (shadow_start,PAGE_SIZE ); + shadow_end = (unsigned long )kasan_mem_to_shadow (va + size); + shadow_end =round_up (shadow_end,PAGE_SIZE ); + +kasan_populate_shadow (shadow_start, shadow_end, nid); +} +void __init kasan_init (void ) {int i;
@@ -395,9 +407,6 @@ void __init kasan_init(void)kasan_mem_to_shadow ((void *)VMALLOC_END +1 ), shadow_cpu_entry_begin); -kasan_populate_shadow ((unsigned long )shadow_cpu_entry_begin, - (unsigned long )shadow_cpu_entry_end,0 ); -kasan_populate_early_shadow (shadow_cpu_entry_end,kasan_mem_to_shadow ((void *)__START_KERNEL_map));
Signature · 1 line
2.33.8
PATCH v2 2/5 x86/mm: Recompute physical address for every page of per-CPU CEA mapping
In reply to Vasiliy KovalevFrom: Sean Christopherson <[email protected]>
commit 80d72a8f76e8f3f0b5a70b8c7022578e17bde8e7 upstream.
Recompute the physical address for each per-CPU page in the CPU entry area, a recent commit inadvertantly modified cea_map_percpu_pages() such that every PTE is mapped to the physical address of the first page.
--- arch/x86/mm/cpu_entry_area.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/mm/cpu_entry_area.c +1 -1
@@ -60,7 +60,7 @@ cea_map_percpu_pages(void *cea_vaddr, void *ptr, int pages, pgprot_t prot) early_pfn_to_nid(PFN_DOWN(pa)));for ( ; pages; pages--, cea_vaddr+=PAGE_SIZE , ptr +=PAGE_SIZE ) -cea_set_pte (cea_vaddr, pa, prot); +cea_set_pte (cea_vaddr,per_cpu_ptr_to_phys (ptr), prot); }static void __init percpu_setup_debug_store (unsigned int cpu)
Signature · 1 line
2.33.8
PATCH v2 3/5 x86/mm: Populate KASAN shadow for entire per-CPU range of CPU entry area
In reply to Vasiliy KovalevFrom: Sean Christopherson <[email protected]>
commit 97650148a15e0b30099d6175ffe278b9f55ec66a upstream.
Populate a KASAN shadow for the entire possible per-CPU range of the CPU entry area instead of requiring that each individual chunk map a shadow. Mapping shadows individually is error prone, e.g. the per-CPU GDT mapping was left behind, which can lead to not-present page faults during KASAN validation if the kernel performs a software lookup into the GDT. The DS buffer is also likely affected.
The motivation for mapping the per-CPU areas on-demand was to avoid mapping the entire 512GiB range that's reserved for the CPU entry area, shaving a few bytes by not creating shadows for potentially unused memory was not a goal.
The bug is most easily reproduced by doing a sigreturn with a garbage CS in the sigcontext, e.g.
int main(void)
{
struct sigcontext regs; syscall(__NR_mmap, 0x1ffff000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);
syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 7ul, 0x32ul, -1, 0ul);
syscall(__NR_mmap, 0x21000000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);
memset(®s, 0, sizeof(regs));
regs.cs = 0x1d0;
syscall(__NR_rt_sigreturn);
return 0;}
to coerce the kernel into doing a GDT lookup to compute CS.base when reading the instruction bytes on the subsequent #GP to determine whether or not the #GP is something the kernel should handle, e.g. to fixup UMIP violations or to emulate CLI/STI for IOPL=3 applications.
BUG: unable to handle page fault for address: fffffbc8379ace00 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 16c03a067 P4D 16c03a067 PUD 15b990067 PMD 15b98f067 PTE 0 Oops: 0000 [#1] PREEMPT SMP KASAN CPU: 3 PID: 851 Comm: r2 Not tainted 6.1.0-rc3-next-20221103+ #432 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 RIP: 0010:kasan_check_range+0xdf/0x190 Call Trace: <TASK> get_desc+0xb0/0x1d0 insn_get_seg_base+0x104/0x270 insn_fetch_from_user+0x66/0x80 fixup_umip_exception+0xb1/0x530 exc_general_protection+0x181/0x210 asm_exc_general_protection+0x22/0x30 RIP: 0003:0x0 Code: Unable to access opcode bytes at 0xffffffffffffffd6. RSP: 0003:0000000000000000 EFLAGS: 00000202 RAX: 0000000000000000 RBX: 0000000000000000 RCX: 00000000000001d0 RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 </TASK>
--- arch/x86/mm/cpu_entry_area.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
arch/x86/mm/cpu_entry_area.c +3 -5
@@ -54,11 +54,6 @@ void cea_set_pte(void *cea_vaddr, phys_addr_t pa, pgprot_t flags)static void __init cea_map_percpu_pages (void *cea_vaddr,void *ptr,int pages,pgprot_t prot) { -phys_addr_t pa =per_cpu_ptr_to_phys (ptr); - -kasan_populate_shadow_for_vaddr (cea_vaddr, pages *PAGE_SIZE , -early_pfn_to_nid (PFN_DOWN (pa))); -for ( ; pages; pages--, cea_vaddr+=PAGE_SIZE , ptr +=PAGE_SIZE )cea_set_pte (cea_vaddr,per_cpu_ptr_to_phys (ptr), prot); }
@@ -158,6 +153,9 @@ static void __init setup_cpu_entry_area(unsigned int cpu)pgprot_t tss_prot =PAGE_KERNEL ;#endif +kasan_populate_shadow_for_vaddr (cea,CPU_ENTRY_AREA_SIZE , +early_cpu_to_node (cpu)); +cea_set_pte (&cea->gdt,get_cpu_gdt_paddr (cpu), gdt_prot); cea_map_percpu_pages(&cea->entry_stack_page,
Signature · 1 line
2.33.8
PATCH v2 4/5 x86/mm: Randomize per-cpu entry area
In reply to Vasiliy KovalevFrom: Peter Zijlstra <[email protected]>
commit 97e3d26b5e5f371b3ee223d94dd123e6c442ba80 upstream.
Seth found that the CPU-entry-area; the piece of per-cpu data that is mapped into the userspace page-tables for kPTI is not subject to any randomization -- irrespective of kASLR settings.
On x86_64 a whole P4D (512 GB) of virtual address space is reserved for this structure, which is plenty large enough to randomize things a little.
As such, use a straight forward randomization scheme that avoids duplicates to spread the existing CPUs over the available space.
[ bp: Fix le build. ]
[kovalev: Replaced the random number generator (prandom -> random) to prevent regression in kernel booting with configurations CONFIG_RANDOMIZE_BASE=y and CONFIG_KASAN=y in the absence of commit d4150779e60f ("random32: use real rng for non-deterministic randomness").]
--- arch/x86/include/asm/cpu_entry_area.h | 4 --- arch/x86/include/asm/pgtable_areas.h | 8 ++++- arch/x86/kernel/hw_breakpoint.c | 2 +- arch/x86/mm/cpu_entry_area.c | 47 ++++++++++++++++++++++++--- 4 files changed, 51 insertions(+), 10 deletions(-)
arch/x86/include/asm/cpu_entry_area.h +0 -4
@@ -130,10 +130,6 @@ struct cpu_entry_area { };#define CPU_ENTRY_AREA_SIZE (sizeof(struct cpu_entry_area)) -#define CPU_ENTRY_AREA_ARRAY_SIZE (CPU_ENTRY_AREA_SIZE * NR_CPUS) - -/* Total size includes the readonly IDT mapping page as well: */ -#define CPU_ENTRY_AREA_TOTAL_SIZE (CPU_ENTRY_AREA_ARRAY_SIZE + PAGE_SIZE) DECLARE_PER_CPU(struct cpu_entry_area *, cpu_entry_area); DECLARE_PER_CPU(struct cea_exception_stacks *, cea_exception_stacks);
arch/x86/include/asm/pgtable_areas.h +7 -1
@@ -11,6 +11,12 @@#define CPU_ENTRY_AREA_RO_IDT_VADDR ((void *)CPU_ENTRY_AREA_RO_IDT) -#define CPU_ENTRY_AREA_MAP_SIZE (CPU_ENTRY_AREA_PER_CPU + CPU_ENTRY_AREA_ARRAY_SIZE - CPU_ENTRY_AREA_BASE) +#ifdef CONFIG_X86_32 +#define CPU_ENTRY_AREA_MAP_SIZE (CPU_ENTRY_AREA_PER_CPU + \ + (CPU_ENTRY_AREA_SIZE * NR_CPUS) - \ + CPU_ENTRY_AREA_BASE) +#else +#define CPU_ENTRY_AREA_MAP_SIZE P4D_SIZE +#endif #endif /* _ASM_X86_PGTABLE_AREAS_H */
arch/x86/kernel/hw_breakpoint.c +1 -1
@@ -266,7 +266,7 @@ static inline bool within_cpu_entry(unsigned long addr, unsigned long end)/* CPU entry erea is always used for CPU entry */ if (within_area (addr, end,CPU_ENTRY_AREA_BASE , -CPU_ENTRY_AREA_TOTAL_SIZE )) +CPU_ENTRY_AREA_MAP_SIZE ))return true;/*
arch/x86/mm/cpu_entry_area.c +43 -4
78 lines not on this page · raw message
Signature · 1 line
2.33.8
PATCH v2 5/5 x86/mm: Do not shuffle CPU entry areas without KASLR
In reply to Vasiliy KovalevFrom: Michal Koutný <[email protected]>
commit a3f547addcaa10df5a226526bc9e2d9a94542344 upstream.
The commit 97e3d26b5e5f ("x86/mm: Randomize per-cpu entry area") fixed an omission of KASLR on CPU entry areas. It doesn't take into account KASLR switches though, which may result in unintended non-determinism when a user wants to avoid it (e.g. debugging, benchmarking).
Generate only a single combination of CPU entry areas offsets -- the linear array that existed prior randomization when KASLR is turned off.
Since we have 3f148f331814 ("x86/kasan: Map shadow for percpu pages on demand") and followups, we can use the more relaxed guard kasrl_enabled() (in contrast to kaslr_memory_enabled()).
--- arch/x86/mm/cpu_entry_area.c | 7 +++++++ 1 file changed, 7 insertions(+)
arch/x86/mm/cpu_entry_area.c +7 -0
@@ -11,6 +11,7 @@#include <asm/fixmap.h> #include <asm/desc.h> #include <asm/kasan.h> +#include <asm/setup.h> static DEFINE_PER_CPU_PAGE_ALIGNED(struct entry_stack_page, entry_stack_storage);
@@ -30,6 +31,12 @@ static __init void init_cea_offsets(void)unsigned int max_cea;unsigned int i, j; +if (!kaslr_enabled ()) { +for_each_possible_cpu (i) +per_cpu (_cea_offset, i) = i; +return ; + } + max_cea = (CPU_ENTRY_AREA_MAP_SIZE -PAGE_SIZE ) /CPU_ENTRY_AREA_SIZE ;/* O(sodding terrible) */
Signature · 1 line
2.33.8