Skip to content

Commit 8c150ba

Browse files
oleg-nesterovKAGA-KOKO
authored andcommitted
x86: Introduce TS_COMPAT_RESTART to fix get_nr_restart_syscall()
The comment in get_nr_restart_syscall() says: * 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. Yes, but if not in a syscall then the status & (TS_COMPAT|TS_I386_REGS_POKED) check below can't really help: - TS_COMPAT can't be set - TS_I386_REGS_POKED is only set if regs->orig_ax was changed by 32bit debugger; and even in this case get_nr_restart_syscall() is only correct if the tracee is 32bit too. Suppose that a 64bit debugger plays with a 32bit tracee and * Tracee calls sleep(2) // TS_COMPAT is set * User interrupts the tracee by CTRL-C after 1 sec and does "(gdb) call func()" * gdb saves the regs by PTRACE_GETREGS * does PTRACE_SETREGS to set %rip='func' and %orig_rax=-1 * PTRACE_CONT // TS_COMPAT is cleared * func() hits int3. * Debugger catches SIGTRAP. * Restore original regs by PTRACE_SETREGS. * PTRACE_CONT get_nr_restart_syscall() wrongly returns __NR_restart_syscall==219, the tracee calls ia32_sys_call_table[219] == sys_madvise. Add the sticky TS_COMPAT_RESTART flag which survives after return to user mode. It's going to be removed in the next step again by storing the information in the restart block. As a further cleanup it might be possible to remove also TS_I386_REGS_POKED with that. Test-case: $ cvs -d :pserver:anoncvs:[email protected]:/cvs/systemtap co ptrace-tests $ gcc -o erestartsys-trap-debuggee ptrace-tests/tests/erestartsys-trap-debuggee.c --m32 $ gcc -o erestartsys-trap-debugger ptrace-tests/tests/erestartsys-trap-debugger.c -lutil $ ./erestartsys-trap-debugger Unexpected: retval 1, errno 22 erestartsys-trap-debugger: ptrace-tests/tests/erestartsys-trap-debugger.c:421 Fixes: 609c19a ("x86/ptrace: Stop setting TS_COMPAT in ptrace code") Reported-by: Jan Kratochvil <[email protected]> Signed-off-by: Oleg Nesterov <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Cc: [email protected] Link: https://lore.kernel.org/r/[email protected]
1 parent 66c1b6d commit 8c150ba

File tree

2 files changed

+14
-24
lines changed

2 files changed

+14
-24
lines changed

arch/x86/include/asm/thread_info.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,10 +214,22 @@ static inline int arch_within_stack_frames(const void * const stack,
214214
*/
215215
#define TS_COMPAT 0x0002 /* 32bit syscall active (64BIT)*/
216216

217+
#ifndef __ASSEMBLY__
217218
#ifdef CONFIG_COMPAT
218219
#define TS_I386_REGS_POKED 0x0004 /* regs poked by 32-bit ptracer */
220+
#define TS_COMPAT_RESTART 0x0008
221+
222+
#define arch_set_restart_data arch_set_restart_data
223+
224+
static inline void arch_set_restart_data(struct restart_block *restart)
225+
{
226+
struct thread_info *ti = current_thread_info();
227+
if (ti->status & TS_COMPAT)
228+
ti->status |= TS_COMPAT_RESTART;
229+
else
230+
ti->status &= ~TS_COMPAT_RESTART;
231+
}
219232
#endif
220-
#ifndef __ASSEMBLY__
221233

222234
#ifdef CONFIG_X86_32
223235
#define in_ia32_syscall() true

arch/x86/kernel/signal.c

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -766,30 +766,8 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
766766

767767
static inline unsigned long get_nr_restart_syscall(const struct pt_regs *regs)
768768
{
769-
/*
770-
* This function is fundamentally broken as currently
771-
* implemented.
772-
*
773-
* The idea is that we want to trigger a call to the
774-
* restart_block() syscall and that we want in_ia32_syscall(),
775-
* in_x32_syscall(), etc. to match whatever they were in the
776-
* syscall being restarted. We assume that the syscall
777-
* instruction at (regs->ip - 2) matches whatever syscall
778-
* instruction we used to enter in the first place.
779-
*
780-
* The problem is that we can get here when ptrace pokes
781-
* syscall-like values into regs even if we're not in a syscall
782-
* at all.
783-
*
784-
* For now, we maintain historical behavior and guess based on
785-
* stored state. We could do better by saving the actual
786-
* syscall arch in restart_block or (with caveats on x32) by
787-
* checking if regs->ip points to 'int $0x80'. The current
788-
* behavior is incorrect if a tracer has a different bitness
789-
* than the tracee.
790-
*/
791769
#ifdef CONFIG_IA32_EMULATION
792-
if (current_thread_info()->status & (TS_COMPAT|TS_I386_REGS_POKED))
770+
if (current_thread_info()->status & TS_COMPAT_RESTART)
793771
return __NR_ia32_restart_syscall;
794772
#endif
795773
#ifdef CONFIG_X86_X32_ABI

0 commit comments

Comments
 (0)