This series consists of improvements to Xen''s ability to print traces of its own stack, and specifically for the stack overflow case to be able to use frame pointers in a debug build. v4 contains some fixes to the wild function pointer call case, including a test case. There was a missed indirection in the test, and when printing the return address. I have dev tested the series in debug and non-debug cases, with and without memory guards, and I believe that all the stack traces look correct (given the available information Xen has), and that the boundaries are now correct. This series has had a substantial rebase on top of the %pS series. Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> CC: Keir Fraser <keir@xen.org> CC: Jan Beulich <JBeulich@suse.com> CC: Tim Deegan <tim@xen.org> CC: George Dunlap <george.dunlap@eu.citrix.com>
Before, show_trace() had two implementations depending on CONFIG_FRAME_POINTER. Some parts were common, while the loops to wander up the stack were different. The version aided by frame pointers had a special case for function calls on wild function pointers, but this doesn''t need to be a special case. After the refactoring, there are now two implementations of _show_trace() which differ depending on CONFIG_FRAME_POINTER, and a single show_trace() with the common bits, including the logic for wild function pointers. Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> Acked-by: Keir Fraser <keir@xen.org> CC: Jan Beulich <JBeulich@suse.com> CC: Tim Deegan <tim@xen.org> --- Changes in v4: * Single underscore on _show_trace() * Undo accidental removal of deference in the wild function pointer test * Correct the printing of the return address in the wild pointer case --- xen/arch/x86/traps.c | 65 +++++++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c index e5b3585..2906641 100644 --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -195,12 +195,14 @@ static void show_guest_stack(struct vcpu *v, struct cpu_user_regs *regs) #if !defined(CONFIG_FRAME_POINTER) -static void show_trace(struct cpu_user_regs *regs) +/* + * Stack trace from pointers found in stack, unaided by frame pointers. For + * caller convenience, this has the same prototype as its alternative, and + * simply ignores the base pointer parameter. + */ +static void _show_trace(unsigned long sp, unsigned long __maybe_unused bp) { - unsigned long *stack = ESP_BEFORE_EXCEPTION(regs), addr; - - printk("Xen call trace:\n" - " [<%p>] %pS\n", _p(regs->eip), _p(regs->eip)); + unsigned long *stack = (unsigned long *)sp, addr; while ( ((long)stack & (STACK_SIZE-BYTES_PER_LONG)) != 0 ) { @@ -208,35 +210,22 @@ static void show_trace(struct cpu_user_regs *regs) if ( is_active_kernel_text(addr) ) printk(" [<%p>] %pS\n", _p(addr), _p(addr)); } - - printk("\n"); } #else -static void show_trace(struct cpu_user_regs *regs) +/* Stack trace from frames in the stack, using frame pointers */ +static void _show_trace(unsigned long sp, unsigned long bp) { unsigned long *frame, next, addr, low, high; - /* - * If RIP is not pointing into hypervisor code then someone may have - * called into oblivion. Peek to see if they left a return address at - * top of stack. - */ - addr = is_active_kernel_text(regs->eip) || - !is_active_kernel_text(*ESP_BEFORE_EXCEPTION(regs)) ? - regs->eip : *ESP_BEFORE_EXCEPTION(regs); - - printk("Xen call trace:\n" - " [<%p>] %pS\n", _p(addr), _p(addr)); - /* Bounds for range of valid frame pointer. */ - low = (unsigned long)(ESP_BEFORE_EXCEPTION(regs) - 2); + low = sp - 2*sizeof(unsigned long); high = (low & ~(STACK_SIZE - 1)) + (STACK_SIZE - sizeof(struct cpu_info) - 2*sizeof(unsigned long)); /* The initial frame pointer. */ - next = regs->ebp; + next = bp; for ( ; ; ) { @@ -268,12 +257,40 @@ static void show_trace(struct cpu_user_regs *regs) low = (unsigned long)&frame[2]; } - - printk("\n"); } #endif +static void show_trace(const struct cpu_user_regs *regs) +{ + unsigned long *sp = ESP_BEFORE_EXCEPTION(regs); + + printk("Xen call trace:\n"); + + /* + * If RIP looks sensible, or the top of the stack doesn''t, print RIP at + * the top of the stack trace. + */ + if ( is_active_kernel_text(regs->rip) || + !is_active_kernel_text(*sp) ) + printk(" [<%p>] %pS\n", _p(regs->rip), _p(regs->rip)); + /* + * Else RIP looks bad but the top of the stack looks good. Perhaps we + * followed a wild function pointer? Lets assume the top of the stack is a + * return address; print it and skip past so _show_trace() doesn''t print + * it again. + */ + else + { + printk(" [<%p>] %pS\n", _p(*sp), _p(*sp)); + sp++; + } + + _show_trace(*sp, regs->rbp); + + printk("\n"); +} + void show_stack(struct cpu_user_regs *regs) { unsigned long *stack = ESP_BEFORE_EXCEPTION(regs), addr; -- 1.7.10.4
Andrew Cooper
2013-Nov-20 13:09 UTC
[Patch v4 2/4] x86/stack: Adjust boundary conditions for printed stacks.
Move the boundary into current.h along with the other stack manipulation code. The boundary is now the word adjacent to a struct cpu_info on the stack. This also fixes the somewhat spurious bounds for the case with frame pointers. Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> Acked-by: Keir Fraser <keir@xen.org> Reviewed-by: Jan Beulich <JBeulich@suse.com> CC: Tim Deegan <tim@xen.org> --- Changes since v1: * Change printable bottom depending on frame pointers --- xen/arch/x86/traps.c | 9 ++++----- xen/include/asm-x86/current.h | 9 +++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c index 2906641..231d0fa 100644 --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -203,8 +203,9 @@ static void show_guest_stack(struct vcpu *v, struct cpu_user_regs *regs) static void _show_trace(unsigned long sp, unsigned long __maybe_unused bp) { unsigned long *stack = (unsigned long *)sp, addr; + unsigned long *bottom = (unsigned long *)get_printable_stack_bottom(sp); - while ( ((long)stack & (STACK_SIZE-BYTES_PER_LONG)) != 0 ) + while ( stack <= bottom ) { addr = *stack++; if ( is_active_kernel_text(addr) ) @@ -217,12 +218,10 @@ static void _show_trace(unsigned long sp, unsigned long __maybe_unused bp) /* Stack trace from frames in the stack, using frame pointers */ static void _show_trace(unsigned long sp, unsigned long bp) { - unsigned long *frame, next, addr, low, high; + unsigned long *frame, next, addr; /* Bounds for range of valid frame pointer. */ - low = sp - 2*sizeof(unsigned long); - high = (low & ~(STACK_SIZE - 1)) + - (STACK_SIZE - sizeof(struct cpu_info) - 2*sizeof(unsigned long)); + unsigned long low = sp, high = get_printable_stack_bottom(sp); /* The initial frame pointer. */ next = bp; diff --git a/xen/include/asm-x86/current.h b/xen/include/asm-x86/current.h index bec4dbe..c2792ce 100644 --- a/xen/include/asm-x86/current.h +++ b/xen/include/asm-x86/current.h @@ -50,6 +50,15 @@ static inline struct cpu_info *get_cpu_info(void) #define get_stack_bottom() \ ((unsigned long)&get_cpu_info()->guest_cpu_user_regs.es) +/* + * Get the bottom-of-stack, as useful for printing stack traces. This is the + * highest word on the stack which might be part of a stack trace, and is the + * adjacent word to a struct cpu_info on the stack. + */ +#define get_printable_stack_bottom(sp) \ + ((sp & (~(STACK_SIZE-1))) + \ + (STACK_SIZE - sizeof(struct cpu_info) - sizeof(unsigned long))) + #define reset_stack_and_jump(__fn) \ __asm__ __volatile__ ( \ "mov %0,%%"__OP"sp; jmp %c1" \ -- 1.7.10.4
Andrew Cooper
2013-Nov-20 13:09 UTC
[Patch v4 3/4] x86/stack: Change show_stack_overflow() to use frame pointers if available
Pass a full set of cpu_user_regs, and defer the hand-coded stack printing to _show_trace(), which will correctly use frame pointers if available. Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> Acked-by: Keir Fraser <keir@xen.org> Reviewed-by: Jan Beulich <JBeulich@suse.com> CC: Tim Deegan <tim@xen.org> --- Changes in v4: * knockon effects from changes in patch 1 --- xen/arch/x86/traps.c | 14 ++++---------- xen/arch/x86/x86_64/traps.c | 2 +- xen/include/asm-x86/processor.h | 2 +- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c index 231d0fa..f2be1c0 100644 --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -316,11 +316,11 @@ void show_stack(struct cpu_user_regs *regs) show_trace(regs); } -void show_stack_overflow(unsigned int cpu, unsigned long esp) +void show_stack_overflow(unsigned int cpu, const struct cpu_user_regs *regs) { #ifdef MEMORY_GUARD + unsigned long esp = regs->rsp; unsigned long esp_top, esp_bottom; - unsigned long *stack, addr; esp_bottom = (esp | (STACK_SIZE - 1)) + 1; esp_top = esp_bottom - PRIMARY_STACK_SIZE; @@ -340,16 +340,10 @@ void show_stack_overflow(unsigned int cpu, unsigned long esp) if ( esp < esp_top ) esp = esp_top; - printk("Xen stack overflow (dumping trace %p-%p):\n ", + printk("Xen stack overflow (dumping trace %p-%p):\n", (void *)esp, (void *)esp_bottom); - stack = (unsigned long *)esp; - while ( ((long)stack & (STACK_SIZE-BYTES_PER_LONG)) != 0 ) - { - addr = *stack++; - if ( is_active_kernel_text(addr) ) - printk("%p: [<%p>] %pS\n", stack, _p(addr), _p(addr)); - } + _show_trace(esp, regs->rbp); printk("\n"); #endif diff --git a/xen/arch/x86/x86_64/traps.c b/xen/arch/x86/x86_64/traps.c index 8e6a7c1..bcf72b6 100644 --- a/xen/arch/x86/x86_64/traps.c +++ b/xen/arch/x86/x86_64/traps.c @@ -248,7 +248,7 @@ void do_double_fault(struct cpu_user_regs *regs) printk("CPU: %d\n", cpu); _show_registers(regs, crs, CTXT_hypervisor, NULL); - show_stack_overflow(cpu, regs->rsp); + show_stack_overflow(cpu, regs); panic("DOUBLE FAULT -- system shutdown\n"); } diff --git a/xen/include/asm-x86/processor.h b/xen/include/asm-x86/processor.h index 73a3202..c120460 100644 --- a/xen/include/asm-x86/processor.h +++ b/xen/include/asm-x86/processor.h @@ -509,7 +509,7 @@ extern always_inline void prefetchw(const void *x) #endif void show_stack(struct cpu_user_regs *regs); -void show_stack_overflow(unsigned int cpu, unsigned long esp); +void show_stack_overflow(unsigned int cpu, const struct cpu_user_regs *regs); void show_registers(struct cpu_user_regs *regs); void show_execution_state(struct cpu_user_regs *regs); #define dump_execution_state() run_in_exception_handler(show_execution_state) -- 1.7.10.4
Andrew Cooper
2013-Nov-20 13:09 UTC
[Patch v4 4/4] DO NOT APPLY: Test code for interesting stack overflows
I do not recommend trying stacktest3 without stack guards - it interacts very badly between trashing the NMI stack and the NMI watchdog. --- xen/arch/x86/traps.c | 110 +++++++++++++++++++++++++++++++++++++++++++ xen/arch/x86/x86_64/traps.c | 15 ++++++ xen/drivers/char/console.c | 8 ++++ xen/drivers/char/serial.c | 12 +++++ xen/include/xen/console.h | 1 + xen/include/xen/serial.h | 1 + 6 files changed, 147 insertions(+) diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c index f2be1c0..2a7fb55 100644 --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -34,6 +34,7 @@ #include <xen/console.h> #include <xen/shutdown.h> #include <xen/guest_access.h> +#include <xen/keyhandler.h> #include <asm/regs.h> #include <xen/delay.h> #include <xen/event.h> @@ -3762,6 +3763,115 @@ void asm_domain_crash_synchronous(unsigned long addr) __domain_crash_synchronous(); } +static noinline int recursion(int depth) +{ + /* junk on stack to fool naive algorithm */ + volatile unsigned long addr1 = (unsigned long)&do_get_debugreg; + volatile unsigned long tsc; + volatile unsigned long addr2 = (unsigned long)&do_set_debugreg; + + if ( depth == 0 ) + { + printk("Did you mean recursion()?\n"); + run_in_exception_handler(show_stack); + return 0; + } + else + { + int r; + rdtscll(tsc); + printk("depth %d, addr1 0x%016"PRIx64", addr2 0x%016"PRIx64", tsc %"PRIu64"\n", + depth, addr1, addr2, tsc); + r = recursion(depth-1); + if ( r ) + printk("done\n"); /* So GCC cant perform tailcall optimisation */ + return r; + } +} + +static void stacktest1(unsigned char key) +{ + printk("In %s()\n", __func__); + recursion(5); +} + +static void stacktest2(unsigned char key, struct cpu_user_regs * regs) +{ + printk("In %s()\n", __func__); + recursion(5); +} + +volatile int in_stacktest3 = 0; +unsigned long saved_sp, saved_ip, saved_flags = 0; +static void stacktest3(unsigned char key) +{ + printk("In %s()\n", __func__); + + local_irq_save(saved_flags); + in_stacktest3 = 1; + + /* Hack up an ability to longjump() out of the #DF handler. */ +recover: + asm volatile ("mov %%rsp, %0;\n\t" + : "=m"(saved_sp) ); + saved_ip = (unsigned long)&&recover; + + if ( in_stacktest3 ) + { + printk("Recovery info: sp %p, ip %p\n", + _p(saved_sp), _p(saved_ip)); + recursion(500); + } + else + printk("Recovered from #DF\n"); + + local_irq_restore(saved_flags); +} + +typedef void (*fn_ptr)(void); +static void stacktest4(unsigned char key, struct cpu_user_regs * regs) +{ + volatile fn_ptr wild_function = NULL; + printk("In %s()\n", __func__); + wild_function(); +} + +static struct keyhandler stacktest_handler[] = { + { + .irq_callback = 0, + .u.fn = stacktest1, + .desc = "Xen stack #1 - Recurse a little and see trace." + }, + + { + .irq_callback = 1, + .u.irq_fn = stacktest2, + .desc = "Xen stack #2 - Recurse a little and see trace in irq." + }, + + { + .irq_callback = 0, + .u.fn = stacktest3, + .desc = "Xen stack #3 - Cause stack overflow." + }, + + { + .irq_callback = 1, + .u.irq_fn = stacktest4, + .desc = "Xen stack #4 - Wild function pointer call." + }, + }; + +static int __init initialize_crashtable(void) +{ + register_keyhandler(''1'', &stacktest_handler[0]); + register_keyhandler(''2'', &stacktest_handler[1]); + register_keyhandler(''3'', &stacktest_handler[2]); + register_keyhandler(''4'', &stacktest_handler[3]); + return 1; +} +__initcall(initialize_crashtable); + /* * Local variables: * mode: C diff --git a/xen/arch/x86/x86_64/traps.c b/xen/arch/x86/x86_64/traps.c index bcf72b6..a687954 100644 --- a/xen/arch/x86/x86_64/traps.c +++ b/xen/arch/x86/x86_64/traps.c @@ -223,6 +223,9 @@ void show_page_walk(unsigned long addr) l1_table_offset(addr), l1e_get_intpte(l1e), pfn); } +extern volatile int in_stacktest3; +extern unsigned long saved_sp, saved_ip; + void double_fault(void); void do_double_fault(struct cpu_user_regs *regs) { @@ -250,6 +253,18 @@ void do_double_fault(struct cpu_user_regs *regs) _show_registers(regs, crs, CTXT_hypervisor, NULL); show_stack_overflow(cpu, regs); + if ( in_stacktest3 ) + { + in_stacktest3 = 0; + + undo_console_force_unlock(); + preempt_count() = 0; + enable_nmis(); + + asm volatile ("mov %0, %%rsp; jmp *%1": + : "r" (saved_sp), "r" (saved_ip) : "memory" ); + } + panic("DOUBLE FAULT -- system shutdown\n"); } diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c index 508f845..967fd53 100644 --- a/xen/drivers/char/console.c +++ b/xen/drivers/char/console.c @@ -803,6 +803,14 @@ void console_force_unlock(void) console_start_sync(); } +void undo_console_force_unlock(void) +{ + watchdog_enable(); + undo_serial_force_unlock(sercon_handle); + console_locks_busted = 0; + console_end_sync(); +} + void console_start_sync(void) { atomic_inc(&print_everything); diff --git a/xen/drivers/char/serial.c b/xen/drivers/char/serial.c index 9b006f2..79874f1 100644 --- a/xen/drivers/char/serial.c +++ b/xen/drivers/char/serial.c @@ -380,6 +380,18 @@ void serial_force_unlock(int handle) serial_start_sync(handle); } +void undo_serial_force_unlock(int handle) +{ + struct serial_port *port; + + if ( handle == -1 ) + return; + + port = &com[handle & SERHND_IDX]; + + serial_end_sync(handle); +} + void serial_start_sync(int handle) { struct serial_port *port; diff --git a/xen/include/xen/console.h b/xen/include/xen/console.h index cfb07a2..41c96f8 100644 --- a/xen/include/xen/console.h +++ b/xen/include/xen/console.h @@ -21,6 +21,7 @@ int console_has(const char *device); int fill_console_start_info(struct dom0_vga_console_info *); void console_force_unlock(void); +void undo_console_force_unlock(void); void console_start_sync(void); void console_end_sync(void); diff --git a/xen/include/xen/serial.h b/xen/include/xen/serial.h index f38c9b7..fbdcad4 100644 --- a/xen/include/xen/serial.h +++ b/xen/include/xen/serial.h @@ -123,6 +123,7 @@ char serial_getc(int handle); /* Forcibly prevent serial lockup when the system is in a bad way. */ /* (NB. This also forces an implicit serial_start_sync()). */ void serial_force_unlock(int handle); +void undo_serial_force_unlock(int handle); /* Start/end a synchronous region (temporarily disable interrupt-driven tx). */ void serial_start_sync(int handle); -- 1.7.10.4
>>> On 20.11.13 at 14:09, Andrew Cooper <andrew.cooper3@citrix.com> wrote: > +static void show_trace(const struct cpu_user_regs *regs) > +{ > + unsigned long *sp = ESP_BEFORE_EXCEPTION(regs);So you correctly made it a pointer here...> + > + printk("Xen call trace:\n"); > + > + /* > + * If RIP looks sensible, or the top of the stack doesn''t, print RIP at > + * the top of the stack trace. > + */ > + if ( is_active_kernel_text(regs->rip) || > + !is_active_kernel_text(*sp) )... and de-reference it here ...> + printk(" [<%p>] %pS\n", _p(regs->rip), _p(regs->rip)); > + /* > + * Else RIP looks bad but the top of the stack looks good. Perhaps we > + * followed a wild function pointer? Lets assume the top of the stack is a > + * return address; print it and skip past so _show_trace() doesn''t print > + * it again. > + */ > + else > + { > + printk(" [<%p>] %pS\n", _p(*sp), _p(*sp)); > + sp++; > + } > + > + _show_trace(*sp, regs->rbp);... but then you now also de-reference it here? How did that end up producing sane stack dumps? Since one of the two _show_trace() variants wants a pointer here anyway, you would probably best switch its first argument and use the inverse casting in the other variant. Jan
On 20/11/13 13:40, Jan Beulich wrote:>>>> On 20.11.13 at 14:09, Andrew Cooper <andrew.cooper3@citrix.com> wrote: >> +static void show_trace(const struct cpu_user_regs *regs) >> +{ >> + unsigned long *sp = ESP_BEFORE_EXCEPTION(regs); > So you correctly made it a pointer here... > >> + >> + printk("Xen call trace:\n"); >> + >> + /* >> + * If RIP looks sensible, or the top of the stack doesn''t, print RIP at >> + * the top of the stack trace. >> + */ >> + if ( is_active_kernel_text(regs->rip) || >> + !is_active_kernel_text(*sp) ) > ... and de-reference it here ... > >> + printk(" [<%p>] %pS\n", _p(regs->rip), _p(regs->rip)); >> + /* >> + * Else RIP looks bad but the top of the stack looks good. Perhaps we >> + * followed a wild function pointer? Lets assume the top of the stack is a >> + * return address; print it and skip past so _show_trace() doesn''t print >> + * it again. >> + */ >> + else >> + { >> + printk(" [<%p>] %pS\n", _p(*sp), _p(*sp)); >> + sp++; >> + } >> + >> + _show_trace(*sp, regs->rbp); > ... but then you now also de-reference it here? How did that end > up producing sane stack dumps? > > Since one of the two _show_trace() variants wants a pointer here > anyway, you would probably best switch its first argument and use > the inverse casting in the other variant. > > Jan >Huh - I appear to have tested the debug build twice rather than 1 debug and 1 non-debug build. I clearly need to be rather more careful! I am not so certain about changing the _show_trace() prototype. The naive algorithm wants everything as pointers, while the frame-pointer algorithm wants everything as integers. Switching to passing by pointer would require an equal amount of ugly casting back to an integer for get_printable_stack_bottom() etc. Personally I would prefer to keep as integers to be in line with the registers; I think it looks more natural to take sp as a resister value and cast it to a pointer to look at the stack, than passing around a pointer and casting it to an integer for bounds. ~Andrew
>>> On 20.11.13 at 15:11, Andrew Cooper <andrew.cooper3@citrix.com> wrote: > I am not so certain about changing the _show_trace() prototype. The > naive algorithm wants everything as pointers, while the frame-pointer > algorithm wants everything as integers. Switching to passing by pointer > would require an equal amount of ugly casting back to an integer for > get_printable_stack_bottom() etc. > > Personally I would prefer to keep as integers to be in line with the > registers; I think it looks more natural to take sp as a resister value > and cast it to a pointer to look at the stack, than passing around a > pointer and casting it to an integer for bounds.Fine with me, this was just a suggestion. Jan
Before, show_trace() had two implementations depending on CONFIG_FRAME_POINTER. Some parts were common, while the loops to wander up the stack were different. The version aided by frame pointers had a special case for function calls on wild function pointers, but this doesn''t need to be a special case. After the refactoring, there are now two implementations of _show_trace() which differ depending on CONFIG_FRAME_POINTER, and a single show_trace() with the common bits, including the logic for wild function pointers. Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> Acked-by: Keir Fraser <keir@xen.org> CC: Jan Beulich <JBeulich@suse.com> CC: Tim Deegan <tim@xen.org> --- Changes in v5: * Fix inappropriate deference of stack pointer. This is now correctly tested in both a debug and non-debug build of Xen. Changes in v4: * Single underscore on _show_trace() * Undo accidental removal of deference in the wild function pointer test * Correct the printing of the return address in the wild pointer case --- xen/arch/x86/traps.c | 65 +++++++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c index e5b3585..8a01065 100644 --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -195,12 +195,14 @@ static void show_guest_stack(struct vcpu *v, struct cpu_user_regs *regs) #if !defined(CONFIG_FRAME_POINTER) -static void show_trace(struct cpu_user_regs *regs) +/* + * Stack trace from pointers found in stack, unaided by frame pointers. For + * caller convenience, this has the same prototype as its alternative, and + * simply ignores the base pointer parameter. + */ +static void _show_trace(unsigned long sp, unsigned long __maybe_unused bp) { - unsigned long *stack = ESP_BEFORE_EXCEPTION(regs), addr; - - printk("Xen call trace:\n" - " [<%p>] %pS\n", _p(regs->eip), _p(regs->eip)); + unsigned long *stack = (unsigned long *)sp, addr; while ( ((long)stack & (STACK_SIZE-BYTES_PER_LONG)) != 0 ) { @@ -208,35 +210,22 @@ static void show_trace(struct cpu_user_regs *regs) if ( is_active_kernel_text(addr) ) printk(" [<%p>] %pS\n", _p(addr), _p(addr)); } - - printk("\n"); } #else -static void show_trace(struct cpu_user_regs *regs) +/* Stack trace from frames in the stack, using frame pointers */ +static void _show_trace(unsigned long sp, unsigned long bp) { unsigned long *frame, next, addr, low, high; - /* - * If RIP is not pointing into hypervisor code then someone may have - * called into oblivion. Peek to see if they left a return address at - * top of stack. - */ - addr = is_active_kernel_text(regs->eip) || - !is_active_kernel_text(*ESP_BEFORE_EXCEPTION(regs)) ? - regs->eip : *ESP_BEFORE_EXCEPTION(regs); - - printk("Xen call trace:\n" - " [<%p>] %pS\n", _p(addr), _p(addr)); - /* Bounds for range of valid frame pointer. */ - low = (unsigned long)(ESP_BEFORE_EXCEPTION(regs) - 2); + low = sp - 2*sizeof(unsigned long); high = (low & ~(STACK_SIZE - 1)) + (STACK_SIZE - sizeof(struct cpu_info) - 2*sizeof(unsigned long)); /* The initial frame pointer. */ - next = regs->ebp; + next = bp; for ( ; ; ) { @@ -268,12 +257,40 @@ static void show_trace(struct cpu_user_regs *regs) low = (unsigned long)&frame[2]; } - - printk("\n"); } #endif +static void show_trace(const struct cpu_user_regs *regs) +{ + unsigned long *sp = ESP_BEFORE_EXCEPTION(regs); + + printk("Xen call trace:\n"); + + /* + * If RIP looks sensible, or the top of the stack doesn''t, print RIP at + * the top of the stack trace. + */ + if ( is_active_kernel_text(regs->rip) || + !is_active_kernel_text(*sp) ) + printk(" [<%p>] %pS\n", _p(regs->rip), _p(regs->rip)); + /* + * Else RIP looks bad but the top of the stack looks good. Perhaps we + * followed a wild function pointer? Lets assume the top of the stack is a + * return address; print it and skip past so _show_trace() doesn''t print + * it again. + */ + else + { + printk(" [<%p>] %pS\n", _p(*sp), _p(*sp)); + sp++; + } + + _show_trace((unsigned long)sp, regs->rbp); + + printk("\n"); +} + void show_stack(struct cpu_user_regs *regs) { unsigned long *stack = ESP_BEFORE_EXCEPTION(regs), addr; -- 1.7.10.4