Abuse the kernel profiling framework to check at runtime for kernel stack overflows. Chris Index: arch/i386/conf/GENERIC =================================================================== RCS file: /cvs/src/sys/arch/i386/conf/GENERIC,v retrieving revision 1.513 diff -u -p -u -p -r1.513 GENERIC --- arch/i386/conf/GENERIC 27 Jul 2006 04:18:44 -0000 1.513 +++ arch/i386/conf/GENERIC 28 Jul 2006 06:27:14 -0000 @@ -13,6 +13,10 @@ machine i386 include "../../../conf/GENERIC" maxusers 32 # estimated number of users +makeoptions DEBUG="-g" # compile full symbol table +makeoptions PROF="-pg" # build profiled kernel +option GPROF # kernel profiling, kgmon(8) + option I386_CPU # CPU classes; at least one is REQUIRED option I486_CPU option I586_CPU Index: arch/i386/i386/machdep.c =================================================================== RCS file: /cvs/src/sys/arch/i386/i386/machdep.c,v retrieving revision 1.362 diff -u -p -u -p -r1.362 machdep.c --- arch/i386/i386/machdep.c 10 Jul 2006 19:45:22 -0000 1.362 +++ arch/i386/i386/machdep.c 28 Jul 2006 06:23:46 -0000 @@ -2931,6 +2931,8 @@ init386(paddr_t first_avail) bios_memmap_t *im; proc0.p_addr = proc0paddr; + proc0.p_addr->overflow_test0 = 0x12345678; + proc0.p_addr->overflow_test1 = ~proc0.p_addr->overflow_test0; cpu_info_primary.ci_curpcb = &proc0.p_addr->u_pcb; /* Index: arch/i386/isa/npx.c =================================================================== RCS file: /cvs/src/sys/arch/i386/isa/npx.c,v retrieving revision 1.40 diff -u -p -u -p -r1.40 npx.c --- arch/i386/isa/npx.c 25 Jul 2006 19:16:51 -0000 1.40 +++ arch/i386/isa/npx.c 28 Jul 2006 06:23:46 -0000 @@ -417,6 +417,7 @@ npxintr(arg) struct intrframe *frame = arg; int code; union sigval sv; + static int nprints = 0; uvmexp.traps++; IPRINTF(("%s: fp intr\n", ci->ci_dev.dv_xname)); @@ -447,6 +448,13 @@ npxintr(arg) panic("npxintr: wrong process"); #endif + if (p->p_addr && nprints < 10) { + if (p->p_addr->overflow_test0 != ~p->p_addr->overflow_test1) { + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", p->p_pid, p->p_addr->overflow_test0, p->p_addr->overflow_test1, __FILE__, __LINE__); + nprints++; + } + } + /* * Find the address of fpcurproc's saved FPU state. (Given the * invariant above, this is always the one in curpcb.) @@ -532,6 +540,13 @@ npxintr(arg) psignal(p, SIGFPE); } + if (p->p_addr && nprints < 10) { + if (p->p_addr->overflow_test0 != ~p->p_addr->overflow_test1) { + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", p->p_pid, p->p_addr->overflow_test0, p->p_addr->overflow_test1, __FILE__, __LINE__); + nprints++; + } + } + return (1); } @@ -575,6 +590,7 @@ npxdna_xmm(struct cpu_info *ci) { struct proc *p; int s; + static int nprints = 0; if (ci->ci_fpsaving) { printf("recursive npx trap; cr0=%x\n", rcr0()); @@ -589,6 +605,13 @@ npxdna_xmm(struct cpu_info *ci) p = curproc; #endif + if (p->p_addr && nprints < 10) { + if (p->p_addr->overflow_test0 != ~p->p_addr->overflow_test1) { + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", p->p_pid, p->p_addr->overflow_test0, p->p_addr->overflow_test1, __FILE__, __LINE__); + nprints++; + } + } + IPRINTF(("%s: dna for %lx%s\n", ci->ci_dev.dv_xname, (u_long)p, (p->p_md.md_flags & MDP_USEDFPU) ? " (used fpu)" : "")); @@ -653,6 +676,7 @@ npxdna_s87(struct cpu_info *ci) { struct proc *p; int s; + static int nprints = 0; KDASSERT(i386_use_fxsave == 0); @@ -668,6 +692,13 @@ npxdna_s87(struct cpu_info *ci) p = curproc; #endif + if (p->p_addr && nprints < 10) { + if (p->p_addr->overflow_test0 != ~p->p_addr->overflow_test1) { + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", p->p_pid, p->p_addr->overflow_test0, p->p_addr->overflow_test1, __FILE__, __LINE__); + nprints++; + } + } + IPRINTF(("%s: dna for %lx%s\n", ci->ci_dev.dv_xname, (u_long)p, (p->p_md.md_flags & MDP_USEDFPU) ? " (used fpu)" : "")); @@ -741,6 +772,7 @@ npxsave_cpu(struct cpu_info *ci, int sav { struct proc *p; int s; + static int nprints = 0; KDASSERT(ci == curcpu()); @@ -748,6 +780,13 @@ npxsave_cpu(struct cpu_info *ci, int sav if (p == NULL) return; + if (p->p_addr && nprints < 10) { + if (p->p_addr->overflow_test0 != ~p->p_addr->overflow_test1) { + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", p->p_pid, p->p_addr->overflow_test0, p->p_addr->overflow_test1, __FILE__, __LINE__); + nprints++; + } + } + IPRINTF(("%s: fp cpu %s %lx\n", ci->ci_dev.dv_xname, save ? "save" : "flush", (u_long)p)); @@ -798,8 +837,16 @@ npxsave_proc(struct proc *p, int save) { struct cpu_info *ci = curcpu(); struct cpu_info *oci; + static int nprints = 0; KDASSERT(p->p_addr != NULL); + + if (p->p_addr && nprints < 10) { + if (p->p_addr->overflow_test0 != ~p->p_addr->overflow_test1) { + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", p->p_pid, p->p_addr->overflow_test0, p->p_addr->overflow_test1, __FILE__, __LINE__); + nprints++; + } + } oci = p->p_addr->u_pcb.pcb_fpcpu; if (oci == NULL) Index: ddb/db_output.c =================================================================== RCS file: /cvs/src/sys/ddb/db_output.c,v retrieving revision 1.25 diff -u -p -u -p -r1.25 db_output.c --- ddb/db_output.c 6 Jul 2006 18:14:14 -0000 1.25 +++ ddb/db_output.c 28 Jul 2006 06:23:46 -0000 @@ -113,6 +113,8 @@ db_more(void) char *p; int quit_output = 0; + return; + for (p = "--db_more--"; *p; p++) cnputc(*p); switch(cngetc()) { Index: kern/kern_exit.c =================================================================== RCS file: /cvs/src/sys/kern/kern_exit.c,v retrieving revision 1.62 diff -u -p -u -p -r1.62 kern_exit.c --- kern/kern_exit.c 23 Jun 2006 13:46:05 -0000 1.62 +++ kern/kern_exit.c 28 Jul 2006 06:24:59 -0000 @@ -66,6 +66,7 @@ #ifdef SYSVSEM #include #endif +#include #include "systrace.h" #include @@ -114,6 +115,14 @@ void exit1(struct proc *p, int rv, int flags) { struct proc *q, *nq; + static int nprints = 0; + + if (p->p_addr && nprints < 10) { + if (p->p_addr->overflow_test0 != ~p->p_addr->overflow_test1) { + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", p->p_pid, p->p_addr->overflow_test0, p->p_addr->overflow_test1, __FILE__, __LINE__); + nprints++; + } + } if (p->p_pid == 1) panic("init died (signal %d, exit %d)", @@ -352,6 +361,14 @@ exit1(struct proc *p, int rv, int flags) * cpu_switch(), finishing our execution (pun intended). */ uvmexp.swtch++; + + if (p->p_addr && nprints < 10) { + if (p->p_addr->overflow_test0 != ~p->p_addr->overflow_test1) { + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", p->p_pid, p->p_addr->overflow_test0, p->p_addr->overflow_test1, __FILE__, __LINE__); + nprints++; + } + } + cpu_exit(p); } @@ -399,6 +416,7 @@ void reaper(void) { struct proc *p; + static int nprints = 0; KERNEL_PROC_UNLOCK(curproc); @@ -423,6 +441,14 @@ reaper(void) * that process's context. This must be done before * uvm_exit(), in case these resources are in the PCB. */ + + if (p->p_addr && nprints < 10) { + if (p->p_addr->overflow_test0 != ~p->p_addr->overflow_test1) { + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", p->p_pid, p->p_addr->overflow_test0, p->p_addr->overflow_test1, __FILE__, __LINE__); + nprints++; + } + } + cpu_wait(p); /* Index: kern/kern_fork.c =================================================================== RCS file: /cvs/src/sys/kern/kern_fork.c,v retrieving revision 1.84 diff -u -p -u -p -r1.84 kern_fork.c --- kern/kern_fork.c 30 Apr 2006 15:37:07 -0000 1.84 +++ kern/kern_fork.c 28 Jul 2006 06:25:13 -0000 @@ -55,6 +55,7 @@ #include #include #include +#include #include #include @@ -338,6 +339,8 @@ fork1(struct proc *p1, int exitsig, int PHOLD(p1); p2->p_addr = (struct user *)uaddr; + p2->p_addr->overflow_test0 = 0xecececec; + p2->p_addr->overflow_test1 = ~p2->p_addr->overflow_test0; /* * Finish creating the child process. It will return through a Index: kern/kern_sig.c =================================================================== RCS file: /cvs/src/sys/kern/kern_sig.c,v retrieving revision 1.84 diff -u -p -u -p -r1.84 kern_sig.c --- kern/kern_sig.c 15 Jun 2006 20:08:01 -0000 1.84 +++ kern/kern_sig.c 28 Jul 2006 06:23:46 -0000 @@ -1305,7 +1305,14 @@ coredump(struct proc *p) int error, error1; char name[MAXCOMLEN+6]; /* progname.core */ struct core core; + int nprints = 0; + if (p->p_addr && nprints < 10) { + if (p->p_addr->overflow_test0 != ~p->p_addr->overflow_test1) { + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", p->p_pid, p->p_addr->overflow_test0, p->p_addr->overflow_test1, __FILE__, __LINE__); + nprints++; + } + } /* * Don't dump if not root and the process has used set user or * group privileges. @@ -1368,6 +1375,12 @@ coredump(struct proc *p) core.c_tsize = (u_long)ctob(vm->vm_tsize); core.c_dsize = (u_long)ctob(vm->vm_dsize); core.c_ssize = (u_long)round_page(ctob(vm->vm_ssize)); + if (p->p_addr && nprints < 10) { + if (p->p_addr->overflow_test0 != ~p->p_addr->overflow_test1) { + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", p->p_pid, p->p_addr->overflow_test0, p->p_addr->overflow_test1, __FILE__, __LINE__); + nprints++; + } + } error = cpu_coredump(p, vp, cred, &core); if (error) goto out; Index: kern/sched_bsd.c =================================================================== RCS file: /cvs/src/sys/kern/sched_bsd.c,v retrieving revision 1.5 diff -u -p -u -p -r1.5 sched_bsd.c --- kern/sched_bsd.c 17 Jun 2005 22:33:34 -0000 1.5 +++ kern/sched_bsd.c 28 Jul 2006 06:23:46 -0000 @@ -47,6 +47,7 @@ #include #include #include +#include #ifdef KTRACE #include @@ -412,7 +413,6 @@ preempt(newp) SCHED_UNLOCK(s); } - /* * Must be called at splstatclock() or higher. */ @@ -429,6 +429,7 @@ mi_switch() #ifdef __HAVE_CPUINFO struct schedstate_percpu *spc = &p->p_cpu->ci_schedstate; #endif + static int nprints = 0; SCHED_ASSERT_LOCKED(); @@ -534,6 +535,12 @@ mi_switch() __mp_acquire_count(&kernel_lock, hold_count); __mp_acquire_count(&sched_lock, sched_count + 1); #endif + if (p->p_addr && nprints < 10) { + if (p->p_addr->overflow_test0 != ~p->p_addr->overflow_test1) { + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", p->p_pid, p->p_addr->overflow_test0, p->p_addr->overflow_test1, __FILE__, __LINE__); + nprints++; + } + } } /* Index: kern/subr_prf.c =================================================================== RCS file: /cvs/src/sys/kern/subr_prf.c,v retrieving revision 1.67 diff -u -p -u -p -r1.67 subr_prf.c --- kern/subr_prf.c 6 Jul 2006 18:14:49 -0000 1.67 +++ kern/subr_prf.c 28 Jul 2006 06:23:46 -0000 @@ -132,7 +132,7 @@ int db_console = 0; /* * panic on spl assertion failure? */ -int splassert_ctl = 0; +int splassert_ctl = 2; /* * v_putc: routine to putc on virtual console Index: lib/libkern/Makefile =================================================================== RCS file: /cvs/src/sys/lib/libkern/Makefile,v retrieving revision 1.18 diff -u -p -u -p -r1.18 Makefile --- lib/libkern/Makefile 28 Nov 2004 07:20:25 -0000 1.18 +++ lib/libkern/Makefile 28 Jul 2006 06:23:46 -0000 @@ -28,9 +28,10 @@ SRCS+= adddi3.c anddi3.c ashldi3.c ashrd SRCS+= getsn.c srandom.c bcd.c strchr.c # Files to clean up -CLEANFILES+= lib${LIB}.o lib${LIB}.po +CLEANFILES+= lib${LIB}.o lib${LIB}.po mcount.o # mcount cannot be compiled with profiling +# XXX no dependency checks are done on mcount.o? mcount.po: mcount.o cp mcount.o mcount.po Index: lib/libkern/mcount.c =================================================================== RCS file: /cvs/src/sys/lib/libkern/mcount.c,v retrieving revision 1.8 diff -u -p -u -p -r1.8 mcount.c --- lib/libkern/mcount.c 7 Aug 2004 00:38:32 -0000 1.8 +++ lib/libkern/mcount.c 28 Jul 2006 06:40:08 -0000 @@ -40,6 +40,8 @@ static char rcsid[] = "$OpenBSD: mcount. #include #include +#include +#include /* * mcount is called on entry to each function compiled with the profiling @@ -60,6 +62,10 @@ static char rcsid[] = "$OpenBSD: mcount. * as static and gcc doesn't check for function calls in assembler * stubs. */ +#include +extern int cold; +static int ntimes = 0; +static int instuff = 0; _MCOUNT_DECL(u_long frompc, u_long selfpc) __attribute__((unused)); _MCOUNT_DECL(u_long frompc, u_long selfpc) /* _mcount; may be static, inline, etc */ { @@ -69,6 +75,29 @@ _MCOUNT_DECL(u_long frompc, u_long selfp long toindex; #ifdef _KERNEL int s; +#endif + +#ifdef _KERNEL + if (! cold && !instuff) { + frompcindex = NULL; + top = NULL; + prevtop = NULL; + toindex = 0; + s = 0; + if (curproc && curproc->p_addr && ntimes < 30) { + instuff = 1; + if ((char *) &s >= ((char *) (curproc->p_addr)) && (char *) &s <= (char *) &curproc->p_addr->overflow_test1) { + ntimes++; + printf("Kernel stack overflow, pid: %d, \"%s\", %p %p %p\n", curproc->p_pid, curproc->p_comm, &s, ((char *)(curproc->p_addr)), &curproc->p_addr->overflow_test1); + db_stack_dump(); + } + if (curproc->p_addr->overflow_test0 != ~curproc->p_addr->overflow_test1 && ntimes < 30) { + ntimes++; + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", curproc->p_pid, curproc->p_addr->overflow_test0, curproc->p_addr->overflow_test1, __FILE__, __LINE__); + } + instuff = 0; + } + } #endif p = &_gmonparam; Index: sys/user.h =================================================================== RCS file: /cvs/src/sys/sys/user.h,v retrieving revision 1.6 diff -u -p -u -p -r1.6 user.h --- sys/user.h 2 Jun 2003 23:28:22 -0000 1.6 +++ sys/user.h 28 Jul 2006 06:45:56 -0000 @@ -56,6 +56,9 @@ struct user { struct pcb u_pcb; + /* For overflow tests */ + unsigned int overflow_test0; + struct pstats u_stats; /* p_stats points here (use it!) */ /* @@ -64,6 +67,9 @@ struct user { */ struct kinfo_proc u_kproc; /* proc + eproc */ struct md_coredump u_md; /* machine dependent glop */ + + /* For overflow tests */ + unsigned int overflow_test1; }; /* Index: uvm/uvm_glue.c =================================================================== RCS file: /cvs/src/sys/uvm/uvm_glue.c,v retrieving revision 1.44 diff -u -p -u -p -r1.44 uvm_glue.c --- uvm/uvm_glue.c 25 May 2006 22:42:22 -0000 1.44 +++ uvm/uvm_glue.c 28 Jul 2006 06:23:46 -0000 @@ -255,6 +255,7 @@ uvm_fork(p1, p2, shared, stack, stacksiz { struct user *up = p2->p_addr; int rv; + static int nprints = 0; if (shared == TRUE) { p2->p_vmspace = NULL; @@ -300,6 +301,18 @@ uvm_fork(p1, p2, shared, stack, stacksiz * slice and will not return here. If this is a kernel thread, * the specified entry point will be executed. */ + if (p1->p_addr && nprints < 10) { + if (p1->p_addr->overflow_test0 != ~p1->p_addr->overflow_test1) { + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", p1->p_pid, p1->p_addr->overflow_test0, p1->p_addr->overflow_test1, __FILE__, __LINE__); + nprints++; + } + } + if (p2->p_addr && nprints < 10) { + if (p2->p_addr->overflow_test0 != ~p2->p_addr->overflow_test1) { + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", p2->p_pid, p2->p_addr->overflow_test0, p2->p_addr->overflow_test1, __FILE__, __LINE__); + nprints++; + } + } cpu_fork(p1, p2, stack, stacksize, func, arg); } @@ -316,7 +329,14 @@ uvm_exit(p) struct proc *p; { vaddr_t va = (vaddr_t)p->p_addr; + static int nprints = 0; + if (p->p_addr && nprints < 10) { + if (p->p_addr->overflow_test0 != ~p->p_addr->overflow_test1) { + printf("overflow test failed, pid: %d, test0: %08x, test1: %08x %s:%d\n", p->p_pid, p->p_addr->overflow_test0, p->p_addr->overflow_test1, __FILE__, __LINE__); + nprints++; + } + } uvmspace_free(p->p_vmspace); p->p_flag &= ~P_INMEM; uvm_fault_unwire(kernel_map, va, va + USPACE);