Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 4123

kernel-2.6.18-194.11.1.el5.src.rpm

From: Jerome Marchand <jmarchan@redhat.com>
Date: Tue, 24 Jun 2008 14:56:07 +0200
Subject: [x86_64] ia32 syscall restart fix
Message-id: 4860EEE7.3020307@redhat.com
O-Subject: [RHEL5.3 Patch] x86_64: ia32 syscall restart fix
Bugzilla: 434998
RH-Acked-by: David S. Miller <davem@redhat.com>
RH-Acked-by: Chuck Ebbert <cebbert@redhat.com>
RH-Acked-by: Roland McGrath <roland@redhat.com>

Bugzilla:
https://bugzilla.redhat.com/show_bug.cgi?id=434998

Description:
The code to restart syscalls after signals depends on checking for a
negative orig_rax, and for particular negative -ERESTART* values in rax.
These fields are 64 bits and for a 32-bit task they get zero-extended,
therefore they are never negative and syscall restart behavior is lost.

Solution:
Doing sign-extension where it matters.

For orig_rax, 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_rax, though it uses the new current_syscall()
inline to better document the subtle importance of the used of
signedness there.

The rax 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.

Upstream status:
commit 40f0933d51f4cba26a5c009a26bb230f4514c1b6

Test status:
Built on all arch and tested on x86_64 using the reproducer provided in
bugzilla.

Brew build:
http://brewweb.devel.redhat.com/brew/taskinfo?taskID=1365391

Regards,
Jerome

diff --git a/arch/x86_64/ia32/ptrace32.c b/arch/x86_64/ia32/ptrace32.c
index 2de8d7b..8a5dad9 100644
--- a/arch/x86_64/ia32/ptrace32.c
+++ b/arch/x86_64/ia32/ptrace32.c
@@ -86,10 +86,18 @@ static int putreg32(struct task_struct *child, unsigned regno, u32 val)
 	R32(esi, rsi);
 	R32(ebp, rbp);
 	R32(eax, rax);
-	R32(orig_eax, orig_rax);
 	R32(eip, rip);
 	R32(esp, rsp);
 
+	case offsetof(struct user_regs_struct32, orig_eax): {
+		/*
+		 * Sign-extend the value so that orig_eax = -1
+		 * causes (long)orig_rax < 0 tests to fire correctly.
+		 */
+		stack[offsetof(struct pt_regs, orig_rax)/8] = (long) (s32) val;
+		break;
+	}
+
 	case offsetof(struct user_regs_struct32, eflags): {
 		__u64 *flags = &stack[offsetof(struct pt_regs, eflags)/8];
 		val &= FLAG_MASK;
diff --git a/arch/x86_64/kernel/signal.c b/arch/x86_64/kernel/signal.c
index 54573ca..0481e0a 100644
--- a/arch/x86_64/kernel/signal.c
+++ b/arch/x86_64/kernel/signal.c
@@ -315,6 +315,35 @@ give_sigsegv:
 }
 
 /*
+ * 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_rax;
+}
+
+/*
+ * Return a value that is -EFOO if the system call in @regs->orig_rax
+ * 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->rax;
+#endif
+	return regs->rax;
+}
+
+/*
  * OK, we're invoking a handler
  */	
 
@@ -331,9 +360,9 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
 #endif
 
 	/* Are we from a system call? */
-	if ((long)regs->orig_rax >= 0) {
+	if (current_syscall(regs) >= 0) {
 		/* If so, check system call restarting.. */
-		switch (regs->rax) {
+		switch (current_syscall_ret(regs)) {
 		        case -ERESTART_RESTARTBLOCK:
 			case -ERESTARTNOHAND:
 				regs->rax = -EINTR;
@@ -439,10 +468,9 @@ static void do_signal(struct pt_regs *regs)
 	}
 
 	/* Did we come from a system call? */
-	if ((long)regs->orig_rax >= 0) {
+	if (current_syscall(regs) >= 0) {
 		/* Restart the system call - no handlers present */
-		long res = regs->rax;
-		switch (res) {
+		switch (current_syscall_ret(regs)) {
 		case -ERESTARTNOHAND:
 		case -ERESTARTSYS:
 		case -ERESTARTNOINTR: