Skip to contentSkip to conversation

x86-64: Remove syscall instructions at fixed addresses

PATCH Superseded Andy Lutomirski 4 participants 28 messages May 27, 2011 – May 31, 2011

Subsystems (3)

v1 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

cover x86-64: Remove syscall instructions at fixed addresses

I lied about taking awhile to do this.

There are a bunch of syscall instructions in kernel space at fixed addresses that user code can execute.

One is a time() fallback. Patch 3/5 removes it.

Several are data that isn't marked NX. Patch 2/5 makes vvars NX and 5/5 makes the HPET NX.

The last one is the gettimeofday fallback. We need that, but it doesn't have to be a real syscall. Patch 3/5 adds int 0xCC (callable only from the vsyscall page) that implements the gettimeofday fallback and nothing else.

Patch 1/5 is just a dumb but harmless bug fix from the last vdso series.

I've only tested this in KVM with a hacked-up initramfs, but Ingo wanted it for 2.6.40, so here it is.

Andy Lutomirski (5):
x86-64: Fix alignment of jiffies variable
x86-64: Give vvars their own page
x86-64: Remove kernel.vsyscall64 sysctl
x86-64: Replace vsyscall gettimeofday fallback with int 0xcc
x86-64: Map the HPET NX

 arch/x86/include/asm/fixmap.h        |    1 +
 arch/x86/include/asm/pgtable_types.h |    6 ++-
 arch/x86/include/asm/traps.h         |    4 ++
 arch/x86/include/asm/vgtod.h         |    1 -
 arch/x86/include/asm/vsyscall.h      |    6 ++
 arch/x86/include/asm/vvar.h          |   24 ++++-----
 arch/x86/kernel/entry_64.S           |    2 +
 arch/x86/kernel/hpet.c               |    2 +-
 arch/x86/kernel/traps.c              |    4 ++
 arch/x86/kernel/vmlinux.lds.S        |   27 ++++++----
 arch/x86/kernel/vsyscall_64.c        |   86 ++++++++++++++++++---------------
 arch/x86/vdso/vclock_gettime.c       |   55 ++++++++-------------
 tools/power/x86/turbostat/turbostat  |  Bin 0 -> 29200 bytes
 13 files changed, 117 insertions(+), 101 deletions(-)
 create mode 100755 tools/power/x86/turbostat/turbostat
Signature · 1 line
1.7.5.1
#1 lore raw

PATCH 1/5 x86-64: Fix alignment of jiffies variable

It's declared __attribute__((aligned(16)) but it's explicitly not aligned. This is probably harmless but it's a but embarrassing.

---
 arch/x86/include/asm/vvar.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
arch/x86/include/asm/vvar.h +1 -1
index 341b355..a4eaca4 100644
@@ -45,7 +45,7 @@
 /* DECLARE_VVAR(offset, type, name) */
 
 DECLARE_VVAR(0, volatile unsigned long, jiffies)
-DECLARE_VVAR(8, int, vgetcpu_mode)
+DECLARE_VVAR(16, int, vgetcpu_mode)
 DECLARE_VVAR(128, struct vsyscall_gtod_data, vsyscall_gtod_data)
 
 #undef DECLARE_VVAR
Signature · 1 line
1.7.5.1
#2 lore raw

PATCH 2/5 x86-64: Give vvars their own page

In reply to Andy Lutomirski

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.

---
 arch/x86/include/asm/fixmap.h        |    1 +
 arch/x86/include/asm/pgtable_types.h |    2 ++
 arch/x86/include/asm/vvar.h          |   22 ++++++++++------------
 arch/x86/kernel/vmlinux.lds.S        |   27 ++++++++++++++++-----------
 arch/x86/kernel/vsyscall_64.c        |    5 +++++
 tools/power/x86/turbostat/turbostat  |  Bin 0 -> 29200 bytes
 6 files changed, 34 insertions(+), 23 deletions(-)
 create mode 100755 tools/power/x86/turbostat/turbostat
arch/x86/include/asm/fixmap.h +1 -0
index 4729b2b..460c74e 100644
@@ -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,
arch/x86/include/asm/pgtable_types.h +2 -0
index d56187c..6a29aed6 100644
@@ -108,6 +108,7 @@
 #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)
@@ -130,6 +131,7 @@
 #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)
arch/x86/include/asm/vvar.h +10 -12
index a4eaca4..de656ac 100644
@@ -10,15 +10,14 @@
  * 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)
 
@@ -26,17 +25,17 @@
  * 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
arch/x86/kernel/vmlinux.lds.S +16 -11
index 1432bd4..3c1ec1c 100644
@@ -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,17 +186,28 @@ 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 = .;
+
+#define EMIT_VVAR(name, offset) .vvar_ ## name		\
+	(__vvar_page + offset) :			\
+	 AT(ADDR(.vvar_ ## name) - LOAD_OFFSET) {	\
+		*(.vvar_ ## x)				\
+	} :data
+
+#define __VVAR_KERNEL_LDS
+#include <asm/vvar.h>
+#undef __VVAR_KERNEL_LDS
+
+       . = ALIGN(__vvar_page + PAGE_SIZE, PAGE_SIZE);
+
 #undef EMIT_VVAR
 
 #endif /* CONFIG_X86_64 */
arch/x86/kernel/vsyscall_64.c +5 -0
index 5f6ad03..ee22180 100644
@@ -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)
tools/power/x86/turbostat/turbostat new file binary +0 -0
new file mode 100755
index 0000000000000000000000000000000000000000..97aabd2a0c77eed562f15bc5285911a4db3fa763
GIT binary patch
Signature · 1 line
1.7.5.1
maintainer
#3 lore raw

Re: [PATCH 2/5] x86-64: Give vvars their own page

Quoted 7 lines · Andy Lutomirski

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.

Quoted 8 lines
---
 arch/x86/include/asm/fixmap.h        |    1 +
 arch/x86/include/asm/pgtable_types.h |    2 ++
 arch/x86/include/asm/vvar.h          |   22 ++++++++++------------
 arch/x86/kernel/vmlinux.lds.S        |   27 ++++++++++++++++-----------
 arch/x86/kernel/vsyscall_64.c        |    5 +++++
 tools/power/x86/turbostat/turbostat  |  Bin 0 -> 29200 bytes

You've added the turbostat binary to the diffstat too. I believe this wasn't your intention, no? :)

Signature · 2 lines
Regards/Gruss,
    Boris.
#4 lore raw

Re: [PATCH 2/5] x86-64: Give vvars their own page

Quoted 17 lines · Borislav Petkov
Quoted 7 lines · Andy Lutomirski

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.

Quoted 8 lines

---
 arch/x86/include/asm/fixmap.h        |    1 + arch/x86/include/asm/pgtable_types.h |    2 ++ arch/x86/include/asm/vvar.h          |   22 ++++++++++------------ arch/x86/kernel/vmlinux.lds.S        |   27 ++++++++++++++++----------- arch/x86/kernel/vsyscall_64.c        |    5 +++++ tools/power/x86/turbostat/turbostat  |  Bin 0 -> 29200 bytes

Quoted 2 lines

