Skip to content

Commit 49275fe

Browse files
amlutoIngo Molnar
authored andcommitted
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 <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: Brian Gerst <[email protected]> Cc: Dave Hansen <[email protected]> Cc: David Laight <[email protected]> Cc: H. Peter Anvin <[email protected]> Cc: Josh Poimboeuf <[email protected]> Cc: Juergen Gross <[email protected]> Cc: Kees Cook <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Signed-off-by: Ingo Molnar <[email protected]>
1 parent 146122e commit 49275fe

File tree

1 file changed

+33
-1
lines changed

1 file changed

+33
-1
lines changed

arch/x86/entry/vsyscall/vsyscall_64.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <asm/unistd.h>
3838
#include <asm/fixmap.h>
3939
#include <asm/traps.h>
40+
#include <asm/paravirt.h>
4041

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

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+
332362
void __init map_vsyscall(void)
333363
{
334364
extern char __vsyscall_page;
335365
unsigned long physaddr_vsyscall = __pa_symbol(&__vsyscall_page);
336366

337-
if (vsyscall_mode != NONE)
367+
if (vsyscall_mode != NONE) {
338368
__set_fixmap(VSYSCALL_PAGE, physaddr_vsyscall,
339369
vsyscall_mode == NATIVE
340370
? PAGE_KERNEL_VSYSCALL
341371
: PAGE_KERNEL_VVAR);
372+
set_vsyscall_pgtable_user_bits();
373+
}
342374

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

0 commit comments

Comments
 (0)