diff options
author | Roland McGrath <roland@redhat.com> | 2008-02-28 22:57:07 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-03-11 12:11:54 -0400 |
commit | 40f0933d51f4cba26a5c009a26bb230f4514c1b6 (patch) | |
tree | 29a55b7ae8ca7488a9d84fb9de234cff9f8f80b4 /arch/x86/kernel/ptrace.c | |
parent | 9a46d7e5b63903a70cd96c2c1391a7a26a8dbec9 (diff) |
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>
Diffstat (limited to 'arch/x86/kernel/ptrace.c')
-rw-r--r-- | arch/x86/kernel/ptrace.c | 9 |
1 files changed, 8 insertions, 1 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 8f64abe699fd..d5904eef1d31 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -1055,10 +1055,17 @@ static int putreg32(struct task_struct *child, unsigned regno, u32 value) | |||
1055 | R32(esi, si); | 1055 | R32(esi, si); |
1056 | R32(ebp, bp); | 1056 | R32(ebp, bp); |
1057 | R32(eax, ax); | 1057 | R32(eax, ax); |
1058 | R32(orig_eax, orig_ax); | ||
1059 | R32(eip, ip); | 1058 | R32(eip, ip); |
1060 | R32(esp, sp); | 1059 | R32(esp, sp); |
1061 | 1060 | ||
1061 | case offsetof(struct user32, regs.orig_eax): | ||
1062 | /* | ||
1063 | * Sign-extend the value so that orig_eax = -1 | ||
1064 | * causes (long)orig_ax < 0 tests to fire correctly. | ||
1065 | */ | ||
1066 | regs->orig_ax = (long) (s32) value; | ||
1067 | break; | ||
1068 | |||
1062 | case offsetof(struct user32, regs.eflags): | 1069 | case offsetof(struct user32, regs.eflags): |
1063 | return set_flags(child, value); | 1070 | return set_flags(child, value); |
1064 | 1071 | ||