You've added the turbostat binary to the diffstat too. I believe this wasn't your intention, no? :)

Foiled again!

--Andy

#5 lore raw

PATCH 3/5 x86-64: Remove kernel.vsyscall64 sysctl

In reply to Andy Lutomirski

It's unnecessary overhead in code that's supposed to be highly optimized. Removing it allows us to remove one of the two syscall instructions in the vsyscall page.

The only sensible use for it is for UML users, and it doesn't fully address inconsistent vsyscall results on UML. The real fix for UML is to stop using vsyscalls entirely.

---
 arch/x86/include/asm/vgtod.h   |    1 -
 arch/x86/kernel/vsyscall_64.c  |   34 +------------------------
 arch/x86/vdso/vclock_gettime.c |   55 +++++++++++++++------------------------
 3 files changed, 22 insertions(+), 68 deletions(-)
arch/x86/include/asm/vgtod.h +0 -1
index 646b4c1..aa5add8 100644
@@ -11,7 +11,6 @@ struct vsyscall_gtod_data {
 	time_t		wall_time_sec;
 	u32		wall_time_nsec;
 
-	int		sysctl_enabled;
 	struct timezone sys_tz;
 	struct { /* extract of a clocksource struct */
 		cycle_t (*vread)(void);
arch/x86/kernel/vsyscall_64.c +1 -33
index ee22180..3e8dac7 100644
@@ -53,7 +53,6 @@ DEFINE_VVAR(int, vgetcpu_mode);
 DEFINE_VVAR(struct vsyscall_gtod_data, vsyscall_gtod_data) =
 {
 	.lock = SEQLOCK_UNLOCKED,
-	.sysctl_enabled = 1,
 };
 
 void update_vsyscall_tz(void)
@@ -103,15 +102,6 @@ static __always_inline int gettimeofday(struct timeval *tv, struct timezone *tz)
 	return ret;
 }
 
-static __always_inline long time_syscall(long *t)
-{
-	long secs;
-	asm volatile("syscall"
-		: "=a" (secs)
-		: "0" (__NR_time),"D" (t) : __syscall_clobber);
-	return secs;
-}
-
 static __always_inline void do_vgettimeofday(struct timeval * tv)
 {
 	cycle_t now, base, mask, cycle_delta;
@@ -122,8 +112,7 @@ static __always_inline void do_vgettimeofday(struct timeval * tv)
 		seq = read_seqbegin(&VVAR(vsyscall_gtod_data).lock);
 
 		vread = VVAR(vsyscall_gtod_data).clock.vread;
-		if (unlikely(!VVAR(vsyscall_gtod_data).sysctl_enabled ||
-			     !vread)) {
+		if (unlikely(!vread)) {
 			gettimeofday(tv,NULL);
 			return;
 		}
@@ -165,8 +154,6 @@ time_t __vsyscall(1) vtime(time_t *t)
 {
 	unsigned seq;
 	time_t result;
-	if (unlikely(!VVAR(vsyscall_gtod_data).sysctl_enabled))
-		return time_syscall(t);
 
 	do {
 		seq = read_seqbegin(&VVAR(vsyscall_gtod_data).lock);
@@ -227,22 +214,6 @@ static long __vsyscall(3) venosys_1(void)
 	return -ENOSYS;
 }
 
-#ifdef CONFIG_SYSCTL
-static ctl_table kernel_table2[] = {
-	{ .procname = "vsyscall64",
-	  .data = &vsyscall_gtod_data.sysctl_enabled, .maxlen = sizeof(int),
-	  .mode = 0644,
-	  .proc_handler = proc_dointvec },
-	{}
-};
-
-static ctl_table kernel_root_table2[] = {
-	{ .procname = "kernel", .mode = 0555,
-	  .child = kernel_table2 },
-	{}
-};
-#endif
-
 /* Assume __initcall executes before all user space. Hopefully kmod
    doesn't violate that. We'll find out if it does. */
 static void __cpuinit vsyscall_set_cpu(int cpu)
@@ -301,9 +272,6 @@ static int __init vsyscall_init(void)
 	BUG_ON((unsigned long) &vtime != VSYSCALL_ADDR(__NR_vtime));
 	BUG_ON((VSYSCALL_ADDR(0) != __fix_to_virt(VSYSCALL_FIRST_PAGE)));
 	BUG_ON((unsigned long) &vgetcpu != VSYSCALL_ADDR(__NR_vgetcpu));
-#ifdef CONFIG_SYSCTL
-	register_sysctl_table(kernel_root_table2);
-#endif
 	on_each_cpu(cpu_vsyscall_init, NULL, 1);
 	/* notifier priority > KVM */
 	hotcpu_notifier(cpu_vsyscall_notifier, 30);
arch/x86/vdso/vclock_gettime.c +21 -34

76 lines not on this page · raw message

Signature · 1 line
1.7.5.1
#6 lore raw

PATCH 4/5 x86-64: Replace vsyscall gettimeofday fallback with int 0xcc

In reply to Andy Lutomirski

Now the only way to issue a syscall with side effects through the vsyscall page is to call a misaligned instruction. I haven't checked for that.

---
 arch/x86/include/asm/traps.h    |    4 +++
 arch/x86/include/asm/vsyscall.h |    6 +++++
 arch/x86/kernel/entry_64.S      |    2 +
 arch/x86/kernel/traps.c         |    4 +++
 arch/x86/kernel/vsyscall_64.c   |   47 ++++++++++++++++++++++++++++++++++-----
 5 files changed, 57 insertions(+), 6 deletions(-)
arch/x86/include/asm/traps.h +4 -0
index 0310da6..7eae1e4 100644
@@ -1,6 +1,8 @@
 #ifndef _ASM_X86_TRAPS_H
 #define _ASM_X86_TRAPS_H
 
+#include <linux/kprobes.h>
+
 #include <asm/debugreg.h>
 #include <asm/siginfo.h>			/* TRAP_TRACE, ... */
 
@@ -38,6 +40,7 @@ asmlinkage void alignment_check(void);
 asmlinkage void machine_check(void);
 #endif /* CONFIG_X86_MCE */
 asmlinkage void simd_coprocessor_error(void);
+asmlinkage void intcc(void);
 
 dotraplinkage void do_divide_error(struct pt_regs *, long);
 dotraplinkage void do_debug(struct pt_regs *, long);
@@ -64,6 +67,7 @@ dotraplinkage void do_alignment_check(struct pt_regs *, long);
 dotraplinkage void do_machine_check(struct pt_regs *, long);
 #endif
 dotraplinkage void do_simd_coprocessor_error(struct pt_regs *, long);
+dotraplinkage void do_intcc(struct pt_regs *, long);
 #ifdef CONFIG_X86_32
 dotraplinkage void do_iret_error(struct pt_regs *, long);
 #endif
arch/x86/include/asm/vsyscall.h +6 -0
index d555973..293ae08 100644
@@ -31,6 +31,12 @@ extern struct timezone sys_tz;
 
 extern void map_vsyscall(void);
 
+/* Emulation */
+static inline bool in_vsyscall_page(unsigned long addr)
+{
+	return (addr & ~(PAGE_SIZE - 1)) == VSYSCALL_START;
+}
+
 #endif /* __KERNEL__ */
 
 #endif /* _ASM_X86_VSYSCALL_H */
arch/x86/kernel/entry_64.S +2 -0
index 8a445a0..8e12f50 100644
@@ -1121,6 +1121,8 @@ zeroentry spurious_interrupt_bug do_spurious_interrupt_bug
 zeroentry coprocessor_error do_coprocessor_error
 errorentry alignment_check do_alignment_check
 zeroentry simd_coprocessor_error do_simd_coprocessor_error
+zeroentry intcc do_intcc
+
 
 	/* Reload gs selector with exception handling */
 	/* edi:  new selector */
arch/x86/kernel/traps.c +4 -0
index b9b6716..d34894e 100644
@@ -872,6 +872,10 @@ void __init trap_init(void)
 	set_bit(SYSCALL_VECTOR, used_vectors);
 #endif
 
+	set_system_intr_gate(0xCC, &intcc);
+	set_bit(0xCC, used_vectors);
+	printk(KERN_ERR "intcc gate isntalled\n");
+
 	/*
 	 * Should be a barrier for any external CPU state:
 	 */
arch/x86/kernel/vsyscall_64.c +41 -6

79 lines not on this page · raw message

Signature · 1 line
1.7.5.1
#7 lore raw

Re: [PATCH 4/5] x86-64: Replace vsyscall gettimeofday fallback with int 0xcc

arch/x86/kernel/traps.c

Quoted 12 lines · Andy Lutomirski
arch/x86/kernel/entry_64.S +2 -0
@@ -1121,6 +1121,8 @@ zeroentry spurious_interrupt_bug do_spurious_interrupt_bug
 zeroentry coprocessor_error do_coprocessor_error
 errorentry alignment_check do_alignment_check
 zeroentry simd_coprocessor_error do_simd_coprocessor_error
+zeroentry intcc do_intcc
+
 
 	/* Reload gs selector with exception handling */
 	/* edi:  new selector */
arch/x86/kernel/traps.c +0 -0

I forgot to reply to your prior question about zeroentry vs. paranoidzeroentry.

That distinction is an undocumented x86-64-ism.

Background:

The SWAPGS instruction is rather fragile: it must nest perfectly and only in single depth, it should only be used if entering from user mode to kernel mode and then when returning to user-space, and precisely so. If we mess that up even slightly, we crash.

So when we have a secondary entry, already in kernel mode, we *must not* use SWAPGS blindly - nor must we forget doing a SWAPGS when it's not switched/swapped yet.

Now, there's a secondary complication: there's a cheap way to test which mode the CPU is in and an expensive way.

The cheap way is to pick this info off the entry frame on the kernel stack, from the CS of the ptregs area of the kernel stack:

        xorl %ebx,%ebx
        testl $3,CS+8(%rsp)
        je error_kernelspace
        SWAPGS

The expensive (paranoid) way is to read back the MSR_GS_BASE value (which is what SWAPGS modifies):

        movl $1,%ebx
        movl $MSR_GS_BASE,%ecx
        rdmsr
        testl %edx,%edx
        js 1f   /* negative -> in kernel */
        SWAPGS
        xorl %ebx,%ebx

1: ret

and the whole paranoid non-paranoid macro complexity is about whether to suffer that RDMSR cost.

If we are at an interrupt or user-trap/gate-alike boundary then we can use the faster check: the stack will be a reliable indicator of whether SWAPGS was already done: if we see that we are a secondary entry interrupting kernel mode execution, then we know that the GS base has already been switched. If it says that we interrupted user-space execution then we must do the SWAPGS.

But if we are in an NMI/MCE/DEBUG/whatever super-atomic entry context, which might have triggered right after a normal entry wrote CS to the stack but before we executed SWAPGS, then the only safe way to check for GS is the slower method: the RDMSR.

So we try only to mark those entry methods 'paranoid' that absolutely need the more expensive check for the GS base - and we generate all 'normal' entry points with the regular (faster) entry macros.

I hope this explains!

All in one, your zeroentry choice should be fine: INT 0xCC will not issue in NMI context.

Btw, as a sidenote, and since you are already touching this code, would you be interested in putting this explanation into the source code? It's certainly not obvious and whoever wrote those macros did not think of documenting them for later generations ;-)

arch/x86/kernel/traps.c void __init trap_init(void) · line 877

Quoted 8 lines

+++ b/arch/x86/kernel/traps.c

arch/x86/kernel/traps.c +3 -0
@@ -872,6 +872,10 @@ void __init trap_init(void)
 	set_bit(SYSCALL_VECTOR, used_vectors);
 #endif
 
+	set_system_intr_gate(0xCC, &intcc);
+	set_bit(0xCC, used_vectors);
+	printk(KERN_ERR "intcc gate isntalled\n");

I think you mentioned it but i cannot remember your reasoning why you marked it 0xcc (and not closer to the existing syscall vector) - please add a comment about it into the source code as well.

Ok, i suspect you marked it 0xCC because that's the INT3 instruction
- not very useful for exploits?

arch/x86/kernel/traps.c void __init trap_init(void)

Quoted 5 lines
arch/x86/kernel/traps.c +5 -0
+void dotraplinkage do_intcc(struct pt_regs *regs, long error_code)
+{
+	/* Kernel code must never get here. */
+	if (!user_mode(regs))
+		BUG();

Nit: you can use BUG_ON() for that.

arch/x86/kernel/traps.c

Quoted 5 lines
arch/x86/kernel/traps.c +5 -0
+	local_irq_enable();
+
+	if (!in_vsyscall_page(regs->ip)) {
+		struct task_struct *tsk = current;
+		if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&

Nit: please put an empty new line between local variable definitions and the first statement that follows - we do this for visual clarity.

A not-so-nit: i'd not limit this message to unhandled signals alone. An attacker could install a SIGSEGV handler, send a SIGSEGV and attempt the exploit right then - he'll get a free attempt with no logging performed, right?.

arch/x86/kernel/traps.c

Quoted 5 lines
arch/x86/kernel/traps.c +5 -0
+		    printk_ratelimit()) {
+			printk(KERN_INFO
+			       "%s[%d] illegal int $0xCC ip:%lx sp:%lx",
+			       tsk->comm, task_pid_nr(tsk),
+			       regs->ip, regs->sp);

I'd suggest putting the text 'exploit attempt?' into the printk somewhere - a sysadmin might not necessarily know what an illegal int $0xCC is..

Quoted 4 lines
arch/x86/kernel/traps.c +4 -0
+			print_vma_addr(" in ", regs->ip);
+			printk("\n");
+		}
+

arch/x86/kernel/traps.c

Quoted 10 lines
arch/x86/kernel/traps.c +10 -0
+		force_sig(SIGSEGV, current);
+		return;
+	}
+
+	if (current->seccomp.mode) {
+		do_exit(SIGKILL);
+		return;
+	}
+
+	regs->ax = sys_gettimeofday((struct timeval __user *)regs->di, NULL);

Does the vsyscall gettimeofday ignore the zone parameter too?

arch/x86/kernel/traps.c

Quoted 4 lines
arch/x86/kernel/traps.c +4 -0
+
+	local_irq_disable();
+	return;
+}

Nit: no need for a 'return;' at the end of a void function.

Thanks,

Ingo

#8 lore raw

Re: [PATCH 4/5] x86-64: Replace vsyscall gettimeofday fallback with int 0xcc

Quoted 12 lines · Ingo Molnar
Quoted 9 lines · Andy Lutomirski
arch/x86/kernel/entry_64.S +0 -0
@@ -1121,6 +1121,8 @@ zeroentry spurious_interrupt_bug do_spurious_interrupt_bug

 zeroentry coprocessor_error do_coprocessor_error errorentry alignment_check do_alignment_check zeroentry simd_coprocessor_error do_simd_coprocessor_error

arch/x86/kernel/entry_64.S +2 -0
+zeroentry intcc do_intcc
+
Quoted 8 lines

arch/x86/kernel/traps.c

Quoted 3 lines

      /* Reload gs selector with exception handling */ /* edi:  new selector */

arch/x86/kernel/traps.c +0 -0

I forgot to reply to your prior question about zeroentry vs. paranoidzeroentry.

That distinction is an undocumented x86-64-ism.

Is this an erratum or just the undocumented fact that swapgs twice puts usergs back and confuses the kernel?

Quoted 5 lines

Btw, as a sidenote, and since you are already touching this code, would you be interested in putting this explanation into the source code? It's certainly not obvious and whoever wrote those macros did not think of documenting them for later generations ;-)

Will do.

Quoted 14 lines

arch/x86/kernel/traps.c void __init trap_init(void) · line 877

Quoted 9 lines

+++ b/arch/x86/kernel/traps.c

arch/x86/kernel/traps.c +3 -0
@@ -872,6 +872,10 @@ void __init trap_init(void)
       set_bit(SYSCALL_VECTOR, used_vectors);
  #endif
 
+     set_system_intr_gate(0xCC, &intcc);
+     set_bit(0xCC, used_vectors);
+     printk(KERN_ERR "intcc gate isntalled\n");

I think you mentioned it but i cannot remember your reasoning why you marked it 0xcc (and not closer to the existing syscall vector) - please add a comment about it into the source code as well.

Quoted 2 lines

Ok, i suspect you marked it 0xCC because that's the INT3 instruction
- not very useful for exploits?

Exactly.

The comments in irq_vectors.h make it sound like vectors 0x81..0xed are used for device interrupts but AFAICT it's only 0x20..0x39 that are used, so the precise choice of vector doesn't matter that much.

Quoted 8 lines

arch/x86/kernel/traps.c void __init trap_init(void)

Quoted 6 lines
arch/x86/kernel/traps.c +5 -0
+void dotraplinkage do_intcc(struct pt_regs *regs, long error_code)
+{
+     /* Kernel code must never get here. */
+     if (!user_mode(regs))
+             BUG();

Nit: you can use BUG_ON() for that.

Yep.

Quoted 7 lines
Quoted 6 lines

+     local_irq_enable();
+
+     if (!in_vsyscall_page(regs->ip)) {
+             struct task_struct *tsk = current;
+             if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&

Quoted 7 lines

Nit: please put an empty new line between local variable definitions and the first statement that follows - we do this for visual clarity.

A not-so-nit: i'd not limit this message to unhandled signals alone. An attacker could install a SIGSEGV handler, send a SIGSEGV and attempt the exploit right then - he'll get a free attempt with no logging performed, right?.

I think if an exploit can call sigaction, then we've already lost. But I can still make the change.

Quoted 10 lines
Quoted 6 lines

+                 printk_ratelimit()) {
+                     printk(KERN_INFO
+                            "%s[%d] illegal int $0xCC ip:%lx sp:%lx",
+                            tsk->comm, task_pid_nr(tsk),
+                            regs->ip, regs->sp);

I'd suggest putting the text 'exploit attempt?' into the printk somewhere - a sysadmin might not necessarily know what an illegal int $0xCC is..

Will do.

Quoted 17 lines
Quoted 5 lines

+                     print_vma_addr(" in ", regs->ip);
+                     printk("\n");
+             }

arch/x86/kernel/traps.c +1 -0
+

arch/x86/kernel/traps.c

Quoted 10 lines
arch/x86/kernel/traps.c +10 -0
+             force_sig(SIGSEGV, current);
+             return;
+     }
+
+     if (current->seccomp.mode) {
+             do_exit(SIGKILL);
+             return;
+     }
+
+     regs->ax = sys_gettimeofday((struct timeval __user *)regs->di, NULL);

Does the vsyscall gettimeofday ignore the zone parameter too?

No, but the vsyscall gettimeofday doesn't use the fallback to get the timezone.

Quoted 7 lines

arch/x86/kernel/traps.c

Quoted 5 lines
arch/x86/kernel/traps.c +4 -0
+
+     local_irq_disable();
+     return;
+}

Nit: no need for a 'return;' at the end of a void function.

:)

That pointless "return" statement was to hide the fact that the local_irq_enable wasn't correctly matched.

I'm changing this code a fair bit in preparation for the extra bonus patch to defang vsyscalls even more by trapping all of them.

--Andy

#9 lore raw

Re: [PATCH 4/5] x86-64: Replace vsyscall gettimeofday fallback with int 0xcc

Quoted 20 lines · Andrew Lutomirski
Quoted 19 lines · Ingo Molnar

arch/x86/kernel/traps.c

Quoted 12 lines · Andy Lutomirski
arch/x86/kernel/entry_64.S +0 -0
@@ -1121,6 +1121,8 @@ zeroentry spurious_interrupt_bug do_spurious_interrupt_bug

 zeroentry coprocessor_error do_coprocessor_error errorentry alignment_check do_alignment_check zeroentry simd_coprocessor_error do_simd_coprocessor_error

arch/x86/kernel/entry_64.S +2 -0
+zeroentry intcc do_intcc
+

      /* Reload gs selector with exception handling */ /* edi:  new selector */

arch/x86/kernel/traps.c +0 -0

I forgot to reply to your prior question about zeroentry vs. paranoidzeroentry.

Quoted 4 lines
Quoted 1 line

That distinction is an undocumented x86-64-ism.

Is this an erratum or just the undocumented fact that swapgs twice puts usergs back and confuses the kernel?

There's no erratum needed for this to be unreliable: if an NMI hits like this:

	SYSENTER

	<=== ... NMI entry ...

	SWAPGS

then the CS check of the entry frame will show 'we interrupted kernel mode code', but in reality the SWAPGS has not been done yet.

Regular interrupts (and pagefaults, etc.) can never interrupt the above sequence 'in the middle', where the CS check is unreliable.

So yes, it's about not confusing the kernel into the wrong SWAPGS state.

I suspect you could trigger badness very quickly: mark the NMI entry zeroentry and run some more extreme NMI load like:

# 100 KHz NMI with precise (no skid) cycles PEBS profiling:

perf record -a -e cycles:pp -F 100000 sleep 60

and i guess you'll see a nasty crash very quickly.

Thanks,

Ingo

#10 lore raw

Re: [PATCH 4/5] x86-64: Replace vsyscall gettimeofday fallback with int 0xcc

In reply to Andrew Lutomirski
Quoted 8 lines · Andrew Lutomirski
Quoted 2 lines

Ok, i suspect you marked it 0xCC because that's the INT3 instruction
- not very useful for exploits?

Exactly.

The comments in irq_vectors.h make it sound like vectors 0x81..0xed are used for device interrupts but AFAICT it's only 0x20..0x39 that are used, so the precise choice of vector doesn't matter that much.

No, we use almost all of the vector space for device interrupts. Why do you think only 0x20..0x39 is used?

Quoted 6 lines
Quoted 4 lines

A not-so-nit: i'd not limit this message to unhandled signals alone. An attacker could install a SIGSEGV handler, send a SIGSEGV and attempt the exploit right then - he'll get a free attempt with no logging performed, right?.

I think if an exploit can call sigaction, then we've already lost.

Yes, indeed. In theory an app could be catching SIGSEGV and we could have an exploit there. But that's pretty theoretical ...

Quoted 1 line

But I can still make the change.

If you did it to not repeat the message then i think the printk_ratelimit() is more than enough. force_sig() will be able to sort out repeat signals just fine.

Quoted 10 lines
Quoted 5 lines
Quoted 3 lines

+     local_irq_disable();
+     return;
+}

