Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date: Thu, 19 May 2016 09:14:12 -0700
From: casey.schaufler@...el.com
To: kernel-hardening@...ts.openwall.com
Cc: Casey Schaufler <casey.schaufler@...el.com>
Subject: [PATCH] Subject: [RFC PATCH] mm: Hardened usercopy

From: Casey Schaufler <casey.schaufler@...el.com>

This is a work in progress, first stage of porting
the PAX_USERCOPY feature for upstream consumption.
The work isn't complete, but this should provide the
basic structure and initial range checking. There
are known gaps:

	- The SLOB memory allocator does not work
	  on x86, however the grsecurity version does
	  not work there either
	- This is relative to the 4.3.3 kernel, same
	  as the publicly released version of
	  grsecurity that serves as its model.
	- I don't have a good set of test cases.
	  Any pointers would be greatly appreciated.
	  I am not myself especially clever, and the
	  sort of things you have to do to generate
	  exploits do not come naturally to me.

Further, there are questions about the way I've done
the port.

	- The hard usercopy implementation is fully
	  ifdefed, whereas the grsecurity version makes
	  wholesale changes to the base code. I'm open
	  to opinions regarding which would be preferred
	  upstream.
	- In part because of the above, it's possible
	  that I've missed important bits. Reports of
	  glaring ommissions (or sins of commission)
	  are always welcome.
	- Attestation. How best to ensure the the original
	  authors get what they deserve.

This is definitely RFC territory. Please C away.

Signed-off-by: Casey Schaufler <casey.schaufler@...el.com>
---
 arch/arm/include/asm/uaccess.h      |   6 ++
 arch/ia64/include/asm/uaccess.h     |  25 +++++++
 arch/powerpc/include/asm/uaccess.h  |  35 ++++++++++
 arch/sparc/include/asm/uaccess_32.h |  20 ++++++
 arch/sparc/include/asm/uaccess_64.h |  19 ++++++
 arch/x86/include/asm/uaccess_32.h   |   6 ++
 arch/x86/include/asm/uaccess_64.h   |   3 +
 fs/cifs/cifsfs.c                    |  13 ++++
 fs/dcache.c                         |   5 ++
 fs/exec.c                           | 125 +++++++++++++++++++++++++++++++++++
 fs/jfs/super.c                      |   7 ++
 fs/seq_file.c                       |   5 ++
 include/linux/gfp.h                 |  15 +++++
 include/linux/sched.h               |   3 +
 include/linux/slab.h                |  11 ++++
 include/linux/thread_info.h         |  11 ++++
 ipc/msgutil.c                       |   8 +++
 kernel/fork.c                       |   5 ++
 mm/slab.c                           |  39 +++++++++++
 mm/slab.h                           |   6 ++
 mm/slab_common.c                    |  42 ++++++++++++
 mm/slob.c                           | 126 ++++++++++++++++++++++++++++++++++++
 mm/slub.c                           |  42 ++++++++++++
 net/decnet/af_decnet.c              |   3 +
 security/Kconfig                    |  15 +++++
 virt/kvm/kvm_main.c                 |   5 ++
 26 files changed, 600 insertions(+)

diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h
index 8cc85a4..dcb71c4 100644
--- a/arch/arm/include/asm/uaccess.h
+++ b/arch/arm/include/asm/uaccess.h
@@ -497,6 +497,9 @@ static inline unsigned long __must_check
 __copy_from_user(void *to, const void __user *from, unsigned long n)
 {
 	unsigned int __ua_flags = uaccess_save_and_enable();
+#ifdef CONFIG_HARDUSERCOPY
+	check_object_size(to, n, false);
+#endif
 	n = arm_copy_from_user(to, from, n);
 	uaccess_restore(__ua_flags);
 	return n;
@@ -511,6 +514,9 @@ static inline unsigned long __must_check
 __copy_to_user(void __user *to, const void *from, unsigned long n)
 {
 	unsigned int __ua_flags = uaccess_save_and_enable();
+#ifdef CONFIG_HARDUSERCOPY
+	check_object_size(to, n, false);
+#endif
 	n = arm_copy_to_user(to, from, n);
 	uaccess_restore(__ua_flags);
 	return n;
diff --git a/arch/ia64/include/asm/uaccess.h b/arch/ia64/include/asm/uaccess.h
index 4f3fb6cc..e5ddd11 100644
--- a/arch/ia64/include/asm/uaccess.h
+++ b/arch/ia64/include/asm/uaccess.h
@@ -241,12 +241,21 @@ extern unsigned long __must_check __copy_user (void __user *to, const void __use
 static inline unsigned long
 __copy_to_user (void __user *to, const void *from, unsigned long count)
 {
+#ifdef CONFIG_HARDUSERCOPY
+	if (!__builtin_constant_p(count))
+		check_object_size(from, count, true);
+#endif
+
 	return __copy_user(to, (__force void __user *) from, count);
 }
 
 static inline unsigned long
 __copy_from_user (void *to, const void __user *from, unsigned long count)
 {
+#ifdef CONFIG_HARDUSERCOPY
+	if (!__builtin_constant_p(count))
+		check_object_size(from, count, true);
+#endif
 	return __copy_user((__force void __user *) to, from, count);
 }
 
@@ -258,8 +267,16 @@ __copy_from_user (void *to, const void __user *from, unsigned long count)
 	const void *__cu_from = (from);							\
 	long __cu_len = (n);								\
 											\
+#ifdef CONFIG_HARDUSERCOPY								\
+	if (__access_ok(__cu_from, __cu_len, get_fs())) {				\
+		if (!__builtin_constant_p(n))						\
+			check_object_size(__cu_from, __cu_len, true);			\
+		__cu_len = __copy_user((__force void __user *) __cu_to, __cu_from, __cu_len);	\
+	}										\
+#else											\
 	if (__access_ok(__cu_to, __cu_len, get_fs()))					\
 		__cu_len = __copy_user(__cu_to, (__force void __user *) __cu_from, __cu_len);	\
+#endif											\
 	__cu_len;									\
 })
 
@@ -270,8 +287,16 @@ __copy_from_user (void *to, const void __user *from, unsigned long count)
 	long __cu_len = (n);								\
 											\
 	__chk_user_ptr(__cu_from);							\
+#ifdef CONFIG_HARDUSERCOPY								\
+	if (__access_ok(__cu_from, __cu_len, get_fs())) {				\
+		if (!__builtin_constant_p(n))						\
+			check_object_size(__cu_to, __cu_len, false);			\
+		__cu_len = __copy_user((__force void __user *) __cu_to, __cu_from, __cu_len);	\
+	}										\
+#else											\
 	if (__access_ok(__cu_from, __cu_len, get_fs()))					\
 		__cu_len = __copy_user((__force void __user *) __cu_to, __cu_from, __cu_len);	\
+#endif											\
 	__cu_len;									\
 })
 
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
index 2a8ebae..08f83c9 100644
--- a/arch/powerpc/include/asm/uaccess.h
+++ b/arch/powerpc/include/asm/uaccess.h
@@ -325,10 +325,22 @@ static inline unsigned long copy_from_user(void *to,
 {
 	unsigned long over;
 
+#ifdef CONFIG_HARDUSERCOPY
+	if (access_ok(VERIFY_READ, from, n)) {
+		if (!__builtin_constant_p(n))
+			check_object_size(to, n, false);
+		return __copy_tofrom_user((__force void __user *)to, from, n);
+	}
+#else
 	if (access_ok(VERIFY_READ, from, n))
 		return __copy_tofrom_user((__force void __user *)to, from, n);
+#endif
 	if ((unsigned long)from < TASK_SIZE) {
 		over = (unsigned long)from + n - TASK_SIZE;
+#ifdef CONFIG_HARDUSERCOPY
+		if (!__builtin_constant_p(n - over))
+			check_object_size(to, n - over, false);
+#endif
 		return __copy_tofrom_user((__force void __user *)to, from,
 				n - over) + over;
 	}
@@ -340,10 +352,22 @@ static inline unsigned long copy_to_user(void __user *to,
 {
 	unsigned long over;
 
+#ifdef CONFIG_HARDUSERCOPY
+	if (access_ok(VERIFY_WRITE, to, n)) {
+		if (!__builtin_constant_p(n))
+			check_object_size(from, n, true);
+		return __copy_tofrom_user(to, (__force void __user *)from, n);
+	}
+#else
 	if (access_ok(VERIFY_WRITE, to, n))
 		return __copy_tofrom_user(to, (__force void __user *)from, n);
+#endif
 	if ((unsigned long)to < TASK_SIZE) {
 		over = (unsigned long)to + n - TASK_SIZE;
+#ifdef CONFIG_HARDUSERCOPY
+		if (!__builtin_constant_p(n))
+			check_object_size(from, n - over, true);
+#endif
 		return __copy_tofrom_user(to, (__force void __user *)from,
 				n - over) + over;
 	}
@@ -387,6 +411,12 @@ static inline unsigned long __copy_from_user_inatomic(void *to,
 		if (ret == 0)
 			return 0;
 	}
+
+#ifdef CONFIG_HARDUSERCOPY
+	if (!__builtin_constant_p(n))
+		check_object_size(to, n, false);
+#endif
+
 	return __copy_tofrom_user((__force void __user *)to, from, n);
 }
 
@@ -413,6 +443,11 @@ static inline unsigned long __copy_to_user_inatomic(void __user *to,
 		if (ret == 0)
 			return 0;
 	}
+#ifdef CONFIG_HARDUSERCOPY
+	if (!__builtin_constant_p(n))
+		check_object_size(from, n, true);
+#endif
+
 	return __copy_tofrom_user(to, (__force const void __user *)from, n);
 }
 
diff --git a/arch/sparc/include/asm/uaccess_32.h b/arch/sparc/include/asm/uaccess_32.h
index 64ee103..a8c10ad 100644
--- a/arch/sparc/include/asm/uaccess_32.h
+++ b/arch/sparc/include/asm/uaccess_32.h
@@ -313,22 +313,42 @@ unsigned long __copy_user(void __user *to, const void __user *from, unsigned lon
 
 static inline unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)
 {
+#ifdef CONFIG_HARDUSERCOPY
+	if (n && __access_ok((unsigned long) to, n)) {
+		if (!__builtin_constant_p(n))
+			check_object_size(from, n, true);
+		return __copy_user(to, (__force void __user *) from, n);
+	} else
+#else
 	if (n && __access_ok((unsigned long) to, n))
 		return __copy_user(to, (__force void __user *) from, n);
 	else
+#endif
 		return n;
 }
 
 static inline unsigned long __copy_to_user(void __user *to, const void *from, unsigned long n)
 {
+#ifdef CONFIG_HARDUSERCOPY
+	if (!__builtin_constant_p(n))
+		check_object_size(from, n, true);
+#endif
 	return __copy_user(to, (__force void __user *) from, n);
 }
 
 static inline unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
 {
+#ifdef CONFIG_HARDUSERCOPY
+	if (n && __access_ok((unsigned long) from, n)) {
+		if (!__builtin_constant_p(n))
+			check_object_size(to, n, false);
+		return __copy_user((__force void __user *) to, from, n);
+	} else
+#else
 	if (n && __access_ok((unsigned long) from, n))
 		return __copy_user((__force void __user *) to, from, n);
 	else
+#endif
 		return n;
 }
 
diff --git a/arch/sparc/include/asm/uaccess_64.h b/arch/sparc/include/asm/uaccess_64.h
index ea6e9a2..58d5e0a 100644
--- a/arch/sparc/include/asm/uaccess_64.h
+++ b/arch/sparc/include/asm/uaccess_64.h
@@ -250,8 +250,18 @@ unsigned long copy_from_user_fixup(void *to, const void __user *from,
 static inline unsigned long __must_check
 copy_from_user(void *to, const void __user *from, unsigned long size)
 {
+#ifdef CONFIG_HARDUSERCOPY
+	unsigned long ret;
+#else
 	unsigned long ret = ___copy_from_user(to, from, size);
+#endif
+
+#ifdef CONFIG_HARDUSERCOPY
+	if (!__builtin_constant_p(size))
+		check_object_size(to, size, false);
 
+	ret = ___copy_from_user(to, from, size);
+#endif
 	if (unlikely(ret))
 		ret = copy_from_user_fixup(to, from, size);
 
@@ -267,8 +277,17 @@ unsigned long copy_to_user_fixup(void __user *to, const void *from,
 static inline unsigned long __must_check
 copy_to_user(void __user *to, const void *from, unsigned long size)
 {
+#ifdef CONFIG_HARDUSERCOPY
+	unsigned long ret;
+#else
 	unsigned long ret = ___copy_to_user(to, from, size);
+#endif
 
+#ifdef CONFIG_HARDUSERCOPY
+	if (!__builtin_constant_p(size))
+		check_object_size(from, size, true);
+	ret = ___copy_to_user(to, from, size);
+#endif
 	if (unlikely(ret))
 		ret = copy_to_user_fixup(to, from, size);
 	return ret;
diff --git a/arch/x86/include/asm/uaccess_32.h b/arch/x86/include/asm/uaccess_32.h
index f5dcb52..8b6a3b6 100644
--- a/arch/x86/include/asm/uaccess_32.h
+++ b/arch/x86/include/asm/uaccess_32.h
@@ -43,6 +43,9 @@ unsigned long __must_check __copy_from_user_ll_nocache_nozero
 static __always_inline unsigned long __must_check
 __copy_to_user_inatomic(void __user *to, const void *from, unsigned long n)
 {
+#ifdef CONFIG_HARDUSERCOPY
+	check_object_size(from, n, true);
+#endif
 	if (__builtin_constant_p(n)) {
 		unsigned long ret;
 
@@ -143,6 +146,9 @@ static __always_inline unsigned long
 __copy_from_user(void *to, const void __user *from, unsigned long n)
 {
 	might_fault();
+#ifdef CONFIG_HARDUSERCOPY
+	check_object_size(to, n, false);
+#endif
 	if (__builtin_constant_p(n)) {
 		unsigned long ret;
 
diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h
index f2f9b39..673dcef 100644
--- a/arch/x86/include/asm/uaccess_64.h
+++ b/arch/x86/include/asm/uaccess_64.h
@@ -53,6 +53,9 @@ int __copy_from_user_nocheck(void *dst, const void __user *src, unsigned size)
 {
 	int ret = 0;
 
+#ifdef CONFIG_HARDUSERCOPY
+	check_object_size(dst, size, false);
+#endif
 	if (!__builtin_constant_p(size))
 		return copy_user_generic(dst, (__force void *)src, size);
 	switch (size) {
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index e739950..cf3133e 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -1083,9 +1083,16 @@ cifs_init_request_bufs(void)
 	cifs_dbg(VFS, "CIFSMaxBufSize %d 0x%x\n",
 		 CIFSMaxBufSize, CIFSMaxBufSize);
 */
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+	cifs_req_cachep = kmem_cache_create("cifs_request",
+					    CIFSMaxBufSize + max_hdr_size, 0,
+					    SLAB_HWCACHE_ALIGN|SLAB_USERCOPY,
+					    NULL);
+#else
 	cifs_req_cachep = kmem_cache_create("cifs_request",
 					    CIFSMaxBufSize + max_hdr_size, 0,
 					    SLAB_HWCACHE_ALIGN, NULL);
+#endif
 	if (cifs_req_cachep == NULL)
 		return -ENOMEM;
 
@@ -1111,9 +1118,15 @@ cifs_init_request_bufs(void)
 	more SMBs to use small buffer alloc and is still much more
 	efficient to alloc 1 per page off the slab compared to 17K (5page)
 	alloc of large cifs buffers even when page debugging is on */
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+	cifs_sm_req_cachep = kmem_cache_create("cifs_small_rq",
+			MAX_CIFS_SMALL_BUFFER_SIZE, 0,
+			SLAB_USERCOPY | SLAB_HWCACHE_ALIGN, NULL);
+#else
 	cifs_sm_req_cachep = kmem_cache_create("cifs_small_rq",
 			MAX_CIFS_SMALL_BUFFER_SIZE, 0, SLAB_HWCACHE_ALIGN,
 			NULL);
+#endif
 	if (cifs_sm_req_cachep == NULL) {
 		mempool_destroy(cifs_req_poolp);
 		kmem_cache_destroy(cifs_req_cachep);
diff --git a/fs/dcache.c b/fs/dcache.c
index 5c33aeb..f6e0f58 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -3450,8 +3450,13 @@ void __init vfs_caches_init_early(void)
 
 void __init vfs_caches_init(void)
 {
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+	names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,
+			SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_USERCOPY, NULL);
+#else /* CONFIG_HARDUSERCOPY_SLABS */
 	names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,
 			SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
+#endif /* CONFIG_HARDUSERCOPY_SLABS */
 
 	dcache_init();
 	inode_init();
diff --git a/fs/exec.c b/fs/exec.c
index b06623a..0e67eb2 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1749,3 +1749,128 @@ COMPAT_SYSCALL_DEFINE5(execveat, int, fd,
 				  argv, envp, flags);
 }
 #endif
+
+#ifdef CONFIG_HARDUSERCOPY
+/*
+ *	0: not at all,
+ *	1: fully,
+ *	2: fully inside frame,
+ *	-1: partially (implies an error)
+ */
+static noinline int check_stack_object(const void *obj, unsigned long len)
+{
+	const void * const stack = task_stack_page(current);
+	const void * const stackend = stack + THREAD_SIZE;
+
+#if defined(CONFIG_FRAME_POINTER) && defined(CONFIG_X86)
+	const void *frame = NULL;
+	const void *oldframe;
+#endif
+
+	if (obj + len < obj)
+		return -1;
+
+	if (obj + len <= stack || stackend <= obj)
+		return 0;
+
+	if (obj < stack || stackend < obj + len)
+		return -1;
+
+#if defined(CONFIG_FRAME_POINTER) && defined(CONFIG_X86)
+	oldframe = __builtin_frame_address(1);
+	if (oldframe)
+		frame = __builtin_frame_address(2);
+	/*
+	  low ----------------------------------------------> high
+	  [saved bp][saved ip][args][local vars][saved bp][saved ip]
+			      ^----------------^
+			  allow copies only within here
+	*/
+	while (stack <= frame && frame < stackend) {
+		/* 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)
+			return obj >= oldframe + 2 * sizeof(void *) ? 2 : -1;
+		oldframe = frame;
+		frame = *(const void * const *)frame;
+	}
+	return -1;
+#else
+	return 1;
+#endif
+}
+
+static inline void gr_handle_kernel_exploit(void) {}
+
+static __noreturn void report_hardusercopy(const void *ptr, unsigned long len,
+						bool to_user, const char *type)
+{
+	if (current->signal->curr_ip)
+		printk(KERN_EMERG "Hard user copy: From %pI4: kernel memory %s attempt detected %s %p (%s) (%lu bytes)\n",
+			&current->signal->curr_ip, to_user ? "leak" : "overwrite", to_user ? "from" : "to", ptr, type ? : "unknown", len);
+	else
+		printk(KERN_EMERG "Hard user copy: kernel memory %s attempt detected %s %p (%s) (%lu bytes)\n",
+			to_user ? "leak" : "overwrite", to_user ? "from" : "to", ptr, type ? : "unknown", len);
+	dump_stack();
+	gr_handle_kernel_exploit();
+	do_group_exit(SIGKILL);
+}
+
+static inline bool check_kernel_text_object(unsigned long low,
+						unsigned long high)
+{
+	unsigned long textlow = (unsigned long)_stext;
+	unsigned long texthigh = (unsigned long)_etext;
+
+#ifdef CONFIG_X86_64
+	/* check against linear mapping as well */
+	if (high > (unsigned long)__va(__pa(textlow)) &&
+	    low < (unsigned long)__va(__pa(texthigh)))
+		return true;
+#endif
+
+	if (high <= textlow || low >= texthigh)
+		return false;
+	else
+		return true;
+}
+
+void __check_object_size(const void *ptr, unsigned long n, bool to_user,
+				bool const_size)
+{
+	const char *type;
+
+#if !defined(CONFIG_STACK_GROWSUP) && !defined(CONFIG_X86_64)
+	unsigned long stackstart = (unsigned long)task_stack_page(current);
+	unsigned long currentsp = (unsigned long)&stackstart;
+	if (unlikely((currentsp < stackstart + 512 ||
+		     currentsp >= stackstart + THREAD_SIZE) && !in_interrupt()))
+		BUG();
+#endif
+
+	if (!n)
+		return;
+
+	type = check_heap_object(ptr, n);
+	if (!type) {
+		int ret = check_stack_object(ptr, n);
+		if (ret == 1 || ret == 2)
+			return;
+		if (ret == 0) {
+			if (check_kernel_text_object((unsigned long)ptr,
+						(unsigned long)ptr + n))
+				type = "<kernel text>";
+			else
+				return;
+		} else
+			type = "<process stack>";
+	}
+
+	report_hardusercopy(ptr, n, to_user, type);
+
+}
+EXPORT_SYMBOL(__check_object_size);
+#endif /* CONFIG_HARDUSERCOPY */
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index 4cd9798..493668e 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -899,10 +899,17 @@ static int __init init_jfs_fs(void)
 	int i;
 	int rc;
 
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+	jfs_inode_cachep =
+	    kmem_cache_create("jfs_ip", sizeof(struct jfs_inode_info), 0,
+			    SLAB_USERCOPY|SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD,
+			    init_once);
+#else
 	jfs_inode_cachep =
 	    kmem_cache_create("jfs_ip", sizeof(struct jfs_inode_info), 0,
 			    SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD,
 			    init_once);
+#endif
 	if (jfs_inode_cachep == NULL)
 		return -ENOMEM;
 
diff --git a/fs/seq_file.c b/fs/seq_file.c
index 225586e..a468a16 100644
--- a/fs/seq_file.c
+++ b/fs/seq_file.c
@@ -30,7 +30,12 @@ static void *seq_buf_alloc(unsigned long size)
 	 * __GFP_NORETRY to avoid oom-killings with high-order allocations -
 	 * it's better to fall back to vmalloc() than to kill things.
 	 */
+#ifdef CONFIG_HARDUSERCOPY
+	buf = kmalloc(size, GFP_KERNEL | GFP_USERCOPY | __GFP_NORETRY |
+				__GFP_NOWARN);
+#else
 	buf = kmalloc(size, GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN);
+#endif
 	if (!buf && size > PAGE_SIZE)
 		buf = vmalloc(size);
 	return buf;
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index f92cbd2..5807645 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -35,6 +35,10 @@ struct vm_area_struct;
 #define ___GFP_NO_KSWAPD	0x400000u
 #define ___GFP_OTHER_NODE	0x800000u
 #define ___GFP_WRITE		0x1000000u
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+#define ___GFP_USERCOPY         0x2000000u
+#endif
+
 /* If the above are modified, __GFP_BITS_SHIFT may need updating */
 
 /*
@@ -97,6 +101,9 @@ struct vm_area_struct;
 #define __GFP_NO_KSWAPD	((__force gfp_t)___GFP_NO_KSWAPD)
 #define __GFP_OTHER_NODE ((__force gfp_t)___GFP_OTHER_NODE) /* On behalf of other node */
 #define __GFP_WRITE	((__force gfp_t)___GFP_WRITE)	/* Allocator intends to dirty page */
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+#define __GFP_USERCOPY	((__force gfp_t)___GFP_USERCOPY)/* Allocator intends to copy page to/from userland */
+#endif
 
 /*
  * This may seem redundant, but it's a way of annotating false positives vs.
@@ -104,7 +111,11 @@ struct vm_area_struct;
  */
 #define __GFP_NOTRACK_FALSE_POSITIVE (__GFP_NOTRACK)
 
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+#define __GFP_BITS_SHIFT 26	/* Room for N __GFP_FOO bits */
+#else
 #define __GFP_BITS_SHIFT 25	/* Room for N __GFP_FOO bits */
+#endif
 #define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
 
 /* This equals 0, but use constants in case they ever change */
@@ -149,6 +160,10 @@ struct vm_area_struct;
 /* 4GB DMA on some platforms */
 #define GFP_DMA32	__GFP_DMA32
 
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+#define GFP_USERCOPY         __GFP_USERCOPY
+#endif
+
 /* Convert GFP flags to their corresponding migrate type */
 static inline int gfpflags_to_migratetype(const gfp_t gfp_flags)
 {
diff --git a/include/linux/sched.h b/include/linux/sched.h
index b7b9501..048eea8 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -757,6 +757,9 @@ struct signal_struct {
 #ifdef CONFIG_TASKSTATS
 	struct taskstats *stats;
 #endif
+#ifdef CONFIG_HARDUSERCOPY
+	u32 curr_ip;
+#endif
 #ifdef CONFIG_AUDIT
 	unsigned audit_tty;
 	unsigned audit_tty_log_passwd;
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 7e37d44..b6d311c 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -21,6 +21,9 @@
  * The ones marked DEBUG are only valid if CONFIG_DEBUG_SLAB is set.
  */
 #define SLAB_DEBUG_FREE		0x00000100UL	/* DEBUG: Perform (expensive) checks on free */
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+#define SLAB_USERCOPY           0x00000200UL    /* HARD: Allow copying objs to/from userland */
+#endif
 #define SLAB_RED_ZONE		0x00000400UL	/* DEBUG: Red zone objs in a cache */
 #define SLAB_POISON		0x00000800UL	/* DEBUG: Poison objects */
 #define SLAB_HWCACHE_ALIGN	0x00002000UL	/* Align objs on cache lines */
@@ -144,6 +147,10 @@ void kfree(const void *);
 void kzfree(const void *);
 size_t ksize(const void *);
 
+#ifdef CONFIG_HARDUSERCOPY
+const char *check_heap_object(const void *ptr, unsigned long n);
+#endif
+
 /*
  * Some archs want to perform DMA into kmalloc caches and need a guaranteed
  * alignment larger than the alignment of a 64-bit integer.
@@ -235,6 +242,10 @@ extern struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1];
 extern struct kmem_cache *kmalloc_dma_caches[KMALLOC_SHIFT_HIGH + 1];
 #endif
 
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+extern struct kmem_cache *kmalloc_usercopy_caches[KMALLOC_SHIFT_HIGH + 1];
+#endif
+
 /*
  * Figure out which kmalloc slab an allocation of a certain size
  * belongs to.
diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h
index ff307b5..3449364 100644
--- a/include/linux/thread_info.h
+++ b/include/linux/thread_info.h
@@ -145,6 +145,17 @@ static inline bool test_and_clear_restore_sigmask(void)
 #error "no set_restore_sigmask() provided and default one won't work"
 #endif
 
+#ifdef CONFIG_HARDUSERCOPY
+extern void __check_object_size(const void *ptr, unsigned long n,
+					bool to_user, bool const_size);
+
+static inline void check_object_size(const void *ptr, unsigned long n,
+					bool to_user)
+{
+	__check_object_size(ptr, n, to_user, __builtin_constant_p(n));
+}
+#endif /* CONFIG_HARDUSERCOPY */
+
 #endif	/* __KERNEL__ */
 
 #endif /* _LINUX_THREAD_INFO_H */
diff --git a/ipc/msgutil.c b/ipc/msgutil.c
index 71f448e..818bdd3 100644
--- a/ipc/msgutil.c
+++ b/ipc/msgutil.c
@@ -55,7 +55,11 @@ static struct msg_msg *alloc_msg(size_t len)
 	size_t alen;
 
 	alen = min(len, DATALEN_MSG);
+#ifdef CONFIG_HARDUSERCOPY
+	msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL|GFP_USERCOPY);
+#else
 	msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL);
+#endif
 	if (msg == NULL)
 		return NULL;
 
@@ -67,7 +71,11 @@ static struct msg_msg *alloc_msg(size_t len)
 	while (len > 0) {
 		struct msg_msgseg *seg;
 		alen = min(len, DATALEN_SEG);
+#ifdef CONFIG_HARDUSERCOPY
+		seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL|GFP_USERCOPY);
+#else
 		seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL);
+#endif
 		if (seg == NULL)
 			goto out_err;
 		*pseg = seg;
diff --git a/kernel/fork.c b/kernel/fork.c
index 2845623..519d6b6 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -187,8 +187,13 @@ static void free_thread_info(struct thread_info *ti)
 
 void thread_info_cache_init(void)
 {
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+	thread_info_cache = kmem_cache_create("thread_info", THREAD_SIZE,
+					      THREAD_SIZE, SLAB_USERCOPY, NULL);
+#else /* CONFIG_HARDUSERCOPY_SLABS */
 	thread_info_cache = kmem_cache_create("thread_info", THREAD_SIZE,
 					      THREAD_SIZE, 0, NULL);
+#endif /* CONFIG_HARDUSERCOPY_SLABS */
 	BUG_ON(thread_info_cache == NULL);
 }
 # endif
diff --git a/mm/slab.c b/mm/slab.c
index 4fcc5dd..4207a19 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -1451,8 +1451,14 @@ void __init kmem_cache_init(void)
 	 * Initialize the caches that provide memory for the  kmem_cache_node
 	 * structures first.  Without this, further allocations will bug.
 	 */
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+	kmalloc_caches[INDEX_NODE] = create_kmalloc_cache("kmalloc-node",
+				kmalloc_size(INDEX_NODE),
+				SLAB_USERCOPY | ARCH_KMALLOC_FLAGS);
+#else /* CONFIG_HARDUSERCOPY_SLABS */
 	kmalloc_caches[INDEX_NODE] = create_kmalloc_cache("kmalloc-node",
 				kmalloc_size(INDEX_NODE), ARCH_KMALLOC_FLAGS);
+#endif /* CONFIG_HARDUSERCOPY_SLABS */
 	slab_state = PARTIAL_NODE;
 	setup_kmalloc_cache_index_table();
 
@@ -4238,6 +4244,39 @@ static int __init slab_proc_init(void)
 module_init(slab_proc_init);
 #endif
 
+#ifdef CONFIG_HARDUSERCOPY
+const char *check_heap_object(const void *ptr, unsigned long n)
+{
+	struct page *page;
+	struct kmem_cache *cachep;
+	unsigned int objnr;
+	unsigned long offset;
+
+	if (ZERO_OR_NULL_PTR(ptr))
+		return "<null>";
+
+	if (!virt_addr_valid(ptr))
+		return NULL;
+
+	page = virt_to_head_page(ptr);
+
+	if (!PageSlab(page))
+		return NULL;
+
+	cachep = page->slab_cache;
+	if (!(cachep->flags & SLAB_USERCOPY))
+		return cachep->name;
+
+	objnr = obj_to_index(cachep, page, ptr);
+	BUG_ON(objnr >= cachep->num);
+	offset = ptr - index_to_obj(cachep, page, objnr) - obj_offset(cachep);
+	if (offset <= cachep->object_size && n <= cachep->object_size - offset)
+		return NULL;
+
+	return cachep->name;
+}
+#endif /* CONFIG_HARDUSERCOPY */
+
 /**
  * ksize - get the actual amount of memory allocated for a given object
  * @objp: Pointer to the object
diff --git a/mm/slab.h b/mm/slab.h
index a3a967d..d5307a8 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -114,8 +114,14 @@ static inline unsigned long kmem_cache_flags(unsigned long object_size,
 
 
 /* Legal flag mask for kmem_cache_create(), for various configurations */
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+#define SLAB_CORE_FLAGS (SLAB_HWCACHE_ALIGN | SLAB_CACHE_DMA | SLAB_PANIC | \
+			 SLAB_DESTROY_BY_RCU | SLAB_DEBUG_OBJECTS | \
+			 SLAB_USERCOPY)
+#else
 #define SLAB_CORE_FLAGS (SLAB_HWCACHE_ALIGN | SLAB_CACHE_DMA | SLAB_PANIC | \
 			 SLAB_DESTROY_BY_RCU | SLAB_DEBUG_OBJECTS )
+#endif
 
 #if defined(CONFIG_DEBUG_SLAB)
 #define SLAB_DEBUG_FLAGS (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER)
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 5ce4fae..655cc17d 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -43,7 +43,11 @@ struct kmem_cache *kmem_cache;
  * Merge control. If this is set then no merging of slab caches will occur.
  * (Could be removed. This was introduced to pacify the merge skeptics.)
  */
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+static int slab_nomerge = 1;
+#else
 static int slab_nomerge;
+#endif
 
 static int __init setup_slab_nomerge(char *str)
 {
@@ -741,6 +745,11 @@ struct kmem_cache *kmalloc_dma_caches[KMALLOC_SHIFT_HIGH + 1];
 EXPORT_SYMBOL(kmalloc_dma_caches);
 #endif
 
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+struct kmem_cache *kmalloc_usercopy_caches[KMALLOC_SHIFT_HIGH + 1];
+EXPORT_SYMBOL(kmalloc_usercopy_caches);
+#endif /* CONFIG_HARDUSERCOPY_SLABS */
+
 /*
  * Conversion table for small slabs sizes / 8 to the index in the
  * kmalloc array. This is necessary for slabs < 192 since we have non power
@@ -805,6 +814,11 @@ struct kmem_cache *kmalloc_slab(size_t size, gfp_t flags)
 		return kmalloc_dma_caches[index];
 
 #endif
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+	if (unlikely((flags & GFP_USERCOPY)))
+		return kmalloc_usercopy_caches[index];
+#endif /* CONFIG_HARDUSERCOPY_SLABS */
+
 	return kmalloc_caches[index];
 }
 
@@ -897,7 +911,11 @@ void __init create_kmalloc_caches(unsigned long flags)
 
 	for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) {
 		if (!kmalloc_caches[i])
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+			new_kmalloc_cache(i, SLAB_USERCOPY | flags);
+#else
 			new_kmalloc_cache(i, flags);
+#endif
 
 		/*
 		 * Caches that are not of the two-to-the-power-of size.
@@ -905,9 +923,17 @@ void __init create_kmalloc_caches(unsigned long flags)
 		 * earlier power of two caches
 		 */
 		if (KMALLOC_MIN_SIZE <= 32 && !kmalloc_caches[1] && i == 6)
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+			new_kmalloc_cache(1, SLAB_USERCOPY | flags);
+#else
 			new_kmalloc_cache(1, flags);
+#endif
 		if (KMALLOC_MIN_SIZE <= 64 && !kmalloc_caches[2] && i == 7)
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+			new_kmalloc_cache(2, SLAB_USERCOPY | flags);
+#else
 			new_kmalloc_cache(2, flags);
+#endif
 	}
 
 	/* Kmalloc array is now usable */
@@ -928,6 +954,22 @@ void __init create_kmalloc_caches(unsigned long flags)
 		}
 	}
 #endif
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+	for (i = 0; i <= KMALLOC_SHIFT_HIGH; i++) {
+		struct kmem_cache *s = kmalloc_caches[i];
+
+		if (s) {
+			int size = kmalloc_size(i);
+			char *n = kasprintf(GFP_NOWAIT,
+				 "usercopy-kmalloc-%d", size);
+
+			BUG_ON(!n);
+			kmalloc_usercopy_caches[i] = create_kmalloc_cache(n,
+				size, SLAB_USERCOPY | flags);
+		}
+        }
+#endif /* CONFIG_HARDUSERCOPY_SLABS*/
+
 }
 #endif /* !CONFIG_SLOB */
 
diff --git a/mm/slob.c b/mm/slob.c
index 0d7e5df..554cc69 100644
--- a/mm/slob.c
+++ b/mm/slob.c
@@ -501,6 +501,67 @@ void kfree(const void *block)
 }
 EXPORT_SYMBOL(kfree);
 
+#ifdef CONFIG_HARDUSERCOPY
+const char *check_heap_object(const void *ptr, unsigned long n)
+{
+	struct page *page;
+	const slob_t *free;
+	const void *base;
+	unsigned long flags;
+
+	if (ZERO_OR_NULL_PTR(ptr))
+		return "<null>";
+
+	if (!virt_addr_valid(ptr))
+		return NULL;
+
+	page = virt_to_head_page(ptr);
+	if (!PageSlab(page))
+		return NULL;
+
+	if (page->private) {
+		base = page;
+		if (base <= ptr && n <= page->private - (ptr - base))
+			return NULL;
+		return "<slob>";
+	}
+
+	/* some tricky double walking to find the chunk */
+	spin_lock_irqsave(&slob_lock, flags);
+	base = (void *)((unsigned long)ptr & PAGE_MASK);
+	free = page->freelist;
+
+	while (!slob_last(free) && (void *)free <= ptr) {
+		base = free + slob_units(free);
+		free = slob_next(free);
+	}
+
+	while (base < (void *)free) {
+		slobidx_t m = ((slob_t *)base)[0].units, align = ((slob_t *)base)[1].units;
+		int size = SLOB_UNIT * SLOB_UNITS(m + align);
+		int offset;
+
+		if (ptr < base + align)
+			break;
+
+		offset = ptr - base - align;
+		if (offset >= m) {
+			base += size;
+			continue;
+		}
+
+		if (n > m - offset)
+			break;
+
+		spin_unlock_irqrestore(&slob_lock, flags);
+		return NULL;
+	}
+
+	spin_unlock_irqrestore(&slob_lock, flags);
+	return "<slob>";
+}
+#endif /* CONFIG_HARDUSERCOPY */
+
 /* can't use ksize for kmem_cache_alloc memory, only kmalloc */
 size_t ksize(const void *block)
 {
@@ -532,6 +593,53 @@ int __kmem_cache_create(struct kmem_cache *c, unsigned long flags)
 	return 0;
 }
 
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+static __always_inline void *
+__do_kmalloc_node_align(size_t size, gfp_t gfp, int node, unsigned long caller, int align)
+{
+	slob_t *m;
+	void *ret = NULL;
+
+	gfp &= gfp_allowed_mask;
+
+	lockdep_trace_alloc(gfp);
+
+	if (size < PAGE_SIZE - align) {
+		if (!size)
+			return ZERO_SIZE_PTR;
+
+		m = slob_alloc(size + align, gfp, align, node);
+
+		if (!m)
+			return NULL;
+		BUILD_BUG_ON(ARCH_KMALLOC_MINALIGN < 2 * SLOB_UNIT);
+		BUILD_BUG_ON(ARCH_SLAB_MINALIGN < 2 * SLOB_UNIT);
+		m[0].units = size;
+		m[1].units = align;
+		ret = (void *)m + align;
+
+		trace_kmalloc_node(caller, ret,
+				   size, size + align, gfp, node);
+	} else {
+		unsigned int order = get_order(size);
+		struct page *page;
+
+		if (likely(order))
+			gfp |= __GFP_COMP;
+		page = slob_new_pages(gfp, order, node);
+		if (page) {
+			ret = page_address(page);
+			page->private = size;
+		}
+
+		trace_kmalloc_node(caller, ret,
+				   size, PAGE_SIZE << order, gfp, node);
+	}
+
+	return ret;
+}
+#endif /* CONFIG_HARDUSERCOPY_SLABS */
+
 static void *slob_alloc_node(struct kmem_cache *c, gfp_t flags, int node)
 {
 	void *b;
@@ -540,6 +648,10 @@ static void *slob_alloc_node(struct kmem_cache *c, gfp_t flags, int node)
 
 	lockdep_trace_alloc(flags);
 
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+	b = __do_kmalloc_node_align(c->size, flags, node, _RET_IP_, c->align);
+#else /* CONFIG_HARDUSERCOPY_SLABS */
+
 	if (c->size < PAGE_SIZE) {
 		b = slob_alloc(c->size, flags, c->align, node);
 		trace_kmem_cache_alloc_node(_RET_IP_, b, c->object_size,
@@ -551,6 +663,7 @@ static void *slob_alloc_node(struct kmem_cache *c, gfp_t flags, int node)
 					    PAGE_SIZE << get_order(c->size),
 					    flags, node);
 	}
+#endif /* CONFIG_HARDUSERCOPY_SLABS */
 
 	if (b && c->ctor)
 		c->ctor(b);
@@ -597,6 +710,15 @@ static void kmem_rcu_free(struct rcu_head *head)
 
 void kmem_cache_free(struct kmem_cache *c, void *b)
 {
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+	int size = c->size;
+
+	if (size + c->align < PAGE_SIZE) {
+		size += c->align;
+		b -= c->align;
+	}
+#endif /* CONFIG_HARDUSERCOPY_SLABS */
+
 	kmemleak_free_recursive(b, c->flags);
 	if (unlikely(c->flags & SLAB_DESTROY_BY_RCU)) {
 		struct slob_rcu *slob_rcu;
@@ -607,7 +729,11 @@ void kmem_cache_free(struct kmem_cache *c, void *b)
 		__kmem_cache_free(b, c->size);
 	}
 
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+	trace_kfree(_RET_IP_, b);
+#else /* CONFIG_HARDUSERCOPY_SLABS */
 	trace_kmem_cache_free(_RET_IP_, b);
+#endif /* CONFIG_HARDUSERCOPY_SLABS */
 }
 EXPORT_SYMBOL(kmem_cache_free);
 
diff --git a/mm/slub.c b/mm/slub.c
index f614b5d..4ed0635 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -3475,6 +3475,36 @@ void *__kmalloc_node(size_t size, gfp_t flags, int node)
 EXPORT_SYMBOL(__kmalloc_node);
 #endif
 
+#ifdef CONFIG_HARDUSERCOPY
+const char *check_heap_object(const void *ptr, unsigned long n)
+{
+	struct page *page;
+	struct kmem_cache *s;
+	unsigned long offset;
+
+	if (ZERO_OR_NULL_PTR(ptr))
+		return "<null>";
+
+	if (!virt_addr_valid(ptr))
+		return NULL;
+
+	page = virt_to_head_page(ptr);
+
+	if (!PageSlab(page))
+		return NULL;
+
+	s = page->slab_cache;
+	if (!(s->flags & SLAB_USERCOPY))
+		return s->name;
+
+	offset = (ptr - page_address(page)) % s->size;
+	if (offset <= s->object_size && n <= s->object_size - offset)
+		return NULL;
+
+	return s->name;
+}
+#endif /* CONFIG_HARDUSERCOPY */
+
 static size_t __ksize(const void *object)
 {
 	struct page *page;
@@ -4677,6 +4707,14 @@ static ssize_t cache_dma_show(struct kmem_cache *s, char *buf)
 SLAB_ATTR_RO(cache_dma);
 #endif
 
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+static ssize_t usercopy_show(struct kmem_cache *s, char *buf)
+{
+	return sprintf(buf, "%d\n", !!(s->flags & SLAB_USERCOPY));
+}
+SLAB_ATTR_RO(usercopy);
+#endif /* CONFIG_HARDUSERCOPY_SLABS */
+
 static ssize_t destroy_by_rcu_show(struct kmem_cache *s, char *buf)
 {
 	return sprintf(buf, "%d\n", !!(s->flags & SLAB_DESTROY_BY_RCU));
@@ -5019,6 +5057,9 @@ static struct attribute *slab_attrs[] = {
 #ifdef CONFIG_ZONE_DMA
 	&cache_dma_attr.attr,
 #endif
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+	&usercopy_attr.attr,
+#endif /* CONFIG_HARDUSERCOPY_SLABS */
 #ifdef CONFIG_NUMA
 	&remote_node_defrag_ratio_attr.attr,
 #endif
@@ -5284,6 +5325,7 @@ static int sysfs_slab_add(struct kmem_cache *s)
 
 	s->kobj.kset = cache_kset(s);
 	err = kobject_init_and_add(&s->kobj, &slab_ktype, NULL, "%s", name);
+
 	if (err)
 		goto out;
 
diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
index 675cf94..f8d2803 100644
--- a/net/decnet/af_decnet.c
+++ b/net/decnet/af_decnet.c
@@ -466,6 +466,9 @@ static struct proto dn_proto = {
 	.sysctl_rmem		= sysctl_decnet_rmem,
 	.max_header		= DN_MAX_NSP_DATA_HEADER + 64,
 	.obj_size		= sizeof(struct dn_sock),
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+	.slab_flags		= SLAB_USERCOPY,
+#endif
 };
 
 static struct sock *dn_alloc_sock(struct net *net, struct socket *sock, gfp_t gfp, int kern)
diff --git a/security/Kconfig b/security/Kconfig
index e452378..476f203 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -118,6 +118,21 @@ config LSM_MMAP_MIN_ADDR
 	  this low address space will need the permission specific to the
 	  systems running LSM.
 
+config HARDUSERCOPY_SLABS
+	bool "Memory set correctly for harden user copy"
+	default n
+	help
+	  Just for debug now.
+	  If you are unsure as to whether this is required, answer N.
+
+config HARDUSERCOPY
+	bool "Harden user copy"
+	select HARDUSERCOPY_SLABS
+	default n
+	help
+	  Just for debug now.
+	  If you are unsure as to whether this is required, answer N.
+
 source security/selinux/Kconfig
 source security/smack/Kconfig
 source security/tomoyo/Kconfig
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 8db1d93..6d479c1 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -3560,8 +3560,13 @@ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align,
 	/* A kmem cache lets us meet the alignment requirements of fx_save. */
 	if (!vcpu_align)
 		vcpu_align = __alignof__(struct kvm_vcpu);
+#ifdef CONFIG_HARDUSERCOPY_SLABS
+	kvm_vcpu_cache = kmem_cache_create("kvm_vcpu", vcpu_size, vcpu_align,
+					   SLAB_USERCOPY, NULL);
+#else
 	kvm_vcpu_cache = kmem_cache_create("kvm_vcpu", vcpu_size, vcpu_align,
 					   0, NULL);
+#endif
 	if (!kvm_vcpu_cache) {
 		r = -ENOMEM;
 		goto out_free_3;
-- 
2.1.4

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.