Skip to contentSkip to conversation

x86 vdso updates

GIT PULL Thomas Gleixner 5 participants 23 messages May 26, 2011 – May 30, 2011 x86

lore
Share & tools
lore
#0 lore raw

Linus,

Please pull the latest x86-vdso-for-linus git tree from:

git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip.git x86-vdso-for-linus

Thanks,

tglx

------------------>
Andy Lutomirski (8):
x86-64: Clean up vdso/kernel shared variables
x86-64: Remove unnecessary barrier in vread_tsc
x86-64: Don't generate cmov in vread_tsc
x86-64: Vclock_gettime(CLOCK_MONOTONIC) can't ever see nsec < 0
x86-64: Move vread_tsc into a new file with sensible options
x86-64: Turn off -pg and turn on -foptimize-sibling-calls for vDSO
x86-64: Add time to vDSO
x86-64: Optimize vDSO time()

Thomas Gleixner (1):
x86: vdso: Remove unused variable

 arch/x86/include/asm/tsc.h      |    4 ++
 arch/x86/include/asm/vdso.h     |   14 -------
 arch/x86/include/asm/vgtod.h    |    2 -
 arch/x86/include/asm/vsyscall.h |   12 +-----
 arch/x86/include/asm/vvar.h     |   52 +++++++++++++++++++++++++++
 arch/x86/kernel/Makefile        |    8 +++--
 arch/x86/kernel/time.c          |    2 +-
 arch/x86/kernel/tsc.c           |   19 ----------
 arch/x86/kernel/vmlinux.lds.S   |   34 ++++++------------
 arch/x86/kernel/vread_tsc_64.c  |   36 +++++++++++++++++++
 arch/x86/kernel/vsyscall_64.c   |   46 ++++++++++--------------
 arch/x86/vdso/Makefile          |   17 ++++++++-
 arch/x86/vdso/vclock_gettime.c  |   74 ++++++++++++++++++++++++++++-----------
 arch/x86/vdso/vdso.lds.S        |    9 +----
 arch/x86/vdso/vextern.h         |   16 --------
 arch/x86/vdso/vgetcpu.c         |    3 +-
 arch/x86/vdso/vma.c             |   27 --------------
 arch/x86/vdso/vvar.c            |   12 ------
 18 files changed, 202 insertions(+), 185 deletions(-)
 create mode 100644 arch/x86/include/asm/vvar.h
 create mode 100644 arch/x86/kernel/vread_tsc_64.c
 delete mode 100644 arch/x86/vdso/vextern.h
 delete mode 100644 arch/x86/vdso/vvar.c
arch/x86/include/asm/tsc.h +4 -0
index 83e2efd..9db5583 100644
@@ -51,6 +51,10 @@ extern int unsynchronized_tsc(void);
 extern int check_tsc_unstable(void);
 extern unsigned long native_calibrate_tsc(void);
 
+#ifdef CONFIG_X86_64
+extern cycles_t vread_tsc(void);
+#endif
+
 /*
  * Boot-time check whether the TSCs are synchronized across
  * all CPUs/cores:
arch/x86/include/asm/vdso.h +0 -14
index 9064052..bb05228 100644
@@ -1,20 +1,6 @@
 #ifndef _ASM_X86_VDSO_H
 #define _ASM_X86_VDSO_H
 
-#ifdef CONFIG_X86_64
-extern const char VDSO64_PRELINK[];
-
-/*
- * Given a pointer to the vDSO image, find the pointer to VDSO64_name
- * as that symbol is defined in the vDSO sources or linker script.
- */
-#define VDSO64_SYMBOL(base, name)					\
-({									\
-	extern const char VDSO64_##name[];				\
-	(void *)(VDSO64_##name - VDSO64_PRELINK + (unsigned long)(base)); \
-})
-#endif
-
 #if defined CONFIG_X86_32 || defined CONFIG_COMPAT
 extern const char VDSO32_PRELINK[];
 
arch/x86/include/asm/vgtod.h +0 -2
index 3d61e20..646b4c1 100644
@@ -23,8 +23,6 @@ struct vsyscall_gtod_data {
 	struct timespec wall_to_monotonic;
 	struct timespec wall_time_coarse;
 };
-extern struct vsyscall_gtod_data __vsyscall_gtod_data
-__section_vsyscall_gtod_data;
 extern struct vsyscall_gtod_data vsyscall_gtod_data;
 
 #endif /* _ASM_X86_VGTOD_H */
arch/x86/include/asm/vsyscall.h +2 -10
index d0983d2..d555973 100644
@@ -16,27 +16,19 @@ enum vsyscall_num {
 #ifdef __KERNEL__
 #include <linux/seqlock.h>
 
-#define __section_vgetcpu_mode __attribute__ ((unused, __section__ (".vgetcpu_mode"), aligned(16)))
-#define __section_jiffies __attribute__ ((unused, __section__ (".jiffies"), aligned(16)))
-
 /* Definitions for CONFIG_GENERIC_TIME definitions */
-#define __section_vsyscall_gtod_data __attribute__ \
-	((unused, __section__ (".vsyscall_gtod_data"),aligned(16)))
-#define __section_vsyscall_clock __attribute__ \
-	((unused, __section__ (".vsyscall_clock"),aligned(16)))
 #define __vsyscall_fn \
 	__attribute__ ((unused, __section__(".vsyscall_fn"))) notrace
 
 #define VGETCPU_RDTSCP	1
 #define VGETCPU_LSL	2
 
-extern int __vgetcpu_mode;
-extern volatile unsigned long __jiffies;
-
 /* kernel space (writeable) */
 extern int vgetcpu_mode;
 extern struct timezone sys_tz;
 
+#include <asm/vvar.h>
+
 extern void map_vsyscall(void);
 
 #endif /* __KERNEL__ */
arch/x86/include/asm/vvar.h new file +52 -0

52 lines not on this page · raw message

arch/x86/kernel/Makefile +5 -3
index 2508064..f5abe3a 100644
@@ -8,7 +8,6 @@ CPPFLAGS_vmlinux.lds += -U$(UTS_MACHINE)
 
 ifdef CONFIG_FUNCTION_TRACER
 # Do not profile debug and lowlevel utilities
-CFLAGS_REMOVE_tsc.o = -pg
 CFLAGS_REMOVE_rtc.o = -pg
 CFLAGS_REMOVE_paravirt-spinlocks.o = -pg
 CFLAGS_REMOVE_pvclock.o = -pg
@@ -24,13 +23,16 @@ endif
 nostackp := $(call cc-option, -fno-stack-protector)
 CFLAGS_vsyscall_64.o	:= $(PROFILING) -g0 $(nostackp)
 CFLAGS_hpet.o		:= $(nostackp)
-CFLAGS_tsc.o		:= $(nostackp)
+CFLAGS_vread_tsc_64.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
 
+# vread_tsc_64 is hot and should be fully optimized:
+CFLAGS_REMOVE_vread_tsc_64.o = -pg -fno-optimize-sibling-calls
+
 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
@@ -39,7 +41,7 @@ obj-$(CONFIG_IRQ_WORK)  += irq_work.o
 obj-y			+= probe_roms.o
 obj-$(CONFIG_X86_32)	+= sys_i386_32.o i386_ksyms_32.o
 obj-$(CONFIG_X86_64)	+= sys_x86_64.o x8664_ksyms_64.o
-obj-$(CONFIG_X86_64)	+= syscall_64.o vsyscall_64.o
+obj-$(CONFIG_X86_64)	+= syscall_64.o vsyscall_64.o vread_tsc_64.o
 obj-y			+= bootflag.o e820.o
 obj-y			+= pci-dma.o quirks.o topology.o kdebugfs.o
 obj-y			+= alternative.o i8253.o pci-nommu.o hw_breakpoint.o
arch/x86/kernel/time.c +1 -1
index 25a28a2..00cbb27 100644
@@ -23,7 +23,7 @@
 #include <asm/time.h>
 
 #ifdef CONFIG_X86_64
-volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES;
+DEFINE_VVAR(volatile unsigned long, jiffies) = INITIAL_JIFFIES;
 #endif
 
 unsigned long profile_pc(struct pt_regs *regs)
arch/x86/kernel/tsc.c +0 -19
index 9335bf7..6cc6922 100644
@@ -763,25 +763,6 @@ static cycle_t read_tsc(struct clocksource *cs)
 		ret : clocksource_tsc.cycle_last;
 }
 
-#ifdef CONFIG_X86_64
-static cycle_t __vsyscall_fn vread_tsc(void)
-{
-	cycle_t ret;
-
-	/*
-	 * Surround the RDTSC by barriers, to make sure it's not
-	 * speculated to outside the seqlock critical section and
-	 * does not cause time warps:
-	 */
-	rdtsc_barrier();
-	ret = (cycle_t)vget_cycles();
-	rdtsc_barrier();
-
-	return ret >= __vsyscall_gtod_data.clock.cycle_last ?
-		ret : __vsyscall_gtod_data.clock.cycle_last;
-}
-#endif
-
 static void resume_tsc(struct clocksource *cs)
 {
 	clocksource_tsc.cycle_last = 0;
arch/x86/kernel/vmlinux.lds.S +11 -23
index 49927a8..74b5ad4 100644
@@ -161,6 +161,12 @@ 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 = .;
@@ -175,18 +181,6 @@ SECTIONS
 		*(.vsyscall_fn)
 	}
 
-	. = ALIGN(L1_CACHE_BYTES);
-	.vsyscall_gtod_data : AT(VLOAD(.vsyscall_gtod_data)) {
-		*(.vsyscall_gtod_data)
-	}
-
-	vsyscall_gtod_data = VVIRT(.vsyscall_gtod_data);
-	.vsyscall_clock : AT(VLOAD(.vsyscall_clock)) {
-		*(.vsyscall_clock)
-	}
-	vsyscall_clock = VVIRT(.vsyscall_clock);
-
-
 	.vsyscall_1 ADDR(.vsyscall_0) + 1024: AT(VLOAD(.vsyscall_1)) {
 		*(.vsyscall_1)
 	}
@@ -194,21 +188,14 @@ SECTIONS
 		*(.vsyscall_2)
 	}
 