Nit: no need for a 'return;' at the end of a void function.

:)

That pointless "return" statement was to hide the fact that the local_irq_enable wasn't correctly matched.

indeed. I noticed the do_exit() local_irq_enable() assymetry which is harmless (we never return), but missed the force_sig() one that isn't so harmless.

Quoted 2 lines

I'm changing this code a fair bit in preparation for the extra bonus patch to defang vsyscalls even more by trapping all of them.

ok.

Thanks,

Ingo

#11 lore raw

Re: [PATCH 4/5] x86-64: Replace vsyscall gettimeofday fallback with int 0xcc

Quoted 6 lines · Ingo Molnar
Quoted 3 lines · Andrew Lutomirski
Quoted 2 lines

Ok, i suspect you marked it 0xCC because that's the INT3 instruction
- not very useful for exploits?

Quoted 8 lines
Quoted 5 lines

Exactly.

The comments in irq_vectors.h make it sound like vectors 0x81..0xed are used for device interrupts but AFAICT it's only 0x20..0x39 that are used, so the precise choice of vector doesn't matter that much.

No, we use almost all of the vector space for device interrupts. Why do you think only 0x20..0x39 is used?

Possibility my inability to understand all the IRQ mapping code in just half an hour of trying.

In arch/x86/kernel/irq.c, arch_probe_nr_irqs returns NR_IRQS_LEGACY, which I think means that the genirq code allocates will only expect IRQs on that many vectors.

