x86: ia32 syscall restart fix
The code to restart syscalls after signals depends on checking for a negative orig_ax, and for particular negative -ERESTART* values in ax. These fields are 64 bits and for a 32-bit task they get zero-extended. The syscall restart behavior is lost, a regression from a native 32-bit kernel and from 64-bit tasks' behavior. This patch fixes the problem by doing sign-extension where it matters. For orig_ax, the only time the value should be -1 but winds up as 0x0ffffffff is via a 32-bit ptrace call. So the patch changes ptrace to sign-extend the 32-bit orig_eax value when it's stored; it doesn't change the checks on orig_ax, though it uses the new current_syscall() inline to better document the subtle importance of the used of signedness there. The ax value is stored a lot of ways and it seems hard to get them all sign-extended at their origins. So for that, we use the current_syscall_ret() to sign-extend it only for 32-bit tasks at the time of the -ERESTART* comparisons. Signed-off-by: Roland McGrath <roland@redhat.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
parent
9a46d7e5b6
commit
40f0933d51
|
@ -1055,10 +1055,17 @@ static int putreg32(struct task_struct *child, unsigned regno, u32 value)
|
||||||
R32(esi, si);
|
R32(esi, si);
|
||||||
R32(ebp, bp);
|
R32(ebp, bp);
|
||||||
R32(eax, ax);
|
R32(eax, ax);
|
||||||
R32(orig_eax, orig_ax);
|
|
||||||
R32(eip, ip);
|
R32(eip, ip);
|
||||||
R32(esp, sp);
|
R32(esp, sp);
|
||||||
|
|
||||||
|
case offsetof(struct user32, regs.orig_eax):
|
||||||
|
/*
|
||||||
|
* Sign-extend the value so that orig_eax = -1
|
||||||
|
* causes (long)orig_ax < 0 tests to fire correctly.
|
||||||
|
*/
|
||||||
|
regs->orig_ax = (long) (s32) value;
|
||||||
|
break;
|
||||||
|
|
||||||
case offsetof(struct user32, regs.eflags):
|
case offsetof(struct user32, regs.eflags):
|
||||||
return set_flags(child, value);
|
return set_flags(child, value);
|
||||||
|
|
||||||
|
|
|
@ -310,6 +310,35 @@ give_sigsegv:
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return -1L or the syscall number that @regs is executing.
|
||||||
|
*/
|
||||||
|
static long current_syscall(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We always sign-extend a -1 value being set here,
|
||||||
|
* so this is always either -1L or a syscall number.
|
||||||
|
*/
|
||||||
|
return regs->orig_ax;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a value that is -EFOO if the system call in @regs->orig_ax
|
||||||
|
* returned an error. This only works for @regs from @current.
|
||||||
|
*/
|
||||||
|
static long current_syscall_ret(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_IA32_EMULATION
|
||||||
|
if (test_thread_flag(TIF_IA32))
|
||||||
|
/*
|
||||||
|
* Sign-extend the value so (int)-EFOO becomes (long)-EFOO
|
||||||
|
* and will match correctly in comparisons.
|
||||||
|
*/
|
||||||
|
return (int) regs->ax;
|
||||||
|
#endif
|
||||||
|
return regs->ax;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* OK, we're invoking a handler
|
* OK, we're invoking a handler
|
||||||
*/
|
*/
|
||||||
|
@ -327,9 +356,9 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Are we from a system call? */
|
/* Are we from a system call? */
|
||||||
if ((long)regs->orig_ax >= 0) {
|
if (current_syscall(regs) >= 0) {
|
||||||
/* If so, check system call restarting.. */
|
/* If so, check system call restarting.. */
|
||||||
switch (regs->ax) {
|
switch (current_syscall_ret(regs)) {
|
||||||
case -ERESTART_RESTARTBLOCK:
|
case -ERESTART_RESTARTBLOCK:
|
||||||
case -ERESTARTNOHAND:
|
case -ERESTARTNOHAND:
|
||||||
regs->ax = -EINTR;
|
regs->ax = -EINTR;
|
||||||
|
@ -426,10 +455,9 @@ static void do_signal(struct pt_regs *regs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Did we come from a system call? */
|
/* Did we come from a system call? */
|
||||||
if ((long)regs->orig_ax >= 0) {
|
if (current_syscall(regs) >= 0) {
|
||||||
/* Restart the system call - no handlers present */
|
/* Restart the system call - no handlers present */
|
||||||
long res = regs->ax;
|
switch (current_syscall_ret(regs)) {
|
||||||
switch (res) {
|
|
||||||
case -ERESTARTNOHAND:
|
case -ERESTARTNOHAND:
|
||||||
case -ERESTARTSYS:
|
case -ERESTARTSYS:
|
||||||
case -ERESTARTNOINTR:
|
case -ERESTARTNOINTR:
|
||||||
|
|
Loading…
Reference in New Issue