lkml.org 
[lkml]   [2017]   [Dec]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[patch V181 04/54] x86/vsyscall/64: Explicitly set _PAGE_USER in the pagetable hierarchy
    From: Andy Lutomirski <luto@kernel.org>

    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: Ingo Molnar <mingo@kernel.org>
    Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
    Cc: Borislav Petkov <bp@alien8.de>
    Cc: Brian Gerst <brgerst@gmail.com>
    Cc: David Laight <David.Laight@aculab.com>
    Cc: Kees Cook <keescook@chromium.org>
    Cc: Linus Torvalds <torvalds@linux-foundation.org>
    Cc: Peter Zijlstra <peterz@infradead.org>
    Cc; Juergen Gross <jgross@suse.com>
    ---
    arch/x86/entry/vsyscall/vsyscall_64.c | 34 +++++++++++++++++++++++++++++++++-
    1 file changed, 33 insertions(+), 1 deletion(-)

    --- a/arch/x86/entry/vsyscall/vsyscall_64.c
    +++ b/arch/x86/entry/vsyscall/vsyscall_64.c
    @@ -37,6 +37,7 @@
    #include <asm/unistd.h>
    #include <asm/fixmap.h>
    #include <asm/traps.h>
    +#include <asm/paravirt.h>

    #define CREATE_TRACE_POINTS
    #include "vsyscall_trace.h"
    @@ -329,16 +330,47 @@ int in_gate_area_no_mm(unsigned long add
    return vsyscall_mode != NONE && (addr & PAGE_MASK) == VSYSCALL_ADDR;
    }

    +/*
    + * The VSYSCALL page is the only user-accessible page in the kernel address
    + * range. Normally, the kernel page tables can have _PAGE_USER clear, but
    + * the tables covering VSYSCALL_ADDR need _PAGE_USER set if vsyscalls
    + * are enabled.
    + *
    + * Some day we may create a "minimal" vsyscall mode in which we emulate
    + * vsyscalls but leave the page not present. If so, we skip calling
    + * this.
    + */
    +static void __init set_vsyscall_pgtable_user_bits(void)
    +{
    + pgd_t *pgd;
    + p4d_t *p4d;
    + pud_t *pud;
    + pmd_t *pmd;
    +
    + pgd = pgd_offset_k(VSYSCALL_ADDR);
    + set_pgd(pgd, __pgd(pgd_val(*pgd) | _PAGE_USER));
    + p4d = p4d_offset(pgd, VSYSCALL_ADDR);
    +#if CONFIG_PGTABLE_LEVELS >= 5
    + p4d->p4d |= _PAGE_USER;
    +#endif
    + pud = pud_offset(p4d, VSYSCALL_ADDR);
    + set_pud(pud, __pud(pud_val(*pud) | _PAGE_USER));
    + pmd = pmd_offset(pud, VSYSCALL_ADDR);
    + set_pmd(pmd, __pmd(pmd_val(*pmd) | _PAGE_USER));
    +}
    +
    void __init map_vsyscall(void)
    {
    extern char __vsyscall_page;
    unsigned long physaddr_vsyscall = __pa_symbol(&__vsyscall_page);

    - if (vsyscall_mode != NONE)
    + if (vsyscall_mode != NONE) {
    __set_fixmap(VSYSCALL_PAGE, physaddr_vsyscall,
    vsyscall_mode == NATIVE
    ? PAGE_KERNEL_VSYSCALL
    : PAGE_KERNEL_VVAR);
    + set_vsyscall_pgtable_user_bits();
    + }

    BUILD_BUG_ON((unsigned long)__fix_to_virt(VSYSCALL_PAGE) !=
    (unsigned long)VSYSCALL_ADDR);

    \
     
     \ /
      Last update: 2017-12-20 23:10    [W:7.424 / U:0.032 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site