linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/4] kasan/tests: add tests for user memory access functions
@ 2016-05-06 12:45 Andrey Ryabinin
  2016-05-06 12:45 ` [PATCH 2/4] mm/kasan: print name of mem[set,cpy,move]() caller in report Andrey Ryabinin
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Andrey Ryabinin @ 2016-05-06 12:45 UTC (permalink / raw)
  To: Andrew Morton
  Cc: kasan-dev, linux-mm, linux-kernel, Andrey Ryabinin,
	Alexander Potapenko, Dmitry Vyukov

This patch adds some tests for user memory access API.
KASAN doesn't pass these tests yet, but follow on patches will fix that.

Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
---
 lib/test_kasan.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/lib/test_kasan.c b/lib/test_kasan.c
index bd75a03..c640fdb 100644
--- a/lib/test_kasan.c
+++ b/lib/test_kasan.c
@@ -12,9 +12,12 @@
 #define pr_fmt(fmt) "kasan test: %s " fmt, __func__
 
 #include <linux/kernel.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
 #include <linux/printk.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/uaccess.h>
 #include <linux/module.h>
 
 static noinline void __init kmalloc_oob_right(void)
@@ -389,6 +392,51 @@ static noinline void __init ksize_unpoisons_memory(void)
 	kfree(ptr);
 }
 
+static noinline void __init copy_user_test(void)
+{
+	char *kmem;
+	char __user *usermem;
+	size_t size = 10;
+	int unused;
+
+	kmem = kmalloc(size, GFP_KERNEL);
+	if (!kmem)
+		return;
+
+	usermem = (char __user *)vm_mmap(NULL, 0, PAGE_SIZE,
+			    PROT_READ | PROT_WRITE | PROT_EXEC,
+			    MAP_ANONYMOUS | MAP_PRIVATE, 0);
+	if (IS_ERR(usermem)) {
+		pr_err("Failed to allocate user memory\n");
+		kfree(kmem);
+		return;
+	}
+
+	pr_info("out-of-bounds in copy_from_user()\n");
+	unused = copy_from_user(kmem, usermem, size + 1);
+
+	pr_info("out-of-bounds in copy_to_user()\n");
+	unused = copy_to_user(usermem, kmem, size + 1);
+
+	pr_info("out-of-bounds in __copy_from_user()\n");
+	unused = __copy_from_user(kmem, usermem, size + 1);
+
+	pr_info("out-of-bounds in __copy_to_user()\n");
+	unused = __copy_to_user(usermem, kmem, size + 1);
+
+	pr_info("out-of-bounds in __copy_from_user_inatomic()\n");
+	unused = __copy_from_user_inatomic(kmem, usermem, size + 1);
+
+	pr_info("out-of-bounds in __copy_to_user_inatomic()\n");
+	unused = __copy_to_user_inatomic(usermem, kmem, size + 1);
+
+	pr_info("out-of-bounds in strncpy_from_user()\n");
+	unused = strncpy_from_user(kmem, usermem, size + 1);
+
+	vm_munmap((unsigned long)usermem, PAGE_SIZE);
+	kfree(kmem);
+}
+
 static int __init kmalloc_tests_init(void)
 {
 	kmalloc_oob_right();
@@ -416,6 +464,7 @@ static int __init kmalloc_tests_init(void)
 	kasan_quarantine_cache();
 #endif
 	ksize_unpoisons_memory();
+	copy_user_test();
 	return -EAGAIN;
 }
 
-- 
2.7.3

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/4] mm/kasan: print name of mem[set,cpy,move]() caller in report
  2016-05-06 12:45 [PATCH 1/4] kasan/tests: add tests for user memory access functions Andrey Ryabinin
@ 2016-05-06 12:45 ` Andrey Ryabinin
  2016-05-13 12:15   ` Alexander Potapenko
  2016-05-06 12:45 ` [PATCH 3/4] mm/kasan: add API to check memory regions Andrey Ryabinin
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 10+ messages in thread
From: Andrey Ryabinin @ 2016-05-06 12:45 UTC (permalink / raw)
  To: Andrew Morton
  Cc: kasan-dev, linux-mm, linux-kernel, Andrey Ryabinin,
	Alexander Potapenko, Dmitry Vyukov

When bogus memory access happens in mem[set,cpy,move]() it's usually
caller's fault. So don't blame mem[set,cpy,move]() in bug report, blame
the caller instead.

Before:
	BUG: KASAN: out-of-bounds access in memset+0x23/0x40 at <address>
After:
	BUG: KASAN: out-of-bounds access in <memset_caller> at <address>

Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
---
 mm/kasan/kasan.c | 64 ++++++++++++++++++++++++++++++--------------------------
 1 file changed, 34 insertions(+), 30 deletions(-)

diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
index ef2e87b..6e4072c 100644
--- a/mm/kasan/kasan.c
+++ b/mm/kasan/kasan.c
@@ -273,32 +273,36 @@ static __always_inline bool memory_is_poisoned(unsigned long addr, size_t size)
 	return memory_is_poisoned_n(addr, size);
 }
 
-
-static __always_inline void check_memory_region(unsigned long addr,
-						size_t size, bool write)
+static __always_inline void check_memory_region_inline(unsigned long addr,
+						size_t size, bool write,
+						unsigned long ret_ip)
 {
 	if (unlikely(size == 0))
 		return;
 
 	if (unlikely((void *)addr <
 		kasan_shadow_to_mem((void *)KASAN_SHADOW_START))) {
-		kasan_report(addr, size, write, _RET_IP_);
+		kasan_report(addr, size, write, ret_ip);
 		return;
 	}
 
 	if (likely(!memory_is_poisoned(addr, size)))
 		return;
 
-	kasan_report(addr, size, write, _RET_IP_);
+	kasan_report(addr, size, write, ret_ip);
 }
 
-void __asan_loadN(unsigned long addr, size_t size);
-void __asan_storeN(unsigned long addr, size_t size);
+static void check_memory_region(unsigned long addr,
+				size_t size, bool write,
+				unsigned long ret_ip)
+{
+	check_memory_region_inline(addr, size, write, ret_ip);
+}
 
 #undef memset
 void *memset(void *addr, int c, size_t len)
 {
-	__asan_storeN((unsigned long)addr, len);
+	check_memory_region((unsigned long)addr, len, true, _RET_IP_);
 
 	return __memset(addr, c, len);
 }
@@ -306,8 +310,8 @@ void *memset(void *addr, int c, size_t len)
 #undef memmove
 void *memmove(void *dest, const void *src, size_t len)
 {
-	__asan_loadN((unsigned long)src, len);
-	__asan_storeN((unsigned long)dest, len);
+	check_memory_region((unsigned long)src, len, false, _RET_IP_);
+	check_memory_region((unsigned long)dest, len, true, _RET_IP_);
 
 	return __memmove(dest, src, len);
 }
@@ -315,8 +319,8 @@ void *memmove(void *dest, const void *src, size_t len)
 #undef memcpy
 void *memcpy(void *dest, const void *src, size_t len)
 {
-	__asan_loadN((unsigned long)src, len);
-	__asan_storeN((unsigned long)dest, len);
+	check_memory_region((unsigned long)src, len, false, _RET_IP_);
+	check_memory_region((unsigned long)dest, len, true, _RET_IP_);
 
 	return __memcpy(dest, src, len);
 }
@@ -698,22 +702,22 @@ void __asan_unregister_globals(struct kasan_global *globals, size_t size)
 }
 EXPORT_SYMBOL(__asan_unregister_globals);
 