If I'm wrong then my patch could be bad: if something tries to use vector 0xcc for a device interrupt, then the vsyscall emulation code will eat that interrupt.

(0xcc is barely below the maximum. INVALIDATE_TLB_VECTOR_START could be as low as 0xcf.)

--Andy

#12 lore raw

Re: [PATCH 4/5] x86-64: Replace vsyscall gettimeofday fallback with int 0xcc

Quoted 13 lines · Andrew Lutomirski
Quoted 12 lines · Ingo Molnar
Quoted 8 lines · Andrew Lutomirski
Quoted 2 lines

Ok, i suspect you marked it 0xCC because that's the INT3 instruction
- not very useful for exploits?

Exactly.

The comments in irq_vectors.h make it sound like vectors 0x81..0xed are used for device interrupts but AFAICT it's only 0x20..0x39 that are used, so the precise choice of vector doesn't matter that much.

Quoted 5 lines
Quoted 2 lines

No, we use almost all of the vector space for device interrupts. Why do you think only 0x20..0x39 is used?

Possibility my inability to understand all the IRQ mapping code in just half an hour of trying.

Hey, you managed to find all the scattered pieces in just half an hour, i'm impressed ;-)

Quoted 7 lines

In arch/x86/kernel/irq.c, arch_probe_nr_irqs returns NR_IRQS_LEGACY, which I think means that the genirq code allocates will only expect IRQs on that many vectors.