-	.vgetcpu_mode : AT(VLOAD(.vgetcpu_mode)) {
-		*(.vgetcpu_mode)
-	}
-	vgetcpu_mode = VVIRT(.vgetcpu_mode);
-
-	. = ALIGN(L1_CACHE_BYTES);
-	.jiffies : AT(VLOAD(.jiffies)) {
-		*(.jiffies)
-	}
-	jiffies = VVIRT(.jiffies);
-
 	.vsyscall_3 ADDR(.vsyscall_0) + 3072: AT(VLOAD(.vsyscall_3)) {
 		*(.vsyscall_3)
 	}
 
+#define __VVAR_KERNEL_LDS
+#include <asm/vvar.h>
+#undef __VVAR_KERNEL_LDS
+
 	. = __vsyscall_0 + PAGE_SIZE;
 
 #undef VSYSCALL_ADDR
@@ -216,6 +203,7 @@ SECTIONS
 #undef VLOAD
 #undef VVIRT_OFFSET
 #undef VVIRT
+#undef EMIT_VVAR
 
 #endif /* CONFIG_X86_64 */
 
arch/x86/kernel/vread_tsc_64.c new file +36 -0
new file mode 100644
index 0000000..a81aa9e
@@ -0,0 +1,36 @@
+/* This code runs in userspace. */
+
+#define DISABLE_BRANCH_PROFILING
+#include <asm/vgtod.h>
+
+notrace cycle_t __vsyscall_fn vread_tsc(void)
+{
+	cycle_t ret;
+	u64 last;
+
+	/*
+	 * Empirically, a fence (of type that depends on the CPU)
+	 * before rdtsc is enough to ensure that rdtsc is ordered
+	 * with respect to loads.  The various CPU manuals are unclear
+	 * as to whether rdtsc can be reordered with later loads,
+	 * but no one has ever seen it happen.
+	 */
+	rdtsc_barrier();
+	ret = (cycle_t)vget_cycles();
+
+	last = VVAR(vsyscall_gtod_data).clock.cycle_last;
+
+	if (likely(ret >= last))
+		return ret;
+
+	/*
+	 * GCC likes to generate cmov here, but this branch is extremely
+	 * predictable (it's just a funciton of time and the likely is
+	 * very likely) and there's a data dependence, so force GCC
+	 * to generate a branch instead.  I don't barrier() because
+	 * we don't actually need a barrier, and if this function
+	 * ever gets inlined it will generate worse code.
+	 */
+	asm volatile ("");
+	return last;
+}
arch/x86/kernel/vsyscall_64.c +20 -26

89 lines not on this page · raw message

arch/x86/vdso/Makefile +15 -2
index b6552b1..bef0bc9 100644
@@ -11,7 +11,7 @@ vdso-install-$(VDSO32-y)	+= $(vdso32-images)
 
 
 # files to link into the vdso
-vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o vvar.o
+vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o
 
 # files to link into kernel
 obj-$(VDSO64-y)			+= vma.o vdso.o
@@ -37,11 +37,24 @@ $(obj)/%.so: OBJCOPYFLAGS := -S
 $(obj)/%.so: $(obj)/%.so.dbg FORCE
 	$(call if_changed,objcopy)
 
+#
+# Don't omit frame pointers for ease of userspace debugging, but do
+# optimize sibling calls.
+#
 CFL := $(PROFILING) -mcmodel=small -fPIC -O2 -fasynchronous-unwind-tables -m64 \
-       $(filter -g%,$(KBUILD_CFLAGS)) $(call cc-option, -fno-stack-protector)
+       $(filter -g%,$(KBUILD_CFLAGS)) $(call cc-option, -fno-stack-protector) \
+       -fno-omit-frame-pointer -foptimize-sibling-calls
 
 $(vobjs): KBUILD_CFLAGS += $(CFL)
 
