x86 fixes
Subsystems (4)
Linus,
Please pull the latest x86-urgent-for-linus git tree from:
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86-urgent-for-linus
# HEAD: f7d665627e103e82d34306c7d3f6f46f387c0d8b x86/syscalls/64: Add compat_sys_keyctl for 32-bit userspace
Two fixes and a cleanup-fix, to the syscall entry code and to ptrace.
Thanks,
Ingo
------------------>
Andy Lutomirski (2):
x86/vdso: Error out if the vDSO isn't a valid DSO x86/ptrace: Stop setting TS_COMPAT in ptrace code
David Howells (1):
x86/syscalls/64: Add compat_sys_keyctl for 32-bit userspace
arch/x86/entry/common.c | 6 +++++- arch/x86/entry/syscalls/syscall_32.tbl | 2 +- arch/x86/entry/vdso/vdso2c.h | 6 ++++++ arch/x86/include/asm/syscall.h | 5 +---- arch/x86/include/asm/thread_info.h | 3 +++ arch/x86/kernel/ptrace.c | 15 +++++++++------ arch/x86/kernel/signal.c | 26 ++++++++++++++++++++++++-- 7 files changed, 49 insertions(+), 14 deletions(-)
arch/x86/entry/common.c +5 -1
@@ -270,8 +270,12 @@ __visible inline void prepare_exit_to_usermode(struct pt_regs *regs)* handling, because syscall restart has a fixup for compat * syscalls. The fixup is exercised by the ptrace_syscall_32 * selftest. +* +* We also need to clear TS_REGS_POKED_I386: the 32-bit tracer +* special case only applies after poking regs and before the +* very next return to user mode. */ - ti->status &= ~TS_COMPAT ; + ti->status &= ~(TS_COMPAT |TS_I386_REGS_POKED );#endif user_enter_irqoff ();
arch/x86/entry/syscalls/syscall_32.tbl +1 -1
@@ -294,7 +294,7 @@ # 285 sys_setaltroot 286 i386 add_key sys_add_key 287 i386 request_key sys_request_key -288 i386 keyctl sys_keyctl +288 i386 keyctl sys_keyctl compat_sys_keyctl 289 i386 ioprio_set sys_ioprio_set 290 i386 ioprio_get sys_ioprio_get 291 i386 inotify_init sys_inotify_init
arch/x86/entry/vdso/vdso2c.h +6 -0
@@ -22,6 +22,9 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,ELF (Phdr) *pt = (ELF (Phdr) *)(raw_addr +GET_LE (&hdr->e_phoff)); +if (hdr->e_type !=ET_DYN ) +fail ("input is not a shared object\n" ); +/* Walk the segment table. */ for (i =0 ; i <GET_LE (&hdr->e_phnum); i++) {if (GET_LE (&pt[i].p_type) ==PT_LOAD ) {
@@ -49,6 +52,9 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,if (stripped_len < load_size)fail ("stripped input is too short\n" ); +if (!dyn) +fail ("input has no PT_DYNAMIC section -- your toolchain is buggy\n" ); +/* Walk the dynamic table */ for (i =0 ; dyn + i < dyn_end &&GET_LE (&dyn[i].d_tag) !=DT_NULL ; i++) {
arch/x86/include/asm/syscall.h +1 -4
@@ -60,7 +60,7 @@ static inline long syscall_get_error(struct task_struct *task,* TS_COMPAT is set for 32-bit syscall entries and then * remains set until we return to user mode. */ -if (task_thread_info (task)->status &TS_COMPAT ) +if (task_thread_info (task)->status & (TS_COMPAT |TS_I386_REGS_POKED ))/* * Sign-extend the value so (int)-EFOO becomes (long)-EFOO * and will match correctly in comparisons.
@@ -239,9 +239,6 @@ static inline int syscall_get_arch(void)* TS_COMPAT is set for 32-bit syscall entry and then * remains set until we return to user mode. * -* TIF_IA32 tasks should always have TS_COMPAT set at -* system call time. -* * x32 tasks should be considered AUDIT_ARCH_X86_64. */ if (task_thread_info (current)->status &TS_COMPAT )
arch/x86/include/asm/thread_info.h +3 -0
@@ -219,6 +219,9 @@ static inline unsigned long current_stack_pointer(void)* have to worry about atomic accesses. */ #define TS_COMPAT 0x0002/* 32bit syscall active (64BIT)*/ +#ifdef CONFIG_COMPAT +#define TS_I386_REGS_POKED 0x0004/* regs poked by 32-bit ptracer */ +#endif #define TS_RESTORE_SIGMASK 0x0008/* restore signal mask in do_signal() */ #ifndef __ASSEMBLY__
arch/x86/kernel/ptrace.c +9 -6
@@ -923,15 +923,18 @@ static int putreg32(struct task_struct *child, unsigned regno, u32 value) case offsetof(struct user32, regs.orig_eax):/* -* A 32-bit debugger setting orig_eax means to restore -* the state of the task restarting a 32-bit syscall. -* Make sure we interpret the -ERESTART* codes correctly -* in case the task is not actually still sitting at the -* exit from a 32-bit syscall with TS_COMPAT still set. +* Warning: bizarre corner case fixup here. A 32-bit +* debugger setting orig_eax to -1 wants to disable +* syscall restart. Make sure that the syscall +* restart code sign-extends orig_ax. Also make sure +* we interpret the -ERESTART* codes correctly if +* loaded into regs->ax in case the task is not +* actually still sitting at the exit from a 32-bit +* syscall with TS_COMPAT still set. */ regs->orig_ax = value;if (syscall_get_nr (child, regs) >=0 ) -task_thread_info (child)->status |=TS_COMPAT ; +task_thread_info (child)->status |=TS_I386_REGS_POKED ;break ; case offsetof(struct user32, regs.eflags):
arch/x86/kernel/signal.c +24 -2
@@ -760,8 +760,30 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)static inline unsigned long get_nr_restart_syscall (const struct pt_regs *regs) { -#ifdef CONFIG_X86_64 -if (in_ia32_syscall ()) +/* +* This function is fundamentally broken as currently +* implemented. +* +* The idea is that we want to trigger a call to the +* restart_block() syscall and that we want in_ia32_syscall(), +* in_x32_syscall(), etc. to match whatever they were in the +* syscall being restarted. We assume that the syscall +* instruction at (regs->ip - 2) matches whatever syscall +* instruction we used to enter in the first place. +* +* The problem is that we can get here when ptrace pokes +* syscall-like values into regs even if we're not in a syscall +* at all. +* +* For now, we maintain historical behavior and guess based on +* stored state. We could do better by saving the actual +* syscall arch in restart_block or (with caveats on x32) by +* checking if regs->ip points to 'int $0x80'. The current +* behavior is incorrect if a tracer has a different bitness +* than the tracee. +*/ +#ifdef CONFIG_IA32_EMULATION +if (current_thread_info ()->status & (TS_COMPAT |TS_I386_REGS_POKED ))return __NR_ia32_restart_syscall;#endif #ifdef CONFIG_X86_X32_ABI