Skip to contentSkip to conversation

vsyscall tweaks

PATCH In review Andy Lutomirski 4 participants 10 messages Aug 9, 2011 – Aug 17, 2011

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover vsyscall tweaks

With the new vsyscall emulation code, int 0xcc's behavior depends on where it is called from. A few dynamic instrumentation tools cleverly copy the instruction out of the vsyscall page and execute it, resulting in a segfault. This is nothing new: sysenter would die in a similar way (unless the code got lucky). The only examples I know of are pin and DynamoRIO -- valgrind is already smart enough not to recompile vsyscalls 0 and 1 and "smart" enough to crash unconditionally on vsyscall 2. I can't test pin because it refuses to run on newfangled things like Linux 3.0+ (and I don't want to muck with my kernel version to run a program that's broken anyway). Patch 1 in this series fixes DynamoRIO in limited testing.

Bug reports against valgrind and DynamoRIO exist, and I haven't managed to get permission to post to pin's message board yet.

The other three patches are minor. Patch 2 removes compile flags that are now unnecessary because vsyscall_64.c does not get called in user mode. Patch 3 wires up the getcpu syscall on x86-64 as further encouragement to stop using the vsyscall. Patch 4 adds a feature-removal-schedule.txt entry suggesting that vsyscalls might be disabled in some configurations some day.

x86 maintainers / Linus: feel free to drop any of patches 2-4. I think the getcpu syscall should be added, though, since it's rather odd to have a "syscall" in the vdso and the vsyscall page that can't be called the old-fashioned way.

Changes from RFC v2:
- Fix some more int 0xcc references.
- Add patches 2-4.

Andy Lutomirski (4):
x86-64: Allow emulated vsyscalls from user addresses
x86: Remove unnecessary compile flag tweaks for vsyscall code
x86-64: Wire up getcpu syscall
Add vsyscalls to feature-removal-schedule.txt

 Documentation/feature-removal-schedule.txt |   14 +++
 arch/x86/include/asm/irq_vectors.h         |   11 ++-
 arch/x86/include/asm/traps.h               |    8 ++-
 arch/x86/include/asm/unistd_64.h           |    2 +
 arch/x86/kernel/Makefile                   |   13 ---
 arch/x86/kernel/entry_64.S                 |    4 +-
 arch/x86/kernel/traps.c                    |   14 +++-
 arch/x86/kernel/vsyscall_64.c              |  131 ++++++++++++++--------------
 arch/x86/kernel/vsyscall_emu_64.S          |    6 +-
 9 files changed, 111 insertions(+), 92 deletions(-)
Signature · 1 line
1.7.6
#1 lore raw

PATCH 1/4 x86-64: Allow emulated vsyscalls from user addresses

