Commit 1771c6e1 authored by Andrey Ryabinin's avatar Andrey Ryabinin Committed by Linus Torvalds

x86/kasan: instrument user memory access API

Exchange between user and kernel memory is coded in assembly language.
Which means that such accesses won't be spotted by KASAN as a compiler
instruments only C code.

Add explicit KASAN checks to user memory access API to ensure that
userspace writes to (or reads from) a valid kernel memory.

Note: Unlike others strncpy_from_user() is written mostly in C and KASAN
sees memory accesses in it.  However, it makes sense to add explicit
check for all @count bytes that *potentially* could be written to the
kernel.

[aryabinin@virtuozzo.com: move kasan check under the condition]
  Link: http://lkml.kernel.org/r/1462869209-21096-1-git-send-email-aryabinin@virtuozzo.com
Link: http://lkml.kernel.org/r/1462538722-1574-4-git-send-email-aryabinin@virtuozzo.comSigned-off-by: default avatarAndrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 64f8ebaf
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/kasan-checks.h>
#include <linux/thread_info.h> #include <linux/thread_info.h>
#include <linux/string.h> #include <linux/string.h>
#include <asm/asm.h> #include <asm/asm.h>
...@@ -721,6 +722,8 @@ copy_from_user(void *to, const void __user *from, unsigned long n) ...@@ -721,6 +722,8 @@ copy_from_user(void *to, const void __user *from, unsigned long n)
might_fault(); might_fault();
kasan_check_write(to, n);
/* /*
* While we would like to have the compiler do the checking for us * While we would like to have the compiler do the checking for us
* even in the non-constant size case, any false positives there are * even in the non-constant size case, any false positives there are
...@@ -754,6 +757,8 @@ copy_to_user(void __user *to, const void *from, unsigned long n) ...@@ -754,6 +757,8 @@ copy_to_user(void __user *to, const void *from, unsigned long n)
{ {
int sz = __compiletime_object_size(from); int sz = __compiletime_object_size(from);
kasan_check_read(from, n);
might_fault(); might_fault();
/* See the comment in copy_from_user() above. */ /* See the comment in copy_from_user() above. */
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/lockdep.h> #include <linux/lockdep.h>
#include <linux/kasan-checks.h>
#include <asm/alternative.h> #include <asm/alternative.h>
#include <asm/cpufeatures.h> #include <asm/cpufeatures.h>
#include <asm/page.h> #include <asm/page.h>
...@@ -109,6 +110,7 @@ static __always_inline __must_check ...@@ -109,6 +110,7 @@ static __always_inline __must_check
int __copy_from_user(void *dst, const void __user *src, unsigned size) int __copy_from_user(void *dst, const void __user *src, unsigned size)
{ {
might_fault(); might_fault();
kasan_check_write(dst, size);
return __copy_from_user_nocheck(dst, src, size); return __copy_from_user_nocheck(dst, src, size);
} }
...@@ -175,6 +177,7 @@ static __always_inline __must_check ...@@ -175,6 +177,7 @@ static __always_inline __must_check
int __copy_to_user(void __user *dst, const void *src, unsigned size) int __copy_to_user(void __user *dst, const void *src, unsigned size)
{ {
might_fault(); might_fault();
kasan_check_read(src, size);
return __copy_to_user_nocheck(dst, src, size); return __copy_to_user_nocheck(dst, src, size);
} }
...@@ -242,12 +245,14 @@ int __copy_in_user(void __user *dst, const void __user *src, unsigned size) ...@@ -242,12 +245,14 @@ int __copy_in_user(void __user *dst, const void __user *src, unsigned size)
static __must_check __always_inline int static __must_check __always_inline int
__copy_from_user_inatomic(void *dst, const void __user *src, unsigned size) __copy_from_user_inatomic(void *dst, const void __user *src, unsigned size)
{ {
kasan_check_write(dst, size);
return __copy_from_user_nocheck(dst, src, size); return __copy_from_user_nocheck(dst, src, size);
} }
static __must_check __always_inline int static __must_check __always_inline int
__copy_to_user_inatomic(void __user *dst, const void *src, unsigned size) __copy_to_user_inatomic(void __user *dst, const void *src, unsigned size)
{ {
kasan_check_read(src, size);
return __copy_to_user_nocheck(dst, src, size); return __copy_to_user_nocheck(dst, src, size);
} }
...@@ -258,6 +263,7 @@ static inline int ...@@ -258,6 +263,7 @@ static inline int
__copy_from_user_nocache(void *dst, const void __user *src, unsigned size) __copy_from_user_nocache(void *dst, const void __user *src, unsigned size)
{ {
might_fault(); might_fault();
kasan_check_write(dst, size);
return __copy_user_nocache(dst, src, size, 1); return __copy_user_nocache(dst, src, size, 1);
} }
...@@ -265,6 +271,7 @@ static inline int ...@@ -265,6 +271,7 @@ static inline int
__copy_from_user_inatomic_nocache(void *dst, const void __user *src, __copy_from_user_inatomic_nocache(void *dst, const void __user *src,
unsigned size) unsigned size)
{ {
kasan_check_write(dst, size);
return __copy_user_nocache(dst, src, size, 0); return __copy_user_nocache(dst, src, size, 0);
} }
......
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/kasan-checks.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/errno.h> #include <linux/errno.h>
...@@ -109,6 +110,7 @@ long strncpy_from_user(char *dst, const char __user *src, long count) ...@@ -109,6 +110,7 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
unsigned long max = max_addr - src_addr; unsigned long max = max_addr - src_addr;
long retval; long retval;
kasan_check_write(dst, count);
user_access_begin(); user_access_begin();
retval = do_strncpy_from_user(dst, src, count, max); retval = do_strncpy_from_user(dst, src, count, max);
user_access_end(); user_access_end();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment