diff -u linux-2.6.32.21/fs/exec.c linux-2.6.32.21-new/fs/exec.c
--- linux-2.6.32.21/fs/exec.c	2010-09-04 15:54:52.000000000 -0400
+++ linux-2.6.32.21-new/fs/exec.c	2010-09-14 20:45:47.000000000 -0400
@@ -1780,6 +1780,62 @@
 #endif
 
 #ifdef CONFIG_PAX_USERCOPY
+#if defined(CONFIG_FRAME_POINTER) && defined(CONFIG_X86)
+struct stack_frame {
+	struct stack_frame *next_frame;
+	unsigned long return_address;
+};
+#endif
+
+/* 0: not at all, 1: fully, 2: fully inside frame,
+  -1: partially (implies an error) */
+
+int object_is_on_stack(const void *obj, unsigned long len)
+{
+	const void *stack = task_stack_page(current);
+	const void *stackend = stack + THREAD_SIZE;
+
+	if (obj + len < obj)
+		return -1;
+
+	if (stack <= obj && obj + len <= stackend) {
+#if defined(CONFIG_FRAME_POINTER) && defined(CONFIG_X86)
+		void *frame = __builtin_frame_address(2);
+		void *oldframe = __builtin_frame_address(1);
+		/*
+		  bottom ----------------------------------------------> top
+		  [saved bp][saved ip][args][local vars][saved bp][saved ip]
+				      ^----------------^
+				  allow copies only within here
+		*/
+		while (frame) {
+			/* if obj + len extends past the last frame, this
+			   check won't pass and the next frame will be 0,
+			   causing us to bail out and correctly report
+			   the copy as invalid
+			*/
+			if (obj + len <= frame) {
+				if (obj >= (oldframe + (2 * sizeof(void *))))
+					return 2;
+				else
+					return -1;
+			}
+			oldframe = frame;
+			frame = ((struct stack_frame *)frame)->next_frame;
+		}
+		return -1;
+#else
+		return 1;
+#endif
+	}
+
+	if (obj + len <= stack || stackend <=  obj)
+		return 0;
+
+	return -1;
+}
+
+
 void pax_report_leak_to_user(const void *ptr, unsigned long len)
 {
 	if (current->signal->curr_ip)
diff -u linux-2.6.32.21/include/linux/sched.h linux-2.6.32.21-new/include/linux/sched.h
--- linux-2.6.32.21/include/linux/sched.h	2010-09-04 15:54:52.000000000 -0400
+++ linux-2.6.32.21-new/include/linux/sched.h	2010-09-14 18:41:02.000000000 -0400
@@ -2354,23 +2354,9 @@
 	return (obj >= stack) && (obj < (stack + THREAD_SIZE));
 }
 
-/* 0: not at all, 1: fully, -1: partially (implies an error) */
-static inline int object_is_on_stack(const void *obj, unsigned long len)
-{
-	const void *stack = task_stack_page(current);
-	const void *stackend = stack + THREAD_SIZE;
-
-	if (obj + len < obj)
-		return -1;
-
-	if (stack <= obj && obj + len <= stackend)
-		return 1;
-
-	if (obj + len <= stack || stackend <=  obj)
-		return 0;
-
-	return -1;
-}
+#ifdef CONFIG_PAX_USERCOPY
+extern int object_is_on_stack(const void *obj, unsigned long len);
+#endif
 
 extern void thread_info_cache_init(void);
 
diff -u linux-2.6.32.21/security/Kconfig linux-2.6.32.21-new/security/Kconfig
--- linux-2.6.32.21/security/Kconfig	2010-09-04 15:54:52.000000000 -0400
+++ linux-2.6.32.21-new/security/Kconfig	2010-09-14 20:52:17.000000000 -0400
@@ -492,6 +492,10 @@
 	  Note that the current implementation provides the strictest checks
 	  for the SLUB allocator.
 
+	  If frame pointers are enabled on x86, this option will also
+	  restrict copies into and out of the kernel stack to local variables
+	  within a single frame.
+
 	  Since this has a negligible performance impact, you should enable
 	  this feature.
 