A few dynamic recompilation tools are too clever for their own good. They trace control flow through the vsyscall page and recompile that code somewhere else. Then they expect it to work. DynamoRIO
(http://dynamorio.org/) and supposedly Pin (http://www.pintool.org/) are affected. They crash when tracing programs that use vsyscalls. Valgrind is smart enough not to cause problems. It crashes on the getcpu vsyscall, but that has nothing to do with emulation.

This patch makes each of the three vsyscall entries use a different vector so that they can work when relocated. It assumes that the code that relocates them is okay with the int instruction acting like ret. DynamoRIO at least appears to work. Pin is untestable because it can't handle Linux version 3.0+.

We print an obnoxious (rate-limited) message to the log when this happens. Hopefully it will inspire the JIT tools to learn not to trace into kernel address space.

---

Suresh: can you comment on whether interrupt vectors 0x40-0x42 are good choices?

 arch/x86/include/asm/irq_vectors.h |   11 ++--
 arch/x86/include/asm/traps.h       |    8 ++-
 arch/x86/kernel/entry_64.S         |    4 +-
 arch/x86/kernel/traps.c            |   14 +++-
 arch/x86/kernel/vsyscall_64.c      |  128 ++++++++++++++++++-----------------
 arch/x86/kernel/vsyscall_emu_64.S  |    6 +-
 6 files changed, 95 insertions(+), 76 deletions(-)
arch/x86/include/asm/irq_vectors.h +6 -5
index f9a3209..b9c229a 100644
@@ -15,10 +15,9 @@
  * IDT entries:
  *
  *  Vectors   0 ...  31 : system traps and exceptions - hardcoded events
- *  Vectors  32 ... 127 : device interrupts
- *  Vector  128         : legacy int80 syscall interface
- *  Vector  204         : legacy x86_64 vsyscall emulation
- *  Vectors 129 ... INVALIDATE_TLB_VECTOR_START-1 except 204 : device interrupts
+ *  Vectors  32 ... INVALIDATE_TLB_VECTOR_START-1 : device interrupts, except:
+ *   Vectors 64 ... 66  : legacy x86_64 vsyscall emulation
+ *   Vector  128        : legacy int80 syscall interface
  *  Vectors INVALIDATE_TLB_VECTOR_START ... 255 : special interrupts
  *
  * 64-bit x86 has per CPU IDT tables, 32-bit has one shared IDT table.
@@ -52,7 +51,9 @@
 # define SYSCALL_VECTOR			0x80
 #endif
 #ifdef CONFIG_X86_64
-# define VSYSCALL_EMU_VECTOR		0xcc
+# define VSYSCALL0_EMU_VECTOR		0x40
+# define VSYSCALL1_EMU_VECTOR		0x41
+# define VSYSCALL2_EMU_VECTOR		0x42
 #endif
 
 /*
arch/x86/include/asm/traps.h +6 -2
index 2bae0a5..4335ff7 100644
@@ -40,7 +40,9 @@ asmlinkage void alignment_check(void);
 asmlinkage void machine_check(void);
 #endif /* CONFIG_X86_MCE */
 asmlinkage void simd_coprocessor_error(void);
-asmlinkage void emulate_vsyscall(void);
+asmlinkage void emulate_vsyscall0(void);
+asmlinkage void emulate_vsyscall1(void);
+asmlinkage void emulate_vsyscall2(void);
 
 dotraplinkage void do_divide_error(struct pt_regs *, long);
 dotraplinkage void do_debug(struct pt_regs *, long);
@@ -67,7 +69,9 @@ 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_emulate_vsyscall(struct pt_regs *, long);
+dotraplinkage void do_emulate_vsyscall0(struct pt_regs *, long);
+dotraplinkage void do_emulate_vsyscall1(struct pt_regs *, long);
+dotraplinkage void do_emulate_vsyscall2(struct pt_regs *, long);
 #ifdef CONFIG_X86_32
 dotraplinkage void do_iret_error(struct pt_regs *, long);
 #endif
arch/x86/kernel/entry_64.S +3 -1
index e13329d..10489e5 100644
@@ -1111,7 +1111,9 @@ 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 emulate_vsyscall do_emulate_vsyscall
+zeroentry emulate_vsyscall0 do_emulate_vsyscall0
+zeroentry emulate_vsyscall1 do_emulate_vsyscall1
+zeroentry emulate_vsyscall2 do_emulate_vsyscall2
 
 
 	/* Reload gs selector with exception handling */
arch/x86/kernel/traps.c +11 -3
index 9682ec5..6ae5e3a 100644
@@ -873,9 +873,17 @@ void __init trap_init(void)
 #endif
 
 #ifdef CONFIG_X86_64
-	BUG_ON(test_bit(VSYSCALL_EMU_VECTOR, used_vectors));
-	set_system_intr_gate(VSYSCALL_EMU_VECTOR, &emulate_vsyscall);
-	set_bit(VSYSCALL_EMU_VECTOR, used_vectors);
+	BUG_ON(test_bit(VSYSCALL0_EMU_VECTOR, used_vectors));
+	set_system_intr_gate(VSYSCALL0_EMU_VECTOR, &emulate_vsyscall0);
+	set_bit(VSYSCALL0_EMU_VECTOR, used_vectors);
+
+	BUG_ON(test_bit(VSYSCALL1_EMU_VECTOR, used_vectors));
+	set_system_intr_gate(VSYSCALL1_EMU_VECTOR, &emulate_vsyscall1);
+	set_bit(VSYSCALL1_EMU_VECTOR, used_vectors);
+
+	BUG_ON(test_bit(VSYSCALL2_EMU_VECTOR, used_vectors));
+	set_system_intr_gate(VSYSCALL2_EMU_VECTOR, &emulate_vsyscall2);
+	set_bit(VSYSCALL2_EMU_VECTOR, used_vectors);
 #endif
 
 	/*
arch/x86/kernel/vsyscall_64.c +66 -62

188 lines not on this page · raw message

arch/x86/kernel/vsyscall_emu_64.S +3 -3
index ffa845e..a4f02a3 100644
@@ -13,15 +13,15 @@
 
 .section .vsyscall_0, "a"
 ENTRY(vsyscall_0)
-	int $VSYSCALL_EMU_VECTOR
+	int $VSYSCALL0_EMU_VECTOR
 END(vsyscall_0)
 
 .section .vsyscall_1, "a"
 ENTRY(vsyscall_1)
-	int $VSYSCALL_EMU_VECTOR
+	int $VSYSCALL1_EMU_VECTOR
 END(vsyscall_1)
 
 .section .vsyscall_2, "a"
 ENTRY(vsyscall_2)
-	int $VSYSCALL_EMU_VECTOR
+	int $VSYSCALL2_EMU_VECTOR
 END(vsyscall_2)
Signature · 1 line
1.7.6
#2 lore raw

Re: [PATCH 1/4] x86-64: Allow emulated vsyscalls from user addresses

Quoted 11 lines · Andy Lutomirski

*

  *  Vectors   0 ...  31 : system traps and exceptions - hardcoded events
- *  Vectors  32 ... 127 : device interrupts
- *  Vector  128         : legacy int80 syscall interface
- *  Vector  204         : legacy x86_64 vsyscall emulation
- *  Vectors 129 ... INVALIDATE_TLB_VECTOR_START-1 except 204 : device interrupts
+ *  Vectors  32 ... INVALIDATE_TLB_VECTOR_START-1 : device interrupts, except:
+ *   Vectors 64 ... 66  : legacy x86_64 vsyscall emulation
+ *   Vector  128        : legacy int80 syscall interface

* Vectors INVALIDATE_TLB_VECTOR_START ... 255 : special interrupts *

Hi Andy,

Was there a reason to not use the vectors in the 0x2X range?

-hpa

#3 lore raw

Re: [PATCH 1/4] x86-64: Allow emulated vsyscalls from user addresses

Quoted 13 lines · H. Peter Anvin
Quoted 11 lines · Andy Lutomirski

  *
  *  Vectors   0 ...  31 : system traps and exceptions - hardcoded events
- *  Vectors  32 ... 127 : device interrupts
- *  Vector  128         : legacy int80 syscall interface
- *  Vector  204         : legacy x86_64 vsyscall emulation
- *  Vectors 129 ... INVALIDATE_TLB_VECTOR_START-1 except 204 : device interrupts
+ *  Vectors  32 ... INVALIDATE_TLB_VECTOR_START-1 : device interrupts, except:
+ *   Vectors 64 ... 66  : legacy x86_64 vsyscall emulation
+ *   Vector  128        : legacy int80 syscall interface
  *  Vectors INVALIDATE_TLB_VECTOR_START ... 255 : special interrupts *

Quoted 3 lines

Hi Andy,

Was there a reason to not use the vectors in the 0x2X range?

My general lack of understanding of how all the numbers work, and the fact that I was waiting for Suresh to answer. I assumed that /proc/interrupts showed numbers that corresponded to offsets from FIRST_EXTERNAL_VECTOR, in which case the 0x2X range is no good. I think that's wrong and they start at 0x40 (maybe?). This comment in irq_vector.h doesn't help:

/*
* Vectors 0x30-0x3f are used for ISA interrupts.
* round up to the next 16-vector boundary
*/
#define IRQ0_VECTOR ((FIRST_EXTERNAL_VECTOR + 16) & ~15)

If IRQ0_VECTOR is a legacy ISA interrupt, then it's 0x30. So what's with the rounding? In any case, I don't actually know which vector irq 18 uses, and I don't know how to dump that information out of /proc or /sys.

In any case, I won't fix this unless you and/or the rest of the x86 maintainers tell me you prefer this patch to the one that adds a vsyscall boot parameter.

--Andy

#4 lore raw

PATCH 2/4 x86: Remove unnecessary compile flag tweaks for vsyscall code

In reply to Andy Lutomirski

As of commit 98d0ac38ca7b1b7a552c9a2359174ff84decb600

Author: Andy Lutomirski <[email protected]>
Date:   Thu Jul 14 06:47:22 2011 -0400

x86-64: Move vread_tsc and vread_hpet into the vDSO

user code no longer directly calls into code in arch/x86/kernel/, so we don't need compile flag hacks to make it safe. All vdso code is in the vdso directory now.

---
 arch/x86/kernel/Makefile      |   13 -------------
 arch/x86/kernel/vsyscall_64.c |    3 ---
 2 files changed, 0 insertions(+), 16 deletions(-)
arch/x86/kernel/Makefile +0 -13
index 0410557..82f2912 100644
@@ -17,19 +17,6 @@ CFLAGS_REMOVE_ftrace.o = -pg
 CFLAGS_REMOVE_early_printk.o = -pg
 endif
 
-#
-# vsyscalls (which work on the user stack) should have
-# no stack-protector checks:
-#
-nostackp := $(call cc-option, -fno-stack-protector)
-CFLAGS_vsyscall_64.o	:= $(PROFILING) -g0 $(nostackp)
-CFLAGS_hpet.o		:= $(nostackp)
-CFLAGS_paravirt.o	:= $(nostackp)
-GCOV_PROFILE_vsyscall_64.o	:= n
-GCOV_PROFILE_hpet.o		:= n
-GCOV_PROFILE_tsc.o		:= n
-GCOV_PROFILE_paravirt.o		:= n
-
 obj-y			:= process_$(BITS).o signal.o entry_$(BITS).o
 obj-y			+= traps.o irq.o irq_$(BITS).o dumpstack_$(BITS).o
 obj-y			+= time.o ioport.o ldt.o dumpstack.o
arch/x86/kernel/vsyscall_64.c +0 -3
index 36259be..ee5c495 100644
@@ -16,9 +16,6 @@
  *  use the vDSO.
  */
 
-/* Disable profiling for userspace code: */
-#define DISABLE_BRANCH_PROFILING
-
 #include <linux/time.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
Signature · 1 line
1.7.6
#5 lore raw

PATCH 3/4 x86-64: Wire up getcpu syscall

In reply to Andy Lutomirski

getcpu is available as a vdso entry and an emulated vsyscall. Programs that for some reason don't want to use the vdso should still be able to call getcpu without relying on the slow emulated vsyscall. It costs almost nothing to expose it as a real syscall.

---
 arch/x86/include/asm/unistd_64.h |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
arch/x86/include/asm/unistd_64.h +2 -0
index 705bf13..d92641c 100644
@@ -681,6 +681,8 @@ __SYSCALL(__NR_syncfs, sys_syncfs)
 __SYSCALL(__NR_sendmmsg, sys_sendmmsg)
 #define __NR_setns				308
 __SYSCALL(__NR_setns, sys_setns)
+#define __NR_getcpu				309
+__SYSCALL(__NR_getcpu, sys_getcpu)
 
 #ifndef __NO_STUBS
 #define __ARCH_WANT_OLD_READDIR
Signature · 1 line
1.7.6
#6 lore raw

PATCH 4/4 Add vsyscalls to feature-removal-schedule.txt

In reply to Andy Lutomirski

In a couple years, we'll see how well we've done at eradicating vsyscall-using binaries. Hopefully we can then disable them by default. We will probably have to leave the option to enable them around forever.

---
 Documentation/feature-removal-schedule.txt |   14 ++++++++++++++
 1 files changed, 14 insertions(+), 0 deletions(-)
Documentation/feature-removal-schedule.txt +14 -0
index 43f4809..981562f 100644
@@ -575,6 +575,20 @@ Who:	Hans Verkuil <[email protected]>
 
 ----------------------------
 
+What:	vsyscalls may go away by default
+When:	3.6
+Why:	All vsyscall functionality is available as regular syscalls and through
+	the vDSO.  Vsyscalls are currently emulated and are slower than normal
+	syscalls.  Userspace exploits can still use the emulated vsyscall traps
+	as a form of 'ret' instruction.
+	
+	In the mean time, do *not* rely on the semantics of int 0x40, int 0x41,
+	and int 0x42.  They may change or go away completely with no notice.
+	Any attempt to use them will result in a warning in the kernel log.
+Who:	Andy Lutomirski <[email protected]>
+
+----------------------------
+
 What:	g_file_storage driver
 When:	3.8
 Why:	This driver has been superseded by g_mass_storage.
Signature · 1 line
1.7.6
#7 lore raw

NACK! Re: [PATCH 4/4] Add vsyscalls to feature-removal-schedule.txt

Quoted 3 lines · Andy Lutomirski

In a couple years, we'll see how well we've done at eradicating vsyscall-using binaries. Hopefully we can then disable them by default. We will probably have to leave the option to enable them around forever.

NACK NACK NACK!

this would break all old (old = meaning all today's) x86-64 installations.

Sorry but I don't think feature removal should be ever for anything commonly used, especially not for anything used by nearly EVERY binary on an old installation. We usually use it for obscure things used rarely.

Repeat after me: BINARY COMPATIBILITY is important. It's not a joke. It's not a toy. It's one of the basic values of Linux.

Sorry to be honest I don't know how you can even suggest such a thing with a straight face.

-Andi

Signature · 1 line
[email protected] -- Speaking for myself only
#8 lore raw

Re: NACK! Re: [PATCH 4/4] Add vsyscalls to feature-removal-schedule.txt

Quoted 8 lines · Andi Kleen
Quoted 3 lines · Andy Lutomirski

In a couple years, we'll see how well we've done at eradicating vsyscall-using binaries.  Hopefully we can then disable them by default.  We will probably have to leave the option to enable them around forever.

NACK NACK NACK!

It's already dropped from the version in tip/x86/vdso.

Quoted 1 line

this would break all old (old = meaning all today's) x86-64 installations.

Somewhat to my surprise, fully up-to-date Fedora 15 seems to work fine with vsyscall=none, and I wouldn't be terribly surprised if a few of the distributions starting making it the default sooner or later. I might do a followup patch to make the switch betweeen vsyscall=emulate and vsyscall=none work as a sysctl. (Switching from native to emulate at runtime could be problematic.)

--Andy

#9 lore raw

Re: NACK! Re: [PATCH 4/4] Add vsyscalls to feature-removal-schedule.txt

Quoted 6 lines · Andrew Lutomirski

Somewhat to my surprise, fully up-to-date Fedora 15 seems to work fine with vsyscall=none, and I wouldn't be terribly surprised if a few of the distributions starting making it the default sooner or later. I might do a followup patch to make the switch betweeen vsyscall=emulate and vsyscall=none work as a sysctl. (Switching from native to emulate at runtime could be problematic.)

As long as no binaries break (emulation is fine) it's perfectly fine to phase out the executable vsyscall page and keep it as a data page only - and we can put that into feature-removal-schedule.txt just fine.

Thanks,

Ingo