+#
+# vDSO code runs in userspace and -pg doesn't help with profiling anyway.
+#
+CFLAGS_REMOVE_vdso-note.o = -pg
+CFLAGS_REMOVE_vclock_gettime.o = -pg
+CFLAGS_REMOVE_vgetcpu.o = -pg
+CFLAGS_REMOVE_vvar.o = -pg
+
 targets += vdso-syms.lds
 obj-$(VDSO64-y)			+= vdso-syms.lds
 
arch/x86/vdso/vclock_gettime.c +53 -21

108 lines not on this page · raw message

arch/x86/vdso/vdso.lds.S +2 -7
index 4e5dd3b..b96b267 100644
@@ -23,15 +23,10 @@ VERSION {
 		__vdso_gettimeofday;
 		getcpu;
 		__vdso_getcpu;
+		time;
+		__vdso_time;
 	local: *;
 	};
 }
 
 VDSO64_PRELINK = VDSO_PRELINK;
-
-/*
- * Define VDSO64_x for each VEXTERN(x), for use via VDSO64_SYMBOL.
- */
-#define VEXTERN(x)	VDSO64_ ## x = vdso_ ## x;
-#include "vextern.h"
-#undef	VEXTERN
arch/x86/vdso/vextern.h deleted +0 -16
deleted file mode 100644
index 1683ba2..0000000
@@ -1,16 +0,0 @@
-#ifndef VEXTERN
-#include <asm/vsyscall.h>
-#define VEXTERN(x) \
-	extern typeof(x) *vdso_ ## x __attribute__((visibility("hidden")));
-#endif
-
-#define VMAGIC 0xfeedbabeabcdefabUL
-
-/* Any kernel variables used in the vDSO must be exported in the main
-   kernel's vmlinux.lds.S/vsyscall.h/proper __section and
-   put into vextern.h and be referenced as a pointer with vdso prefix.
-   The main kernel later fills in the values.   */
-
-VEXTERN(jiffies)
-VEXTERN(vgetcpu_mode)
-VEXTERN(vsyscall_gtod_data)
arch/x86/vdso/vgetcpu.c +1 -2
index 9fbc6b2..5463ad5 100644
@@ -11,14 +11,13 @@
 #include <linux/time.h>
 #include <asm/vsyscall.h>
 #include <asm/vgtod.h>
-#include "vextern.h"
 
 notrace long
 __vdso_getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *unused)
 {
 	unsigned int p;
 
-	if (*vdso_vgetcpu_mode == VGETCPU_RDTSCP) {
+	if (VVAR(vgetcpu_mode) == VGETCPU_RDTSCP) {
 		/* Load per CPU data from RDTSCP */
 		native_read_tscp(&p);
 	} else {
arch/x86/vdso/vma.c +0 -27
index 4b5d26f..7abd2be 100644
@@ -15,9 +15,6 @@
 #include <asm/proto.h>
 #include <asm/vdso.h>
 
-#include "vextern.h"		/* Just for VMAGIC.  */
-#undef VEXTERN
-
 unsigned int __read_mostly vdso_enabled = 1;
 
 extern char vdso_start[], vdso_end[];
@@ -26,20 +23,10 @@ extern unsigned short vdso_sync_cpuid;
 static struct page **vdso_pages;
 static unsigned vdso_size;
 
-static inline void *var_ref(void *p, char *name)
-{
-	if (*(void **)p != (void *)VMAGIC) {
-		printk("VDSO: variable %s broken\n", name);
-		vdso_enabled = 0;
-	}
-	return p;
-}
-
 static int __init init_vdso_vars(void)
 {
 	int npages = (vdso_end - vdso_start + PAGE_SIZE - 1) / PAGE_SIZE;
 	int i;
-	char *vbase;
 
 	vdso_size = npages << PAGE_SHIFT;
 	vdso_pages = kmalloc(sizeof(struct page *) * npages, GFP_KERNEL);
@@ -54,20 +41,6 @@ static int __init init_vdso_vars(void)
 		copy_page(page_address(p), vdso_start + i*PAGE_SIZE);
 	}
 
-	vbase = vmap(vdso_pages, npages, 0, PAGE_KERNEL);
-	if (!vbase)
-		goto oom;
-
-	if (memcmp(vbase, "\177ELF", 4)) {
-		printk("VDSO: I'm broken; not ELF\n");
-		vdso_enabled = 0;
-	}
-
-#define VEXTERN(x) \
-	*(typeof(__ ## x) **) var_ref(VDSO64_SYMBOL(vbase, x), #x) = &__ ## x;
-#include "vextern.h"
-#undef VEXTERN
-	vunmap(vbase);
 	return 0;
 
  oom:
arch/x86/vdso/vvar.c deleted +0 -12
deleted file mode 100644
index 1b7e703..0000000
@@ -1,12 +0,0 @@
-/* Define pointer to external vDSO variables.
-   These are part of the vDSO. The kernel fills in the real addresses
-   at boot time. This is done because when the vdso is linked the
-   kernel isn't yet and we don't know the final addresses. */
-#include <linux/kernel.h>
-#include <linux/time.h>
-#include <asm/vsyscall.h>
-#include <asm/timex.h>
-#include <asm/vgtod.h>
-
-#define VEXTERN(x) typeof (__ ## x) *const vdso_ ## x = (void *)VMAGIC;
-#include "vextern.h"
#2 lore raw
Quoted 8 lines · Andy Lutomirski
Quoted 1 line · Thomas Gleixner

x86-64: Add time to vDSO

And here's the glibc bug:

http://sourceware.org/bugzilla/show_bug.cgi?id=12813

Let the deprecation of the vsyscall page begin :)

Yeah :-)

On a related note, now that these bits are upstream, what are your rough plans for doing the int81 vsyscall emulation patch?

The int81 patch would actually be (much!) more important to the average Linux user than getting rid of the vsyscall from static
binaries: the vsyscall is the last predictable executable address in PIE daemons with a dangerous SYSENTER instruction in it ...

I'd actually consider accelerating it into v2.6.40.

Thanks,

Ingo

#3 lore raw
Quoted 17 lines · Ingo Molnar
Quoted 8 lines · Andy Lutomirski
Quoted 1 line · Thomas Gleixner

      x86-64: Add time to vDSO

And here's the glibc bug:

http://sourceware.org/bugzilla/show_bug.cgi?id=12813

Let the deprecation of the vsyscall page begin :)

Yeah :-)

On a related note, now that these bits are upstream, what are your rough plans for doing the int81 vsyscall emulation patch?

Quoted 6 lines

The int81 patch would actually be (much!) more important to the average Linux user than getting rid of the vsyscall from static
binaries: the vsyscall is the last predictable executable address in PIE daemons with a dangerous SYSENTER instruction in it ...

I'd actually consider accelerating it into v2.6.40.

Working on it slowly. In principle I'm writing my thesis right now on things that have very little to do with software :)

My rough plans for the near-term stuff are:

1. Move vvars out of the vsyscall page. Otherwise we have at least one predictable syscall instruction every 1<<16 seconds because the time is executable. It's prettier that way, too.

2. Remove the vsyscall64 sysctl. That will reduce the number of vsyscalls that require the kernel's help to one. (This is a bit unfortunate for UML users, but I'm not sure what to do about that. UML vgetcpu is already terminally broken.)