If I'm wrong then my patch could be bad: if something tries to use vector 0xcc for a device interrupt, then the vsyscall emulation code will eat that interrupt.

I saw the used_vector trick you did and it looked safe to me: we set up these gates very early on, when there's no device interrupts yet.

If you want to be really sure you could do a BUG_ON(test_bit()) before setting it.

Quoted 2 lines

(0xcc is barely below the maximum. INVALIDATE_TLB_VECTOR_START could be as low as 0xcf.)

Yeah - 0xcc could be fine even if it's in the middle - we are able to skip over used ones.

Thanks,

Ingo

#13 lore raw

Re: [PATCH 4/5] x86-64: Replace vsyscall gettimeofday fallback with int 0xcc

Quoted 16 lines · Ingo Molnar
Quoted 13 lines · Andrew Lutomirski
Quoted 12 lines · Ingo Molnar
Quoted 8 lines · Andrew Lutomirski
Quoted 2 lines

Ok, i suspect you marked it 0xCC because that's the INT3 instruction
- not very useful for exploits?

Exactly.

The comments in irq_vectors.h make it sound like vectors 0x81..0xed are used for device interrupts but AFAICT it's only 0x20..0x39 that are used, so the precise choice of vector doesn't matter that much.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

No, we use almost all of the vector space for device interrupts. Why do you think only 0x20..0x39 is used?

Possibility my inability to understand all the IRQ mapping code in just half an hour of trying.

Hey, you managed to find all the scattered pieces in just half an hour, i'm impressed ;-)

grep and an SSD are amazing. I'll add the BUG_ON just to satisfy my paranoia. I'll also update the comment in irq_vectors.h.

--Andy

maintainer
#14 lore raw

Re: [PATCH 4/5] x86-64: Replace vsyscall gettimeofday fallback with int 0xcc

In reply to Ingo Molnar

I would welcome it very much if this explanation landed somewhere into <Documentation/x86/x86_64/> for all those of us who find ourselves staring at entry code now and then :).

Thanks.

Quoted 165 lines · Ingo Molnar

arch/x86/kernel/traps.c

Quoted 12 lines · Andy Lutomirski
arch/x86/kernel/entry_64.S +2 -0
@@ -1121,6 +1121,8 @@ zeroentry spurious_interrupt_bug do_spurious_interrupt_bug
 zeroentry coprocessor_error do_coprocessor_error
 errorentry alignment_check do_alignment_check
 zeroentry simd_coprocessor_error do_simd_coprocessor_error
+zeroentry intcc do_intcc
+
 
 	/* Reload gs selector with exception handling */
 	/* edi:  new selector */
arch/x86/kernel/traps.c +0 -0

I forgot to reply to your prior question about zeroentry vs. paranoidzeroentry.

That distinction is an undocumented x86-64-ism.

Background:

The SWAPGS instruction is rather fragile: it must nest perfectly and only in single depth, it should only be used if entering from user mode to kernel mode and then when returning to user-space, and precisely so. If we mess that up even slightly, we crash.

