diff options
-rw-r--r-- | arch/x86/entry/common.c | 6 | ||||
-rw-r--r-- | arch/x86/entry/syscalls/syscall_32.tbl | 2 | ||||
-rw-r--r-- | arch/x86/entry/vdso/vdso2c.h | 6 | ||||
-rw-r--r-- | arch/x86/include/asm/syscall.h | 5 | ||||
-rw-r--r-- | arch/x86/include/asm/thread_info.h | 3 | ||||
-rw-r--r-- | arch/x86/kernel/ptrace.c | 15 | ||||
-rw-r--r-- | arch/x86/kernel/signal.c | 26 |
7 files changed, 49 insertions, 14 deletions
diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index a1e71d431fed..1433f6b4607d 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c | |||
@@ -204,8 +204,12 @@ __visible inline void prepare_exit_to_usermode(struct pt_regs *regs) | |||
204 | * handling, because syscall restart has a fixup for compat | 204 | * handling, because syscall restart has a fixup for compat |
205 | * syscalls. The fixup is exercised by the ptrace_syscall_32 | 205 | * syscalls. The fixup is exercised by the ptrace_syscall_32 |
206 | * selftest. | 206 | * selftest. |
207 | * | ||
208 | * We also need to clear TS_REGS_POKED_I386: the 32-bit tracer | ||
209 | * special case only applies after poking regs and before the | ||
210 | * very next return to user mode. | ||
207 | */ | 211 | */ |
208 | ti->status &= ~TS_COMPAT; | 212 | ti->status &= ~(TS_COMPAT|TS_I386_REGS_POKED); |
209 | #endif | 213 | #endif |
210 | 214 | ||
211 | user_enter_irqoff(); | 215 | user_enter_irqoff(); |
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl index 4cddd17153fb..f848572169ea 100644 --- a/arch/x86/entry/syscalls/syscall_32.tbl +++ b/arch/x86/entry/syscalls/syscall_32.tbl | |||
@@ -294,7 +294,7 @@ | |||
294 | # 285 sys_setaltroot | 294 | # 285 sys_setaltroot |
295 | 286 i386 add_key sys_add_key | 295 | 286 i386 add_key sys_add_key |
296 | 287 i386 request_key sys_request_key | 296 | 287 i386 request_key sys_request_key |
297 | 288 i386 keyctl sys_keyctl | 297 | 288 i386 keyctl sys_keyctl compat_sys_keyctl |
298 | 289 i386 ioprio_set sys_ioprio_set | 298 | 289 i386 ioprio_set sys_ioprio_set |
299 | 290 i386 ioprio_get sys_ioprio_get | 299 | 290 i386 ioprio_get sys_ioprio_get |
300 | 291 i386 inotify_init sys_inotify_init | 300 | 291 i386 inotify_init sys_inotify_init |
diff --git a/arch/x86/entry/vdso/vdso2c.h b/arch/x86/entry/vdso/vdso2c.h index 63a03bb91497..4f741192846d 100644 --- a/arch/x86/entry/vdso/vdso2c.h +++ b/arch/x86/entry/vdso/vdso2c.h | |||
@@ -22,6 +22,9 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len, | |||
22 | 22 | ||
23 | ELF(Phdr) *pt = (ELF(Phdr) *)(raw_addr + GET_LE(&hdr->e_phoff)); | 23 | ELF(Phdr) *pt = (ELF(Phdr) *)(raw_addr + GET_LE(&hdr->e_phoff)); |
24 | 24 | ||
25 | if (hdr->e_type != ET_DYN) | ||
26 | fail("input is not a shared object\n"); | ||
27 | |||
25 | /* Walk the segment table. */ | 28 | /* Walk the segment table. */ |
26 | for (i = 0; i < GET_LE(&hdr->e_phnum); i++) { | 29 | for (i = 0; i < GET_LE(&hdr->e_phnum); i++) { |
27 | if (GET_LE(&pt[i].p_type) == PT_LOAD) { | 30 | if (GET_LE(&pt[i].p_type) == PT_LOAD) { |
@@ -49,6 +52,9 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len, | |||
49 | if (stripped_len < load_size) | 52 | if (stripped_len < load_size) |
50 | fail("stripped input is too short\n"); | 53 | fail("stripped input is too short\n"); |
51 | 54 | ||
55 | if (!dyn) | ||
56 | fail("input has no PT_DYNAMIC section -- your toolchain is buggy\n"); | ||
57 | |||
52 | /* Walk the dynamic table */ | 58 | /* Walk the dynamic table */ |
53 | for (i = 0; dyn + i < dyn_end && | 59 | for (i = 0; dyn + i < dyn_end && |
54 | GET_LE(&dyn[i].d_tag) != DT_NULL; i++) { | 60 | GET_LE(&dyn[i].d_tag) != DT_NULL; i++) { |
diff --git a/arch/x86/include/asm/syscall.h b/arch/x86/include/asm/syscall.h index 999b7cd2e78c..4e23dd15c661 100644 --- a/arch/x86/include/asm/syscall.h +++ b/arch/x86/include/asm/syscall.h | |||
@@ -60,7 +60,7 @@ static inline long syscall_get_error(struct task_struct *task, | |||
60 | * TS_COMPAT is set for 32-bit syscall entries and then | 60 | * TS_COMPAT is set for 32-bit syscall entries and then |
61 | * remains set until we return to user mode. | 61 | * remains set until we return to user mode. |
62 | */ | 62 | */ |
63 | if (task_thread_info(task)->status & TS_COMPAT) | 63 | if (task_thread_info(task)->status & (TS_COMPAT|TS_I386_REGS_POKED)) |
64 | /* | 64 | /* |
65 | * Sign-extend the value so (int)-EFOO becomes (long)-EFOO | 65 | * Sign-extend the value so (int)-EFOO becomes (long)-EFOO |
66 | * and will match correctly in comparisons. | 66 | * and will match correctly in comparisons. |
@@ -239,9 +239,6 @@ static inline int syscall_get_arch(void) | |||
239 | * TS_COMPAT is set for 32-bit syscall entry and then | 239 | * TS_COMPAT is set for 32-bit syscall entry and then |
240 | * remains set until we return to user mode. | 240 | * remains set until we return to user mode. |
241 | * | 241 | * |
242 | * TIF_IA32 tasks should always have TS_COMPAT set at | ||
243 | * system call time. | ||
244 | * | ||
245 | * x32 tasks should be considered AUDIT_ARCH_X86_64. | 242 | * x32 tasks should be considered AUDIT_ARCH_X86_64. |
246 | */ | 243 | */ |
247 | if (task_thread_info(current)->status & TS_COMPAT) | 244 | if (task_thread_info(current)->status & TS_COMPAT) |
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index b45ffdda3549..84b59846154a 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h | |||
@@ -219,6 +219,9 @@ static inline unsigned long current_stack_pointer(void) | |||
219 | * have to worry about atomic accesses. | 219 | * have to worry about atomic accesses. |
220 | */ | 220 | */ |
221 | #define TS_COMPAT 0x0002 /* 32bit syscall active (64BIT)*/ | 221 | #define TS_COMPAT 0x0002 /* 32bit syscall active (64BIT)*/ |
222 | #ifdef CONFIG_COMPAT | ||
223 | #define TS_I386_REGS_POKED 0x0004 /* regs poked by 32-bit ptracer */ | ||
224 | #endif | ||
222 | 225 | ||
223 | #ifndef __ASSEMBLY__ | 226 | #ifndef __ASSEMBLY__ |
224 | 227 | ||
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 600edd225e81..f79576a541ff 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -923,15 +923,18 @@ static int putreg32(struct task_struct *child, unsigned regno, u32 value) | |||
923 | 923 | ||
924 | case offsetof(struct user32, regs.orig_eax): | 924 | case offsetof(struct user32, regs.orig_eax): |
925 | /* | 925 | /* |
926 | * A 32-bit debugger setting orig_eax means to restore | 926 | * Warning: bizarre corner case fixup here. A 32-bit |
927 | * the state of the task restarting a 32-bit syscall. | 927 | * debugger setting orig_eax to -1 wants to disable |
928 | * Make sure we interpret the -ERESTART* codes correctly | 928 | * syscall restart. Make sure that the syscall |
929 | * in case the task is not actually still sitting at the | 929 | * restart code sign-extends orig_ax. Also make sure |
930 | * exit from a 32-bit syscall with TS_COMPAT still set. | 930 | * we interpret the -ERESTART* codes correctly if |
931 | * loaded into regs->ax in case the task is not | ||
932 | * actually still sitting at the exit from a 32-bit | ||
933 | * syscall with TS_COMPAT still set. | ||
931 | */ | 934 | */ |
932 | regs->orig_ax = value; | 935 | regs->orig_ax = value; |
933 | if (syscall_get_nr(child, regs) >= 0) | 936 | if (syscall_get_nr(child, regs) >= 0) |
934 | task_thread_info(child)->status |= TS_COMPAT; | 937 | task_thread_info(child)->status |= TS_I386_REGS_POKED; |
935 | break; | 938 | break; |
936 | 939 | ||
937 | case offsetof(struct user32, regs.eflags): | 940 | case offsetof(struct user32, regs.eflags): |
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c index 99f285b512db..04cb3212db2d 100644 --- a/arch/x86/kernel/signal.c +++ b/arch/x86/kernel/signal.c | |||
@@ -760,8 +760,30 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs) | |||
760 | 760 | ||
761 | static inline unsigned long get_nr_restart_syscall(const struct pt_regs *regs) | 761 | static inline unsigned long get_nr_restart_syscall(const struct pt_regs *regs) |
762 | { | 762 | { |
763 | #ifdef CONFIG_X86_64 | 763 | /* |
764 | if (in_ia32_syscall()) | 764 | * This function is fundamentally broken as currently |
765 | * implemented. | ||
766 | * | ||
767 | * The idea is that we want to trigger a call to the | ||
768 | * restart_block() syscall and that we want in_ia32_syscall(), | ||
769 | * in_x32_syscall(), etc. to match whatever they were in the | ||
770 | * syscall being restarted. We assume that the syscall | ||
771 | * instruction at (regs->ip - 2) matches whatever syscall | ||
772 | * instruction we used to enter in the first place. | ||
773 | * | ||
774 | * The problem is that we can get here when ptrace pokes | ||
775 | * syscall-like values into regs even if we're not in a syscall | ||
776 | * at all. | ||
777 | * | ||
778 | * For now, we maintain historical behavior and guess based on | ||
779 | * stored state. We could do better by saving the actual | ||
780 | * syscall arch in restart_block or (with caveats on x32) by | ||
781 | * checking if regs->ip points to 'int $0x80'. The current | ||
782 | * behavior is incorrect if a tracer has a different bitness | ||
783 | * than the tracee. | ||
784 | */ | ||
785 | #ifdef CONFIG_IA32_EMULATION | ||
786 | if (current_thread_info()->status & (TS_COMPAT|TS_I386_REGS_POKED)) | ||
765 | return __NR_ia32_restart_syscall; | 787 | return __NR_ia32_restart_syscall; |
766 | #endif | 788 | #endif |
767 | #ifdef CONFIG_X86_X32_ABI | 789 | #ifdef CONFIG_X86_X32_ABI |