3. Add int 0xcc and use it from vgettimeofday. It will SIGSEGV if called from a user address (so it has no risk of ever becoming ABI) and it will do gettimeofday if called from the right address. (I like 0xcc better than 0x81 because then I don't have to wonder whether any syscall-like instructions start with 0x81.) I'm not convinced that the existing syscall entries are usable, because syscall itself has a different calling convention and int 0x80 is a compat syscall.

I might get this done in something resembling time for 2.6.40, but no guarantees.

For later, the whole vsyscall page could become 0xcd <0xcc * 1023>, repeated four times.

To save me a bunch of spec reading and code dissection, can you explain the difference between zeroentry and paranoidzeroentry?

Quoted 5 lines

Thanks,

       Ingo

#4 lore raw
Quoted 30 lines · Andrew Lutomirski
Quoted 17 lines · Ingo Molnar
Quoted 8 lines · Andy Lutomirski
Quoted 1 line · Thomas Gleixner

      x86-64: Add time to vDSO

And here's the glibc bug:

http://sourceware.org/bugzilla/show_bug.cgi?id=12813

Let the deprecation of the vsyscall page begin :)

Yeah :-)

On a related note, now that these bits are upstream, what are your rough plans for doing the int81 vsyscall emulation patch?

Quoted 6 lines

The int81 patch would actually be (much!) more important to the average Linux user than getting rid of the vsyscall from static
binaries: the vsyscall is the last predictable executable address in PIE daemons with a dangerous SYSENTER instruction in it ...

I'd actually consider accelerating it into v2.6.40.

Working on it slowly.  In principle I'm writing my thesis right now on things that have very little to do with software :)

My rough plans for the near-term stuff are:

Quoted 8 lines

1. Move vvars out of the vsyscall page.  Otherwise we have at least one predictable syscall instruction every 1<<16 seconds because the time is executable.  It's prettier that way, too.

2. Remove the vsyscall64 sysctl.  That will reduce the number of vsyscalls that require the kernel's help to one.  (This is a bit unfortunate for UML users, but I'm not sure what to do about that. UML vgetcpu is already terminally broken.)

Can you please be a bit more precise on that?
When there is anything I can do, let me know...

Signature · 2 lines
Thanks,
//richard
#5 lore raw
Quoted 8 lines · richard -rw- weinberger
Quoted 4 lines · Andrew Lutomirski

2. Remove the vsyscall64 sysctl.  That will reduce the number of vsyscalls that require the kernel's help to one.  (This is a bit unfortunate for UML users, but I'm not sure what to do about that. UML vgetcpu is already terminally broken.)

Can you please be a bit more precise on that?
When there is anything I can do, let me know...

glibc's implementation of time, sched_getcpu, and (for static binaries) gettimeofday calls a fixed address in kernel space which returns the answer. On UML, that address is presumably in *host* kernel space and the host will return what the host thinks the answer is.

That's a bit unfortunate if the guest has a different idea of cpu numbering or what time it is. Right now, setting kernel.vsyscall64=0 will turn gettimeofday and time (but not sched_getcpu) into a real syscall. The problem is that I want to remove the vsyscall64 sysctl.

If this is considered enough of a regression, then I guess we can leave vsyscall64 around for awhile, but it will require extra work in the soon-to-be syscall emulation hack to make sure that UML can still trap the syscall.

The real solution is to fix glibc to use the vDSO which should avoid this problem entirely.

--Andy

Quoted 5 lines

--
Thanks,
//richard

#6 lore raw
Quoted 22 lines · Andrew Lutomirski
Quoted 8 lines · richard -rw- weinberger
Quoted 4 lines · Andrew Lutomirski

2. Remove the vsyscall64 sysctl.  That will reduce the number of vsyscalls that require the kernel's help to one.  (This is a bit unfortunate for UML users, but I'm not sure what to do about that. UML vgetcpu is already terminally broken.)

Can you please be a bit more precise on that?
When there is anything I can do, let me know...

glibc's implementation of time, sched_getcpu, and (for static binaries) gettimeofday calls a fixed address in kernel space which returns the answer.  On UML, that address is presumably in *host* kernel space and the host will return what the host thinks the answer is.

That's a bit unfortunate if the guest has a different idea of cpu numbering or what time it is.  Right now, setting kernel.vsyscall64=0 will turn gettimeofday and time (but not sched_getcpu) into a real syscall.  The problem is that I want to remove the vsyscall64 sysctl.

Quoted 4 lines

If this is considered enough of a regression, then I guess we can leave vsyscall64 around for awhile, but it will require extra work in the soon-to-be syscall emulation hack to make sure that UML can still trap the syscall.

As long the time within UML is synchronous with the host everything is fine, right?
So, as _last_ choice we could disable the ability to change the time within UML.

IMHO it's not a big deal when getcpu() returns a wrong CPU layout on UML.

Quoted 2 lines

The real solution is to fix glibc to use the vDSO which should avoid this problem entirely.

Is this "bug" known? And the biggest question, will Ulrich fix it some day?

Signature · 2 lines
Thanks,
//richard
#7 lore raw
Quoted 9 lines · richard -rw- weinberger
Quoted 5 lines · Andrew Lutomirski

On Fri, May 27, 2011 at 7:59 AM, richard -rw- weinberger If this is considered enough of a regression, then I guess we can leave vsyscall64 around for awhile, but it will require extra work in the soon-to-be syscall emulation hack to make sure that UML can still trap the syscall.

As long the time within UML is synchronous with the host everything is fine, right?

I think so. I haven't used UML in a long time.

Quoted 8 lines

So, as _last_ choice we could disable the ability to change the time within UML.

IMHO it's not a big deal when getcpu() returns a wrong CPU layout on UML.

Quoted 2 lines

The real solution is to fix glibc to use the vDSO which should avoid this problem entirely.

Is this "bug" known? And the biggest question, will Ulrich fix it some day?

I added the bit about UML to
http://sourceware.org/bugzilla/show_bug.cgi?id=12813. I don't know what Ulrich will do.

--Andy

#8 lore raw
Quoted 9 lines · Andrew Lutomirski
Quoted 7 lines · richard -rw- weinberger
Quoted 5 lines · Andrew Lutomirski

On Fri, May 27, 2011 at 7:59 AM, richard -rw- weinberger If this is considered enough of a regression, then I guess we can leave vsyscall64 around for awhile, but it will require extra work in the soon-to-be syscall emulation hack to make sure that UML can still trap the syscall.

Quoted 12 lines
Quoted 2 lines

As long the time within UML is synchronous with the host everything is fine, right?

I think so.  I haven't used UML in a long time.

Quoted 7 lines

So, as _last_ choice we could disable the ability to change the time within UML.

IMHO it's not a big deal when getcpu() returns a wrong CPU layout on UML.

Quoted 2 lines

The real solution is to fix glibc to use the vDSO which should avoid this problem entirely.

Yesterday I had a closer look at 64bit UML.
Glibc is always using vsyscalls because 64bit UML does not support the vDSO.