So when we have a secondary entry, already in kernel mode, we *must not* use SWAPGS blindly - nor must we forget doing a SWAPGS when it's not switched/swapped yet.

Now, there's a secondary complication: there's a cheap way to test which mode the CPU is in and an expensive way.

The cheap way is to pick this info off the entry frame on the kernel stack, from the CS of the ptregs area of the kernel stack:

        xorl %ebx,%ebx
        testl $3,CS+8(%rsp)
        je error_kernelspace
        SWAPGS

The expensive (paranoid) way is to read back the MSR_GS_BASE value (which is what SWAPGS modifies):

        movl $1,%ebx
        movl $MSR_GS_BASE,%ecx
        rdmsr
        testl %edx,%edx
        js 1f   /* negative -> in kernel */
        SWAPGS
        xorl %ebx,%ebx

1: ret

and the whole paranoid non-paranoid macro complexity is about whether to suffer that RDMSR cost.

If we are at an interrupt or user-trap/gate-alike boundary then we can use the faster check: the stack will be a reliable indicator of whether SWAPGS was already done: if we see that we are a secondary entry interrupting kernel mode execution, then we know that the GS base has already been switched. If it says that we interrupted user-space execution then we must do the SWAPGS.

But if we are in an NMI/MCE/DEBUG/whatever super-atomic entry context, which might have triggered right after a normal entry wrote CS to the stack but before we executed SWAPGS, then the only safe way to check for GS is the slower method: the RDMSR.

So we try only to mark those entry methods 'paranoid' that absolutely need the more expensive check for the GS base - and we generate all 'normal' entry points with the regular (faster) entry macros.

I hope this explains!

All in one, your zeroentry choice should be fine: INT 0xCC will not issue in NMI context.

Btw, as a sidenote, and since you are already touching this code, would you be interested in putting this explanation into the source code? It's certainly not obvious and whoever wrote those macros did not think of documenting them for later generations ;-)

arch/x86/kernel/traps.c void __init trap_init(void) · line 877

Quoted 8 lines

+++ b/arch/x86/kernel/traps.c

arch/x86/kernel/traps.c +3 -0
@@ -872,6 +872,10 @@ void __init trap_init(void)
 	set_bit(SYSCALL_VECTOR, used_vectors);
 #endif
 
+	set_system_intr_gate(0xCC, &intcc);
+	set_bit(0xCC, used_vectors);
+	printk(KERN_ERR "intcc gate isntalled\n");

I think you mentioned it but i cannot remember your reasoning why you marked it 0xcc (and not closer to the existing syscall vector) - please add a comment about it into the source code as well.

Ok, i suspect you marked it 0xCC because that's the INT3 instruction
- not very useful for exploits?

arch/x86/kernel/traps.c void __init trap_init(void)

Quoted 5 lines
arch/x86/kernel/traps.c +5 -0
+void dotraplinkage do_intcc(struct pt_regs *regs, long error_code)
+{
+	/* Kernel code must never get here. */
+	if (!user_mode(regs))
+		BUG();

Nit: you can use BUG_ON() for that.

arch/x86/kernel/traps.c

Quoted 5 lines
arch/x86/kernel/traps.c +5 -0
+	local_irq_enable();
+
+	if (!in_vsyscall_page(regs->ip)) {
+		struct task_struct *tsk = current;
+		if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&

Nit: please put an empty new line between local variable definitions and the first statement that follows - we do this for visual clarity.

A not-so-nit: i'd not limit this message to unhandled signals alone. An attacker could install a SIGSEGV handler, send a SIGSEGV and attempt the exploit right then - he'll get a free attempt with no logging performed, right?.

arch/x86/kernel/traps.c

Quoted 5 lines
arch/x86/kernel/traps.c +5 -0
+		    printk_ratelimit()) {
+			printk(KERN_INFO
+			       "%s[%d] illegal int $0xCC ip:%lx sp:%lx",
+			       tsk->comm, task_pid_nr(tsk),
+			       regs->ip, regs->sp);

I'd suggest putting the text 'exploit attempt?' into the printk somewhere - a sysadmin might not necessarily know what an illegal int $0xCC is..

Quoted 4 lines
arch/x86/kernel/traps.c +4 -0
+			print_vma_addr(" in ", regs->ip);
+			printk("\n");
+		}
+

arch/x86/kernel/traps.c

Quoted 10 lines
arch/x86/kernel/traps.c +10 -0
+		force_sig(SIGSEGV, current);
+		return;
+	}
+
+	if (current->seccomp.mode) {
+		do_exit(SIGKILL);
+		return;
+	}
+
+	regs->ax = sys_gettimeofday((struct timeval __user *)regs->di, NULL);

Does the vsyscall gettimeofday ignore the zone parameter too?

arch/x86/kernel/traps.c

Quoted 4 lines
arch/x86/kernel/traps.c +4 -0
+
+	local_irq_disable();
+	return;
+}

Nit: no need for a 'return;' at the end of a void function.

Thanks,

Ingo
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Signature · 2 lines
Regards/Gruss,
    Boris.
#15 lore raw

Re: [PATCH 4/5] x86-64: Replace vsyscall gettimeofday fallback with int 0xcc

In reply to Andy Lutomirski
Quoted 4 lines · Andy Lutomirski

Now the only way to issue a syscall with side effects through the vsyscall page is to call a misaligned instruction. I haven't checked for that.

Quoted 9 lines
---
 arch/x86/include/asm/traps.h    |    4 +++
 arch/x86/include/asm/vsyscall.h |    6 +++++
 arch/x86/kernel/entry_64.S      |    2 +
 arch/x86/kernel/traps.c         |    4 +++
 arch/x86/kernel/vsyscall_64.c   |   47 ++++++++++++++++++++++++++++++++++-----
 5 files changed, 57 insertions(+), 6 deletions(-)

one very tiny nit below.

[...]

arch/x86/kernel/traps.c void __init trap_init(void) · line 877

Quoted 11 lines
arch/x86/kernel/traps.c +3 -0
index b9b6716..d34894e 100644
@@ -872,6 +872,10 @@ void __init trap_init(void)
 	set_bit(SYSCALL_VECTOR, used_vectors);
 #endif
 
+	set_system_intr_gate(0xCC, &intcc);
+	set_bit(0xCC, used_vectors);
+	printk(KERN_ERR "intcc gate isntalled\n");

Let's spell the error message correctly:

	printk(KERN_ERR "intcc gate installed\n");
Signature · 3 lines
Jesper Juhl <[email protected]>       http://www.chaosbits.net/
Don't top-post http://www.catb.org/jargon/html/T/top-post.html
Plain text mails only, please.
#16 lore raw

Re: [PATCH 4/5] x86-64: Replace vsyscall gettimeofday fallback with int 0xcc

Quoted 27 lines · Jesper Juhl
Quoted 4 lines · Andy Lutomirski

Now the only way to issue a syscall with side effects through the vsyscall page is to call a misaligned instruction. I haven't checked for that.

