[PATCH] Always check that RIPs are canonical during signal handling

Linux Kernel Mailing List <[email protected]> Wed, 12 Apr 2006 19:59:31 GMT
Newsgroups gmane.linux.kernel.commits.2-4
Message-ID <[email protected]>
commit e5a190da220758a739a31189440669c37fcd9773
tree 5ce75f4f0a50a2dba708533cb2b855f20cc2894d
parent 09d3b3dcfa80c9094f1748c1be064b9326c9ef2b
author Andi Kleen <[email protected]> Tue, 11 Apr 2006 12:34:45 +0200
committer Marcelo Tosatti <[email protected]> Thu, 13 Apr 2006 01:16:58 -0500

[PATCH] Always check that RIPs are canonical during signal handling

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.

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 |   19 +++++++++++++------
 1 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/arch/x86_64/kernel/signal.c b/arch/x86_64/kernel/signal.c
index 99fbbbe..f438b0d 100644
--- a/arch/x86_64/kernel/signal.c
+++ b/arch/x86_64/kernel/signal.c
@@ -137,15 +137,18 @@ restore_sigcontext(struct pt_regs *regs,
 
 
 #define COPY(x)		err |= __get_user(regs->x, &sc->x)
-#define COPY_CANON(x)   \
-	COPY(x); \
-	if ((regs->x >> 48)  != 0 && (regs->x >> 48) != 0xffff) \
-				regs->x = 0; 
 
 	/* fs and gs are ignored because we cannot handle the 64bit base easily */ 
 
-	COPY(rdi); COPY(rsi); COPY(rbp); COPY_CANON(rsp); COPY(rbx);
-	COPY(rdx); COPY(rcx); COPY_CANON(rip);
+	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) { 
+		regs->rip = 0;
+		regs->rsp = 0;
+		return -EFAULT;
+	}
 	COPY(r8);
 	COPY(r9);
 	COPY(r10);
@@ -357,6 +360,10 @@ static void setup_rt_frame(int sig, stru
 	regs->rdx = (unsigned long)&frame->uc; 
 	regs->rsp = (unsigned long) frame;
 	regs->rip = (unsigned long) ka->sa.sa_handler;
+	if (regs->rip >= TASK_SIZE) { 
+		regs->rip = 0;
+		goto give_sigsegv;
+	}
 	regs->cs = __USER_CS;
 	regs->ss = __USER_DS;