x86-64: Give vvars their own page
authorAndy Lutomirski <luto@MIT.EDU>
Sun, 5 Jun 2011 17:50:19 +0000 (13:50 -0400)
committerIngo Molnar <mingo@elte.hu>
Sun, 5 Jun 2011 19:30:32 +0000 (21:30 +0200)
Move vvars out of the vsyscall page into their own page and mark
it NX.

Without this patch, an attacker who can force a daemon to call
some fixed address could wait until the time contains, say,
0xCD80, and then execute the current time.

Signed-off-by: Andy Lutomirski <luto@mit.edu>
Cc: Jesper Juhl <jj@chaosbits.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Arjan van de Ven <arjan@infradead.org>
Cc: Jan Beulich <JBeulich@novell.com>
Cc: richard -rw- weinberger <richard.weinberger@gmail.com>
Cc: Mikael Pettersson <mikpe@it.uu.se>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Louis Rilling <Louis.Rilling@kerlabs.com>
Cc: Valdis.Kletnieks@vt.edu
Cc: pageexec@freemail.hu
Link: http://lkml.kernel.org/r/b1460f81dc4463d66ea3f2b5ce240f58d48effec.1307292171.git.luto@mit.edu
Signed-off-by: Ingo Molnar <mingo@elte.hu>
arch/x86/include/asm/fixmap.h
arch/x86/include/asm/pgtable_types.h
arch/x86/include/asm/vvar.h
arch/x86/kernel/vmlinux.lds.S
arch/x86/kernel/vsyscall_64.c