On 32bit UML simply scans the ELF auxiliary vector provided by the host to get the address of the vDSO.
How can I get this address on a 64bit host?

Signature · 2 lines
Thanks,
//richard
#9 lore raw
Quoted 23 lines · richard -rw- weinberger
Quoted 9 lines · Andrew Lutomirski
Quoted 7 lines · richard -rw- weinberger
Quoted 5 lines · Andrew Lutomirski

On Fri, May 27, 2011 at 7:59 AM, richard -rw- weinberger If this is considered enough of a regression, then I guess we can leave vsyscall64 around for awhile, but it will require extra work in the soon-to-be syscall emulation hack to make sure that UML can still trap the syscall.

Quoted 12 lines
Quoted 2 lines

As long the time within UML is synchronous with the host everything is fine, right?

I think so.  I haven't used UML in a long time.

Quoted 7 lines

So, as _last_ choice we could disable the ability to change the time within UML.

IMHO it's not a big deal when getcpu() returns a wrong CPU layout on UML.

Quoted 2 lines

The real solution is to fix glibc to use the vDSO which should avoid this problem entirely.

Quoted 6 lines

Yesterday I had a closer look at 64bit UML.
Glibc is always using vsyscalls because 64bit UML does not support the vDSO.

On 32bit UML simply scans the ELF auxiliary vector provided by the host to get the address of the vDSO.
How can I get this address on a 64bit host?

I believe it's exactly the same. There's an auxv entry that points to the vDSO.

--Andy

#10 lore raw
Quoted 25 lines · Andrew Lutomirski
Quoted 23 lines · richard -rw- weinberger
Quoted 21 lines · Andrew Lutomirski
Quoted 9 lines · richard -rw- weinberger
Quoted 5 lines · Andrew Lutomirski

On Fri, May 27, 2011 at 7:59 AM, richard -rw- weinberger If this is considered enough of a regression, then I guess we can leave vsyscall64 around for awhile, but it will require extra work in the soon-to-be syscall emulation hack to make sure that UML can still trap the syscall.

As long the time within UML is synchronous with the host everything is fine, right?

I think so.  I haven't used UML in a long time.

Quoted 7 lines

So, as _last_ choice we could disable the ability to change the time within UML.

IMHO it's not a big deal when getcpu() returns a wrong CPU layout on UML.

Quoted 2 lines

The real solution is to fix glibc to use the vDSO which should avoid this problem entirely.

Quoted 8 lines
Quoted 6 lines

Yesterday I had a closer look at 64bit UML.
Glibc is always using vsyscalls because 64bit UML does not support the vDSO.

On 32bit UML simply scans the ELF auxiliary vector provided by the host to get the address of the vDSO.
How can I get this address on a 64bit host?

I believe it's exactly the same.  There's an auxv entry that points to the vDSO.

I don't think so.
See:
http://www.win.tue.nl/~aeb/linux/lk/lk-4.html
Section "Address space randomization".
The demo program finds the vDSO only on x86.

UML uses quite the same method to find it.
arch/um/os-Linux/elf_aux.c

Signature · 2 lines
Thanks,
//richard
#11 lore raw
Quoted 12 lines · richard -rw- weinberger
Quoted 10 lines · Andrew Lutomirski
Quoted 6 lines · richard -rw- weinberger

Yesterday I had a closer look at 64bit UML.
Glibc is always using vsyscalls because 64bit UML does not support the vDSO.

On 32bit UML simply scans the ELF auxiliary vector provided by the host to get the address of the vDSO.
How can I get this address on a 64bit host?

I believe it's exactly the same.  There's an auxv entry that points to the vDSO.

Quoted 8 lines

I don't think so.
See:
http://www.win.tue.nl/~aeb/linux/lk/lk-4.html
Section "Address space randomization".
The demo program finds the vDSO only on x86.

UML uses quite the same method to find it.
arch/um/os-Linux/elf_aux.c

The attached program works for me.

I don't know what this is supposed to mean, though:

                                /* See if the page is under TASK_SIZE */
                                if (vsyscall_ehdr < (unsigned long) envp)
                                        vsyscall_ehdr = 0;

First, envp != TASK_SIZE.

Second, the vDSO can be wherever it wants. On current kernels at least it is *always* mapped below TASK_SIZE (in the unsigned sense) because it's mapped into user address space.

--Andy

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <elf.h>
#include <stdlib.h>

extern char **environ;

int __captured_argc;
char **__captured_argv;
char **__captured_envp;
Elf64_auxv_t *__captured_auxv;

void _capture_args(int argc, char **argv, char **envp) {
__captured_argc = argc;
__captured_argv = argv;
__captured_envp = envp;

while(*envp)

    envp++;

__captured_auxv = (Elf64_auxv_t*)(envp + 1);
}

static void * initarray_entry __attribute__((section(".preinit_array"), used)) =
(void*)_capture_args;

int main()
{
printf("envp = %p\n", __captured_envp);

for (int i = 0; __captured_auxv[i].a_type != AT_NULL; i++)

    {
      char *desc = "";
      if (__captured_auxv[i].a_type == AT_SYSINFO)
	desc = "AT_SYSINFO";
      else if (__captured_auxv[i].a_type == AT_SYSINFO_EHDR)
	desc = "AT_SYSINFO_EHDR";
      printf("aux type %02u = 0x%016lX %s\n",
	     __captured_auxv[i].a_type, (long)__captured_auxv[i].a_un.a_val,
	     desc);
    }

return 0;
}

#12 lore raw
Quoted 20 lines · Andrew Lutomirski
Quoted 18 lines · richard -rw- weinberger
Quoted 10 lines · Andrew Lutomirski
Quoted 6 lines · richard -rw- weinberger

Yesterday I had a closer look at 64bit UML.
Glibc is always using vsyscalls because 64bit UML does not support the vDSO.

On 32bit UML simply scans the ELF auxiliary vector provided by the host to get the address of the vDSO.
How can I get this address on a 64bit host?

I believe it's exactly the same.  There's an auxv entry that points to the vDSO.

I don't think so.
See:
http://www.win.tue.nl/~aeb/linux/lk/lk-4.html
Section "Address space randomization".
The demo program finds the vDSO only on x86.

Quoted 4 lines
Quoted 2 lines

UML uses quite the same method to find it.
arch/um/os-Linux/elf_aux.c

The attached program works for me.

Shouldn't it print the addresses of both AT_SYSINFO and AT_SYSINFO_EHDR? On my x86_64 system (2.6.37) it seems to find only AT_SYSINFO_EHDR.

Quoted 5 lines

I don't know what this is supposed to mean, though:

                               /* See if the page is under TASK_SIZE */ if (vsyscall_ehdr < (unsigned long) envp) vsyscall_ehdr = 0;

See commits a502a359 and 14251809.

Quoted 6 lines

First, envp != TASK_SIZE.

Second, the vDSO can be wherever it wants.  On current kernels at least it is *always* mapped below TASK_SIZE (in the unsigned sense) because it's mapped into user address space.

Hmm, this would explain why UML cannot find it. :-\