Quoted 9 lines
---
 arch/x86/include/asm/traps.h    |    4 +++
 arch/x86/include/asm/vsyscall.h |    6 +++++
 arch/x86/kernel/entry_64.S      |    2 +
 arch/x86/kernel/traps.c         |    4 +++
 arch/x86/kernel/vsyscall_64.c   |   47 ++++++++++++++++++++++++++++++++++-----
 5 files changed, 57 insertions(+), 6 deletions(-)

one very tiny nit below.

[...]

Quoted 8 lines
arch/x86/kernel/traps.c +0 -0
index b9b6716..d34894e 100644
@@ -872,6 +872,10 @@ void __init trap_init(void)
 	set_bit(SYSCALL_VECTOR, used_vectors);
 #endif
 
Quoted 8 lines

arch/x86/kernel/traps.c void __init trap_init(void) · line 877

Quoted 3 lines
arch/x86/kernel/traps.c +3 -0
+	set_system_intr_gate(0xCC, &intcc);
+	set_bit(0xCC, used_vectors);
+	printk(KERN_ERR "intcc gate isntalled\n");

Let's spell the error message correctly:

	printk(KERN_ERR "intcc gate installed\n");

Hmm, why is this KERN_ERR btw? Shouldn't it just be KERN_NOTICE or KERN_INFO ?

Signature · 3 lines
Jesper Juhl <[email protected]>       http://www.chaosbits.net/
Don't top-post http://www.catb.org/jargon/html/T/top-post.html
Plain text mails only, please.
#17 lore raw

Re: [PATCH 4/5] x86-64: Replace vsyscall gettimeofday fallback with int 0xcc

Quoted 35 lines · Jesper Juhl
Quoted 33 lines · Jesper Juhl
Quoted 4 lines · Andy Lutomirski

Now the only way to issue a syscall with side effects through the vsyscall page is to call a misaligned instruction.  I haven't checked for that.

Quoted 9 lines

---
 arch/x86/include/asm/traps.h    |    4 +++ arch/x86/include/asm/vsyscall.h |    6 +++++ arch/x86/kernel/entry_64.S      |    2 +
 arch/x86/kernel/traps.c         |    4 +++ arch/x86/kernel/vsyscall_64.c   |   47 ++++++++++++++++++++++++++++++++++-----

 5 files changed, 57 insertions(+), 6 deletions(-)

one very tiny nit below.

[...]

arch/x86/kernel/traps.c void __init trap_init(void) · line 877

Quoted 11 lines
arch/x86/kernel/traps.c +3 -0
index b9b6716..d34894e 100644
@@ -872,6 +872,10 @@ void __init trap_init(void)
     set_bit(SYSCALL_VECTOR, used_vectors);
  #endif
 
+   set_system_intr_gate(0xCC, &intcc);
+   set_bit(0xCC, used_vectors);
+   printk(KERN_ERR "intcc gate isntalled\n");

Let's spell the error message correctly:

Quoted 4 lines
Quoted 2 lines

      printk(KERN_ERR "intcc gate installed\n");

Hmm, why is this KERN_ERR btw? Shouldn't it just be KERN_NOTICE or KERN_INFO ?

IMO it shouldn't be there at all. It was a debugging leftover that I forgot to delete.

--Andy

#18 lore raw

Re: [PATCH 4/5] x86-64: Replace vsyscall gettimeofday fallback with int 0xcc

Quoted 36 lines · Andrew Lutomirski
Quoted 35 lines · Jesper Juhl
Quoted 33 lines · Jesper Juhl
Quoted 4 lines · Andy Lutomirski

Now the only way to issue a syscall with side effects through the vsyscall page is to call a misaligned instruction.  I haven't checked for that.

Quoted 9 lines

---
 arch/x86/include/asm/traps.h    |    4 +++ arch/x86/include/asm/vsyscall.h |    6 +++++ arch/x86/kernel/entry_64.S      |    2 +
 arch/x86/kernel/traps.c         |    4 +++ arch/x86/kernel/vsyscall_64.c   |   47 ++++++++++++++++++++++++++++++++++-----

 5 files changed, 57 insertions(+), 6 deletions(-)

one very tiny nit below.

[...]

arch/x86/kernel/traps.c void __init trap_init(void) · line 877

Quoted 11 lines
arch/x86/kernel/traps.c +3 -0
index b9b6716..d34894e 100644
@@ -872,6 +872,10 @@ void __init trap_init(void)
     set_bit(SYSCALL_VECTOR, used_vectors);
  #endif
 
+   set_system_intr_gate(0xCC, &intcc);
+   set_bit(0xCC, used_vectors);
+   printk(KERN_ERR "intcc gate isntalled\n");

Let's spell the error message correctly:

Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

      printk(KERN_ERR "intcc gate installed\n");

Hmm, why is this KERN_ERR btw? Shouldn't it just be KERN_NOTICE or KERN_INFO ?

IMO it shouldn't be there at all. It was a debugging leftover that I forgot to delete.

Just removing it sounds good to me :)

Signature · 3 lines
Jesper Juhl <[email protected]>       http://www.chaosbits.net/
Don't top-post http://www.catb.org/jargon/html/T/top-post.html
Plain text mails only, please.
#19 lore raw

PATCH 5/5 x86-64: Map the HPET NX

In reply to Andy Lutomirski

Currently the HPET mapping is a user-accessible syscall instruction at a fixed address some of the time. A sufficiently determined hacker might be able to guess when.

---
 arch/x86/include/asm/pgtable_types.h |    4 ++--
 arch/x86/kernel/hpet.c               |    2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)
arch/x86/include/asm/pgtable_types.h +2 -2
index 6a29aed6..013286a 100644
@@ -107,8 +107,8 @@
 #define __PAGE_KERNEL_NOCACHE		(__PAGE_KERNEL | _PAGE_PCD | _PAGE_PWT)
 #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_VVAR_NOCACHE	(__PAGE_KERNEL_VVAR | _PAGE_PCD | _PAGE_PWT)
 #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)
@@ -130,8 +130,8 @@
 #define PAGE_KERNEL_LARGE_NOCACHE	__pgprot(__PAGE_KERNEL_LARGE_NOCACHE)
 #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_VVAR_NOCACHE	__pgprot(__PAGE_KERNEL_VVAR_NOCACHE)
 
 #define PAGE_KERNEL_IO			__pgprot(__PAGE_KERNEL_IO)
 #define PAGE_KERNEL_IO_NOCACHE		__pgprot(__PAGE_KERNEL_IO_NOCACHE)
arch/x86/kernel/hpet.c +1 -1
index bfe8f72..bf71830 100644
@@ -71,7 +71,7 @@ static inline void hpet_set_mapping(void)
 {
 	hpet_virt_address = ioremap_nocache(hpet_address, HPET_MMAP_SIZE);
 #ifdef CONFIG_X86_64
-	__set_fixmap(VSYSCALL_HPET, hpet_address, PAGE_KERNEL_VSYSCALL_NOCACHE);
+	__set_fixmap(VSYSCALL_HPET, hpet_address, PAGE_KERNEL_VVAR_NOCACHE);
 #endif
 }
 
Signature · 1 line
1.7.5.1
#20 lore raw
In reply to Andy Lutomirski
Quoted 1 line · Andy Lutomirski

I lied about taking awhile to do this.

