ftrace/x86: Do not change stacks in DEBUG when calling lockdep
When both DYNAMIC_FTRACE and LOCKDEP are set, the TRACE_IRQS_ON/OFF will call into the lockdep code. The lockdep code can call lots of functions that may be traced by ftrace. When ftrace is updating its code and hits a breakpoint, the breakpoint handler will call into lockdep. If lockdep happens to call a function that also has a breakpoint attached, it will jump back into the breakpoint handler resetting the stack to the debug stack and corrupt the contents currently on that stack. The 'do_sym' call that calls do_int3() is protected by modifying the IST table to point to a different location if another breakpoint is hit. But the TRACE_IRQS_OFF/ON are outside that protection, and if a breakpoint is hit from those, the stack will get corrupted, and the kernel will crash: [ 1013.243754] BUG: unable to handle kernel NULL pointer dereference at 0000000000000002 [ 1013.272665] IP: [<ffff880145cc0000>] 0xffff880145cbffff [ 1013.285186] PGD 1401b2067 PUD 14324c067 PMD 0 [ 1013.298832] Oops: 0010 [#1] PREEMPT SMP [ 1013.310600] CPU 2 [ 1013.317904] Modules linked in: ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 xt_state nf_conntrack ip6table_filter ip6_tables crc32c_intel ghash_clmulni_intel microcode usb_debug serio_raw pcspkr iTCO_wdt i2c_i801 iTCO_vendor_support e1000e nfsd nfs_acl auth_rpcgss lockd sunrpc i915 video i2c_algo_bit drm_kms_helper drm i2c_core [last unloaded: scsi_wait_scan] [ 1013.401848] [ 1013.407399] Pid: 112, comm: kworker/2:1 Not tainted 3.4.0+ #30 [ 1013.437943] RIP: 8eb8:[<ffff88014630a000>] [<ffff88014630a000>] 0xffff880146309fff [ 1013.459871] RSP: ffffffff8165e919:ffff88014780f408 EFLAGS: 00010046 [ 1013.477909] RAX: 0000000000000001 RBX: ffffffff81104020 RCX: 0000000000000000 [ 1013.499458] RDX: ffff880148008ea8 RSI: ffffffff8131ef40 RDI: ffffffff82203b20 [ 1013.521612] RBP: ffffffff81005751 R08: 0000000000000000 R09: 0000000000000000 [ 1013.543121] R10: ffffffff82cdc318 R11: 0000000000000000 R12: ffff880145cc0000 [ 1013.564614] R13: ffff880148008eb8 R14: 0000000000000002 R15: ffff88014780cb40 [ 1013.586108] FS: 0000000000000000(0000) GS:ffff880148000000(0000) knlGS:0000000000000000 [ 1013.609458] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 1013.627420] CR2: 0000000000000002 CR3: 0000000141f10000 CR4: 00000000001407e0 [ 1013.649051] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 1013.670724] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 1013.692376] Process kworker/2:1 (pid: 112, threadinfo ffff88013fe0e000, task ffff88014020a6a0) [ 1013.717028] Stack: [ 1013.724131] ffff88014780f570 ffff880145cc0000 0000400000004000 0000000000000000 [ 1013.745918] cccccccccccccccc ffff88014780cca8 ffffffff811072bb ffffffff81651627 [ 1013.767870] ffffffff8118f8a7 ffffffff811072bb ffffffff81f2b6c5 ffffffff81f11bdb [ 1013.790021] Call Trace: [ 1013.800701] Code: 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a <e7> d7 64 81 ff ff ff ff 01 00 00 00 00 00 00 00 65 d9 64 81 ff [ 1013.861443] RIP [<ffff88014630a000>] 0xffff880146309fff [ 1013.884466] RSP <ffff88014780f408> [ 1013.901507] CR2: 0000000000000002 The solution was to reuse the NMI functions that change the IDT table to make the debug stack keep its current stack (in kernel mode) when hitting a breakpoint: call debug_stack_set_zero TRACE_IRQS_ON call debug_stack_reset If the TRACE_IRQS_ON happens to hit a breakpoint then it will keep the current stack and not crash the box. Reported-by: Dave Jones <davej@redhat.com> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
This commit is contained in:
parent
f8988175fd
commit
5963e317b1
@ -190,6 +190,44 @@ ENDPROC(native_usergs_sysret64)
|
|||||||
#endif
|
#endif
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When dynamic function tracer is enabled it will add a breakpoint
|
||||||
|
* to all locations that it is about to modify, sync CPUs, update
|
||||||
|
* all the code, sync CPUs, then remove the breakpoints. In this time
|
||||||
|
* if lockdep is enabled, it might jump back into the debug handler
|
||||||
|
* outside the updating of the IST protection. (TRACE_IRQS_ON/OFF).
|
||||||
|
*
|
||||||
|
* We need to change the IDT table before calling TRACE_IRQS_ON/OFF to
|
||||||
|
* make sure the stack pointer does not get reset back to the top
|
||||||
|
* of the debug stack, and instead just reuses the current stack.
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_DYNAMIC_FTRACE) && defined(CONFIG_TRACE_IRQFLAGS)
|
||||||
|
|
||||||
|
.macro TRACE_IRQS_OFF_DEBUG
|
||||||
|
call debug_stack_set_zero
|
||||||
|
TRACE_IRQS_OFF
|
||||||
|
call debug_stack_reset
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro TRACE_IRQS_ON_DEBUG
|
||||||
|
call debug_stack_set_zero
|
||||||
|
TRACE_IRQS_ON
|
||||||
|
call debug_stack_reset
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro TRACE_IRQS_IRETQ_DEBUG offset=ARGOFFSET
|
||||||
|
bt $9,EFLAGS-\offset(%rsp) /* interrupts off? */
|
||||||
|
jnc 1f
|
||||||
|
TRACE_IRQS_ON_DEBUG
|
||||||
|
1:
|
||||||
|
.endm
|
||||||
|
|
||||||
|
#else
|
||||||
|
# define TRACE_IRQS_OFF_DEBUG TRACE_IRQS_OFF
|
||||||
|
# define TRACE_IRQS_ON_DEBUG TRACE_IRQS_ON
|
||||||
|
# define TRACE_IRQS_IRETQ_DEBUG TRACE_IRQS_IRETQ
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* C code is not supposed to know about undefined top of stack. Every time
|
* C code is not supposed to know about undefined top of stack. Every time
|
||||||
* a C function with an pt_regs argument is called from the SYSCALL based
|
* a C function with an pt_regs argument is called from the SYSCALL based
|
||||||
@ -1098,7 +1136,7 @@ ENTRY(\sym)
|
|||||||
subq $ORIG_RAX-R15, %rsp
|
subq $ORIG_RAX-R15, %rsp
|
||||||
CFI_ADJUST_CFA_OFFSET ORIG_RAX-R15
|
CFI_ADJUST_CFA_OFFSET ORIG_RAX-R15
|
||||||
call save_paranoid
|
call save_paranoid
|
||||||
TRACE_IRQS_OFF
|
TRACE_IRQS_OFF_DEBUG
|
||||||
movq %rsp,%rdi /* pt_regs pointer */
|
movq %rsp,%rdi /* pt_regs pointer */
|
||||||
xorl %esi,%esi /* no error code */
|
xorl %esi,%esi /* no error code */
|
||||||
subq $EXCEPTION_STKSZ, INIT_TSS_IST(\ist)
|
subq $EXCEPTION_STKSZ, INIT_TSS_IST(\ist)
|
||||||
@ -1393,7 +1431,7 @@ paranoidzeroentry machine_check *machine_check_vector(%rip)
|
|||||||
ENTRY(paranoid_exit)
|
ENTRY(paranoid_exit)
|
||||||
DEFAULT_FRAME
|
DEFAULT_FRAME
|
||||||
DISABLE_INTERRUPTS(CLBR_NONE)
|
DISABLE_INTERRUPTS(CLBR_NONE)
|
||||||
TRACE_IRQS_OFF
|
TRACE_IRQS_OFF_DEBUG
|
||||||
testl %ebx,%ebx /* swapgs needed? */
|
testl %ebx,%ebx /* swapgs needed? */
|
||||||
jnz paranoid_restore
|
jnz paranoid_restore
|
||||||
testl $3,CS(%rsp)
|
testl $3,CS(%rsp)
|
||||||
@ -1404,7 +1442,7 @@ paranoid_swapgs:
|
|||||||
RESTORE_ALL 8
|
RESTORE_ALL 8
|
||||||
jmp irq_return
|
jmp irq_return
|
||||||
paranoid_restore:
|
paranoid_restore:
|
||||||
TRACE_IRQS_IRETQ 0
|
TRACE_IRQS_IRETQ_DEBUG 0
|
||||||
RESTORE_ALL 8
|
RESTORE_ALL 8
|
||||||
jmp irq_return
|
jmp irq_return
|
||||||
paranoid_userspace:
|
paranoid_userspace:
|
||||||
|
Loading…
Reference in New Issue
Block a user