Export kernel backtrace in /proc//task//stack. Useful when debugging deadlocks. This somewhat duplicates functionality of SysRq-T, but is less intrusive to the system operation and can be used in the scripts. Exporting kernel stack of a thread is probably unsound security-wise. Use with care. Instead of adding yet another architecture specific function to output thread stack through seq_file API, it introduces "iterator" void do_with_stack(struct task_struct *tsk, int (*actor)(int, void *, void *, void *), void *opaque) that has to be implemented by each architecture, so that generic code can iterate over stack frames in architecture-independent way. lib/do_with_stack.c is provided for archituctures that don't implement their own. It is based on __builtin_{frame,return}_address(). arch/i386/Kconfig.debug | 9 ++++++ arch/i386/kernel/traps.c | 42 ++++++++++++++++++++++++++++ fs/proc/base.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++ lib/Makefile | 2 - lib/do_with_stack.c | 14 +++++++++ 5 files changed, 135 insertions(+), 1 deletion(-) diff -puN fs/proc/base.c~proc-stack fs/proc/base.c --- bk-linux/fs/proc/base.c~proc-stack 2004-11-08 15:08:14.397853776 +0300 +++ bk-linux-nikita/fs/proc/base.c 2004-11-08 15:08:14.571827328 +0300 @@ -96,6 +96,9 @@ enum pid_directory_inos { PROC_TID_ATTR_EXEC, PROC_TID_ATTR_FSCREATE, #endif +#ifdef CONFIG_PROC_STACK + PROC_TID_STACK, +#endif PROC_TID_FD_DIR = 0x8000, /* 0x8000-0xffff */ }; @@ -157,6 +160,9 @@ static struct pid_entry tid_base_stuff[] #ifdef CONFIG_SCHEDSTATS E(PROC_TID_SCHEDSTAT, "schedstat",S_IFREG|S_IRUGO), #endif +#ifdef CONFIG_PROC_STACK + E(PROC_TID_STACK, "stack", S_IFREG|S_IRUGO), +#endif {0,0,NULL,0} }; @@ -425,6 +431,63 @@ static int proc_pid_schedstat(struct tas } #endif +#ifdef CONFIG_PROC_STACK +extern void do_with_stack(struct task_struct *tsk, + int (*actor)(int, void *, void *, void *), + void *opaque); + +struct print_stack_arg { + char *buf; + int off; +}; + +#define PRINT(buf, off, format, ...) \ + (off) += snprintf((buf) + (off), \ + max_t(int, PAGE_SIZE - (off), 0), \ + (format) , ## __VA_ARGS__) + +static int stack_actor(int frameno, void *frame, void *address, void *cookie) +{ + struct print_stack_arg *arg; + int off; + + char *modname; + const char *name; + unsigned long size; + unsigned long offset; + char namebuf[128]; + + arg = cookie; + + if (arg->off > PAGE_SIZE) + return 0; + + name = kallsyms_lookup((unsigned long)address, + &size, &offset, &modname, namebuf); + off = arg->off; + PRINT(arg->buf, off, "%02i: %p %p", frameno, frame, address); + if (name != NULL) + PRINT(arg->buf, off, " %s+%#lx/%#lx", name, offset, size); + PRINT(arg->buf, off, "\n"); + arg->off = min_t(int, off, PAGE_SIZE); + return 0; +} + +#undef PRINT + +static int proc_pid_stack(struct task_struct *task, char *buffer) +{ + struct print_stack_arg cookie; + + cookie.buf = buffer; + cookie.off = 0; + + do_with_stack(task, stack_actor, &cookie); + return cookie.off; +} +/* CONFIG_PROC_STACK */ +#endif + /************************************************************************/ /* Here the fs part begins */ /************************************************************************/ @@ -1382,6 +1445,12 @@ static struct dentry *proc_pident_lookup ei->op.proc_read = proc_pid_schedstat; break; #endif +#ifdef CONFIG_PROC_STACK + case PROC_TID_STACK: + inode->i_fop = &proc_info_file_operations; + ei->op.proc_read = proc_pid_stack; + break; +#endif default: printk("procfs: impossible type (%d)",p->type); iput(inode); diff -puN arch/i386/kernel/traps.c~proc-stack arch/i386/kernel/traps.c --- bk-linux/arch/i386/kernel/traps.c~proc-stack 2004-11-08 15:08:14.422849976 +0300 +++ bk-linux-nikita/arch/i386/kernel/traps.c 2004-11-08 15:08:14.573827024 +0300 @@ -170,6 +170,48 @@ static inline unsigned long print_contex return ebp; } +/* + * do_with_stack() + * + * Iterate over stack frames of @tsk from innermost upward. + * + * @tsk task which stack is traversed + * + * @actor is called on each stack frame with 4 parameters: + * + * frameno, 1-based, starting from innermost. + * + * frameaddr, address on the stack + * + * address, return address + * + * cookie, arbitrary cookie passed to the do_with_stack() + * + * @cookie to be passed to the @actor + * + */ +void do_with_stack(struct task_struct *tsk, + int (*actor)(int, void *, void *, void *), void *opaque) +{ + unsigned long *esp = (unsigned long *)tsk->thread.esp; + unsigned long addr; + int i; + + /* User space on another CPU? */ + if ((tsk->thread.esp ^ (unsigned long)tsk->thread_info) & (PAGE_MASK<<1)) + return; + i = 1; + while (((long) esp & (THREAD_SIZE-1)) != 0) { + addr = *esp; + if (kernel_text_address(addr)) { + actor(i, esp, (void *)addr, opaque); + i ++; + } + esp ++; + } +} + + void show_trace(struct task_struct *task, unsigned long * stack) { unsigned long ebp; diff -puN lib/Makefile~proc-stack lib/Makefile --- bk-linux/lib/Makefile~proc-stack 2004-11-08 15:08:14.447846176 +0300 +++ bk-linux-nikita/lib/Makefile 2004-11-08 15:08:14.573827024 +0300 @@ -5,7 +5,7 @@ lib-y := errno.o ctype.o string.o vsprintf.o cmdline.o \ bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \ kobject.o kref.o idr.o div64.o parser.o int_sqrt.o \ - bitmap.o extable.o kobject_uevent.o + bitmap.o extable.o kobject_uevent.o do_with_stack.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUG diff -puN /dev/null lib/do_with_stack.c --- /dev/null 2004-04-06 17:27:52.000000000 +0400 +++ bk-linux-nikita/lib/do_with_stack.c 2004-11-08 15:08:14.573827024 +0300 @@ -0,0 +1,14 @@ +/* + * Provide a default do_with_stack() function for architectures + * which don't implement their own. + */ + +#include +#include + +void do_with_stack(struct task_struct *tsk, + int (*actor)(int, void *, void *, void *), void *o) +{ + actor(1, __builtin_frame_address(0), __builtin_return_address(0), o); + actor(2, __builtin_frame_address(1), __builtin_return_address(1), o); +} diff -puN arch/i386/Kconfig~proc-stack arch/i386/Kconfig diff -puN lib/Kconfig.debug~proc-stack lib/Kconfig.debug diff -puN arch/i386/Kconfig.debug~proc-stack arch/i386/Kconfig.debug --- bk-linux/arch/i386/Kconfig.debug~proc-stack 2004-11-08 15:08:14.567827936 +0300 +++ bk-linux-nikita/arch/i386/Kconfig.debug 2004-11-08 15:08:14.591824288 +0300 @@ -67,4 +67,13 @@ config X86_MPPARSE source "arch/i386/Kconfig.kgdb" +config PROC_STACK + bool "Export kernel stack in the /proc/pid/stack" + depends on DEBUG_KERNEL + default n + help + If you say Y here, new file /proc/pid/stack will appear that contains + current kernel backtrace of given process. This may have security + implications. If unsure say N. + endmenu _