[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;