Skip to content

Commit 85900ea

Browse files
amlutoIngo Molnar
authored andcommitted
x86/pti: Map the vsyscall page if needed
Make VSYSCALLs work fully in PTI mode by mapping them properly to the user space visible page tables. [ tglx: Hide unused functions (Patch by Arnd Bergmann) ] Signed-off-by: Andy Lutomirski <luto@kernel.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: David Laight <David.Laight@aculab.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Juergen Gross <jgross@suse.com> Cc: Kees Cook <keescook@chromium.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Ingo Molnar <mingo@kernel.org>
1 parent f55f050 commit 85900ea

File tree

3 files changed

+69
-3
lines changed

3 files changed

+69
-3
lines changed

arch/x86/entry/vsyscall/vsyscall_64.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,14 +344,14 @@ int in_gate_area_no_mm(unsigned long addr)
344344
* vsyscalls but leave the page not present. If so, we skip calling
345345
* this.
346346
*/
347-
static void __init set_vsyscall_pgtable_user_bits(void)
347+
void __init set_vsyscall_pgtable_user_bits(pgd_t *root)
348348
{
349349
pgd_t *pgd;
350350
p4d_t *p4d;
351351
pud_t *pud;
352352
pmd_t *pmd;
353353

354-
pgd = pgd_offset_k(VSYSCALL_ADDR);
354+
pgd = pgd_offset_pgd(root, VSYSCALL_ADDR);
355355
set_pgd(pgd, __pgd(pgd_val(*pgd) | _PAGE_USER));
356356
p4d = p4d_offset(pgd, VSYSCALL_ADDR);
357357
#if CONFIG_PGTABLE_LEVELS >= 5
@@ -373,7 +373,7 @@ void __init map_vsyscall(void)
373373
vsyscall_mode == NATIVE
374374
? PAGE_KERNEL_VSYSCALL
375375
: PAGE_KERNEL_VVAR);
376-
set_vsyscall_pgtable_user_bits();
376+
set_vsyscall_pgtable_user_bits(swapper_pg_dir);
377377
}
378378

379379
BUILD_BUG_ON((unsigned long)__fix_to_virt(VSYSCALL_PAGE) !=

arch/x86/include/asm/vsyscall.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#ifdef CONFIG_X86_VSYSCALL_EMULATION
99
extern void map_vsyscall(void);
10+
extern void set_vsyscall_pgtable_user_bits(pgd_t *root);
1011

1112
/*
1213
* Called on instruction fetch fault in vsyscall page.

arch/x86/mm/pti.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
#include <asm/cpufeature.h>
4040
#include <asm/hypervisor.h>
41+
#include <asm/vsyscall.h>
4142
#include <asm/cmdline.h>
4243
#include <asm/pti.h>
4344
#include <asm/pgtable.h>
@@ -223,6 +224,69 @@ static pmd_t *pti_user_pagetable_walk_pmd(unsigned long address)
223224
return pmd_offset(pud, address);
224225
}
225226

227+
#ifdef CONFIG_X86_VSYSCALL_EMULATION
228+
/*
229+
* Walk the shadow copy of the page tables (optionally) trying to allocate
230+
* page table pages on the way down. Does not support large pages.
231+
*
232+
* Note: this is only used when mapping *new* kernel data into the
233+
* user/shadow page tables. It is never used for userspace data.
234+
*
235+
* Returns a pointer to a PTE on success, or NULL on failure.
236+
*/
237+
static __init pte_t *pti_user_pagetable_walk_pte(unsigned long address)
238+
{
239+
gfp_t gfp = (GFP_KERNEL | __GFP_NOTRACK | __GFP_ZERO);
240+
pmd_t *pmd = pti_user_pagetable_walk_pmd(address);
241+
pte_t *pte;
242+
243+
/* We can't do anything sensible if we hit a large mapping. */
244+
if (pmd_large(*pmd)) {
245+
WARN_ON(1);
246+
return NULL;
247+
}
248+
249+
if (pmd_none(*pmd)) {
250+
unsigned long new_pte_page = __get_free_page(gfp);
251+
if (!new_pte_page)
252+
return NULL;
253+
254+
if (pmd_none(*pmd)) {
255+
set_pmd(pmd, __pmd(_KERNPG_TABLE | __pa(new_pte_page)));
256+
new_pte_page = 0;
257+
}
258+
if (new_pte_page)
259+
free_page(new_pte_page);
260+
}
261+
262+
pte = pte_offset_kernel(pmd, address);
263+
if (pte_flags(*pte) & _PAGE_USER) {
264+
WARN_ONCE(1, "attempt to walk to user pte\n");
265+
return NULL;
266+
}
267+
return pte;
268+
}
269+
270+
static void __init pti_setup_vsyscall(void)
271+
{
272+
pte_t *pte, *target_pte;
273+
unsigned int level;
274+
275+
pte = lookup_address(VSYSCALL_ADDR, &level);
276+
if (!pte || WARN_ON(level != PG_LEVEL_4K) || pte_none(*pte))
277+
return;
278+
279+
target_pte = pti_user_pagetable_walk_pte(VSYSCALL_ADDR);
280+
if (WARN_ON(!target_pte))
281+
return;
282+
283+
*target_pte = *pte;
284+
set_vsyscall_pgtable_user_bits(kernel_to_user_pgdp(swapper_pg_dir));
285+
}
286+
#else
287+
static void __init pti_setup_vsyscall(void) { }
288+
#endif
289+
226290
static void __init
227291
pti_clone_pmds(unsigned long start, unsigned long end, pmdval_t clear)
228292
{
@@ -319,4 +383,5 @@ void __init pti_init(void)
319383
pti_clone_user_shared();
320384
pti_clone_entry_text();
321385
pti_setup_espfix64();
386+
pti_setup_vsyscall();
322387
}

0 commit comments

Comments
 (0)