Subject: kernel stack overflow detection
To: None <tech-kern@netbsd.org>
From: YAMAMOTO Takashi <yamt@mwd.biglobe.ne.jp>
List: tech-kern
Date: 05/30/2002 23:05:20
----Next_Part(Thu_May_30_23:05:20_2002_346)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

hi.

I made patches to detect kernel stack overflow.
(because i've seen some overflow on recent i386..)

# alloc 1 more page for kernel stack so that
# kernel stack overflow doesn't cause destruction.
# (i386 only. but i think it's easy for other archs, too)
options KSTACK_SAFE

# detect kernel stack overflow using debug register.
# (i386 only)
options KSTACK_CHECK_DR0

# detect kernel stack overflow using magic number.
# (MI)
options KSTACK_CHECK_MAGIC

comments?

---
YAMAMOTO Takashi<yamt@mwd.biglobe.ne.jp>

----Next_Part(Thu_May_30_23:05:20_2002_346)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="kstack.diff"

Index: kern_fork.c
===================================================================
RCS file: /cvs/cvsroot/syssrc/sys/kern/kern_fork.c,v
retrieving revision 1.88
diff -u -p -r1.88 kern_fork.c
--- kern_fork.c	2001/12/08 00:35:30	1.88
+++ kern_fork.c	2002/05/30 13:42:58
@@ -81,6 +81,7 @@
 __KERNEL_RCSID(0, "$NetBSD: kern_fork.c,v 1.88 2001/12/08 00:35:30 thorpej Exp $");
 
 #include "opt_ktrace.h"
+#include "opt_kstack.h"
 #include "opt_multiprocessor.h"
 
 #include <sys/param.h>
@@ -101,6 +102,66 @@ __KERNEL_RCSID(0, "$NetBSD: kern_fork.c,
 #include <sys/sched.h>
 #include <sys/signalvar.h>
 
+#ifdef KSTACK_CHECK_MAGIC
+#include <sys/user.h>
+
+#define KSTACK_MAGIC	0xdeadbeaf
+
+void kstack_setup_magic(const struct proc *);
+void kstack_check_magic(const struct proc *);
+
+void
+kstack_setup_magic(const struct proc *p)
+{
+	KASSERT(p != 0);
+	KASSERT(p != &proc0);
+
+	/*
+	 * setup magic number on the top of the kernel stack
+	 * so that later modification on it can be detected.
+	 */
+	*(u_int32_t *)KSTACK_END(p) = KSTACK_MAGIC;
+}
+
+void
+kstack_check_magic(const struct proc *p)
+{
+	static int dont_check;
+
+	KASSERT(p != 0);
+
+	if (dont_check)
+		return;
+
+	/* don't check proc0 */ /*XXX*/
+	if (p == &proc0)
+		return;
+
+	if (*(u_int32_t *)KSTACK_END(p) != KSTACK_MAGIC) {
+#ifndef KSTACK_SAFE
+		/* XXX should panic? */
+#endif
+		printf("kernel stack magic changed for pid %u: "
+		    "maybe kernel stack overflow\n", p->p_pid);
+
+#ifdef KSTACK_SAFE
+		/*
+		 * no more checks.
+		 * if kernel stack once overflow, it's enough bad..
+		 */
+		dont_check = 1;
+		printf("kernel stack magic check disabled\n");
+#else
+		/*
+		 * re-setup magic
+		 * XXX this will break stack more..
+		 */
+		kstack_setup_magic(p);
+#endif
+	}
+}
+#endif /*KSTACK_CHECK_MAGIC*/
+
 #include <sys/syscallargs.h>
 
 #include <uvm/uvm_extern.h>
@@ -376,6 +437,9 @@ fork1(struct proc *p1, int flags, int ex
 	    stack, stacksize,
 	    (func != NULL) ? func : child_return,
 	    (arg != NULL) ? arg : p2);
+#ifdef KSTACK_CHECK_MAGIC
+	kstack_setup_magic(p2);
+#endif
 
 	/*
 	 * BEGIN PID ALLOCATION.
Index: kern_synch.c
===================================================================
RCS file: /cvs/cvsroot/syssrc/sys/kern/kern_synch.c,v
retrieving revision 1.108
diff -u -p -r1.108 kern_synch.c
--- kern_synch.c	2002/05/21 01:38:27	1.108
+++ kern_synch.c	2002/05/30 13:42:58
@@ -82,6 +82,7 @@ __KERNEL_RCSID(0, "$NetBSD: kern_synch.c
 
 #include "opt_ddb.h"
 #include "opt_ktrace.h"
+#include "opt_kstack.h"
 #include "opt_lockdebug.h"
 #include "opt_multiprocessor.h"
 
@@ -95,6 +96,11 @@ __KERNEL_RCSID(0, "$NetBSD: kern_synch.c
 #include <sys/resourcevar.h>
 #include <sys/sched.h>
 
+#ifdef KSTACK_CHECK_MAGIC
+/* XXX should be in proc.h or user.h */
+void kstack_check_magic(const struct proc *);
+#endif
+
 #include <uvm/uvm_extern.h>
 
 #ifdef KTRACE
@@ -847,6 +853,10 @@ mi_switch(struct proc *p)
 	 * scheduling flags.
 	 */
 	spc->spc_flags &= ~SPCF_SWITCHCLEAR;
+
+#ifdef KSTACK_CHECK_MAGIC
+	kstack_check_magic(p);
+#endif
 
 	/*
 	 * Pick a new current process and switch to it.  When we
Index: param.h
===================================================================
RCS file: /cvs/cvsroot/syssrc/sys/sys/param.h,v
retrieving revision 1.140
diff -u -p -r1.140 param.h
--- param.h	2002/05/22 03:29:09	1.140
+++ param.h	2002/05/30 13:43:10
@@ -277,6 +277,14 @@
 
 #ifdef _KERNEL
 /*
+ * kernel stack paramaters
+ */
+#ifndef SHAM_USPACE
+#define	SHAM_USPACE	USPACE
+#endif
+#define KSTACK_END(p)   ((caddr_t)ALIGN((p)->p_addr + 1) + USPACE - SHAM_USPACE)
+
+/*
  * macro to convert from milliseconds to hz without integer overflow
  * Default version using only 32bits arithmetics.
  * 64bit port can define 64bit version in their <machine/param.h>
Index: files
===================================================================
RCS file: /cvs/cvsroot/syssrc/sys/conf/files,v
retrieving revision 1.531
diff -u -p -r1.531 files
--- files	2002/05/22 05:49:57	1.531
+++ files	2002/05/30 13:43:34
@@ -138,6 +138,7 @@ defparam opt_kgdb.h		KGDB_DEV KGDB_DEVNA
 				KGDB_DEVADDR KGDB_DEVRATE KGDB_DEVMODE
 defflag				LOCKDEBUG
 defflag				SYSCALL_DEBUG
+defflag	opt_kstack.h		KSTACK_CHECK_MAGIC
 
 # memory (ram) disk options
 #
Index: files.i386
===================================================================
RCS file: /cvs/cvsroot/syssrc/sys/arch/i386/conf/files.i386,v
retrieving revision 1.206
diff -u -p -r1.206 files.i386
--- files.i386	2002/04/18 12:54:12	1.206
+++ files.i386	2002/05/30 13:44:02
@@ -57,6 +57,10 @@ defparam opt_pcibios.h	PCIBIOS_IRQS_HINT
 # Large page size
 defflag			LARGEPAGES
 
+# kernel stack debug
+defflag	opt_kstack_safe.h		KSTACK_SAFE
+defflag	opt_kstack_dr0.h		KSTACK_CHECK_DR0
+
 file	arch/i386/i386/autoconf.c
 file	arch/i386/i386/bus_machdep.c
 file	arch/i386/i386/conf.c
Index: trap.c
===================================================================
RCS file: /cvs/cvsroot/syssrc/sys/arch/i386/i386/trap.c,v
retrieving revision 1.165
diff -u -p -r1.165 trap.c
--- trap.c	2002/02/18 15:58:02	1.165
+++ trap.c	2002/05/30 13:44:28
@@ -86,6 +86,7 @@ __KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.1
 #include "opt_math_emulate.h"
 #include "opt_vm86.h"
 #include "opt_cputype.h"
+#include "opt_kstack_dr0.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -155,6 +156,26 @@ int	trapdebug = 0;
 
 #define	IDTVEC(name)	__CONCAT(X, name)
 
+#ifdef KSTACK_CHECK_DR0
+/* XXX should be in cpufunc.h */
+static __inline u_int
+rdr6(void)
+{
+	u_int val;
+
+	__asm __volatile("movl %%dr6,%0" : "=r" (val));
+	return val;
+}
+
+/* XXX should be in cpufunc.h */
+static __inline void
+ldr6(u_int val)
+{
+
+	__asm __volatile("movl %0,%%dr6" : : "r" (val));
+}
+#endif
+
 /*
  * trap(frame):
  *	Exception, fault, and trap interface to BSD kernel. This
@@ -202,6 +223,22 @@ trap(frame)
 
 	default:
 	we_re_toast:
+#ifdef KSTACK_CHECK_DR0
+		if (type == T_TRCTRAP) {
+			u_int mask, dr6 = rdr6();
+
+			mask = 1 << 0; /* dr0 */
+			if (dr6 & mask) {
+#ifndef KSTACK_SAFE
+				/* XXX should panic? */
+#endif
+				printf("trap on DR0: maybe kernel stack overflow\n");
+				dr6 &= ~mask;
+				ldr6(dr6);
+				return;
+			}
+		}
+#endif
 #ifdef KGDB
 		if (kgdb_trap(type, &frame))
 			return;
Index: pmap.c
===================================================================
RCS file: /cvs/cvsroot/syssrc/sys/arch/i386/i386/pmap.c,v
retrieving revision 1.136
diff -u -p -r1.136 pmap.c
--- pmap.c	2002/03/27 04:47:28	1.136
+++ pmap.c	2002/05/30 13:44:29
@@ -65,6 +65,7 @@ __KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.1
 #include "opt_cputype.h"
 #include "opt_user_ldt.h"
 #include "opt_largepages.h"
+#include "opt_kstack_dr0.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -1654,6 +1655,11 @@ pmap_ldt_cleanup(p)
 }
 #endif /* USER_LDT */
 
