diff mbox

[3.16.y-ckt,stable] Patch "x86_64, traps: Fix the espfix64 #DF fixup and rewrite it in C" has been added to staging queue

Message ID 1417429209-21353-1-git-send-email-luis.henriques@canonical.com
State New
Headers show

Commit Message

Luis Henriques Dec. 1, 2014, 10:20 a.m. UTC
This is a note to let you know that I have just added a patch titled

    x86_64, traps: Fix the espfix64 #DF fixup and rewrite it in C

to the linux-3.16.y-queue branch of the 3.16.y-ckt extended stable tree 
which can be found at:

 http://kernel.ubuntu.com/git?p=ubuntu/linux.git;a=shortlog;h=refs/heads/linux-3.16.y-queue

This patch is scheduled to be released in version 3.16.7-ckt3.

If you, or anyone else, feels it should not be added to this tree, please 
reply to this email.

For more information about the 3.16.y-ckt tree, see
https://wiki.ubuntu.com/Kernel/Dev/ExtendedStable

Thanks.
-Luis

------

From 87729e12d9bf72c8b5fa36e4718f06dff7a7d5e4 Mon Sep 17 00:00:00 2001
From: Andy Lutomirski <luto@amacapital.net>
Date: Sat, 22 Nov 2014 18:00:31 -0800
Subject: x86_64, traps: Fix the espfix64 #DF fixup and rewrite it in C

commit af726f21ed8af2cdaa4e93098dc211521218ae65 upstream.

There's nothing special enough about the espfix64 double fault fixup to
justify writing it in assembly.  Move it to C.

This also fixes a bug: if the double fault came from an IST stack, the
old asm code would return to a partially uninitialized stack frame.

Fixes: 3891a04aafd668686239349ea58f3314ea2af86b
Signed-off-by: Andy Lutomirski <luto@amacapital.net>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Luis Henriques <luis.henriques@canonical.com>
---
 arch/x86/kernel/entry_64.S | 34 ++--------------------------------
 arch/x86/kernel/traps.c    | 24 ++++++++++++++++++++++++
 2 files changed, 26 insertions(+), 32 deletions(-)

--
2.1.0

Comments

Andy Lutomirski Dec. 1, 2014, 1:21 p.m. UTC | #1
On Mon, Dec 1, 2014 at 2:20 AM, Luis Henriques
<luis.henriques@canonical.com> wrote:
> This is a note to let you know that I have just added a patch titled
>
>     x86_64, traps: Fix the espfix64 #DF fixup and rewrite it in C

I would suggest waiting a week or two before applying this and "Fix
bad_iret".  AFAIK no one knows how to usefully exploit these two, and
letting them stew longer in Linus' tree is probably a good idea.

The #SS fix, on the other hand, has a straightforward public exploit.

--Andy