Signature · 2 lines
Thanks,
//richard
#13 lore raw
In reply to Andrew Lutomirski
Quoted 8 lines · Andrew Lutomirski

3. Add int 0xcc and use it from vgettimeofday.  It will SIGSEGV if called from a user address (so it has no risk of ever becoming ABI) and it will do gettimeofday if called from the right address.  (I like 0xcc better than 0x81 because then I don't have to wonder whether any syscall-like instructions start with 0x81.)  I'm not convinced that the existing syscall entries are usable, because syscall itself has a different calling convention and int 0x80 is a compat syscall.

I started looking at what needs to be done and I wanted to get your opinion before I wrote a bunch of code that you'd reject. Here are three ideas for how the int 0xcc / int 0x81 entry could work:

*** Idea 1 ***

Make it a real syscall but with extra constraints. It would have the same calling convention as the syscall instruction, but it would turn into SIGKILL if the calling address isn't in the VSYSCALL page or if the syscall number isn't __NR_clock_gettimeofday. It would BUG() if called from kernel mode. There are two ways to implement this:

1. Have the interrupt entry check constraints, twiddle its stack frame to look like a syscall instruction, and jump to the syscall entry. This way there's little code duplication. (Is it safe to sysret back to userspace from an interrupt gate? I don't see why not, but it seems to violate the spirit of the thing.)

2. Duplicate the syscall entry. Ugly.