index 4729b2b..460c74e 100644 (file)
@@ -78,6 +78,7 @@ enum fixed_addresses {
        VSYSCALL_LAST_PAGE,
        VSYSCALL_FIRST_PAGE = VSYSCALL_LAST_PAGE
                            + ((VSYSCALL_END-VSYSCALL_START) >> PAGE_SHIFT) - 1,
+       VVAR_PAGE,
        VSYSCALL_HPET,
 #endif
        FIX_DBGP_BASE,
index d56187c..6a29aed 100644 (file)
 #define __PAGE_KERNEL_UC_MINUS         (__PAGE_KERNEL | _PAGE_PCD)
 #define __PAGE_KERNEL_VSYSCALL         (__PAGE_KERNEL_RX | _PAGE_USER)
 #define __PAGE_KERNEL_VSYSCALL_NOCACHE (__PAGE_KERNEL_VSYSCALL | _PAGE_PCD | _PAGE_PWT)
+#define __PAGE_KERNEL_VVAR             (__PAGE_KERNEL_RO | _PAGE_USER)
 #define __PAGE_KERNEL_LARGE            (__PAGE_KERNEL | _PAGE_PSE)
 #define __PAGE_KERNEL_LARGE_NOCACHE    (__PAGE_KERNEL | _PAGE_CACHE_UC | _PAGE_PSE)
 #define __PAGE_KERNEL_LARGE_EXEC       (__PAGE_KERNEL_EXEC | _PAGE_PSE)
 #define PAGE_KERNEL_LARGE_EXEC         __pgprot(__PAGE_KERNEL_LARGE_EXEC)
 #define PAGE_KERNEL_VSYSCALL           __pgprot(__PAGE_KERNEL_VSYSCALL)
 #define PAGE_KERNEL_VSYSCALL_NOCACHE   __pgprot(__PAGE_KERNEL_VSYSCALL_NOCACHE)
+#define PAGE_KERNEL_VVAR               __pgprot(__PAGE_KERNEL_VVAR)
 
 #define PAGE_KERNEL_IO                 __pgprot(__PAGE_KERNEL_IO)
 #define PAGE_KERNEL_IO_NOCACHE         __pgprot(__PAGE_KERNEL_IO_NOCACHE)
index a4eaca4..de656ac 100644 (file)
  * In normal kernel code, they are used like any other variable.
  * In user code, they are accessed through the VVAR macro.
  *
- * Each of these variables lives in the vsyscall page, and each
- * one needs a unique offset within the little piece of the page
- * reserved for vvars.  Specify that offset in DECLARE_VVAR.
- * (There are 896 bytes available.  If you mess up, the linker will
- * catch it.)
+ * These variables live in a page of kernel data that has an extra RO
+ * mapping for userspace.  Each variable needs a unique offset within
+ * that page; specify that offset with the DECLARE_VVAR macro.  (If
+ * you mess up, the linker will catch it.)
  */
 
-/* Offset of vars within vsyscall page */
-#define VSYSCALL_VARS_OFFSET (3072 + 128)
+/* Base address of vvars.  This is not ABI. */
+#define VVAR_ADDRESS (-10*1024*1024 - 4096)
 
 #if defined(__VVAR_KERNEL_LDS)
 
  * right place.
  */
 #define DECLARE_VVAR(offset, type, name) \
-       EMIT_VVAR(name, VSYSCALL_VARS_OFFSET + offset)
+       EMIT_VVAR(name, offset)
 
 #else
 
 #define DECLARE_VVAR(offset, type, name)                               \
        static type const * const vvaraddr_ ## name =                   \
-               (void *)(VSYSCALL_START + VSYSCALL_VARS_OFFSET + (offset));
+               (void *)(VVAR_ADDRESS + (offset));
 
 #define DEFINE_VVAR(type, name)                                                \
-       type __vvar_ ## name                                            \
-       __attribute__((section(".vsyscall_var_" #name), aligned(16)))
+       type name                                                       \
+       __attribute__((section(".vvar_" #name), aligned(16)))
 
 #define VVAR(name) (*vvaraddr_ ## name)
 
@@ -49,4 +48,3 @@ DECLARE_VVAR(16, int, vgetcpu_mode)
 DECLARE_VVAR(128, struct vsyscall_gtod_data, vsyscall_gtod_data)
 
 #undef DECLARE_VVAR
-#undef VSYSCALL_VARS_OFFSET
index 89aed99..98b378d 100644 (file)
@@ -161,12 +161,6 @@ SECTIONS
 
 #define VVIRT_OFFSET (VSYSCALL_ADDR - __vsyscall_0)
 #define VVIRT(x) (ADDR(x) - VVIRT_OFFSET)
-#define EMIT_VVAR(x, offset) .vsyscall_var_ ## x       \
-       ADDR(.vsyscall_0) + offset                      \
-       : AT(VLOAD(.vsyscall_var_ ## x)) {              \
-               *(.vsyscall_var_ ## x)                  \
-       }                                               \
-       x = VVIRT(.vsyscall_var_ ## x);
 
        . = ALIGN(4096);
        __vsyscall_0 = .;
@@ -192,19 +186,31 @@ SECTIONS
                *(.vsyscall_3)
        }
 
-#define __VVAR_KERNEL_LDS
-#include <asm/vvar.h>
-#undef __VVAR_KERNEL_LDS
-
-       . = __vsyscall_0 + PAGE_SIZE;
+       . = ALIGN(__vsyscall_0 + PAGE_SIZE, PAGE_SIZE);
 
 #undef VSYSCALL_ADDR
 #undef VLOAD_OFFSET
 #undef VLOAD
 #undef VVIRT_OFFSET
 #undef VVIRT
+
+       __vvar_page = .;
+
+       .vvar : AT(ADDR(.vvar) - LOAD_OFFSET) {
+
+             /* Place all vvars at the offsets in asm/vvar.h. */
+#define EMIT_VVAR(name, offset)                \
+               . = offset;             \
+               *(.vvar_ ## name)
+#define __VVAR_KERNEL_LDS
+#include <asm/vvar.h>
+#undef __VVAR_KERNEL_LDS
 #undef EMIT_VVAR
 
+       } :data
+
+       . = ALIGN(__vvar_page + PAGE_SIZE, PAGE_SIZE);
+
 #endif /* CONFIG_X86_64 */
 
        /* Init code and data - will be freed after init */
index 3e68218..3cf1cef 100644 (file)
@@ -284,9 +284,14 @@ void __init map_vsyscall(void)
 {
        extern char __vsyscall_0;
        unsigned long physaddr_page0 = __pa_symbol(&__vsyscall_0);
+       extern char __vvar_page;
+       unsigned long physaddr_vvar_page = __pa_symbol(&__vvar_page);
 
        /* Note that VSYSCALL_MAPPED_PAGES must agree with the code below. */
        __set_fixmap(VSYSCALL_FIRST_PAGE, physaddr_page0, PAGE_KERNEL_VSYSCALL);
+       __set_fixmap(VVAR_PAGE, physaddr_vvar_page, PAGE_KERNEL_VVAR);
+       BUILD_BUG_ON((unsigned long)__fix_to_virt(VVAR_PAGE) !=
+                    (unsigned long)VVAR_ADDRESS);
 }
 
 static int __init vsyscall_init(void)