Patch "x86/entry/unwind: Create stack frames for saved interrupt registers" has been added to the 4.9-stable tree
Subsystems (4)
This is a note to let you know that I've just added the patch titled
x86/entry/unwind: Create stack frames for saved interrupt registers
to the 4.9-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
x86-entry-unwind-create-stack-frames-for-saved-interrupt-registers.patch
and it can be found in the queue-4.9 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From
946c191161cef10c667b5ee3179db1714fa5b7c0Mon Sep 17 00:00:00 2001
From: Josh Poimboeuf <[email protected]>
Date: Thu, 20 Oct 2016 11:34:40 -0500
Subject: x86/entry/unwind: Create stack frames for saved interrupt registers
From: Josh Poimboeuf <[email protected]>
commit 946c191161cef10c667b5ee3179db1714fa5b7c0 upstream.
With frame pointers, when a task is interrupted, its stack is no longer completely reliable because the function could have been interrupted before it had a chance to save the previous frame pointer on the stack. So the caller of the interrupted function could get skipped by a stack trace.
This is problematic for live patching, which needs to know whether a stack trace of a sleeping task can be relied upon. There's currently no way to detect if a sleeping task was interrupted by a page fault exception or preemption before it went to sleep.
Another issue is that when dumping the stack of an interrupted task, the unwinder has no way of knowing where the saved pt_regs registers are, so it can't print them.
This solves those issues by encoding the pt_regs pointer in the frame pointer on entry from an interrupt or an exception.
This patch also updates the unwinder to be able to decode it, because otherwise the unwinder would be broken by this change.
Note that this causes a change in the behavior of the unwinder: each instance of a pt_regs on the stack is now considered a "frame". So callers of unwind_get_return_address() will now get an occasional 'regs->ip' address that would have previously been skipped over.
--- arch/x86/entry/calling.h | 20 ++++++++++ arch/x86/entry/entry_32.S | 33 +++++++++++++++-- arch/x86/entry/entry_64.S | 10 +++-- arch/x86/include/asm/unwind.h | 16 ++++++++ arch/x86/kernel/unwind_frame.c | 76 ++++++++++++++++++++++++++++++++++++----- 5 files changed, 139 insertions(+), 16 deletions(-)
arch/x86/entry/calling.h +20 -0
@@ -201,6 +201,26 @@ For 32-bit we have the following convent .byte 0xf1 .endm +/* +* This is a sneaky trick to help the unwinder find pt_regs on the stack. The +* frame pointer is replaced with an encoded pointer to pt_regs. The encoding +* is just setting the LSB, which makes it an invalid stack address and is also +* a signal to the unwinder that it's a pt_regs pointer in disguise. +* +* NOTE: This macro must be used *after* SAVE_EXTRA_REGS because it corrupts +* the original rbp. +*/ +.macro ENCODE_FRAME_POINTER ptregs_offset=0 +#ifdef CONFIG_FRAME_POINTER + .if \ptregs_offset + leaq \ptregs_offset(%rsp), %rbp + .else + mov %rsp, %rbp + .endif + orq $0x1, %rbp +#endif +.endm + #endif /* CONFIG_X86_64 *//*
arch/x86/entry/entry_32.S +29 -4
@@ -175,6 +175,22 @@ SET_KERNEL_GS %edx .endm +/* + * This is a sneaky trick to help the unwinder find pt_regs on the stack. The + * frame pointer is replaced with an encoded pointer to pt_regs. The encoding + * is just setting the LSB, which makes it an invalid stack address and is also + * a signal to the unwinder that it's a pt_regs pointer in disguise. + * + * NOTE: This macro must be used *after* SAVE_ALL because it corrupts the + * original rbp. + */ +.macro ENCODE_FRAME_POINTER +#ifdef CONFIG_FRAME_POINTER + mov %esp, %ebp + orl $0x1, %ebp +#endif +.endm + .macro RESTORE_INT_REGS popl %ebx popl %ecx
@@ -624,6 +640,7 @@ common_interrupt: ASM_CLAC addl $-0x80, (%esp) /* Adjust vector into the [-256, -1] range */ SAVE_ALL + ENCODE_FRAME_POINTER TRACE_IRQS_OFF movl %esp, %eax call do_IRQ
@@ -635,6 +652,7 @@ ENTRY(name) \ ASM_CLAC; \ pushl $~(nr); \ SAVE_ALL; \ + ENCODE_FRAME_POINTER; \ TRACE_IRQS_OFF \ movl %esp, %eax; \ call fn; \
@@ -769,6 +787,7 @@ END(spurious_interrupt_bug) ENTRY(xen_hypervisor_callback) pushl $-1 /* orig_ax = -1 => not a system call */ SAVE_ALL + ENCODE_FRAME_POINTER TRACE_IRQS_OFF /*
@@ -823,6 +842,7 @@ ENTRY(xen_failsafe_callback) jmp iret_exc 5: pushl $-1 /* orig_ax = -1 => not a system call */ SAVE_ALL + ENCODE_FRAME_POINTER jmp ret_from_exception .section .fixup, "ax"
@@ -1047,6 +1067,7 @@ error_code: pushl %edx pushl %ecx pushl %ebx + ENCODE_FRAME_POINTER cld movl $(__KERNEL_PERCPU), %ecx movl %ecx, %fs
@@ -1079,6 +1100,7 @@ ENTRY(debug) ASM_CLAC pushl $-1 # mark this as an int SAVE_ALL + ENCODE_FRAME_POINTER xorl %edx, %edx # error code 0 movl %esp, %eax # pt_regs pointer
@@ -1094,11 +1116,11 @@ ENTRY(debug) .Ldebug_from_sysenter_stack: /* We're on the SYSENTER stack. Switch off. */ - movl %esp, %ebp + movl %esp, %ebx movl PER_CPU_VAR(cpu_current_top_of_stack), %esp TRACE_IRQS_OFF call do_debug - movl %ebp, %esp + movl %ebx, %esp jmp ret_from_exception END(debug)
@@ -1121,6 +1143,7 @@ ENTRY(nmi) pushl %eax # pt_regs->orig_ax SAVE_ALL + ENCODE_FRAME_POINTER xorl %edx, %edx # zero error code movl %esp, %eax # pt_regs pointer
@@ -1139,10 +1162,10 @@ ENTRY(nmi) * We're on the SYSENTER stack. Switch off. No one (not even debug) * is using the thread stack right now, so it's safe for us to use it. */ - movl %esp, %ebp + movl %esp, %ebx movl PER_CPU_VAR(cpu_current_top_of_stack), %esp call do_nmi - movl %ebp, %esp + movl %ebx, %esp jmp restore_all_notrace #ifdef CONFIG_X86_ESPFIX32
@@ -1159,6 +1182,7 @@ nmi_espfix_stack: .endr pushl %eax SAVE_ALL + ENCODE_FRAME_POINTER FIXUP_ESPFIX_STACK # %eax == %esp xorl %edx, %edx # zero error code call do_nmi
@@ -1172,6 +1196,7 @@ ENTRY(int3) ASM_CLAC pushl $-1 # mark this as an int SAVE_ALL + ENCODE_FRAME_POINTER TRACE_IRQS_OFF xorl %edx, %edx # zero error code movl %esp, %eax # pt_regs pointer
arch/x86/entry/entry_64.S +7 -3
@@ -469,6 +469,7 @@ END(irq_entries_start) ALLOC_PT_GPREGS_ON_STACK SAVE_C_REGS SAVE_EXTRA_REGS + ENCODE_FRAME_POINTER testb $3, CS(%rsp) jz 1f
@@ -985,6 +986,7 @@ ENTRY(xen_failsafe_callback) ALLOC_PT_GPREGS_ON_STACK SAVE_C_REGS SAVE_EXTRA_REGS + ENCODE_FRAME_POINTER jmp error_exit END(xen_failsafe_callback)
@@ -1028,6 +1030,7 @@ ENTRY(paranoid_entry) cld SAVE_C_REGS 8 SAVE_EXTRA_REGS 8 + ENCODE_FRAME_POINTER 8 movl $1, %ebx movl $MSR_GS_BASE, %ecx rdmsr
@@ -1075,6 +1078,7 @@ ENTRY(error_entry) cld SAVE_C_REGS 8 SAVE_EXTRA_REGS 8 + ENCODE_FRAME_POINTER 8 xorl %ebx, %ebx testb $3, CS+8(%rsp) jz .Lerror_kernelspace
@@ -1259,6 +1263,7 @@ ENTRY(nmi) pushq %r13 /* pt_regs->r13 */ pushq %r14 /* pt_regs->r14 */ pushq %r15 /* pt_regs->r15 */ + ENCODE_FRAME_POINTER /* * At this point we no longer need to worry about stack damage
@@ -1272,11 +1277,10 @@ ENTRY(nmi) /* * Return back to user mode. We must *not* do the normal exit - * work, because we don't want to enable interrupts. Fortunately, - * do_nmi doesn't modify pt_regs. + * work, because we don't want to enable interrupts. */ SWAPGS - jmp restore_c_regs_and_iret + jmp restore_regs_and_iret .Lnmi_from_kernel: /*
arch/x86/include/asm/unwind.h +15 -1
@@ -13,6 +13,7 @@ struct unwind_state {int graph_idx;#ifdef CONFIG_FRAME_POINTER unsigned long *bp; +struct pt_regs *regs;#else unsigned long *sp;#endif
@@ -47,7 +48,15 @@ unsigned long *unwind_get_return_addressif (unwind_done (state))return NULL ; - return state->bp + 1; + return state->regs ? &state->regs->ip : state->bp + 1; +} + +static inline struct pt_regs *unwind_get_entry_regs (struct unwind_state *state) +{ +if (unwind_done (state)) +return NULL ; + +return state->regs; }#else /* !CONFIG_FRAME_POINTER */
@@ -57,6 +66,11 @@ unsigned long *unwind_get_return_address {return NULL ; } + +static inline struct pt_regs *unwind_get_entry_regs (struct unwind_state *state) +{ +return NULL ; +}#endif /* CONFIG_FRAME_POINTER */
arch/x86/kernel/unwind_frame.c +68 -8
118 lines not on this page · raw message
Patches currently in stable-queue which might be from [email protected] are
queue-4.9/x86-entry-unwind-create-stack-frames-for-saved-interrupt-registers.patch