[PATCH] x86-64: Always check that RIPs are canonical during signal handling (update)

Linux Kernel Mailing List <[email protected]> Wed, 19 Apr 2006 21:59:31 GMT
Newsgroups gmane.linux.kernel.commits.2-4
Message-ID <[email protected]>
commit 7e52c418ae80c7a00d97e9a6a1c6b2c9e7184018
tree 5ddde7d5fdf3aaeb6f048dfb69e7fd99d082b1d5
parent e5a190da220758a739a31189440669c37fcd9773
author Andi Kleen <[email protected]> Tue, 18 Apr 2006 12:21:25 +0200
committer Marcelo Tosatti <[email protected]> Thu, 20 Apr 2006 03:58:31 -0500

[PATCH] x86-64: Always check that RIPs are canonical during signal handling (update)

Next try.

First the already existing check in COPY_CANON for sigreturn
wasn't correct. Replace it with a better check against TASK_SIZE.

Also add a check to sigaction which was missing it previously.

This works around a problem in handling non canonical RIPs on SYSRET on Intel
CPUs. They report the #GP on the SYSRET, not the next instruction
as Linux expects it. With these changes this path should never
see a non canonical user RIP.

I reset SIGSEGV to DFL to avoid an endless loop

Roughly based on a patch by Ernie Petrides, but redone by AK.

This is CVE-2006-0741

Cc: [email protected]

Signed-off-by: Andi Kleen <[email protected]>

 arch/x86_64/kernel/signal.c |    7 +++----
 1 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/arch/x86_64/kernel/signal.c b/arch/x86_64/kernel/signal.c
index f438b0d..8bc844f 100644
--- a/arch/x86_64/kernel/signal.c
+++ b/arch/x86_64/kernel/signal.c
@@ -143,10 +143,8 @@ restore_sigcontext(struct pt_regs *regs,
 	COPY(rdi); COPY(rsi); COPY(rbp); COPY(rsp); COPY(rbx);
 	COPY(rdx); COPY(rcx); 
 	COPY(rip);
-	/* rsp check is not strictly needed I think -AK */
-	if (regs->rip >= TASK_SIZE || regs->rsp >= TASK_SIZE) { 
+	if (regs->rip >= TASK_SIZE && regs->rip < VSYSCALL_START) { 
 		regs->rip = 0;
-		regs->rsp = 0;
 		return -EFAULT;
 	}
 	COPY(r8);
@@ -361,8 +359,9 @@ static void setup_rt_frame(int sig, stru
 	regs->rsp = (unsigned long) frame;
 	regs->rip = (unsigned long) ka->sa.sa_handler;
 	if (regs->rip >= TASK_SIZE) { 
+		if (sig == SIGSEGV)
+			ka->sa.sa_handler = SIG_DFL;
 		regs->rip = 0;
-		goto give_sigsegv;
 	}
 	regs->cs = __USER_CS;
 	regs->ss = __USER_DS;