diff options
author | Andy Lutomirski <luto@kernel.org> | 2017-12-11 01:47:19 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2017-12-22 14:13:01 -0500 |
commit | 49275fef986abfb8b476e4708aaecc07e7d3e087 (patch) | |
tree | 9399ac6f7e74d1422e34de7164d62037fff80c25 | |
parent | 146122e24bdf208015d629babba673e28d090709 (diff) |
x86/vsyscall/64: Explicitly set _PAGE_USER in the pagetable hierarchy
The kernel is very erratic as to which pagetables have _PAGE_USER set. The
vsyscall page gets lucky: it seems that all of the relevant pagetables are
among the apparently arbitrary ones that set _PAGE_USER. Rather than
relying on chance, just explicitly set _PAGE_USER.
This will let us clean up pagetable setup to stop setting _PAGE_USER. The
added code can also be reused by pagetable isolation to manage the
_PAGE_USER bit in the usermode tables.
[ tglx: Folded paravirt fix from Juergen Gross ]
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>
-rw-r--r-- | arch/x86/entry/vsyscall/vsyscall_64.c | 34 |
1 files changed, 33 insertions, 1 deletions
diff --git a/arch/x86/entry/vsyscall/vsyscall_64.c b/arch/x86/entry/vsyscall/vsyscall_64.c index f279ba2643dc..daad57c76e42 100644 --- a/arch/x86/entry/vsyscall/vsyscall_64.c +++ b/arch/x86/entry/vsyscall/vsyscall_64.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <asm/unistd.h> | 37 | #include <asm/unistd.h> |
38 | #include <asm/fixmap.h> | 38 | #include <asm/fixmap.h> |
39 | #include <asm/traps.h> | 39 | #include <asm/traps.h> |
40 | #include <asm/paravirt.h> | ||
40 | 41 | ||
41 | #define CREATE_TRACE_POINTS | 42 | #define CREATE_TRACE_POINTS |
42 | #include "vsyscall_trace.h" | 43 | #include "vsyscall_trace.h" |
@@ -329,16 +330,47 @@ int in_gate_area_no_mm(unsigned long addr) | |||
329 | return vsyscall_mode != NONE && (addr & PAGE_MASK) == VSYSCALL_ADDR; | 330 | return vsyscall_mode != NONE && (addr & PAGE_MASK) == VSYSCALL_ADDR; |
330 | } | 331 | } |
331 | 332 | ||
333 | /* | ||
334 | * The VSYSCALL page is the only user-accessible page in the kernel address | ||
335 | * range. Normally, the kernel page tables can have _PAGE_USER clear, but | ||
336 | * the tables covering VSYSCALL_ADDR need _PAGE_USER set if vsyscalls | ||
337 | * are enabled. | ||
338 | * | ||
339 | * Some day we may create a "minimal" vsyscall mode in which we emulate | ||
340 | * vsyscalls but leave the page not present. If so, we skip calling | ||
341 | * this. | ||
342 | */ | ||
343 | static void __init set_vsyscall_pgtable_user_bits(void) | ||
344 | { | ||
345 | pgd_t *pgd; | ||
346 | p4d_t *p4d; | ||
347 | pud_t *pud; | ||
348 | pmd_t *pmd; | ||
349 | |||
350 | pgd = pgd_offset_k(VSYSCALL_ADDR); | ||
351 | set_pgd(pgd, __pgd(pgd_val(*pgd) | _PAGE_USER)); | ||
352 | p4d = p4d_offset(pgd, VSYSCALL_ADDR); | ||
353 | #if CONFIG_PGTABLE_LEVELS >= 5 | ||
354 | p4d->p4d |= _PAGE_USER; | ||
355 | #endif | ||
356 | pud = pud_offset(p4d, VSYSCALL_ADDR); | ||
357 | set_pud(pud, __pud(pud_val(*pud) | _PAGE_USER)); | ||
358 | pmd = pmd_offset(pud, VSYSCALL_ADDR); | ||
359 | set_pmd(pmd, __pmd(pmd_val(*pmd) | _PAGE_USER)); | ||
360 | } | ||
361 | |||
332 | void __init map_vsyscall(void) | 362 | void __init map_vsyscall(void) |
333 | { | 363 | { |
334 | extern char __vsyscall_page; | 364 | extern char __vsyscall_page; |
335 | unsigned long physaddr_vsyscall = __pa_symbol(&__vsyscall_page); | 365 | unsigned long physaddr_vsyscall = __pa_symbol(&__vsyscall_page); |
336 | 366 | ||
337 | if (vsyscall_mode != NONE) | 367 | if (vsyscall_mode != NONE) { |
338 | __set_fixmap(VSYSCALL_PAGE, physaddr_vsyscall, | 368 | __set_fixmap(VSYSCALL_PAGE, physaddr_vsyscall, |
339 | vsyscall_mode == NATIVE | 369 | vsyscall_mode == NATIVE |
340 | ? PAGE_KERNEL_VSYSCALL | 370 | ? PAGE_KERNEL_VSYSCALL |
341 | : PAGE_KERNEL_VVAR); | 371 | : PAGE_KERNEL_VVAR); |
372 | set_vsyscall_pgtable_user_bits(); | ||
373 | } | ||
342 | 374 | ||
343 | BUILD_BUG_ON((unsigned long)__fix_to_virt(VSYSCALL_PAGE) != | 375 | BUILD_BUG_ON((unsigned long)__fix_to_virt(VSYSCALL_PAGE) != |
344 | (unsigned long)VSYSCALL_ADDR); | 376 | (unsigned long)VSYSCALL_ADDR); |