+#ifdef KSTACK_CHECK_DR0
+/* XXX should be in cpufunc.h or somewhere */
+void dr0(caddr_t, u_int32_t, u_int32_t, u_int32_t);
+#endif
+
 /*
  * pmap_activate: activate a process' pmap (fill in %cr3 and LDT info)
  *
@@ -1675,6 +1681,16 @@ pmap_activate(p)
 		lcr3(pcb->pcb_cr3);
 	if (pcb == curpcb)
 		lldt(pcb->pcb_ldt_sel);
+
+#ifdef KSTACK_CHECK_DR0
+	/*
+	 * setup breakpoint on the top of stack
+	 */
+	if (p == &proc0)
+		dr0(0, 0, 0, 0);
+	else
+		dr0(KSTACK_END(p), 1, 3, 1);
+#endif
 }
 
 /*
Index: param.h
===================================================================
RCS file: /cvs/cvsroot/syssrc/sys/arch/i386/include/param.h,v
retrieving revision 1.48
diff -u -p -r1.48 param.h
--- param.h	2002/02/26 15:13:23	1.48
+++ param.h	2002/05/30 13:45:00
@@ -90,7 +90,17 @@
 
 #define	SSIZE		1		/* initial stack size/NBPG */
 #define	SINCR		1		/* increment of stack/NBPG */
-#define	UPAGES		2		/* pages of u-area */
+
+#if defined(_KERNEL_OPT)
+#include "opt_kstack_safe.h"
+#endif
+#define SHAM_UPAGES	2
+#define	SHAM_USPACE	(SHAM_UPAGES * NBPG)
+#ifdef KSTACK_SAFE
+#define	UPAGES		(SHAM_UPAGES + 1) /* "real" pages of u-area */
+#else
+#define	UPAGES		SHAM_UPAGES	/* pages of u-area */
+#endif
 #define	USPACE		(UPAGES * NBPG)	/* total size of u-area */
 
 #ifndef MSGBUFSIZE

----Next_Part(Thu_May_30_23:05:20_2002_346)----