From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758809AbdLRNr1 (ORCPT ); Mon, 18 Dec 2017 08:47:27 -0500 Received: from bombadil.infradead.org ([65.50.211.133]:42156 "EHLO bombadil.infradead.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751853AbdLRNrX (ORCPT ); Mon, 18 Dec 2017 08:47:23 -0500 Date: Mon, 18 Dec 2017 14:46:48 +0100 From: Peter Zijlstra To: Thomas Gleixner Cc: LKML , x86@kernel.org, Linus Torvalds , Andy Lutomirsky , Dave Hansen , Borislav Petkov , Greg KH , keescook@google.com, hughd@google.com, Brian Gerst , Josh Poimboeuf , Denys Vlasenko , Rik van Riel , Boris Ostrovsky , Juergen Gross , David Laight , Eduardo Valentin , aliguori@amazon.com, Will Deacon , daniel.gruss@iaik.tugraz.at, Kees Cook , Borislav Petkov , "Kirill A. Shutemov" Subject: Re: [patch V163 39/51] x86/pti: Put the LDT in its own PGD if PTI is on Message-ID: <20171218134648.73xsa6kpos6t6jnt@hirez.programming.kicks-ass.net> References: <20171218114215.239543034@linutronix.de> <20171218115256.826191724@linutronix.de> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20171218115256.826191724@linutronix.de> User-Agent: NeoMutt/20170609 (1.8.3) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Mon, Dec 18, 2017 at 12:42:54PM +0100, Thomas Gleixner wrote: > This will significantly slow down LDT users, but that shouldn't matter for > important workloads -- the LDT is only used by DOSEMU(2), Wine, and very > old libc implementations. > +/* > + * If PTI is enabled, this maps the LDT into the kernelmode and > + * usermode tables for the given mm. > + * > + * There is no corresponding unmap function. Even if the LDT is freed, we > + * leave the PTEs around until the slot is reused or the mm is destroyed. > + * This is harmless: the LDT is always in ordinary memory, and no one will > + * access the freed slot. > + * > + * If we wanted to unmap freed LDTs, we'd also need to do a flush to make > + * it useful, and the flush would slow down modify_ldt(). > + */ > +static int > +map_ldt_struct(struct mm_struct *mm, struct ldt_struct *ldt, int slot) > +{ > +#ifdef CONFIG_PAGE_TABLE_ISOLATION > + bool is_vmalloc, had_top_level_entry; > + unsigned long va; > + spinlock_t *ptl; > + pgd_t *pgd; > + int i; > + > + if (!static_cpu_has(X86_FEATURE_PTI)) > + return 0; > + > + /* > + * Any given ldt_struct should have map_ldt_struct() called at most > + * once. > + */ > + WARN_ON(ldt->slot != -1); > + > + /* > + * Did we already have the top level entry allocated? We can't > + * use pgd_none() for this because it doens't do anything on > + * 4-level page table kernels. > + */ > + pgd = pgd_offset(mm, LDT_BASE_ADDR); > + had_top_level_entry = (pgd->pgd != 0); > + > + is_vmalloc = is_vmalloc_addr(ldt->entries); > + > + for (i = 0; i * PAGE_SIZE < ldt->nr_entries * LDT_ENTRY_SIZE; i++) { > + unsigned long offset = i << PAGE_SHIFT; > + const void *src = (char *)ldt->entries + offset; > + unsigned long pfn; > + pte_t pte, *ptep; > + > + va = (unsigned long)ldt_slot_va(slot) + offset; > + pfn = is_vmalloc ? vmalloc_to_pfn(src) : > + page_to_pfn(virt_to_page(src)); > + /* > + * Treat the PTI LDT range as a *userspace* range. > + * get_locked_pte() will allocate all needed pagetables > + * and account for them in this mm. > + */ > + ptep = get_locked_pte(mm, va, &ptl); > + if (!ptep) > + return -ENOMEM; > + pte = pfn_pte(pfn, __pgprot(__PAGE_KERNEL & ~_PAGE_GLOBAL)); > + set_pte_at(mm, va, ptep, pte); > + pte_unmap_unlock(ptep, ptl); > + } > + > + if (mm->context.ldt) { > + /* > + * We already had an LDT. The top-level entry should already > + * have been allocated and synchronized with the usermode > + * tables. > + */ > + WARN_ON(!had_top_level_entry); > + if (static_cpu_has(X86_FEATURE_PTI)) > + WARN_ON(!kernel_to_user_pgdp(pgd)->pgd); > + } else { > + /* > + * This is the first time we're mapping an LDT for this process. > + * Sync the pgd to the usermode tables. > + */ > + WARN_ON(had_top_level_entry); > + if (static_cpu_has(X86_FEATURE_PTI)) { > + WARN_ON(kernel_to_user_pgdp(pgd)->pgd); > + set_pgd(kernel_to_user_pgdp(pgd), *pgd); > + } > + } > + > + va = (unsigned long)ldt_slot_va(slot); > + flush_tlb_mm_range(mm, va, va + LDT_SLOT_STRIDE, 0); > + > + ldt->slot = slot; > +#endif > + return 0; > +} So if I understand this right, we need to remap+tlb-flush every time we flip LDTs because this still uses the old vmalloc-a-new-ldt thing? Should we base this on top of the ldt_mapping stuff tglx did? Such that we keep the 2 arrays of pages immutable and don't need no tlb flushes?