>
> to the linux-3.16.y-queue branch of the 3.16.y-ckt extended stable tree
> which can be found at:
>
>  http://kernel.ubuntu.com/git?p=ubuntu/linux.git;a=shortlog;h=refs/heads/linux-3.16.y-queue
>
> This patch is scheduled to be released in version 3.16.7-ckt3.
>
> If you, or anyone else, feels it should not be added to this tree, please
> reply to this email.
>
> For more information about the 3.16.y-ckt tree, see
> https://wiki.ubuntu.com/Kernel/Dev/ExtendedStable
>
> Thanks.
> -Luis
>
> ------
>
> From 87729e12d9bf72c8b5fa36e4718f06dff7a7d5e4 Mon Sep 17 00:00:00 2001
> From: Andy Lutomirski <luto@amacapital.net>
> Date: Sat, 22 Nov 2014 18:00:31 -0800
> Subject: x86_64, traps: Fix the espfix64 #DF fixup and rewrite it in C
>
> commit af726f21ed8af2cdaa4e93098dc211521218ae65 upstream.
>
> There's nothing special enough about the espfix64 double fault fixup to
> justify writing it in assembly.  Move it to C.
>
> This also fixes a bug: if the double fault came from an IST stack, the
> old asm code would return to a partially uninitialized stack frame.
>
> Fixes: 3891a04aafd668686239349ea58f3314ea2af86b
> Signed-off-by: Andy Lutomirski <luto@amacapital.net>
> Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
> Signed-off-by: Luis Henriques <luis.henriques@canonical.com>
> ---
>  arch/x86/kernel/entry_64.S | 34 ++--------------------------------
>  arch/x86/kernel/traps.c    | 24 ++++++++++++++++++++++++
>  2 files changed, 26 insertions(+), 32 deletions(-)
>
> diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S
> index c844f0816ab8..3bbf173ced93 100644
> --- a/arch/x86/kernel/entry_64.S
> +++ b/arch/x86/kernel/entry_64.S
> @@ -842,6 +842,7 @@ ENTRY(native_iret)
>         jnz native_irq_return_ldt
>  #endif
>
> +.global native_irq_return_iret
>  native_irq_return_iret:
>         iretq
>         _ASM_EXTABLE(native_irq_return_iret, bad_iret)
> @@ -936,37 +937,6 @@ ENTRY(retint_kernel)
>         CFI_ENDPROC
>  END(common_interrupt)
>
> -       /*
> -        * If IRET takes a fault on the espfix stack, then we
> -        * end up promoting it to a doublefault.  In that case,
> -        * modify the stack to make it look like we just entered
> -        * the #GP handler from user space, similar to bad_iret.
> -        */
> -#ifdef CONFIG_X86_ESPFIX64
> -       ALIGN
> -__do_double_fault:
> -       XCPT_FRAME 1 RDI+8
> -       movq RSP(%rdi),%rax             /* Trap on the espfix stack? */
> -       sarq $PGDIR_SHIFT,%rax
> -       cmpl $ESPFIX_PGD_ENTRY,%eax
> -       jne do_double_fault             /* No, just deliver the fault */
> -       cmpl $__KERNEL_CS,CS(%rdi)
> -       jne do_double_fault
> -       movq RIP(%rdi),%rax
> -       cmpq $native_irq_return_iret,%rax
> -       jne do_double_fault             /* This shouldn't happen... */
> -       movq PER_CPU_VAR(kernel_stack),%rax
> -       subq $(6*8-KERNEL_STACK_OFFSET),%rax    /* Reset to original stack */
> -       movq %rax,RSP(%rdi)
> -       movq $0,(%rax)                  /* Missing (lost) #GP error code */
> -       movq $general_protection,RIP(%rdi)
> -       retq
> -       CFI_ENDPROC
> -END(__do_double_fault)
> -#else
> -# define __do_double_fault do_double_fault
> -#endif
> -
>  /*
>   * APIC interrupts.
>   */
> @@ -1138,7 +1108,7 @@ idtentry overflow do_overflow has_error_code=0
>  idtentry bounds do_bounds has_error_code=0
>  idtentry invalid_op do_invalid_op has_error_code=0
>  idtentry device_not_available do_device_not_available has_error_code=0
> -idtentry double_fault __do_double_fault has_error_code=1 paranoid=1
> +idtentry double_fault do_double_fault has_error_code=1 paranoid=1
>  idtentry coprocessor_segment_overrun do_coprocessor_segment_overrun has_error_code=0
>  idtentry invalid_TSS do_invalid_TSS has_error_code=1
>  idtentry segment_not_present do_segment_not_present has_error_code=1
> diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
> index 0d0e922fafc1..819662746e23 100644
> --- a/arch/x86/kernel/traps.c
> +++ b/arch/x86/kernel/traps.c
> @@ -259,6 +259,30 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
>         static const char str[] = "double fault";
>         struct task_struct *tsk = current;
>
> +#ifdef CONFIG_X86_ESPFIX64
> +       extern unsigned char native_irq_return_iret[];
> +
> +       /*
> +        * If IRET takes a non-IST fault on the espfix64 stack, then we
> +        * end up promoting it to a doublefault.  In that case, modify
> +        * the stack to make it look like we just entered the #GP
> +        * handler from user space, similar to bad_iret.
> +        */
> +       if (((long)regs->sp >> PGDIR_SHIFT) == ESPFIX_PGD_ENTRY &&
> +               regs->cs == __KERNEL_CS &&
> +               regs->ip == (unsigned long)native_irq_return_iret)
> +       {
> +               struct pt_regs *normal_regs = task_pt_regs(current);
> +
> +               /* Fake a #GP(0) from userspace. */
> +               memmove(&normal_regs->ip, (void *)regs->sp, 5*8);
> +               normal_regs->orig_ax = 0;  /* Missing (lost) #GP error code */
> +               regs->ip = (unsigned long)general_protection;
> +               regs->sp = (unsigned long)&normal_regs->orig_ax;
> +               return;
> +       }
> +#endif
> +
>         exception_enter();
>         /* Return not checked because double check cannot be ignored */
>         notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV);
> --
> 2.1.0
>
Luis Henriques Dec. 1, 2014, 1:48 p.m. UTC | #2
On Mon, Dec 01, 2014 at 05:21:47AM -0800, Andy Lutomirski wrote:
> On Mon, Dec 1, 2014 at 2:20 AM, Luis Henriques
> <luis.henriques@canonical.com> wrote:
> > This is a note to let you know that I have just added a patch titled
> >
> >     x86_64, traps: Fix the espfix64 #DF fixup and rewrite it in C
> 
> I would suggest waiting a week or two before applying this and "Fix
> bad_iret".  AFAIK no one knows how to usefully exploit these two, and
> letting them stew longer in Linus' tree is probably a good idea.
> 
> The #SS fix, on the other hand, has a straightforward public exploit.
> 
> --Andy
> 
> 

