Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

um: Fix ptrace GETREGS/SETREGS bugs

This fix two related bugs:
* PTRACE_GETREGS doesn't get the right orig_ax (syscall) value
* PTRACE_SETREGS can't set the orig_ax value (erased by initial value)

Get rid of the now useless and error-prone get_syscall().

Fix inconsistent behavior in the ptrace implementation for i386 when
updating orig_eax automatically update the syscall number as well. This
is now updated in handle_syscall().

Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Jeff Dike <jdike@addtoit.com>
Cc: Richard Weinberger <richard@nod.at>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Kees Cook <keescook@chromium.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Will Drewry <wad@chromium.org>
Cc: Thomas Meyer <thomas@m3y3r.de>
Cc: Nicolas Iooss <nicolas.iooss_linux@m4x.org>
Cc: Anton Ivanov <aivanov@brocade.com>
Cc: Meredydd Luff <meredydd@senatehouse.org>
Cc: David Drysdale <drysdale@google.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
Acked-by: Kees Cook <keescook@chromium.org>

authored by

Mickaël Salaün and committed by
Richard Weinberger
e04c989e a7df4716

+17 -25
-1
arch/um/include/shared/os.h
··· 284 284 void *arg); 285 285 extern void halt_skas(void); 286 286 extern void reboot_skas(void); 287 - extern int get_syscall(struct uml_pt_regs *regs); 288 287 289 288 /* irq.c */ 290 289 extern int os_waiting_for_events(struct irq_fd *active_fds);
+14 -12
arch/um/kernel/skas/syscall.c
··· 7 7 #include <linux/ptrace.h> 8 8 #include <kern_util.h> 9 9 #include <sysdep/ptrace.h> 10 + #include <sysdep/ptrace_user.h> 10 11 #include <sysdep/syscalls.h> 11 - #include <os.h> 12 12 13 13 void handle_syscall(struct uml_pt_regs *r) 14 14 { 15 15 struct pt_regs *regs = container_of(r, struct pt_regs, regs); 16 - long result; 17 16 int syscall; 18 17 19 - if (syscall_trace_enter(regs)) { 20 - result = -ENOSYS; 18 + /* Initialize the syscall number and default return value. */ 19 + UPT_SYSCALL_NR(r) = PT_SYSCALL_NR(r->gp); 20 + PT_REGS_SET_SYSCALL_RETURN(regs, -ENOSYS); 21 + 22 + if (syscall_trace_enter(regs)) 21 23 goto out; 22 - } 23 24 24 - syscall = get_syscall(r); 25 + /* Update the syscall number after orig_ax has potentially been updated 26 + * with ptrace. 27 + */ 28 + UPT_SYSCALL_NR(r) = PT_SYSCALL_NR(r->gp); 29 + syscall = UPT_SYSCALL_NR(r); 25 30 26 - if ((syscall > __NR_syscall_max) || syscall < 0) 27 - result = -ENOSYS; 28 - else 29 - result = EXECUTE_SYSCALL(syscall, regs); 31 + if (syscall >= 0 && syscall <= __NR_syscall_max) 32 + PT_REGS_SET_SYSCALL_RETURN(regs, 33 + EXECUTE_SYSCALL(syscall, regs)); 30 34 31 35 out: 32 - PT_REGS_SET_SYSCALL_RETURN(regs, result); 33 - 34 36 syscall_trace_leave(regs); 35 37 }
-7
arch/um/os-Linux/skas/process.c
··· 172 172 handle_syscall(regs); 173 173 } 174 174 175 - int get_syscall(struct uml_pt_regs *regs) 176 - { 177 - UPT_SYSCALL_NR(regs) = PT_SYSCALL_NR(regs->gp); 178 - 179 - return UPT_SYSCALL_NR(regs); 180 - } 181 - 182 175 extern char __syscall_stub_start[]; 183 176 184 177 static int userspace_tramp(void *stack)
+3 -5
arch/x86/um/ptrace_32.c
··· 68 68 [EFL] = HOST_EFLAGS, 69 69 [UESP] = HOST_SP, 70 70 [SS] = HOST_SS, 71 + [ORIG_EAX] = HOST_ORIG_AX, 71 72 }; 72 73 73 74 int putreg(struct task_struct *child, int regno, unsigned long value) ··· 84 83 case EAX: 85 84 case EIP: 86 85 case UESP: 86 + case ORIG_EAX: 87 87 break; 88 88 case FS: 89 89 if (value && (value & 3) != 3) ··· 109 107 case EFL: 110 108 value &= FLAG_MASK; 111 109 child->thread.regs.regs.gp[HOST_EFLAGS] |= value; 112 - return 0; 113 - case ORIG_EAX: 114 - child->thread.regs.regs.syscall = value; 115 110 return 0; 116 111 default : 117 112 panic("Bad register in putreg() : %d\n", regno); ··· 142 143 143 144 regno >>= 2; 144 145 switch (regno) { 145 - case ORIG_EAX: 146 - return child->thread.regs.regs.syscall; 147 146 case FS: 148 147 case GS: 149 148 case DS: ··· 160 163 case EDI: 161 164 case EBP: 162 165 case EFL: 166 + case ORIG_EAX: 163 167 break; 164 168 default: 165 169 panic("Bad register in getreg() : %d\n", regno);