diff options
authorRoland McGrath <>2008-03-07 14:56:02 -0800
committerLinus Torvalds <>2008-03-07 19:05:58 -0800
commit84c6f6046c5a2189160a8f0dca8b90427bf690ea (patch)
parent60d5bcec7ed6c00e3ec88749fd81229731363221 (diff)
x86_64: make ptrace always sign-extend orig_ax to 64 bits
This makes 64-bit ptrace calls setting the 64-bit orig_ax field for a 32-bit task sign-extend the low 32 bits up to 64. This matches what a 64-bit debugger expects when tracing a 32-bit task. This follows on my "x86_64 ia32 syscall restart fix". This didn't matter until that was fixed. The debugger ignores or zeros the high half of every register slot it sets (including the orig_rax pseudo-register) uniformly. It expects that the setting of the low 32 bits always has the same meaning as a 32-bit debugger setting those same 32 bits with native 32-bit facilities. This never arose before because the syscall restart check never matched any -ERESTART* values due to lack of sign extension. Before that fix, even 32-bit ptrace setting orig_eax to -1 failed to trigger the restart check anyway. So this was never noticed as a regression of 64-bit debuggers vs 32-bit debuggers on the same 64-bit kernel. Signed-off-by: Roland McGrath <> [ Changed to just do the sign-extension unconditionally on x86-64, since orig_ax is always just a small integer and doesn't need the full 64-bit range ] Signed-off-by: Linus Torvalds <>
1 files changed, 10 insertions, 0 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index f41fdc98efb1..8f64abe699fd 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -323,6 +323,16 @@ static int putreg(struct task_struct *child,
return set_flags(child, value);
#ifdef CONFIG_X86_64
+ /*
+ * Orig_ax is really just a flag with small positive and
+ * negative values, so make sure to always sign-extend it
+ * from 32 bits so that it works correctly regardless of
+ * whether we come from a 32-bit environment or not.
+ */
+ case offsetof(struct user_regs_struct, orig_ax):
+ value = (long) (s32) value;
+ break;
case offsetof(struct user_regs_struct,fs_base):
if (value >= TASK_SIZE_OF(child))
return -EIO;