Heh :-)

A very nice series btw!

Quoted 26 lines

There are a bunch of syscall instructions in kernel space at fixed addresses that user code can execute.

One is a time() fallback. Patch 3/5 removes it.

Several are data that isn't marked NX. Patch 2/5 makes vvars NX and 5/5 makes the HPET NX.

The last one is the gettimeofday fallback. We need that, but it doesn't have to be a real syscall. Patch 3/5 adds int 0xCC (callable only from the vsyscall page) that implements the gettimeofday fallback and nothing else.

Patch 1/5 is just a dumb but harmless bug fix from the last vdso series.

I've only tested this in KVM with a hacked-up initramfs, but Ingo wanted it for 2.6.40, so here it is.

Andy Lutomirski (5):
x86-64: Fix alignment of jiffies variable
x86-64: Give vvars their own page
x86-64: Remove kernel.vsyscall64 sysctl
x86-64: Replace vsyscall gettimeofday fallback with int 0xcc
x86-64: Map the HPET NX

Quoted 15 lines
 arch/x86/include/asm/fixmap.h        |    1 +
 arch/x86/include/asm/pgtable_types.h |    6 ++-
 arch/x86/include/asm/traps.h         |    4 ++
 arch/x86/include/asm/vgtod.h         |    1 -
 arch/x86/include/asm/vsyscall.h      |    6 ++
 arch/x86/include/asm/vvar.h          |   24 ++++-----
 arch/x86/kernel/entry_64.S           |    2 +
 arch/x86/kernel/hpet.c               |    2 +-
 arch/x86/kernel/traps.c              |    4 ++
 arch/x86/kernel/vmlinux.lds.S        |   27 ++++++----
 arch/x86/kernel/vsyscall_64.c        |   86 ++++++++++++++++++---------------
 arch/x86/vdso/vclock_gettime.c       |   55 ++++++++-------------
 tools/power/x86/turbostat/turbostat  |  Bin 0 -> 29200 bytes
 13 files changed, 117 insertions(+), 101 deletions(-)
 create mode 100755 tools/power/x86/turbostat/turbostat

If no-one finds any review problems with these patches and if you fix the details i pointed out for 3/5 then we can do this for v2.6.40.

I really like this series, it makes full-PIE randomized user-space executables fully safe against known-address syscall instructions. As much as i like crazy speedups, they are probably more relevant to the everyday Linux user than the other patches ;-)

Btw., do you know CONFIG_X86_PTDUMP=y and /debug/kernel_page_tables? You could use that to double check that after your patches all executable (and fixed address) pages are removed [or are harmless].

Thanks,

Ingo

#21 lore raw
Quoted 3 lines · Ingo Molnar

Btw., do you know CONFIG_X86_PTDUMP=y and /debug/kernel_page_tables? You could use that to double check that after your patches all executable (and fixed address) pages are removed [or are harmless].

Done. Now there's only one user-executable page and it's mostly harmless.

Maybe I'll try to get rid of vread_tsc and vread_hpet later on to make it even more harmless.

--Andy

#22 lore raw
Quoted 6 lines · Andrew Lutomirski
Quoted 3 lines · Ingo Molnar

Btw., do you know CONFIG_X86_PTDUMP=y and /debug/kernel_page_tables? You could use that to double check that after your patches all executable (and fixed address) pages are removed [or are harmless].

Done. Now there's only one user-executable page and it's mostly harmless.

ok. Will test your v3 series.

Quoted 2 lines

Maybe I'll try to get rid of vread_tsc and vread_hpet later on to make it even more harmless.

Yeah, that's a good idea. They need pushing into the INT 0xCC do_intcc() handler, that's all that's needed AFAICS - or can you see other complications with them?

Thanks,

Ingo

#23 lore raw
Quoted 10 lines · Ingo Molnar
Quoted 6 lines · Andrew Lutomirski
Quoted 3 lines · Ingo Molnar

Btw., do you know CONFIG_X86_PTDUMP=y and /debug/kernel_page_tables? You could use that to double check that after your patches all executable (and fixed address) pages are removed [or are harmless].

Done.  Now there's only one user-executable page and it's mostly harmless.

Quoted 9 lines

ok. Will test your v3 series.

Quoted 2 lines

Maybe I'll try to get rid of vread_tsc and vread_hpet later on to make it even more harmless.

Yeah, that's a good idea. They need pushing into the INT 0xCC do_intcc() handler, that's all that's needed AFAICS - or can you see other complications with them?

They're called from the vDSO. I think they should just be moved into the vDSO since they're not used by the vsyscall code any more, but there are two problems. The clocksource.vread mechanism (or whatever its called) won't really work if we let them get relocated (not a big deal). More importantly, vread_tsc contains an alternative and the vDSO can't currently contain alternative instructions. That can probably be fixed, but it'll take a bit of work.

--Andy

#24 lore raw
Quoted 3 lines · Andrew Lutomirski

[...] More importantly, vread_tsc contains an alternative and the vDSO can't currently contain alternative instructions. That can probably be fixed, but it'll take a bit of work.

You could start with picking the more compatible alternative instruction initially. I don't at all mind losing half a cycle of performance in that case ... this code should be secure first.

Thanks,

Ingo

#25 lore raw
Quoted 10 lines · Ingo Molnar
Quoted 3 lines · Andrew Lutomirski

[...] More importantly, vread_tsc contains an alternative and the vDSO can't currently contain alternative instructions.  That can probably be fixed, but it'll take a bit of work.

You could start with picking the more compatible alternative instruction initially. I don't at all mind losing half a cycle of performance in that case ... this code should be secure first.

The more compatible one is mfence, which in some cases could (I think) be a lot more than half a cycle.

A better option might be rdtscp, which is actually documented to work, but I'm not sure it's available on all supported CPUs.

I'm content to wait a bit on this one. I say let's get the rest done first and tackle the last little hard part at the end.

--Andy

Quoted 5 lines

Thanks,

       Ingo

#26 lore raw
Quoted 6 lines · Andrew Lutomirski
Quoted 3 lines

You could start with picking the more compatible alternative instruction initially. I don't at all mind losing half a cycle of performance in that case ... this code should be secure first.

The more compatible one is mfence, which in some cases could (I think) be a lot more than half a cycle.

I'd still suggest to do the mfence change now and remove the alternatives patching for now - if it's more than half a cycle then it sure will be implemented properly, right?

Thanks,

Ingo

#27 lore raw
Quoted 7 lines · Ingo Molnar
Quoted 4 lines · Andrew Lutomirski
Quoted 3 lines

You could start with picking the more compatible alternative instruction initially. I don't at all mind losing half a cycle of performance in that case ... this code should be secure first.

Quoted 6 lines
Quoted 2 lines

The more compatible one is mfence, which in some cases could (I think) be a lot more than half a cycle.

I'd still suggest to do the mfence change now and remove the alternatives patching for now - if it's more than half a cycle then it sure will be implemented properly, right?

I don't know. I just cut 5 ns off the thing a couple weeks ago and no one beat me to it :)

I'll take a look at how hard the patching will be.

--Andy

Quoted 5 lines

Thanks,

       Ingo