(int 0x80 is ia32_syscall which is unworkable because it's not there on !COMPAT and because it calls the compat wrapper which would make the whole thing a mess.)

Pros:
- ptrace, audit, seccomp, etc. still work. (Although what happens if
ptrace changes the syscall number?)

Cons:
- If we ever want to emulate the whole vsyscall instead of just the
fallback (i.e. stick the int 0xcc instruction at the vsyscall entry) then it's back to the drawing board.

*** Idea 2 ***

Write the whole thing in C.

Pros: Easy to write and easy to maintain.

Cons:
- We'd have to actually think about ptrace, audit, and seccomp semantics.
- A touch slow. Probably doesn't matter.
- If we let ptrace see the entry and think it's a syscall, then
ptrace might think it can emulate the syscall and things will break unless we're very careful.

I'm inclined to go with idea 2 with these elaborations:
- If seccomp is enabled, SIGKILL. Might as well match vDSO behavior.
- Don't audit or call ptrace. These things aren't real syscalls and
that would just be confusing. In any case, audit will never see the non-fallback paths for the vDSO.

--Andy

#14 lore raw
Quoted 22 lines · Andrew Lutomirski
Quoted 8 lines · Andrew Lutomirski

3. Add int 0xcc and use it from vgettimeofday.  It will SIGSEGV if called from a user address (so it has no risk of ever becoming ABI) and it will do gettimeofday if called from the right address.  (I like 0xcc better than 0x81 because then I don't have to wonder whether any syscall-like instructions start with 0x81.)  I'm not convinced that the existing syscall entries are usable, because syscall itself has a different calling convention and int 0x80 is a compat syscall.

I started looking at what needs to be done and I wanted to get your opinion before I wrote a bunch of code that you'd reject. Here are three ideas for how the int 0xcc / int 0x81 entry could work:

*** Idea 1 ***

Make it a real syscall but with extra constraints. It would have the same calling convention as the syscall instruction, but it would turn into SIGKILL if the calling address isn't in the VSYSCALL page or if the syscall number isn't __NR_clock_gettimeofday. It would BUG() if called from kernel mode. There are two ways to implement this:

Quoted 5 lines

1. Have the interrupt entry check constraints, twiddle its stack frame to look like a syscall instruction, and jump to the syscall entry. This way there's little code duplication. (Is it safe to sysret back to userspace from an interrupt gate? I don't see why not, but it seems to violate the spirit of the thing.)

Yeah, i think it should be safe. Lets try this? It looks like the simplest variant.

Thanks,

Ingo

#15 lore raw
Quoted 25 lines · Ingo Molnar
Quoted 22 lines · Andrew Lutomirski
Quoted 8 lines · Andrew Lutomirski

3. Add int 0xcc and use it from vgettimeofday.  It will SIGSEGV if called from a user address (so it has no risk of ever becoming ABI) and it will do gettimeofday if called from the right address.  (I like 0xcc better than 0x81 because then I don't have to wonder whether any syscall-like instructions start with 0x81.)  I'm not convinced that the existing syscall entries are usable, because syscall itself has a different calling convention and int 0x80 is a compat syscall.

I started looking at what needs to be done and I wanted to get your opinion before I wrote a bunch of code that you'd reject.  Here are three ideas for how the int 0xcc / int 0x81 entry could work:

*** Idea 1 ***

Make it a real syscall but with extra constraints.  It would have the same calling convention as the syscall instruction, but it would turn into SIGKILL if the calling address isn't in the VSYSCALL page or if the syscall number isn't __NR_clock_gettimeofday.  It would BUG() if called from kernel mode.  There are two ways to implement this:

Quoted 8 lines
Quoted 5 lines

1. Have the interrupt entry check constraints, twiddle its stack frame to look like a syscall instruction, and jump to the syscall entry. This way there's little code duplication.  (Is it safe to sysret back to userspace from an interrupt gate?  I don't see why not, but it seems to violate the spirit of the thing.)

Yeah, i think it should be safe. Lets try this? It looks like the simplest variant.

The code's in the thread "[PATCH 0/5] x86-64: Remove syscall instructions at fixed addresses".

The interrupt handler ought to be reviewed especially carefully for security since user code can call it at will. It has two glaring problems that I've found already, and I'll send a v2 out soon.

--Andy

#16 lore raw
In reply to Ingo Molnar
Quoted 7 lines · Ingo Molnar
Quoted 4 lines · Andrew Lutomirski
Quoted 3 lines · Andrew Lutomirski

3. Add int 0xcc and use it from vgettimeofday.  It will SIGSEGV if called from a user address (so it has no risk of ever becoming ABI) and it will do gettimeofday if called from the right address.  (I like

...

Quoted 3 lines
Quoted 3 lines

Make it a real syscall but with extra constraints. It would have the same calling convention as the syscall instruction, but it would turn into SIGKILL if the calling address isn't in the VSYSCALL page

This will make things difficult for user-space dynamic binary instrumentation applications, since these normally execute generated code at different addresses than the original code.

Is there a safe fallback for this particular vsyscall?

/Mikael

#17 lore raw
Quoted 13 lines · Mikael Pettersson

Ingo Molnar writes:
 >
 > * Andrew Lutomirski <[email protected]> wrote:
 >
 > > On Fri, May 27, 2011 at 7:36 AM, Andrew Lutomirski <[email protected]> wrote:
 > > > 3. Add int 0xcc and use it from vgettimeofday.  It will SIGSEGV if > > > called from a user address (so it has no risk of ever becoming ABI) > > > and it will do gettimeofday if called from the right address.  (I like
...
 > > Make it a real syscall but with extra constraints.  It would have the > > same calling convention as the syscall instruction, but it would turn > > into SIGKILL if the calling address isn't in the VSYSCALL page

Quoted 5 lines

This will make things difficult for user-space dynamic binary instrumentation applications, since these normally execute generated code at different addresses than the original code.

Is there a safe fallback for this particular vsyscall?

All of the vsyscalls have vDSO versions that work like any other code.

Alternatively, if the dynamic instrumentation code knew about vsyscalls, it could just not instrument addresses in the vsyscall page.

What existing applications would get broken?

--Andy

#18 lore raw
Quoted 14 lines · Andrew Lutomirski
Quoted 13 lines · Mikael Pettersson

Ingo Molnar writes:
 >
 > * Andrew Lutomirski <[email protected]> wrote:
 >
 > > On Fri, May 27, 2011 at 7:36 AM, Andrew Lutomirski <[email protected]> wrote:
 > > > 3. Add int 0xcc and use it from vgettimeofday.  It will SIGSEGV if > > > called from a user address (so it has no risk of ever becoming ABI) > > > and it will do gettimeofday if called from the right address.  (I like
...
 > > Make it a real syscall but with extra constraints.  It would have the > > same calling convention as the syscall instruction, but it would turn > > into SIGKILL if the calling address isn't in the VSYSCALL page

Quoted 7 lines
Quoted 5 lines

This will make things difficult for user-space dynamic binary instrumentation applications, since these normally execute generated code at different addresses than the original code.

Is there a safe fallback for this particular vsyscall?

All of the vsyscalls have vDSO versions that work like any other code.

Easiest would be if we can simply map int $0xcc with rAX==FOO to syscall or int 0x80 with rAX==BAR.

We currently don't even know about the vDSO, it's all just user-space code to us.

Quoted 3 lines

Alternatively, if the dynamic instrumentation code knew about vsyscalls, it could just not instrument addresses in the vsyscall page.

Not instrumenting code is not an option, unless we can prove that the code in question has no relevant side-effects or unexpected control-flow. (Where "side-effects" relate both to the integrity of the instrumentation engine and the application-specific payload it's attaching to the code.)

Quoted 1 line

What existing applications would get broken?

My concern is ThreadSpotter, but any user-space dynamic binary instrumentation engine that instruments down to the raw kernel interface (syscall/sysenter/int instructions) would have a problem with syscalls that only work at specific addresses.

Anyway, if I can map that vsyscall to a plain proper syscall, then I'm OK.

/Mikael

#19 lore raw
Quoted 23 lines · Mikael Pettersson

Andrew Lutomirski writes:
 > On Sun, May 29, 2011 at 10:39 AM, Mikael Pettersson <[email protected]> wrote:
 > > Ingo Molnar writes:
 > >  >
 > >  > * Andrew Lutomirski <[email protected]> wrote:
 > >  >
 > >  > > On Fri, May 27, 2011 at 7:36 AM, Andrew Lutomirski <[email protected]> wrote:
 > >  > > > 3. Add int 0xcc and use it from vgettimeofday.  It will SIGSEGV if > >  > > > called from a user address (so it has no risk of ever becoming ABI) > >  > > > and it will do gettimeofday if called from the right address.  (I like > > ...
 > >  > > Make it a real syscall but with extra constraints.  It would have the > >  > > same calling convention as the syscall instruction, but it would turn > >  > > into SIGKILL if the calling address isn't in the VSYSCALL page > >
 > > This will make things difficult for user-space dynamic binary instrumentation > > applications, since these normally execute generated code at different > > addresses than the original code.
 > >
 > > Is there a safe fallback for this particular vsyscall?
 >
 > All of the vsyscalls have vDSO versions that work like any other code.

Quoted 2 lines

Easiest would be if we can simply map int $0xcc with rAX==FOO to syscall or int 0x80 with rAX==BAR.

Yes and no.

With the code I just posted (and am fixing up now) that will work. But if we want to replace the entire vsyscall page with three int 0xcc and 4090 int3 instructions, then we can't look at eax because it won't contain anything meaningful.

--Andy

Quoted 4 lines

We currently don't even know about the vDSO, it's all just user-space code to us.

Quoted 8 lines

 > Alternatively, if the dynamic instrumentation code knew about > vsyscalls, it could just not instrument addresses in the vsyscall > page.

Not instrumenting code is not an option, unless we can prove that the code in question has no relevant side-effects or unexpected control-flow. (Where "side-effects" relate both to the integrity of the instrumentation engine and the application-specific payload it's attaching to the code.)

Calls to 0xffffffffff600000, 0xffffffffff600400, and 0xffffffffff600800 are syscalls, as an (unfortunate) part of the ABI.

Quoted 7 lines

 > What existing applications would get broken?

My concern is ThreadSpotter, but any user-space dynamic binary instrumentation engine that instruments down to the raw kernel interface (syscall/sysenter/int instructions) would have a problem with syscalls that only work at specific addresses.

I'll look.

Quoted 2 lines

Anyway, if I can map that vsyscall to a plain proper syscall, then I'm OK.

All three vsyscalls can be replaced with real syscalls without side effects. Would it be possible to teach the instrumentation code to deal with that?

--Andy

#20 lore raw
Quoted 27 lines · Andrew Lutomirski
Quoted 23 lines · Mikael Pettersson

Andrew Lutomirski writes:
 > On Sun, May 29, 2011 at 10:39 AM, Mikael Pettersson <[email protected]> wrote:
 > > Ingo Molnar writes:
 > >  >
 > >  > * Andrew Lutomirski <[email protected]> wrote:
 > >  >
 > >  > > On Fri, May 27, 2011 at 7:36 AM, Andrew Lutomirski <[email protected]> wrote:
 > >  > > > 3. Add int 0xcc and use it from vgettimeofday.  It will SIGSEGV if > >  > > > called from a user address (so it has no risk of ever becoming ABI) > >  > > > and it will do gettimeofday if called from the right address.  (I like > > ...
 > >  > > Make it a real syscall but with extra constraints.  It would have the > >  > > same calling convention as the syscall instruction, but it would turn > >  > > into SIGKILL if the calling address isn't in the VSYSCALL page > >
 > > This will make things difficult for user-space dynamic binary instrumentation > > applications, since these normally execute generated code at different > > addresses than the original code.
 > >
 > > Is there a safe fallback for this particular vsyscall?
 >
 > All of the vsyscalls have vDSO versions that work like any other code.

Quoted 2 lines

Easiest would be if we can simply map int $0xcc with rAX==FOO to syscall or int 0x80 with rAX==BAR.

Quoted 6 lines

Yes and no.

With the code I just posted (and am fixing up now) that will work. But if we want to replace the entire vsyscall page with three int 0xcc and 4090 int3 instructions, then we can't look at eax because it won't contain anything meaningful.

I can relatively easily also consider the original application rIP when decoding and translating these instructions.

Quoted 27 lines

--Andy

Quoted 4 lines

We currently don't even know about the vDSO, it's all just user-space code to us.

Quoted 8 lines

 > Alternatively, if the dynamic instrumentation code knew about > vsyscalls, it could just not instrument addresses in the vsyscall > page.

Not instrumenting code is not an option, unless we can prove that the code in question has no relevant side-effects or unexpected control-flow. (Where "side-effects" relate both to the integrity of the instrumentation engine and the application-specific payload it's attaching to the code.)

Calls to 0xffffffffff600000, 0xffffffffff600400, and 0xffffffffff600800 are syscalls, as an (unfortunate) part of the ABI.

Quoted 7 lines

 > What existing applications would get broken?

My concern is ThreadSpotter, but any user-space dynamic binary instrumentation engine that instruments down to the raw kernel interface (syscall/sysenter/int instructions) would have a problem with syscalls that only work at specific addresses.

Quoted 8 lines

I'll look.

Quoted 2 lines

Anyway, if I can map that vsyscall to a plain proper syscall, then I'm OK.

All three vsyscalls can be replaced with real syscalls without side effects. Would it be possible to teach the instrumentation code to deal with that?

Yes, I just need to know how to identify them and what their equivalents are. E.g., an int3 at <known address> becomes syscall rAX=<some constant>.

Sounds like this change will be manageable after all. Thanks.

/Mikael

#21 lore raw
Quoted 55 lines · Mikael Pettersson

Andrew Lutomirski writes:
 > On Sun, May 29, 2011 at 12:01 PM, Mikael Pettersson <[email protected]> wrote:
 > > Andrew Lutomirski writes:
 > >  >
 > >  > All of the vsyscalls have vDSO versions that work like any other code. > >
 > > Easiest would be if we can simply map int $0xcc with rAX==FOO to syscall or > > int 0x80 with rAX==BAR.
 >
 > Yes and no.
 >
 > With the code I just posted (and am fixing up now) that will work. > But if we want to replace the entire vsyscall page with three int 0xcc > and 4090 int3 instructions, then we can't look at eax because it won't > contain anything meaningful.

I can relatively easily also consider the original application rIP when decoding and translating these instructions.

 >
 > --Andy
 >
 > >
 > > We currently don't even know about the vDSO, it's all just user-space code > > to us.
 > >
 > >  > Alternatively, if the dynamic instrumentation code knew about > >  > vsyscalls, it could just not instrument addresses in the vsyscall > >  > page.
 > >
 > > Not instrumenting code is not an option, unless we can prove that the > > code in question has no relevant side-effects or unexpected control-flow. > > (Where "side-effects" relate both to the integrity of the instrumentation > > engine and the application-specific payload it's attaching to the code.) >
 > Calls to 0xffffffffff600000, 0xffffffffff600400, and
 > 0xffffffffff600800 are syscalls, as an (unfortunate) part of the ABI. >
 > >
 > >  > What existing applications would get broken?
 > >
 > > My concern is ThreadSpotter, but any user-space dynamic binary instrumentation > > engine that instruments down to the raw kernel interface (syscall/sysenter/int > > instructions) would have a problem with syscalls that only work at specific > > addresses.
 >
 > I'll look.
 >
 > >
 > > Anyway, if I can map that vsyscall to a plain proper syscall, then I'm OK. >
 > All three vsyscalls can be replaced with real syscalls without side > effects.  Would it be possible to teach the instrumentation code to > deal with that?

Quoted 4 lines

Yes, I just need to know how to identify them and what their equivalents are. E.g., an int3 at <known address> becomes syscall rAX=<some constant>.

Sounds like this change will be manageable after all.  Thanks.

I'm not entirely sure I like that -- that way if we ever change it again we break your stuff again.

Here are two proposals.

1. Teach your code that call 0xffffffffff600000 means gettimeofday(rdi, rsi). That's guaranteed to never change and will keep working even if we start to emulate vsyscalls by marking the page not present and trapping the instruction fetch fault.

2. Use a magic incantation like:

mov $0xce,%al
int $0xcc
ret

for gettimeofday. (The other two vsyscalls could use 0xcc and 0xf4, for example.) If I did this, I would make the 0xcc handler fault if called from kernel space with al and rip not matching and it would log a warning (but not fault) if called from user memory.

The idea is that, as far as a binary instrumentation tools is concerned, int $0xcc is just a two-byte instruction without any funny control flow. Also, if I looked everything up correctly, that magic sequence will either fault or turn into plain ret if called at the wrong offset.

If we went this route, then no software should assume *anything* about int 0xcc because it could be changed again in the future. I might even want to randomize the magic constants on each boot to make certain that no one distributes software that cares.

--Andy

Quoted 3 lines

/Mikael

#22 lore raw
Quoted 64 lines · Andrew Lutomirski
Quoted 55 lines · Mikael Pettersson

Andrew Lutomirski writes:
 > On Sun, May 29, 2011 at 12:01 PM, Mikael Pettersson <[email protected]> wrote:
 > > Andrew Lutomirski writes:
 > >  >
 > >  > All of the vsyscalls have vDSO versions that work like any other code. > >
 > > Easiest would be if we can simply map int $0xcc with rAX==FOO to syscall or > > int 0x80 with rAX==BAR.
 >
 > Yes and no.
 >
 > With the code I just posted (and am fixing up now) that will work. > But if we want to replace the entire vsyscall page with three int 0xcc > and 4090 int3 instructions, then we can't look at eax because it won't > contain anything meaningful.

I can relatively easily also consider the original application rIP when decoding and translating these instructions.

 >
 > --Andy
 >
 > >
 > > We currently don't even know about the vDSO, it's all just user-space code > > to us.
 > >
 > >  > Alternatively, if the dynamic instrumentation code knew about > >  > vsyscalls, it could just not instrument addresses in the vsyscall > >  > page.
 > >
 > > Not instrumenting code is not an option, unless we can prove that the > > code in question has no relevant side-effects or unexpected control-flow. > > (Where "side-effects" relate both to the integrity of the instrumentation > > engine and the application-specific payload it's attaching to the code.) >
 > Calls to 0xffffffffff600000, 0xffffffffff600400, and
 > 0xffffffffff600800 are syscalls, as an (unfortunate) part of the ABI. >
 > >
 > >  > What existing applications would get broken?
 > >
 > > My concern is ThreadSpotter, but any user-space dynamic binary instrumentation > > engine that instruments down to the raw kernel interface (syscall/sysenter/int > > instructions) would have a problem with syscalls that only work at specific > > addresses.
 >
 > I'll look.
 >
 > >
 > > Anyway, if I can map that vsyscall to a plain proper syscall, then I'm OK. >
 > All three vsyscalls can be replaced with real syscalls without side > effects.  Would it be possible to teach the instrumentation code to > deal with that?

Quoted 4 lines

Yes, I just need to know how to identify them and what their equivalents are. E.g., an int3 at <known address> becomes syscall rAX=<some constant>.

Sounds like this change will be manageable after all.  Thanks.

I'm not entirely sure I like that -- that way if we ever change it again we break your stuff again.

Quoted 6 lines

Here are two proposals.

1. Teach your code that call 0xffffffffff600000 means gettimeofday(rdi, rsi).  That's guaranteed to never change and will keep working even if we start to emulate vsyscalls by marking the page not present and trapping the instruction fetch fault.

valgrind might be doing this. It does not appear to trace directly into vsyscalls, or at least it doesn't copy the int 0xcc instruction out of them.

--Andy