diff options
| author | Roland McGrath <roland@redhat.com> | 2009-09-22 20:12:07 -0700 | 
|---|---|---|
| committer | Roland McGrath <roland@redhat.com> | 2009-09-22 22:49:24 -0700 | 
| commit | 8cb3ed13935b9b523c2de7afc8f68473fe1d4531 (patch) | |
| tree | 8c9d53ac4e9facc5750cc6bd7bd07260845b9797 /arch | |
| parent | 08ff18e299b1a1c91f4911fe9f35c4550218c73f (diff) | |
x86: ptrace: set TS_COMPAT when 32-bit ptrace sets orig_eax>=0
The 32-bit ptrace syscall on a 64-bit kernel (32-bit debugger on
32-bit task) behaves differently than a native 32-bit kernel.  When
setting a register state of orig_eax>=0 and eax=-ERESTART* when the
debugged task is NOT on its way out of a 32-bit syscall, the task will
fail to do the syscall restart logic that it should do.
Test case available at http://sources.redhat.com/cgi-bin/cvsweb.cgi/~checkout~/tests/ptrace-tests/tests/erestartsys-trap.c?cvsroot=systemtap
This happens because the 32-bit ptrace syscall sets eax=0xffffffff
when it sets orig_eax>=0.  The resuming task will not sign-extend this
for the -ERESTART* check because TS_COMPAT is not set.  (So the task
thinks it is restarting after a 64-bit syscall, not a 32-bit one.)
The fix is to have 32-bit ptrace calls set TS_COMPAT when setting
orig_eax>=0.  This ensures that the 32-bit syscall restart logic
will apply when the child resumes.
Signed-off-by: Roland McGrath <roland@redhat.com>
Diffstat (limited to 'arch')
| -rw-r--r-- | arch/x86/kernel/ptrace.c | 14 | 
1 files changed, 13 insertions, 1 deletions
| diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 52222fab99f4..7b058a2dc66a 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -1111,10 +1111,22 @@ static int putreg32(struct task_struct *child, unsigned regno, u32 value)  	R32(esi, si);  	R32(ebp, bp);  	R32(eax, ax); -	R32(orig_eax, orig_ax);  	R32(eip, ip);  	R32(esp, sp); +	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. +		 */ +		regs->orig_ax = value; +		if (syscall_get_nr(child, regs) >= 0) +			task_thread_info(child)->status |= TS_COMPAT; +		break; +  	case offsetof(struct user32, regs.eflags):  		return set_flags(child, value); | 