Thank you Andy.  Obviously, I'll follow your suggestion and drop the
following commits from the 3.16 queue:

 - af726f21ed8a "x86_64, traps: Fix the espfix64 #DF fixup and rewrite it in C"
 - b645af2d5905 "x86_64, traps: Rework bad_iret"

and I'm keeping:

 - 6f442be2fb22 "x86_64, traps: Stop using IST for #SS"

In a couple of weeks I'll re-queue the two patches I'm dropping.

Cheers,
--
Luís

> >
> > to the linux-3.16.y-queue branch of the 3.16.y-ckt extended stable tree
> > which can be found at:
> >
> >  http://kernel.ubuntu.com/git?p=ubuntu/linux.git;a=shortlog;h=refs/heads/linux-3.16.y-queue
> >
> > This patch is scheduled to be released in version 3.16.7-ckt3.
> >
> > If you, or anyone else, feels it should not be added to this tree, please
> > reply to this email.
> >
> > For more information about the 3.16.y-ckt tree, see
> > https://wiki.ubuntu.com/Kernel/Dev/ExtendedStable
> >
> > Thanks.
> > -Luis
> >
> > ------
> >
> > From 87729e12d9bf72c8b5fa36e4718f06dff7a7d5e4 Mon Sep 17 00:00:00 2001
> > From: Andy Lutomirski <luto@amacapital.net>
> > Date: Sat, 22 Nov 2014 18:00:31 -0800
> > Subject: x86_64, traps: Fix the espfix64 #DF fixup and rewrite it in C
> >
> > commit af726f21ed8af2cdaa4e93098dc211521218ae65 upstream.
> >
> > There's nothing special enough about the espfix64 double fault fixup to
> > justify writing it in assembly.  Move it to C.
> >
> > This also fixes a bug: if the double fault came from an IST stack, the
> > old asm code would return to a partially uninitialized stack frame.
> >
> > Fixes: 3891a04aafd668686239349ea58f3314ea2af86b
> > Signed-off-by: Andy Lutomirski <luto@amacapital.net>
> > Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
> > Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
> > Signed-off-by: Luis Henriques <luis.henriques@canonical.com>
> > ---
> >  arch/x86/kernel/entry_64.S | 34 ++--------------------------------
> >  arch/x86/kernel/traps.c    | 24 ++++++++++++++++++++++++
> >  2 files changed, 26 insertions(+), 32 deletions(-)
> >
> > diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S
> > index c844f0816ab8..3bbf173ced93 100644
> > --- a/arch/x86/kernel/entry_64.S
> > +++ b/arch/x86/kernel/entry_64.S
> > @@ -842,6 +842,7 @@ ENTRY(native_iret)
> >         jnz native_irq_return_ldt
> >  #endif
> >
> > +.global native_irq_return_iret
> >  native_irq_return_iret:
> >         iretq
> >         _ASM_EXTABLE(native_irq_return_iret, bad_iret)
> > @@ -936,37 +937,6 @@ ENTRY(retint_kernel)
> >         CFI_ENDPROC
> >  END(common_interrupt)
> >
> > -       /*
> > -        * If IRET takes a fault on the espfix stack, then we
> > -        * end up promoting it to a doublefault.  In that case,
> > -        * modify the stack to make it look like we just entered
> > -        * the #GP handler from user space, similar to bad_iret.
> > -        */
> > -#ifdef CONFIG_X86_ESPFIX64
> > -       ALIGN
> > -__do_double_fault:
> > -       XCPT_FRAME 1 RDI+8
> > -       movq RSP(%rdi),%rax             /* Trap on the espfix stack? */
> > -       sarq $PGDIR_SHIFT,%rax
> > -       cmpl $ESPFIX_PGD_ENTRY,%eax
> > -       jne do_double_fault             /* No, just deliver the fault */
> > -       cmpl $__KERNEL_CS,CS(%rdi)
> > -       jne do_double_fault
> > -       movq RIP(%rdi),%rax
> > -       cmpq $native_irq_return_iret,%rax
> > -       jne do_double_fault             /* This shouldn't happen... */
> > -       movq PER_CPU_VAR(kernel_stack),%rax
> > -       subq $(6*8-KERNEL_STACK_OFFSET),%rax    /* Reset to original stack */
> > -       movq %rax,RSP(%rdi)
> > -       movq $0,(%rax)                  /* Missing (lost) #GP error code */
> > -       movq $general_protection,RIP(%rdi)
> > -       retq
> > -       CFI_ENDPROC
> > -END(__do_double_fault)
> > -#else
> > -# define __do_double_fault do_double_fault
> > -#endif
> > -
> >  /*
> >   * APIC interrupts.
> >   */
> > @@ -1138,7 +1108,7 @@ idtentry overflow do_overflow has_error_code=0
> >  idtentry bounds do_bounds has_error_code=0
> >  idtentry invalid_op do_invalid_op has_error_code=0
> >  idtentry device_not_available do_device_not_available has_error_code=0
> > -idtentry double_fault __do_double_fault has_error_code=1 paranoid=1
> > +idtentry double_fault do_double_fault has_error_code=1 paranoid=1
> >  idtentry coprocessor_segment_overrun do_coprocessor_segment_overrun has_error_code=0
> >  idtentry invalid_TSS do_invalid_TSS has_error_code=1
> >  idtentry segment_not_present do_segment_not_present has_error_code=1
> > diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
> > index 0d0e922fafc1..819662746e23 100644
> > --- a/arch/x86/kernel/traps.c
> > +++ b/arch/x86/kernel/traps.c
> > @@ -259,6 +259,30 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
> >         static const char str[] = "double fault";
> >         struct task_struct *tsk = current;
> >
> > +#ifdef CONFIG_X86_ESPFIX64
> > +       extern unsigned char native_irq_return_iret[];
> > +
> > +       /*
> > +        * If IRET takes a non-IST fault on the espfix64 stack, then we
> > +        * end up promoting it to a doublefault.  In that case, modify
> > +        * the stack to make it look like we just entered the #GP
> > +        * handler from user space, similar to bad_iret.
> > +        */
> > +       if (((long)regs->sp >> PGDIR_SHIFT) == ESPFIX_PGD_ENTRY &&
> > +               regs->cs == __KERNEL_CS &&
> > +               regs->ip == (unsigned long)native_irq_return_iret)
> > +       {
> > +               struct pt_regs *normal_regs = task_pt_regs(current);
> > +
> > +               /* Fake a #GP(0) from userspace. */
> > +               memmove(&normal_regs->ip, (void *)regs->sp, 5*8);
> > +               normal_regs->orig_ax = 0;  /* Missing (lost) #GP error code */
> > +               regs->ip = (unsigned long)general_protection;
> > +               regs->sp = (unsigned long)&normal_regs->orig_ax;
> > +               return;
> > +       }
> > +#endif
> > +
> >         exception_enter();
> >         /* Return not checked because double check cannot be ignored */
> >         notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV);
> > --
> > 2.1.0
> >
> 
> 
> 
> -- 
> Andy Lutomirski
> AMA Capital Management, LLC
diff mbox

