* Re: [RFC v2 15/26] mm/asi: Initialize the ASI page-table with core mappings @ 2020-07-01 23:53 hackapple 0 siblings, 0 replies; 2+ messages in thread From: hackapple @ 2020-07-01 23:53 UTC (permalink / raw) To: alexandre.chartre Cc: bp, dave.hansen, graf, hpa, jan.setjeeilers, jwadams, konrad.wilk, kvm, linux-kernel, linux-mm, liran.alon, luto, mingo, pbonzini, peterz, rkrcmar, rppt, tglx, x86 Will it affect TLB? I mean maybe cause TLB miss. ^ permalink raw reply [flat|nested] 2+ messages in thread
* [RFC v2 00/27] Kernel Address Space Isolation @ 2019-07-11 14:25 Alexandre Chartre 2019-07-11 14:25 ` [RFC v2 15/26] mm/asi: Initialize the ASI page-table with core mappings Alexandre Chartre 0 siblings, 1 reply; 2+ messages in thread From: Alexandre Chartre @ 2019-07-11 14:25 UTC (permalink / raw) To: pbonzini, rkrcmar, tglx, mingo, bp, hpa, dave.hansen, luto, peterz, kvm, x86, linux-mm, linux-kernel Cc: konrad.wilk, jan.setjeeilers, liran.alon, jwadams, graf, rppt, alexandre.chartre Hi, This is version 2 of the "KVM Address Space Isolation" RFC. The code has been completely changed compared to v1 and it now provides a generic kernel framework which provides Address Space Isolation; and KVM is now a simple consumer of that framework. That's why the RFC title has been changed from "KVM Address Space Isolation" to "Kernel Address Space Isolation". Kernel Address Space Isolation aims to use address spaces to isolate some parts of the kernel (for example KVM) to prevent leaking sensitive data between hyper-threads under speculative execution attacks. You can refer to the first version of this RFC for more context: https://lkml.org/lkml/2019/5/13/515 The new code is still a proof of concept. It is much more stable than v1: I am able to run a VM with a full OS (and also a nested VM) with multiple vcpus. But it looks like there are still some corner cases which cause the system to crash/hang. I am looking for feedback about this new approach where address space isolation is provided by the kernel, and KVM is a just a consumer of this new framework. Changes ======= - Address Space Isolation (ASI) is now provided as a kernel framework: interfaces for creating and managing an ASI are provided by the kernel, there are not implemented in KVM. - An ASI is associated with a page-table, we don't use mm anymore. Entering isolation is done by just updating CR3 to use the ASI page-table. Exiting isolation restores CR3 with the CR3 value present before entering isolation. - Isolation is exited at the beginning of any interrupt/exception handler, and on context switch. - Isolation doesn't disable interrupt, but if an interrupt occurs the interrupt handler will exit isolation. - The current stack is mapped when entering isolation and unmapped when exiting isolation. - The current task is not mapped by default, but there's an option to map it. In such a case, the current task is mapped when entering isolation and unmap when exiting isolation. - Kernel code mapped to the ASI page-table has been reduced to: . the entire kernel (I still need to test with only the kernel text) . the cpu entry area (because we need the GDT to be mapped) . the cpu ASI session (for managing ASI) . the current stack - Optionally, an ASI can request the following kernel mapping to be added: . the stack canary . the cpu offsets (this_cpu_off) . the current task . RCU data (rcu_data) . CPU HW events (cpu_hw_events). All these optional mappings are used for KVM isolation. Patches: ======== The proposed patches provides a framework for creating an Address Space Isolation (ASI) (represented by a struct asi). The ASI has a page-table which can be populated by copying mappings from the kernel page-table. The ASI can then be entered/exited by switching between the kernel page-table and the ASI page-table. In addition, any interrupt, exception or context switch will automatically abort and exit the isolation. Finally patches use the ASI framework to implement KVM isolation. - 01-03: Core of the ASI framework: create/destroy ASI, enter/exit/abort isolation, ASI page-fault handler. - 04-14: Functions to manage, populate and clear an ASI page-table. - 15-20: ASI core mappings and optional mappings. - 21: Make functions to read cr3/cr4 ASI aware - 22-26: Use ASI in KVM to provide isolation for VMExit handlers. API Overview: ============= Here is a short description of the main ASI functions provided by the framwork. struct asi *asi_create(int map_flags) Create an Address Space Isolation (ASI). map_flags can be used to specify optional kernel mapping to be added to the ASI page-table (for example, ASI_MAP_STACK_CANARY to map the stack canary). void asi_destroy(struct asi *asi) Destroy an ASI. int asi_enter(struct asi *asi) Enter isolation for the specified ASI. This switches from the kernel page-table to the page-table associated with the ASI. void asi_exit(struct asi *asi) Exit isolation for the specified ASI. This switches back to the kernel page-table int asi_map(struct asi *asi, void *ptr, unsigned long size); Copy kernel mapping to the specified ASI page-table. void asi_unmap(struct asi *asi, void *ptr); Clear kernel mapping from the specified ASI page-table. ---- Alexandre Chartre (23): mm/x86: Introduce kernel address space isolation mm/asi: Abort isolation on interrupt, exception and context switch mm/asi: Handle page fault due to address space isolation mm/asi: Functions to track buffers allocated for an ASI page-table mm/asi: Add ASI page-table entry offset functions mm/asi: Add ASI page-table entry allocation functions mm/asi: Add ASI page-table entry set functions mm/asi: Functions to populate an ASI page-table from a VA range mm/asi: Helper functions to map module into ASI mm/asi: Keep track of VA ranges mapped in ASI page-table mm/asi: Functions to clear ASI page-table entries for a VA range mm/asi: Function to copy page-table entries for percpu buffer mm/asi: Add asi_remap() function mm/asi: Handle ASI mapped range leaks and overlaps mm/asi: Initialize the ASI page-table with core mappings mm/asi: Option to map current task into ASI rcu: Move tree.h static forward declarations to tree.c rcu: Make percpu rcu_data non-static mm/asi: Add option to map RCU data mm/asi: Add option to map cpu_hw_events mm/asi: Make functions to read cr3/cr4 ASI aware KVM: x86/asi: Populate the KVM ASI page-table KVM: x86/asi: Map KVM memslots and IO buses into KVM ASI Liran Alon (3): KVM: x86/asi: Introduce address_space_isolation module parameter KVM: x86/asi: Introduce KVM address space isolation KVM: x86/asi: Switch to KVM address space on entry to guest arch/x86/entry/entry_64.S | 42 ++- arch/x86/include/asm/asi.h | 237 ++++++++ arch/x86/include/asm/mmu_context.h | 20 +- arch/x86/include/asm/tlbflush.h | 10 + arch/x86/kernel/asm-offsets.c | 4 + arch/x86/kvm/Makefile | 3 +- arch/x86/kvm/mmu.c | 2 +- arch/x86/kvm/vmx/isolation.c | 231 ++++++++ arch/x86/kvm/vmx/vmx.c | 14 +- arch/x86/kvm/vmx/vmx.h | 24 + arch/x86/kvm/x86.c | 68 +++- arch/x86/kvm/x86.h | 1 + arch/x86/mm/Makefile | 2 + arch/x86/mm/asi.c | 459 +++++++++++++++ arch/x86/mm/asi_pagetable.c | 1077 ++++++++++++++++++++++++++++++++++++ arch/x86/mm/fault.c | 7 + include/linux/kvm_host.h | 7 + kernel/rcu/tree.c | 56 ++- kernel/rcu/tree.h | 56 +-- kernel/sched/core.c | 4 + security/Kconfig | 10 + 21 files changed, 2269 insertions(+), 65 deletions(-) create mode 100644 arch/x86/include/asm/asi.h create mode 100644 arch/x86/kvm/vmx/isolation.c create mode 100644 arch/x86/mm/asi.c create mode 100644 arch/x86/mm/asi_pagetable.c ^ permalink raw reply [flat|nested] 2+ messages in thread
* [RFC v2 15/26] mm/asi: Initialize the ASI page-table with core mappings 2019-07-11 14:25 [RFC v2 00/27] Kernel Address Space Isolation Alexandre Chartre @ 2019-07-11 14:25 ` Alexandre Chartre 0 siblings, 0 replies; 2+ messages in thread From: Alexandre Chartre @ 2019-07-11 14:25 UTC (permalink / raw) To: pbonzini, rkrcmar, tglx, mingo, bp, hpa, dave.hansen, luto, peterz, kvm, x86, linux-mm, linux-kernel Cc: konrad.wilk, jan.setjeeilers, liran.alon, jwadams, graf, rppt, alexandre.chartre Core mappings are the minimal mappings we need to be able to enter isolation and handle an isolation abort or exit. This includes the kernel code, the GDT and the percpu ASI sessions. We also need a stack so we map the current stack when entering isolation and unmap it on exit/abort. Optionally, additional mappins can be added like the stack canary or the percpu offset to be able to use get_cpu_var()/this_cpu_ptr() when isolation is active. Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com> --- arch/x86/include/asm/asi.h | 9 ++++- arch/x86/mm/asi.c | 75 +++++++++++++++++++++++++++++++++++++++--- arch/x86/mm/asi_pagetable.c | 30 ++++++++++++---- 3 files changed, 99 insertions(+), 15 deletions(-) diff --git a/arch/x86/include/asm/asi.h b/arch/x86/include/asm/asi.h index cf5d198..1ac8fd3 100644 --- a/arch/x86/include/asm/asi.h +++ b/arch/x86/include/asm/asi.h @@ -11,6 +11,13 @@ #include <asm/pgtable.h> #include <linux/xarray.h> +/* + * asi_create() map flags. Flags are used to map optional data + * when creating an ASI. + */ +#define ASI_MAP_STACK_CANARY 0x01 /* map stack canary */ +#define ASI_MAP_CPU_PTR 0x02 /* for get_cpu_var()/this_cpu_ptr() */ + enum page_table_level { PGT_LEVEL_PTE, PGT_LEVEL_PMD, @@ -73,7 +80,7 @@ struct asi_session { void asi_init_range_mapping(struct asi *asi); void asi_fini_range_mapping(struct asi *asi); -extern struct asi *asi_create(void); +extern struct asi *asi_create(int map_flags); extern void asi_destroy(struct asi *asi); extern int asi_enter(struct asi *asi); extern void asi_exit(struct asi *asi); diff --git a/arch/x86/mm/asi.c b/arch/x86/mm/asi.c index 25633a6..f049438 100644 --- a/arch/x86/mm/asi.c +++ b/arch/x86/mm/asi.c @@ -19,6 +19,17 @@ /* ASI sessions, one per cpu */ DEFINE_PER_CPU_PAGE_ALIGNED(struct asi_session, cpu_asi_session); +struct asi_map_option { + int flag; + void *ptr; + size_t size; +}; + +struct asi_map_option asi_map_percpu_options[] = { + { ASI_MAP_STACK_CANARY, &fixed_percpu_data, sizeof(fixed_percpu_data) }, + { ASI_MAP_CPU_PTR, &this_cpu_off, sizeof(this_cpu_off) }, +}; + static void asi_log_fault(struct asi *asi, struct pt_regs *regs, unsigned long error_code, unsigned long address) { @@ -85,16 +96,55 @@ bool asi_fault(struct pt_regs *regs, unsigned long error_code, return true; } -static int asi_init_mapping(struct asi *asi) +static int asi_init_mapping(struct asi *asi, int flags) { + struct asi_map_option *option; + int i, err; + + /* + * Map the kernel. + * + * XXX We should check if we can map only kernel text, i.e. map with + * size = _etext - _text + */ + err = asi_map(asi, (void *)__START_KERNEL_map, KERNEL_IMAGE_SIZE); + if (err) + return err; + /* - * TODO: Populate the ASI page-table with minimal mappings so - * that we can at least enter isolation and abort. + * Map the cpu_entry_area because we need the GDT to be mapped. + * Not sure we need anything else from cpu_entry_area. */ + err = asi_map_range(asi, (void *)CPU_ENTRY_AREA_PER_CPU, P4D_SIZE, + PGT_LEVEL_P4D); + if (err) + return err; + + /* + * Map the percpu ASI sessions. This is used by interrupt handlers + * to figure out if we have entered isolation and switch back to + * the kernel address space. + */ + err = ASI_MAP_CPUVAR(asi, cpu_asi_session); + if (err) + return err; + + /* + * Optional percpu mappings. + */ + for (i = 0; i < ARRAY_SIZE(asi_map_percpu_options); i++) { + option = &asi_map_percpu_options[i]; + if (flags & option->flag) { + err = asi_map_percpu(asi, option->ptr, option->size); + if (err) + return err; + } + } + return 0; } -struct asi *asi_create(void) +struct asi *asi_create(int map_flags) { struct page *page; struct asi *asi; @@ -115,7 +165,7 @@ struct asi *asi_create(void) spin_lock_init(&asi->fault_lock); asi_init_backend(asi); - err = asi_init_mapping(asi); + err = asi_init_mapping(asi, map_flags); if (err) goto error; @@ -159,6 +209,7 @@ int asi_enter(struct asi *asi) struct asi *current_asi; struct asi_session *asi_session; unsigned long original_cr3; + int err; state = this_cpu_read(cpu_asi_session.state); /* @@ -190,6 +241,13 @@ int asi_enter(struct asi *asi) WARN_ON(asi_session->abort_depth > 0); /* + * We need a stack to run with isolation, so map the current stack. + */ + err = asi_map(asi, current->stack, PAGE_SIZE << THREAD_SIZE_ORDER); + if (err) + goto err_clear_asi; + + /* * Instructions ordering is important here because we should be * able to deal with any interrupt/exception which will abort * the isolation and restore CR3 to its original value: @@ -211,7 +269,7 @@ int asi_enter(struct asi *asi) if (!original_cr3) { WARN_ON(1); err = -EINVAL; - goto err_clear_asi; + goto err_unmap_stack; } asi_session->original_cr3 = original_cr3; @@ -228,6 +286,8 @@ int asi_enter(struct asi *asi) return 0; +err_unmap_stack: + asi_unmap(asi, current->stack); err_clear_asi: asi_session->asi = NULL; asi_session->task = NULL; @@ -284,6 +344,9 @@ void asi_exit(struct asi *asi) * exit isolation before abort_depth reaches 0. */ asi_session->abort_depth = 0; + + /* unmap stack */ + asi_unmap(asi, current->stack); } EXPORT_SYMBOL(asi_exit); diff --git a/arch/x86/mm/asi_pagetable.c b/arch/x86/mm/asi_pagetable.c index f1ee65b..bcc95f2 100644 --- a/arch/x86/mm/asi_pagetable.c +++ b/arch/x86/mm/asi_pagetable.c @@ -710,12 +710,20 @@ int asi_map_range(struct asi *asi, void *ptr, size_t size, map_addr = round_down(addr, page_dir_size); map_end = round_up(end, page_dir_size); - pr_debug("ASI %p: MAP %px/%lx/%d -> %lx-%lx\n", asi, ptr, size, level, - map_addr, map_end); - if (map_addr < addr) - pr_debug("ASI %p: MAP LEAK %lx-%lx\n", asi, map_addr, addr); - if (map_end > end) - pr_debug("ASI %p: MAP LEAK %lx-%lx\n", asi, end, map_end); + /* + * Don't log info the current stack because it is mapped/unmapped + * everytime we enter/exit isolation. + */ + if (ptr != current->stack) { + pr_debug("ASI %p: MAP %px/%lx/%d -> %lx-%lx\n", + asi, ptr, size, level, map_addr, map_end); + if (map_addr < addr) + pr_debug("ASI %p: MAP LEAK %lx-%lx\n", + asi, map_addr, addr); + if (map_end > end) + pr_debug("ASI %p: MAP LEAK %lx-%lx\n", + asi, end, map_end); + } spin_lock_irqsave(&asi->lock, flags); @@ -989,8 +997,14 @@ void asi_unmap(struct asi *asi, void *ptr) addr = (unsigned long)range_mapping->ptr; end = addr + range_mapping->size; - pr_debug("ASI %p: UNMAP %px/%lx/%d\n", asi, ptr, - range_mapping->size, range_mapping->level); + /* + * Don't log info the current stack because it is mapped/unmapped + * everytime we enter/exit isolation. + */ + if (ptr != current->stack) { + pr_debug("ASI %p: UNMAP %px/%lx/%d\n", asi, ptr, + range_mapping->size, range_mapping->level); + } list_del(&range_mapping->list); asi_unmap_overlap(asi, range_mapping); kfree(range_mapping); -- 1.7.1 ^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2020-07-01 23:53 UTC | newest] Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2020-07-01 23:53 [RFC v2 15/26] mm/asi: Initialize the ASI page-table with core mappings hackapple -- strict thread matches above, loose matches on Subject: below -- 2019-07-11 14:25 [RFC v2 00/27] Kernel Address Space Isolation Alexandre Chartre 2019-07-11 14:25 ` [RFC v2 15/26] mm/asi: Initialize the ASI page-table with core mappings Alexandre Chartre
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox