From e95f07384cf667c38741da80539187fe06cac7ce Mon Sep 17 00:00:00 2001 From: yar Date: Sun, 18 Jun 2006 12:07:00 +0000 Subject: [PATCH] The i386 "call" instruction works as follows: it pushes the return address on the stack and only then "dereferences" %pc. Therefore, in the case of a call to an invalid address, we arrive to the trap handler with the invalid value in tf_eip. This used to prevent db_backtrace() from assigning the most recent and interesting frame on the stack to the right spot in the right function, from which the invalid call was attempted. Try to detect and work around that by recovering the return address from the stack. The work-around requires the fault address be passed to db_backtrace(). Smuggle it as tf_err. MFC after: 1 month Sponsored by: RiNet (Cronyx Plus LLC) --- sys/i386/i386/db_trace.c | 28 +++++++++++++++++++++++++--- sys/i386/i386/trap.c | 3 +++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/sys/i386/i386/db_trace.c b/sys/i386/i386/db_trace.c index 99f941b4e301..ea2c20884222 100644 --- a/sys/i386/i386/db_trace.c +++ b/sys/i386/i386/db_trace.c @@ -401,9 +401,33 @@ db_backtrace(struct thread *td, struct trapframe *tf, struct i386_frame *frame, int *argp; db_expr_t offset; c_db_sym_t sym; - int narg, quit; + int instr, narg, quit; boolean_t first; + /* + * If an indirect call via an invalid pointer caused a trap, + * %pc contains the invalid address while the return address + * of the unlucky caller has been saved by CPU on the stack + * just before the trap frame. In this case, try to recover + * the caller's address so that the first frame is assigned + * to the right spot in the right function, for that is where + * the failure actually happened. + * + * This trick depends on the fault address stashed in tf_err + * by trap_fatal() before entering KDB. + */ + if (kdb_frame && pc == kdb_frame->tf_err) { + /* + * Find where the trap frame actually ends. + * It won't contain tf_esp or tf_ss unless crossing rings. + */ + if (ISPL(kdb_frame->tf_cs)) + instr = (int)(kdb_frame + 1); + else + instr = (int)&kdb_frame->tf_esp; + pc = db_get_value(instr, 4, FALSE); + } + if (count == -1) count = 1024; @@ -428,8 +452,6 @@ db_backtrace(struct thread *td, struct trapframe *tf, struct i386_frame *frame, actframe = frame; if (first) { if (tf != NULL) { - int instr; - instr = db_get_value(pc, 4, FALSE); if ((instr & 0xffffff) == 0x00e58955) { /* pushl %ebp; movl %esp, %ebp */ diff --git a/sys/i386/i386/trap.c b/sys/i386/i386/trap.c index 6e1aec2dbe6b..21b0773faf41 100644 --- a/sys/i386/i386/trap.c +++ b/sys/i386/i386/trap.c @@ -858,10 +858,13 @@ trap_fatal(frame, eva) if (debugger_on_panic || kdb_active) { register_t eflags; eflags = intr_disable(); + frame->tf_err = eva; /* smuggle fault address to ddb */ if (kdb_trap(type, 0, frame)) { + frame->tf_err = code; /* restore error code */ intr_restore(eflags); return; } + frame->tf_err = code; /* restore error code */ intr_restore(eflags); } #endif