Patch

diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S
index c844f0816ab8..3bbf173ced93 100644
--- a/arch/x86/kernel/entry_64.S
+++ b/arch/x86/kernel/entry_64.S
@@ -842,6 +842,7 @@  ENTRY(native_iret)
 	jnz native_irq_return_ldt
 #endif

+.global native_irq_return_iret
 native_irq_return_iret:
 	iretq
 	_ASM_EXTABLE(native_irq_return_iret, bad_iret)
@@ -936,37 +937,6 @@  ENTRY(retint_kernel)
 	CFI_ENDPROC
 END(common_interrupt)

-	/*
-	 * If IRET takes a fault on the espfix stack, then we
-	 * end up promoting it to a doublefault.  In that case,
-	 * modify the stack to make it look like we just entered
-	 * the #GP handler from user space, similar to bad_iret.
-	 */
-#ifdef CONFIG_X86_ESPFIX64
-	ALIGN
-__do_double_fault:
-	XCPT_FRAME 1 RDI+8
-	movq RSP(%rdi),%rax		/* Trap on the espfix stack? */
-	sarq $PGDIR_SHIFT,%rax
-	cmpl $ESPFIX_PGD_ENTRY,%eax
-	jne do_double_fault		/* No, just deliver the fault */
-	cmpl $__KERNEL_CS,CS(%rdi)
-	jne do_double_fault
-	movq RIP(%rdi),%rax
-	cmpq $native_irq_return_iret,%rax
-	jne do_double_fault		/* This shouldn't happen... */
-	movq PER_CPU_VAR(kernel_stack),%rax
-	subq $(6*8-KERNEL_STACK_OFFSET),%rax	/* Reset to original stack */
-	movq %rax,RSP(%rdi)
-	movq $0,(%rax)			/* Missing (lost) #GP error code */
-	movq $general_protection,RIP(%rdi)
-	retq
-	CFI_ENDPROC
-END(__do_double_fault)
-#else
-# define __do_double_fault do_double_fault
-#endif
-
 /*
  * APIC interrupts.
  */
@@ -1138,7 +1108,7 @@  idtentry overflow do_overflow has_error_code=0
 idtentry bounds do_bounds has_error_code=0
 idtentry invalid_op do_invalid_op has_error_code=0
 idtentry device_not_available do_device_not_available has_error_code=0
-idtentry double_fault __do_double_fault has_error_code=1 paranoid=1
+idtentry double_fault do_double_fault has_error_code=1 paranoid=1
 idtentry coprocessor_segment_overrun do_coprocessor_segment_overrun has_error_code=0
 idtentry invalid_TSS do_invalid_TSS has_error_code=1
 idtentry segment_not_present do_segment_not_present has_error_code=1
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 0d0e922fafc1..819662746e23 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -259,6 +259,30 @@  dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
 	static const char str[] = "double fault";
 	struct task_struct *tsk = current;

+#ifdef CONFIG_X86_ESPFIX64
+	extern unsigned char native_irq_return_iret[];
+
+	/*
+	 * If IRET takes a non-IST fault on the espfix64 stack, then we
+	 * end up promoting it to a doublefault.  In that case, modify
+	 * the stack to make it look like we just entered the #GP
+	 * handler from user space, similar to bad_iret.
+	 */
+	if (((long)regs->sp >> PGDIR_SHIFT) == ESPFIX_PGD_ENTRY &&
+		regs->cs == __KERNEL_CS &&
+		regs->ip == (unsigned long)native_irq_return_iret)
+	{
+		struct pt_regs *normal_regs = task_pt_regs(current);
+
+		/* Fake a #GP(0) from userspace. */
+		memmove(&normal_regs->ip, (void *)regs->sp, 5*8);
+		normal_regs->orig_ax = 0;  /* Missing (lost) #GP error code */
+		regs->ip = (unsigned long)general_protection;
+		regs->sp = (unsigned long)&normal_regs->orig_ax;
+		return;
+	}
+#endif
+
 	exception_enter();
 	/* Return not checked because double check cannot be ignored */
 	notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV);