|
Message-ID: <CAGXu5jJMqKKuq0F=RTBvx-8sxqB0FXxtMFbVtQUyhTdGidAD9g@mail.gmail.com> Date: Fri, 20 May 2016 11:22:43 -0700 From: Kees Cook <keescook@...omium.org> To: "kernel-hardening@...ts.openwall.com" <kernel-hardening@...ts.openwall.com> Cc: Casey Schaufler <casey.schaufler@...el.com> Subject: Re: [PATCH] Subject: [RFC PATCH] mm: Hardened usercopy On Thu, May 19, 2016 at 9:14 AM, <casey.schaufler@...el.com> wrote: > 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. So, we just need to add some tests to lkdtm to try stuff like: user_addr = vm_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, 0); one = kmalloc(1024, GFP_KERNEL); two = kmalloc(1024, GFP_KERNEL); memset(one, 'A', 1024); memset(two, 'B', 1024); copy_to_user(user_addr, one, 2048); This should explode, since "one" isn't 2048 long. And for stack frames, we could construct a call chain like: noinline int callee(char * __user buf, unsigned long junk) { unsigned long stuff = junk * junk; return copy_to_user(buf, &stuff, 1024); } int caller(void) { user_addr = vm_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, 0); callee(user_addr, (unsigned long)user_addr); } The copy_to_user exceeds the size of the stack frame on callee and should explode. > > 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. As already suggested, I think it's better to hide the ifdefs in the header files and leave the new function calls in the routines. > - 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. Were there any thing you had to leave out, or were otherwise tricky to extract? > - Attestation. How best to ensure the the original > authors get what they deserve. I tend to keep it simple and direct. I usually end up with things that would read like this: Based on PAX_USERCOPY from PaX Team. > > 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); Are these safe from the gcc bug that has wrecked CONFIG_DEBUG_STRICT_USER_COPY_CHECKS? http://www.openwall.com/lists/kernel-hardening/2015/11/20/2 > + 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", > + ¤t->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. While still RFC, this help should be filled in. The PaX config help explains it pretty well. (Though I would expand on the stack checking details: under what situations does it not work?) I wonder if this should be called "STRICT_USERCOPY" or "CHECKED_USERCOPY"? Hilariously we already have a DEBUG_STRICT_USER_COPY_CHECKS, which doesn't work due to compiler bugs. Notes about the slab merging would be good too. > + > 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 > -Kees -- Kees Cook Chrome OS & Brillo Security
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.