-#define DEFINE_ASAN_LOAD_STORE(size)				\
-	void __asan_load##size(unsigned long addr)		\
-	{							\
-		check_memory_region(addr, size, false);		\
-	}							\
-	EXPORT_SYMBOL(__asan_load##size);			\
-	__alias(__asan_load##size)				\
-	void __asan_load##size##_noabort(unsigned long);	\
-	EXPORT_SYMBOL(__asan_load##size##_noabort);		\
-	void __asan_store##size(unsigned long addr)		\
-	{							\
-		check_memory_region(addr, size, true);		\
-	}							\
-	EXPORT_SYMBOL(__asan_store##size);			\
-	__alias(__asan_store##size)				\
-	void __asan_store##size##_noabort(unsigned long);	\
+#define DEFINE_ASAN_LOAD_STORE(size)					\
+	void __asan_load##size(unsigned long addr)			\
+	{								\
+		check_memory_region_inline(addr, size, false, _RET_IP_);\
+	}								\
+	EXPORT_SYMBOL(__asan_load##size);				\
+	__alias(__asan_load##size)					\
+	void __asan_load##size##_noabort(unsigned long);		\
+	EXPORT_SYMBOL(__asan_load##size##_noabort);			\
+	void __asan_store##size(unsigned long addr)			\
+	{								\
+		check_memory_region_inline(addr, size, true, _RET_IP_);	\
+	}								\
+	EXPORT_SYMBOL(__asan_store##size);				\
+	__alias(__asan_store##size)					\
+	void __asan_store##size##_noabort(unsigned long);		\
 	EXPORT_SYMBOL(__asan_store##size##_noabort)
 
 DEFINE_ASAN_LOAD_STORE(1);
@@ -724,7 +728,7 @@ DEFINE_ASAN_LOAD_STORE(16);
 
 void __asan_loadN(unsigned long addr, size_t size)
 {
-	check_memory_region(addr, size, false);
+	check_memory_region(addr, size, false, _RET_IP_);
 }
 EXPORT_SYMBOL(__asan_loadN);
 
@@ -734,7 +738,7 @@ EXPORT_SYMBOL(__asan_loadN_noabort);
 
 void __asan_storeN(unsigned long addr, size_t size)
 {
-	check_memory_region(addr, size, true);
+	check_memory_region(addr, size, true, _RET_IP_);
 }
 EXPORT_SYMBOL(__asan_storeN);
 
-- 
2.7.3

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/4] mm/kasan: add API to check memory regions
  2016-05-06 12:45 [PATCH 1/4] kasan/tests: add tests for user memory access functions Andrey Ryabinin
  2016-05-06 12:45 ` [PATCH 2/4] mm/kasan: print name of mem[set,cpy,move]() caller in report Andrey Ryabinin
@ 2016-05-06 12:45 ` Andrey Ryabinin
  2016-05-13 12:18   ` Alexander Potapenko
  2016-05-06 12:45 ` [PATCH 4/4] x86/kasan: Instrument user memory access API Andrey Ryabinin
  2016-05-06 22:48 ` [PATCH 1/4] kasan/tests: add tests for user memory access functions Andrew Morton
  3 siblings, 1 reply; 10+ messages in thread
From: Andrey Ryabinin @ 2016-05-06 12:45 UTC (permalink / raw)
  To: Andrew Morton
  Cc: kasan-dev, linux-mm, linux-kernel, Andrey Ryabinin,
	Alexander Potapenko, Dmitry Vyukov

Memory access coded in an assembly won't be seen by KASAN as a compiler
can instrument only C code. Add kasan_check_[read,write]() API
which is going to be used to check a certain memory range.

Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
---
 MAINTAINERS                  |  2 +-
 include/linux/kasan-checks.h | 12 ++++++++++++
 mm/kasan/kasan.c             | 12 ++++++++++++
 3 files changed, 25 insertions(+), 1 deletion(-)
 create mode 100644 include/linux/kasan-checks.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 43b85c1..3a9471c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6363,7 +6363,7 @@ S:	Maintained
 F:	arch/*/include/asm/kasan.h
 F:	arch/*/mm/kasan_init*
 F:	Documentation/kasan.txt
-F:	include/linux/kasan.h
+F:	include/linux/kasan*.h
 F:	lib/test_kasan.c
 F:	mm/kasan/
 F:	scripts/Makefile.kasan
diff --git a/include/linux/kasan-checks.h b/include/linux/kasan-checks.h
new file mode 100644
index 0000000..b7f8ace
--- /dev/null
+++ b/include/linux/kasan-checks.h
@@ -0,0 +1,12 @@
+#ifndef _LINUX_KASAN_CHECKS_H
+#define _LINUX_KASAN_CHECKS_H
+
+#ifdef CONFIG_KASAN
+void kasan_check_read(const void *p, unsigned int size);
+void kasan_check_write(const void *p, unsigned int size);
+#else
+static inline void kasan_check_read(const void *p, unsigned int size) { }
+static inline void kasan_check_write(const void *p, unsigned int size) { }
+#endif
+
+#endif
diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
index 6e4072c..54f0ea7 100644
--- a/mm/kasan/kasan.c
+++ b/mm/kasan/kasan.c
@@ -299,6 +299,18 @@ static void check_memory_region(unsigned long addr,
 	check_memory_region_inline(addr, size, write, ret_ip);
 }
 
+void kasan_check_read(const void *p, unsigned int size)
+{
+	check_memory_region((unsigned long)p, size, false, _RET_IP_);
+}
+EXPORT_SYMBOL(kasan_check_read);
+
+void kasan_check_write(const void *p, unsigned int size)
+{
+	check_memory_region((unsigned long)p, size, true, _RET_IP_);
+}
+EXPORT_SYMBOL(kasan_check_write);
+
 #undef memset
 void *memset(void *addr, int c, size_t len)
 {
-- 
2.7.3

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 4/4] x86/kasan: Instrument user memory access API
  2016-05-06 12:45 [PATCH 1/4] kasan/tests: add tests for user memory access functions Andrey Ryabinin
  2016-05-06 12:45 ` [PATCH 2/4] mm/kasan: print name of mem[set,cpy,move]() caller in report Andrey Ryabinin
  2016-05-06 12:45 ` [PATCH 3/4] mm/kasan: add API to check memory regions Andrey Ryabinin
@ 2016-05-06 12:45 ` Andrey Ryabinin
  2016-05-09  5:08   ` Dmitry Vyukov
  2016-05-09  6:29   ` Ingo Molnar
  2016-05-06 22:48 ` [PATCH 1/4] kasan/tests: add tests for user memory access functions Andrew Morton
  3 siblings, 2 replies; 10+ messages in thread
From: Andrey Ryabinin @ 2016-05-06 12:45 UTC (permalink / raw)
  To: Andrew Morton
  Cc: kasan-dev, linux-mm, linux-kernel, Andrey Ryabinin,
	Alexander Potapenko, Dmitry Vyukov, x86

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.

Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: x86@kernel.org
---
 arch/x86/include/asm/uaccess.h    | 5 +++++
 arch/x86/include/asm/uaccess_64.h | 7 +++++++
 lib/strncpy_from_user.c           | 2 ++
 3 files changed, 14 insertions(+)

diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index 0b17fad..5dd6d18 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -5,6 +5,7 @@
  */
 #include <linux/errno.h>
 #include <linux/compiler.h>
+#include <linux/kasan-checks.h>
 #include <linux/thread_info.h>
 #include <linux/string.h>
 #include <asm/asm.h>
@@ -732,6 +733,8 @@ copy_from_user(void *to, const void __user *from, unsigned long n)
 
 	might_fault();
 
+	kasan_check_write(to, n);
+
 	/*
 	 * 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
@@ -765,6 +768,8 @@ copy_to_user(void __user *to, const void *from, unsigned long n)
 {
 	int sz = __compiletime_object_size(from);
 
+	kasan_check_read(from, n);
+
 	might_fault();
 
 	/* See the comment in copy_from_user() above. */
diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h
index 3076986..2eac2aa 100644
--- a/arch/x86/include/asm/uaccess_64.h
+++ b/arch/x86/include/asm/uaccess_64.h
@@ -7,6 +7,7 @@
 #include <linux/compiler.h>
 #include <linux/errno.h>
 #include <linux/lockdep.h>
+#include <linux/kasan-checks.h>
 #include <asm/alternative.h>
 #include <asm/cpufeatures.h>
 #include <asm/page.h>
@@ -109,6 +110,7 @@ static __always_inline __must_check
 int __copy_from_user(void *dst, const void __user *src, unsigned size)
 {
 	might_fault();
+	kasan_check_write(dst, size);
 	return __copy_from_user_nocheck(dst, src, size);
 }
 
@@ -175,6 +177,7 @@ static __always_inline __must_check
 int __copy_to_user(void __user *dst, const void *src, unsigned size)
 {
 	might_fault();
+	kasan_check_read(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)
 static __must_check __always_inline int
 __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);
 }
 
 static __must_check __always_inline int
 __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);
 }
 
@@ -258,6 +263,7 @@ static inline int
 __copy_from_user_nocache(void *dst, const void __user *src, unsigned size)
 {
 	might_fault();
+	kasan_check_write(dst, size);
 	return __copy_user_nocache(dst, src, size, 1);
 }
 
@@ -265,6 +271,7 @@ static inline int
 __copy_from_user_inatomic_nocache(void *dst, const void __user *src,
 				  unsigned size)
 {
+	kasan_check_write(dst, size);
 	return __copy_user_nocache(dst, src, size, 0);
 }
 
diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c
index 3384032..e3472b0 100644
--- a/lib/strncpy_from_user.c
+++ b/lib/strncpy_from_user.c
@@ -1,5 +1,6 @@
 #include <linux/compiler.h>
 #include <linux/export.h>
+#include <linux/kasan-checks.h>
 #include <linux/uaccess.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -103,6 +104,7 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
 	if (unlikely(count <= 0))
 		return 0;
 
+	kasan_check_write(dst, count);
 	max_addr = user_addr_max();
 	src_addr = (unsigned long)src;
 	if (likely(src_addr < max_addr)) {
-- 
2.7.3

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/4] kasan/tests: add tests for user memory access functions
  2016-05-06 12:45 [PATCH 1/4] kasan/tests: add tests for user memory access functions Andrey Ryabinin
                   ` (2 preceding siblings ...)
  2016-05-06 12:45 ` [PATCH 4/4] x86/kasan: Instrument user memory access API Andrey Ryabinin
@ 2016-05-06 22:48 ` Andrew Morton
  3 siblings, 0 replies; 10+ messages in thread
From: Andrew Morton @ 2016-05-06 22:48 UTC (permalink / raw)
  To: Andrey Ryabinin
  Cc: kasan-dev, linux-mm, linux-kernel, Alexander Potapenko, Dmitry Vyukov

On Fri, 6 May 2016 15:45:19 +0300 Andrey Ryabinin <aryabinin@virtuozzo.com> wrote:

> This patch adds some tests for user memory access API.
> KASAN doesn't pass these tests yet, but follow on patches will fix that.

I'll move this patch from [1/4] to [4/4] to avoid the minor bisection
hole.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 4/4] x86/kasan: Instrument user memory access API
  2016-05-06 12:45 ` [PATCH 4/4] x86/kasan: Instrument user memory access API Andrey Ryabinin
@ 2016-05-09  5:08   ` Dmitry Vyukov
  2016-05-09  6:29   ` Ingo Molnar
  1 sibling, 0 replies; 10+ messages in thread
From: Dmitry Vyukov @ 2016-05-09  5:08 UTC (permalink / raw)
  To: Andrey Ryabinin
  Cc: Andrew Morton, kasan-dev, linux-mm, LKML, Alexander Potapenko, x86

On Fri, May 6, 2016 at 2:45 PM, Andrey Ryabinin <aryabinin@virtuozzo.com> wrote:
> 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.


Reviewed-by: Dmitry Vyukov <dvyukov@google.com>

Thanks!


> Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
> Cc: Alexander Potapenko <glider@google.com>
> Cc: Dmitry Vyukov <dvyukov@google.com>
> Cc: x86@kernel.org
> ---
>  arch/x86/include/asm/uaccess.h    | 5 +++++
>  arch/x86/include/asm/uaccess_64.h | 7 +++++++
>  lib/strncpy_from_user.c           | 2 ++
>  3 files changed, 14 insertions(+)
>
> diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
> index 0b17fad..5dd6d18 100644
> --- a/arch/x86/include/asm/uaccess.h
> +++ b/arch/x86/include/asm/uaccess.h
> @@ -5,6 +5,7 @@
>   */
>  #include <linux/errno.h>
>  #include <linux/compiler.h>
> +#include <linux/kasan-checks.h>
>  #include <linux/thread_info.h>
>  #include <linux/string.h>
>  #include <asm/asm.h>
> @@ -732,6 +733,8 @@ copy_from_user(void *to, const void __user *from, unsigned long n)
>
>         might_fault();
>
> +       kasan_check_write(to, n);
> +
>         /*
>          * 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
> @@ -765,6 +768,8 @@ copy_to_user(void __user *to, const void *from, unsigned long n)
>  {
>         int sz = __compiletime_object_size(from);
>
> +       kasan_check_read(from, n);
> +
>         might_fault();
>
>         /* See the comment in copy_from_user() above. */
> diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h
> index 3076986..2eac2aa 100644
> --- a/arch/x86/include/asm/uaccess_64.h
> +++ b/arch/x86/include/asm/uaccess_64.h
> @@ -7,6 +7,7 @@
>  #include <linux/compiler.h>
>  #include <linux/errno.h>
>  #include <linux/lockdep.h>
> +#include <linux/kasan-checks.h>
>  #include <asm/alternative.h>
>  #include <asm/cpufeatures.h>
>  #include <asm/page.h>
> @@ -109,6 +110,7 @@ static __always_inline __must_check
>  int __copy_from_user(void *dst, const void __user *src, unsigned size)
>  {
>         might_fault();
> +       kasan_check_write(dst, size);
>         return __copy_from_user_nocheck(dst, src, size);
>  }
>
> @@ -175,6 +177,7 @@ static __always_inline __must_check
>  int __copy_to_user(void __user *dst, const void *src, unsigned size)
>  {
>         might_fault();
> +       kasan_check_read(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)
>  static __must_check __always_inline int
>  __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);
>  }
>
>  static __must_check __always_inline int
>  __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);
>  }
>
> @@ -258,6 +263,7 @@ static inline int
>  __copy_from_user_nocache(void *dst, const void __user *src, unsigned size)
>  {
>         might_fault();
> +       kasan_check_write(dst, size);
>         return __copy_user_nocache(dst, src, size, 1);
>  }
>
> @@ -265,6 +271,7 @@ static inline int
>  __copy_from_user_inatomic_nocache(void *dst, const void __user *src,
>                                   unsigned size)
>  {
> +       kasan_check_write(dst, size);
>         return __copy_user_nocache(dst, src, size, 0);
>  }
>
> diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c
> index 3384032..e3472b0 100644
> --- a/lib/strncpy_from_user.c
> +++ b/lib/strncpy_from_user.c
> @@ -1,5 +1,6 @@
>  #include <linux/compiler.h>
>  #include <linux/export.h>
> +#include <linux/kasan-checks.h>
>  #include <linux/uaccess.h>
>  #include <linux/kernel.h>
>  #include <linux/errno.h>
> @@ -103,6 +104,7 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
>         if (unlikely(count <= 0))
>                 return 0;
>
> +       kasan_check_write(dst, count);
>         max_addr = user_addr_max();
>         src_addr = (unsigned long)src;
>         if (likely(src_addr < max_addr)) {
> --
> 2.7.3
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 4/4] x86/kasan: Instrument user memory access API
  2016-05-06 12:45 ` [PATCH 4/4] x86/kasan: Instrument user memory access API Andrey Ryabinin
  2016-05-09  5:08   ` Dmitry Vyukov
@ 2016-05-09  6:29   ` Ingo Molnar
  2016-05-10  8:33     ` [PATCH] x86-kasan-instrument-user-memory-access-api-fix Andrey Ryabinin
  1 sibling, 1 reply; 10+ messages in thread
From: Ingo Molnar @ 2016-05-09  6:29 UTC (permalink / raw)
  To: Andrey Ryabinin
  Cc: Andrew Morton, kasan-dev, linux-mm, linux-kernel,
	Alexander Potapenko, Dmitry Vyukov, x86


* Andrey Ryabinin <aryabinin@virtuozzo.com> wrote:

> 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.
> 
> Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
> Cc: Alexander Potapenko <glider@google.com>
> Cc: Dmitry Vyukov <dvyukov@google.com>
> Cc: x86@kernel.org
> ---
>  arch/x86/include/asm/uaccess.h    | 5 +++++
>  arch/x86/include/asm/uaccess_64.h | 7 +++++++
>  lib/strncpy_from_user.c           | 2 ++
>  3 files changed, 14 insertions(+)

[...]

> diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c
> index 3384032..e3472b0 100644
> --- a/lib/strncpy_from_user.c
> +++ b/lib/strncpy_from_user.c
> @@ -1,5 +1,6 @@
>  #include <linux/compiler.h>
>  #include <linux/export.h>
> +#include <linux/kasan-checks.h>
>  #include <linux/uaccess.h>
>  #include <linux/kernel.h>
>  #include <linux/errno.h>
> @@ -103,6 +104,7 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
>  	if (unlikely(count <= 0))
>  		return 0;
>  
> +	kasan_check_write(dst, count);
>  	max_addr = user_addr_max();
>  	src_addr = (unsigned long)src;
>  	if (likely(src_addr < max_addr)) {

Please do the check inside the condition, before the user_access_begin(), because 
where you've put the check we might still fail and not do a user copy and -EFAULT 
out.

With that fixed:

Reviewed-by: Ingo Molnar <mingo@kernel.org>

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH] x86-kasan-instrument-user-memory-access-api-fix
  2016-05-09  6:29   ` Ingo Molnar
@ 2016-05-10  8:33     ` Andrey Ryabinin
  0 siblings, 0 replies; 10+ messages in thread
From: Andrey Ryabinin @ 2016-05-10  8:33 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Ingo Molnar, kasan-dev, linux-mm, linux-kernel,
	Alexander Potapenko, Dmitry Vyukov, x86, Andrey Ryabinin

Move kasan check under the condition, otherwise we may fail and not
do a user copy.

Reported-by: Ingo Molnar <mingo@kernel.org>
Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
---
 lib/strncpy_from_user.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c
index e3472b0..33f655e 100644
--- a/lib/strncpy_from_user.c
+++ b/lib/strncpy_from_user.c
@@ -104,13 +104,13 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
 	if (unlikely(count <= 0))
 		return 0;
 
-	kasan_check_write(dst, count);
 	max_addr = user_addr_max();
 	src_addr = (unsigned long)src;
 	if (likely(src_addr < max_addr)) {
 		unsigned long max = max_addr - src_addr;
 		long retval;
 
+		kasan_check_write(dst, count);
 		user_access_begin();
 		retval = do_strncpy_from_user(dst, src, count, max);
 		user_access_end();
-- 
2.7.3

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/4] mm/kasan: print name of mem[set,cpy,move]() caller in report
  2016-05-06 12:45 ` [PATCH 2/4] mm/kasan: print name of mem[set,cpy,move]() caller in report Andrey Ryabinin
@ 2016-05-13 12:15   ` Alexander Potapenko
  0 siblings, 0 replies; 10+ messages in thread
From: Alexander Potapenko @ 2016-05-13 12:15 UTC (permalink / raw)
  To: Andrey Ryabinin
  Cc: Andrew Morton, kasan-dev, Linux Memory Management List, LKML,
	Dmitry Vyukov

On Fri, May 6, 2016 at 2:45 PM, Andrey Ryabinin <aryabinin@virtuozzo.com> wrote:
> When bogus memory access happens in mem[set,cpy,move]() it's usually
> caller's fault. So don't blame mem[set,cpy,move]() in bug report, blame
> the caller instead.
>
> Before:
>         BUG: KASAN: out-of-bounds access in memset+0x23/0x40 at <address>
> After:
>         BUG: KASAN: out-of-bounds access in <memset_caller> at <address>
>
> Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
> Cc: Alexander Potapenko <glider@google.com>
> Cc: Dmitry Vyukov <dvyukov@google.com>
> ---
>  mm/kasan/kasan.c | 64 ++++++++++++++++++++++++++++++--------------------------
>  1 file changed, 34 insertions(+), 30 deletions(-)
>
> diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
> index ef2e87b..6e4072c 100644
> --- a/mm/kasan/kasan.c
> +++ b/mm/kasan/kasan.c
> @@ -273,32 +273,36 @@ static __always_inline bool memory_is_poisoned(unsigned long addr, size_t size)
>         return memory_is_poisoned_n(addr, size);
>  }
>
> -
> -static __always_inline void check_memory_region(unsigned long addr,
> -                                               size_t size, bool write)
> +static __always_inline void check_memory_region_inline(unsigned long addr,
> +                                               size_t size, bool write,
> +                                               unsigned long ret_ip)
>  {
>         if (unlikely(size == 0))
>                 return;
>
>         if (unlikely((void *)addr <
>                 kasan_shadow_to_mem((void *)KASAN_SHADOW_START))) {
> -               kasan_report(addr, size, write, _RET_IP_);
> +               kasan_report(addr, size, write, ret_ip);
>                 return;
>         }
>
>         if (likely(!memory_is_poisoned(addr, size)))
>                 return;
>
> -       kasan_report(addr, size, write, _RET_IP_);
> +       kasan_report(addr, size, write, ret_ip);
>  }
>
> -void __asan_loadN(unsigned long addr, size_t size);
> -void __asan_storeN(unsigned long addr, size_t size);
> +static void check_memory_region(unsigned long addr,
> +                               size_t size, bool write,
> +                               unsigned long ret_ip)
> +{
> +       check_memory_region_inline(addr, size, write, ret_ip);
> +}
>
>  #undef memset
>  void *memset(void *addr, int c, size_t len)
>  {
> -       __asan_storeN((unsigned long)addr, len);
> +       check_memory_region((unsigned long)addr, len, true, _RET_IP_);
>
>         return __memset(addr, c, len);
>  }
> @@ -306,8 +310,8 @@ void *memset(void *addr, int c, size_t len)
>  #undef memmove
>  void *memmove(void *dest, const void *src, size_t len)
>  {
> -       __asan_loadN((unsigned long)src, len);
> -       __asan_storeN((unsigned long)dest, len);
> +       check_memory_region((unsigned long)src, len, false, _RET_IP_);
> +       check_memory_region((unsigned long)dest, len, true, _RET_IP_);
>
>         return __memmove(dest, src, len);
>  }
> @@ -315,8 +319,8 @@ void *memmove(void *dest, const void *src, size_t len)
>  #undef memcpy
>  void *memcpy(void *dest, const void *src, size_t len)
>  {
> -       __asan_loadN((unsigned long)src, len);
> -       __asan_storeN((unsigned long)dest, len);
> +       check_memory_region((unsigned long)src, len, false, _RET_IP_);
> +       check_memory_region((unsigned long)dest, len, true, _RET_IP_);
>
>         return __memcpy(dest, src, len);
>  }
> @@ -698,22 +702,22 @@ void __asan_unregister_globals(struct kasan_global *globals, size_t size)
>  }
>  EXPORT_SYMBOL(__asan_unregister_globals);
>
> -#define DEFINE_ASAN_LOAD_STORE(size)                           \
> -       void __asan_load##size(unsigned long addr)              \
> -       {                                                       \
> -               check_memory_region(addr, size, false);         \
> -       }                                                       \
> -       EXPORT_SYMBOL(__asan_load##size);                       \
> -       __alias(__asan_load##size)                              \
> -       void __asan_load##size##_noabort(unsigned long);        \
> -       EXPORT_SYMBOL(__asan_load##size##_noabort);             \
> -       void __asan_store##size(unsigned long addr)             \
> -       {                                                       \
> -               check_memory_region(addr, size, true);          \
> -       }                                                       \
> -       EXPORT_SYMBOL(__asan_store##size);                      \
> -       __alias(__asan_store##size)                             \
> -       void __asan_store##size##_noabort(unsigned long);       \
> +#define DEFINE_ASAN_LOAD_STORE(size)                                   \
> +       void __asan_load##size(unsigned long addr)                      \
> +       {                                                               \
> +               check_memory_region_inline(addr, size, false, _RET_IP_);\
> +       }                                                               \
> +       EXPORT_SYMBOL(__asan_load##size);                               \
> +       __alias(__asan_load##size)                                      \
> +       void __asan_load##size##_noabort(unsigned long);                \
> +       EXPORT_SYMBOL(__asan_load##size##_noabort);                     \
> +       void __asan_store##size(unsigned long addr)                     \
> +       {                                                               \
> +               check_memory_region_inline(addr, size, true, _RET_IP_); \
> +       }                                                               \
> +       EXPORT_SYMBOL(__asan_store##size);                              \
> +       __alias(__asan_store##size)                                     \
> +       void __asan_store##size##_noabort(unsigned long);               \
>         EXPORT_SYMBOL(__asan_store##size##_noabort)
>
>  DEFINE_ASAN_LOAD_STORE(1);
> @@ -724,7 +728,7 @@ DEFINE_ASAN_LOAD_STORE(16);
>
>  void __asan_loadN(unsigned long addr, size_t size)
>  {
> -       check_memory_region(addr, size, false);
> +       check_memory_region(addr, size, false, _RET_IP_);
>  }
>  EXPORT_SYMBOL(__asan_loadN);
>
> @@ -734,7 +738,7 @@ EXPORT_SYMBOL(__asan_loadN_noabort);
>
>  void __asan_storeN(unsigned long addr, size_t size)
>  {
> -       check_memory_region(addr, size, true);
> +       check_memory_region(addr, size, true, _RET_IP_);
>  }
>  EXPORT_SYMBOL(__asan_storeN);
>
> --Reviewed-by:
> 2.7.3
>
Acked-by: Alexander Potapenko <glider@google.com>



-- 
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Matthew Scott Sucherman, Paul Terence Manicle
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/4] mm/kasan: add API to check memory regions
  2016-05-06 12:45 ` [PATCH 3/4] mm/kasan: add API to check memory regions Andrey Ryabinin
@ 2016-05-13 12:18   ` Alexander Potapenko
  0 siblings, 0 replies; 10+ messages in thread
From: Alexander Potapenko @ 2016-05-13 12:18 UTC (permalink / raw)
  To: Andrey Ryabinin
  Cc: Andrew Morton, kasan-dev, Linux Memory Management List, LKML,
	Dmitry Vyukov

On Fri, May 6, 2016 at 2:45 PM, Andrey Ryabinin <aryabinin@virtuozzo.com> wrote:
> Memory access coded in an assembly won't be seen by KASAN as a compiler
> can instrument only C code. Add kasan_check_[read,write]() API
> which is going to be used to check a certain memory range.
>
> Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
> Cc: Alexander Potapenko <glider@google.com>
> Cc: Dmitry Vyukov <dvyukov@google.com>
> ---
>  MAINTAINERS                  |  2 +-
>  include/linux/kasan-checks.h | 12 ++++++++++++
>  mm/kasan/kasan.c             | 12 ++++++++++++
>  3 files changed, 25 insertions(+), 1 deletion(-)
>  create mode 100644 include/linux/kasan-checks.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 43b85c1..3a9471c 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6363,7 +6363,7 @@ S:        Maintained
>  F:     arch/*/include/asm/kasan.h
>  F:     arch/*/mm/kasan_init*
>  F:     Documentation/kasan.txt
> -F:     include/linux/kasan.h
> +F:     include/linux/kasan*.h
>  F:     lib/test_kasan.c
>  F:     mm/kasan/
>  F:     scripts/Makefile.kasan
> diff --git a/include/linux/kasan-checks.h b/include/linux/kasan-checks.h
> new file mode 100644
> index 0000000..b7f8ace
> --- /dev/null
> +++ b/include/linux/kasan-checks.h
> @@ -0,0 +1,12 @@
> +#ifndef _LINUX_KASAN_CHECKS_H
> +#define _LINUX_KASAN_CHECKS_H
> +
> +#ifdef CONFIG_KASAN
> +void kasan_check_read(const void *p, unsigned int size);
> +void kasan_check_write(const void *p, unsigned int size);
> +#else
> +static inline void kasan_check_read(const void *p, unsigned int size) { }
> +static inline void kasan_check_write(const void *p, unsigned int size) { }
> +#endif
> +
> +#endif
> diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
> index 6e4072c..54f0ea7 100644
> --- a/mm/kasan/kasan.c
> +++ b/mm/kasan/kasan.c
> @@ -299,6 +299,18 @@ static void check_memory_region(unsigned long addr,
>         check_memory_region_inline(addr, size, write, ret_ip);
>  }
>
> +void kasan_check_read(const void *p, unsigned int size)
> +{
> +       check_memory_region((unsigned long)p, size, false, _RET_IP_);
> +}
> +EXPORT_SYMBOL(kasan_check_read);
> +
> +void kasan_check_write(const void *p, unsigned int size)
> +{
> +       check_memory_region((unsigned long)p, size, true, _RET_IP_);
> +}
> +EXPORT_SYMBOL(kasan_check_write);
> +
>  #undef memset
>  void *memset(void *addr, int c, size_t len)
>  {
> --
> 2.7.3
>
Acked-by: Alexander Potapenko <glider@google.com>


-- 
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Matthew Scott Sucherman, Paul Terence Manicle
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2016-05-13 12:18 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-06 12:45 [PATCH 1/4] kasan/tests: add tests for user memory access functions Andrey Ryabinin
2016-05-06 12:45 ` [PATCH 2/4] mm/kasan: print name of mem[set,cpy,move]() caller in report Andrey Ryabinin
2016-05-13 12:15   ` Alexander Potapenko
2016-05-06 12:45 ` [PATCH 3/4] mm/kasan: add API to check memory regions Andrey Ryabinin
2016-05-13 12:18   ` Alexander Potapenko
2016-05-06 12:45 ` [PATCH 4/4] x86/kasan: Instrument user memory access API Andrey Ryabinin
2016-05-09  5:08   ` Dmitry Vyukov
2016-05-09  6:29   ` Ingo Molnar
2016-05-10  8:33     ` [PATCH] x86-kasan-instrument-user-memory-access-api-fix Andrey Ryabinin
2016-05-06 22:48 ` [PATCH 1/4] kasan/tests: add tests for user memory access functions Andrew Morton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).