Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1437975 > unrolled thread
| Started by | Kees Cook <keescook@chromium.org> |
|---|---|
| First post | 2016-07-07 00:30 +0200 |
| Last post | 2016-07-10 01:20 +0200 |
| Articles | 20 on this page of 29 — 11 participants |
Back to article view | Back to linux.kernel
[PATCH 0/9] mm: Hardened usercopy Kees Cook <keescook@chromium.org> - 2016-07-07 00:30 +0200
[PATCH 6/9] powerpc/uaccess: Enable hardened usercopy Kees Cook <keescook@chromium.org> - 2016-07-07 00:30 +0200
[PATCH 1/9] mm: Hardened usercopy Kees Cook <keescook@chromium.org> - 2016-07-07 00:30 +0200
Re: [PATCH 1/9] mm: Hardened usercopy Baruch Siach <baruch@tkos.co.il> - 2016-07-07 07:40 +0200
Re: [PATCH 1/9] mm: Hardened usercopy Kees Cook <keescook@chromium.org> - 2016-07-07 19:30 +0200
Re: [PATCH 1/9] mm: Hardened usercopy Baruch Siach <baruch@tkos.co.il> - 2016-07-07 20:50 +0200
Re: [PATCH 1/9] mm: Hardened usercopy Thomas Gleixner <tglx@linutronix.de> - 2016-07-07 09:50 +0200
Re: [PATCH 1/9] mm: Hardened usercopy Kees Cook <keescook@chromium.org> - 2016-07-07 19:40 +0200
Re: [PATCH 1/9] mm: Hardened usercopy Thomas Gleixner <tglx@linutronix.de> - 2016-07-07 21:40 +0200
Re: [PATCH 1/9] mm: Hardened usercopy Arnd Bergmann <arnd@arndb.de> - 2016-07-07 10:10 +0200
Re: [PATCH 1/9] mm: Hardened usercopy Kees Cook <keescook@chromium.org> - 2016-07-07 19:40 +0200
Re: [PATCH 1/9] mm: Hardened usercopy Arnd Bergmann <arnd@arndb.de> - 2016-07-08 11:30 +0200
Re: [PATCH 1/9] mm: Hardened usercopy Rik van Riel <riel@redhat.com> - 2016-07-07 18:30 +0200
Re: [PATCH 1/9] mm: Hardened usercopy Rik van Riel <riel@redhat.com> - 2016-07-07 18:40 +0200
Re: [PATCH 1/9] mm: Hardened usercopy Kees Cook <keescook@chromium.org> - 2016-07-07 19:50 +0200
[PATCH 2/9] x86/uaccess: Enable hardened usercopy Kees Cook <keescook@chromium.org> - 2016-07-07 00:30 +0200
[PATCH 7/9] sparc/uaccess: Enable hardened usercopy Kees Cook <keescook@chromium.org> - 2016-07-07 00:30 +0200
Re: [PATCH 0/9] mm: Hardened usercopy Christian Borntraeger <borntraeger@de.ibm.com> - 2016-07-07 09:40 +0200
Re: [PATCH 0/9] mm: Hardened usercopy Kees Cook <keescook@chromium.org> - 2016-07-07 19:30 +0200
Re: [PATCH 0/9] mm: Hardened usercopy Ingo Molnar <mingo@kernel.org> - 2016-07-08 10:50 +0200
Re: [PATCH 0/9] mm: Hardened usercopy Linus Torvalds <torvalds@linux-foundation.org> - 2016-07-08 18:20 +0200
Re: [PATCH 0/9] mm: Hardened usercopy Ingo Molnar <mingo@kernel.org> - 2016-07-08 20:30 +0200
Re: [PATCH 0/9] mm: Hardened usercopy Rik van Riel <riel@redhat.com> - 2016-07-09 04:50 +0200
Re: [PATCH 0/9] mm: Hardened usercopy Ingo Molnar <mingo@kernel.org> - 2016-07-09 10:00 +0200
Re: [PATCH 0/9] mm: Hardened usercopy Ard Biesheuvel <ard.biesheuvel@linaro.org> - 2016-07-09 10:30 +0200
Re: [PATCH 0/9] mm: Hardened usercopy Kees Cook <keescook@chromium.org> - 2016-07-09 19:10 +0200
Re: [PATCH 0/9] mm: Hardened usercopy Kees Cook <keescook@chromium.org> - 2016-07-09 19:10 +0200
Re: [PATCH 0/9] mm: Hardened usercopy Andy Lutomirski <luto@amacapital.net> - 2016-07-09 23:30 +0200
Re: [PATCH 0/9] mm: Hardened usercopy "PaX Team" <pageexec@freemail.hu> - 2016-07-10 01:20 +0200
Page 1 of 2 [1] 2 Next page →
| From | Kees Cook <keescook@chromium.org> |
|---|---|
| Date | 2016-07-07 00:30 +0200 |
| Subject | [PATCH 0/9] mm: Hardened usercopy |
| Message-ID | <rS3up-6IW-9@gated-at.bofh.it> |
Hi, This is a start of the mainline port of PAX_USERCOPY[1]. After I started writing tests (now in lkdtm in -next) for Casey's earlier port[2], I kept tweaking things further and further until I ended up with a whole new patch series. To that end, I took Rik's feedback and made a number of other changes and clean-ups as well. Based on my understanding, PAX_USERCOPY was designed to catch a few classes of flaws around the use of copy_to_user()/copy_from_user(). These changes don't touch get_user() and put_user(), since these operate on constant sized lengths, and tend to be much less vulnerable. There are effectively three distinct protections in the whole series, each of which I've given a separate CONFIG, though this patch set is only the first of the three intended protections. (Generally speaking, PAX_USERCOPY covers what I'm calling CONFIG_HARDENED_USERCOPY (this) and CONFIG_HARDENED_USERCOPY_WHITELIST (future), and PAX_USERCOPY_SLABS covers CONFIG_HARDENED_USERCOPY_SPLIT_KMALLOC (future).) This series, which adds CONFIG_HARDENED_USERCOPY, checks that objects being copied to/from userspace meet certain criteria: - if address is a heap object, the size must not exceed the object's allocated size. (This will catch all kinds of heap overflow flaws.) - if address range is in the current process stack, it must be within the current stack frame (if such checking is possible) or at least entirely within the current process's stack. (This could catch large lengths that would have extended beyond the current process stack, or overflows if their length extends back into the original stack.) - if the address range is part of kernel data, rodata, or bss, allow it. - if address range is page-allocated, that it doesn't span multiple allocations. - if address is within the kernel text, reject it. - everything else is accepted The patches in the series are: - The core copy_to/from_user() checks, without the slab object checks: 1- mm: Hardened usercopy - Per-arch enablement of the protection: 2- x86/uaccess: Enable hardened usercopy 3- ARM: uaccess: Enable hardened usercopy 4- arm64/uaccess: Enable hardened usercopy 5- ia64/uaccess: Enable hardened usercopy 6- powerpc/uaccess: Enable hardened usercopy 7- sparc/uaccess: Enable hardened usercopy - The heap allocator implementation of object size checking: 8- mm: SLAB hardened usercopy support 9- mm: SLUB hardened usercopy support Some notes: - This is expected to apply on top of -next which contains fixes for the position of _etext on both arm and arm64. - I couldn't detect a measurable performance change with these features enabled. Kernel build times were unchanged, hackbench was unchanged, etc. I think we could flip this to "on by default" at some point. - The SLOB support extracted from grsecurity seems entirely broken. I have no idea what's going on there, I spent my time testing SLAB and SLUB. Having someone else look at SLOB would be nice, but this series doesn't depend on it. Additional features that would be nice, but aren't blocking this series: - Needs more architecture support for stack frame checking (only x86 now). Thanks! -Kees [1] https://grsecurity.net/download.php "grsecurity - test kernel patch" [2] http://www.openwall.com/lists/kernel-hardening/2016/05/19/5
[toc] | [next] | [standalone]
| From | Kees Cook <keescook@chromium.org> |
|---|---|
| Date | 2016-07-07 00:30 +0200 |
| Subject | [PATCH 6/9] powerpc/uaccess: Enable hardened usercopy |
| Message-ID | <rS3uq-6IW-23@gated-at.bofh.it> |
| In reply to | #1437975 |
Enables CONFIG_HARDENED_USERCOPY checks on powerpc.
Based on code from PaX and grsecurity.
Signed-off-by: Kees Cook <keescook@chromium.org>
---
arch/powerpc/Kconfig | 1 +
arch/powerpc/include/asm/uaccess.h | 21 +++++++++++++++++++--
2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 01f7464d9fea..b7a18b2604be 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -164,6 +164,7 @@ config PPC
select ARCH_HAS_UBSAN_SANITIZE_ALL
select ARCH_SUPPORTS_DEFERRED_STRUCT_PAGE_INIT
select HAVE_LIVEPATCH if HAVE_DYNAMIC_FTRACE_WITH_REGS
+ select HAVE_ARCH_HARDENED_USERCOPY
config GENERIC_CSUM
def_bool CPU_LITTLE_ENDIAN
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
index b7c20f0b8fbe..c1dc6c14deb8 100644
--- a/arch/powerpc/include/asm/uaccess.h
+++ b/arch/powerpc/include/asm/uaccess.h
@@ -310,10 +310,15 @@ static inline unsigned long copy_from_user(void *to,
{
unsigned long over;
- if (access_ok(VERIFY_READ, from, n))
+ 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);
+ }
if ((unsigned long)from < TASK_SIZE) {
over = (unsigned long)from + n - TASK_SIZE;
+ if (!__builtin_constant_p(n - over))
+ check_object_size(to, n - over, false);
return __copy_tofrom_user((__force void __user *)to, from,
n - over) + over;
}
@@ -325,10 +330,15 @@ static inline unsigned long copy_to_user(void __user *to,
{
unsigned long over;
- if (access_ok(VERIFY_WRITE, to, n))
+ 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);
+ }
if ((unsigned long)to < TASK_SIZE) {
over = (unsigned long)to + n - TASK_SIZE;
+ if (!__builtin_constant_p(n))
+ check_object_size(from, n - over, true);
return __copy_tofrom_user(to, (__force void __user *)from,
n - over) + over;
}
@@ -372,6 +382,10 @@ static inline unsigned long __copy_from_user_inatomic(void *to,
if (ret == 0)
return 0;
}
+
+ if (!__builtin_constant_p(n))
+ check_object_size(to, n, false);
+
return __copy_tofrom_user((__force void __user *)to, from, n);
}
@@ -398,6 +412,9 @@ static inline unsigned long __copy_to_user_inatomic(void __user *to,
if (ret == 0)
return 0;
}
+ if (!__builtin_constant_p(n))
+ check_object_size(from, n, true);
+
return __copy_tofrom_user(to, (__force const void __user *)from, n);
}
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Kees Cook <keescook@chromium.org> |
|---|---|
| Date | 2016-07-07 00:30 +0200 |
| Subject | [PATCH 1/9] mm: Hardened usercopy |
| Message-ID | <rS3uq-6IW-25@gated-at.bofh.it> |
| In reply to | #1437975 |
This is the start of porting PAX_USERCOPY into the mainline kernel. This
is the first set of features, controlled by CONFIG_HARDENED_USERCOPY. The
work is based on code by PaX Team and Brad Spengler, and an earlier port
from Casey Schaufler. Additional non-slab page tests are from Rik van Riel.
This patch contains the logic for validating several conditions when
performing copy_to_user() and copy_from_user() on the kernel object
being copied to/from:
- address range doesn't wrap around
- address range isn't NULL or zero-allocated (with a non-zero copy size)
- if on the slab allocator:
- object size must be less than or equal to copy size (when check is
implemented in the allocator, which appear in subsequent patches)
- otherwise, object must not span page allocations
- if on the stack
- object must not extend before/after the current process task
- object must be contained by the current stack frame (when there is
arch/build support for identifying stack frames)
- object must not overlap with kernel text
Signed-off-by: Kees Cook <keescook@chromium.org>
---
arch/Kconfig | 7 ++
include/linux/slab.h | 12 +++
include/linux/thread_info.h | 15 +++
mm/Makefile | 4 +
mm/usercopy.c | 239 ++++++++++++++++++++++++++++++++++++++++++++
security/Kconfig | 27 +++++
6 files changed, 304 insertions(+)
create mode 100644 mm/usercopy.c
diff --git a/arch/Kconfig b/arch/Kconfig
index d794384a0404..3ea04d8dcf62 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -424,6 +424,13 @@ config CC_STACKPROTECTOR_STRONG
endchoice
+config HAVE_ARCH_LINEAR_KERNEL_MAPPING
+ bool
+ help
+ An architecture should select this if it has a secondary linear
+ mapping of the kernel text. This is used to verify that kernel
+ text exposures are not visible under CONFIG_HARDENED_USERCOPY.
+
config HAVE_CONTEXT_TRACKING
bool
help
diff --git a/include/linux/slab.h b/include/linux/slab.h
index aeb3e6d00a66..96a16a3fb7cb 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -155,6 +155,18 @@ void kfree(const void *);
void kzfree(const void *);
size_t ksize(const void *);
+#ifdef CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR
+const char *__check_heap_object(const void *ptr, unsigned long n,
+ struct page *page);
+#else
+static inline const char *__check_heap_object(const void *ptr,
+ unsigned long n,
+ struct page *page)
+{
+ return NULL;
+}
+#endif
+
/*
* Some archs want to perform DMA into kmalloc caches and need a guaranteed
* alignment larger than the alignment of a 64-bit integer.
diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h
index b4c2a485b28a..a02200db9c33 100644
--- a/include/linux/thread_info.h
+++ b/include/linux/thread_info.h
@@ -146,6 +146,21 @@ static inline bool test_and_clear_restore_sigmask(void)
#error "no set_restore_sigmask() provided and default one won't work"
#endif
+#ifdef CONFIG_HARDENED_USERCOPY
+extern void __check_object_size(const void *ptr, unsigned long n,
+ bool to_user);
+
+static inline void check_object_size(const void *ptr, unsigned long n,
+ bool to_user)
+{
+ __check_object_size(ptr, n, to_user);
+}
+#else
+static inline void check_object_size(const void *ptr, unsigned long n,
+ bool to_user)
+{ }
+#endif /* CONFIG_HARDENED_USERCOPY */
+
#endif /* __KERNEL__ */
#endif /* _LINUX_THREAD_INFO_H */
diff --git a/mm/Makefile b/mm/Makefile
index 78c6f7dedb83..32d37247c7e5 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -21,6 +21,9 @@ KCOV_INSTRUMENT_memcontrol.o := n
KCOV_INSTRUMENT_mmzone.o := n
KCOV_INSTRUMENT_vmstat.o := n
+# Since __builtin_frame_address does work as used, disable the warning.
+CFLAGS_usercopy.o += $(call cc-disable-warning, frame-address)
+
mmu-y := nommu.o
mmu-$(CONFIG_MMU) := gup.o highmem.o memory.o mincore.o \
mlock.o mmap.o mprotect.o mremap.o msync.o rmap.o \
@@ -99,3 +102,4 @@ obj-$(CONFIG_USERFAULTFD) += userfaultfd.o
obj-$(CONFIG_IDLE_PAGE_TRACKING) += page_idle.o
obj-$(CONFIG_FRAME_VECTOR) += frame_vector.o
obj-$(CONFIG_DEBUG_PAGE_REF) += debug_page_ref.o
+obj-$(CONFIG_HARDENED_USERCOPY) += usercopy.o
diff --git a/mm/usercopy.c b/mm/usercopy.c
new file mode 100644
index 000000000000..ad2765dd6dc4
--- /dev/null
+++ b/mm/usercopy.c
@@ -0,0 +1,239 @@
+/*
+ * This implements the various checks for CONFIG_HARDENED_USERCOPY*,
+ * which are designed to protect kernel memory from needless exposure
+ * and overwrite under many unintended conditions. This code is based
+ * on PAX_USERCOPY, which is:
+ *
+ * Copyright (C) 2001-2016 PaX Team, Bradley Spengler, Open Source
+ * Security Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/sections.h>
+
+/*
+ * Checks if a given pointer and length is contained by the current
+ * stack frame (if possible).
+ *
+ * 0: not at all on the stack
+ * 1: fully on the stack (when can't do frame-checking)
+ * 2: fully inside the current stack frame
+ * -1: error condition (invalid stack position or bad stack frame)
+ */
+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
+
+ /* Object is not on the stack at all. */
+ if (obj + len <= stack || stackend <= obj)
+ return 0;
+
+ /*
+ * Reject: object partially overlaps the stack (passing the
+ * the check above means at least one end is within the stack,
+ * so if this check fails, the other end is outside the stack).
+ */
+ 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 void report_usercopy(const void *ptr, unsigned long len,
+ bool to_user, const char *type)
+{
+ pr_emerg("kernel memory %s attempt detected %s %p (%s) (%lu bytes)\n",
+ to_user ? "exposure" : "overwrite",
+ to_user ? "from" : "to", ptr, type ? : "unknown", len);
+ dump_stack();
+ do_group_exit(SIGKILL);
+}
+
+/* Returns true if any portion of [ptr,ptr+n) over laps with [low,high). */
+static bool overlaps(const void *ptr, unsigned long n, unsigned long low,
+ unsigned long high)
+{
+ unsigned long check_low = (uintptr_t)ptr;
+ unsigned long check_high = check_low + n;
+
+ /* Does not overlap if entirely above or entirely below. */
+ if (check_low >= high || check_high < low)
+ return false;
+
+ return true;
+}
+
+/* Is this address range in the kernel text area? */
+static inline const char *check_kernel_text_object(const void *ptr,
+ unsigned long n)
+{
+ unsigned long textlow = (unsigned long)_stext;
+ unsigned long texthigh = (unsigned long)_etext;
+
+ if (overlaps(ptr, n, textlow, texthigh))
+ return "<kernel text>";
+
+#ifdef HAVE_ARCH_LINEAR_KERNEL_MAPPING
+ /* Check against linear mapping as well. */
+ if (overlaps(ptr, n, (unsigned long)__va(__pa(textlow)),
+ (unsigned long)__va(__pa(texthigh))))
+ return "<linear kernel text>";
+#endif
+
+ return NULL;
+}
+
+static inline const char *check_bogus_address(const void *ptr, unsigned long n)
+{
+ /* Reject if object wraps past end of memory. */
+ if (ptr + n < ptr)
+ return "<wrapped address>";
+
+ /* Reject if NULL or ZERO-allocation. */
+ if (ZERO_OR_NULL_PTR(ptr))
+ return "<null>";
+
+ return NULL;
+}
+
+static inline const char *check_heap_object(const void *ptr, unsigned long n)
+{
+ struct page *page, *endpage;
+ const void *end = ptr + n - 1;
+
+ if (!virt_addr_valid(ptr))
+ return NULL;
+
+ page = virt_to_head_page(ptr);
+
+ /* Check slab allocator for flags and size. */
+ if (PageSlab(page))
+ return __check_heap_object(ptr, n, page);
+
+ /* Is the object wholly within one base page? */
+ if (likely(((unsigned long)ptr & (unsigned long)PAGE_MASK) ==
+ ((unsigned long)end & (unsigned long)PAGE_MASK)))
+ return NULL;
+
+ /* Allow if start and end are inside the same compound page. */
+ endpage = virt_to_head_page(end);
+ if (likely(endpage == page))
+ return NULL;
+
+ /* Allow special areas, device memory, and sometimes kernel data. */
+ if (PageReserved(page) && PageReserved(endpage))
+ return NULL;
+
+ /*
+ * Sometimes the kernel data regions are not marked Reserved. And
+ * sometimes [_sdata,_edata) does not cover rodata and/or bss,
+ * so check each range explicitly.
+ */
+
+ /* Allow kernel data region (if not marked as Reserved). */
+ if (ptr >= (const void *)_sdata && end <= (const void *)_edata)
+ return NULL;
+
+ /* Allow kernel rodata region (if not marked as Reserved). */
+ if (ptr >= (const void *)__start_rodata &&
+ end <= (const void *)__end_rodata)
+ return NULL;
+
+ /* Allow kernel bss region (if not marked as Reserved). */
+ if (ptr >= (const void *)__bss_start &&
+ end <= (const void *)__bss_stop)
+ return NULL;
+
+ /* Uh oh. The "object" spans several independently allocated pages. */
+ return "<spans multiple pages>";
+}
+
+/*
+ * Validates that the given object is one of:
+ * - known safe heap object
+ * - known safe stack object
+ * - not in kernel text
+ */
+void __check_object_size(const void *ptr, unsigned long n, bool to_user)
+{
+ const char *err;
+
+ /* Skip all tests if size is zero. */
+ if (!n)
+ return;
+
+ /* Check for invalid addresses. */
+ err = check_bogus_address(ptr, n);
+ if (err)
+ goto report;
+
+ /* Check for bad heap object. */
+ err = check_heap_object(ptr, n);
+ if (err)
+ goto report;
+
+ /* Check for bad stack object. */
+ switch (check_stack_object(ptr, n)) {
+ case 0:
+ /* Object is not touching the current process stack. */
+ break;
+ case 1:
+ case 2:
+ /*
+ * Object is either in the correct frame (when it
+ * is possible to check) or just generally on the
+ * process stack (when frame checking not available).
+ */
+ return;
+ default:
+ err = "<process stack>";
+ goto report;
+ }
+
+ /* Check for object in kernel to avoid text exposure. */
+ err = check_kernel_text_object(ptr, n);
+ if (!err)
+ return;
+
+report:
+ report_usercopy(ptr, n, to_user, err);
+}
+EXPORT_SYMBOL(__check_object_size);
diff --git a/security/Kconfig b/security/Kconfig
index 176758cdfa57..63340ad0b9f9 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -118,6 +118,33 @@ config LSM_MMAP_MIN_ADDR
this low address space will need the permission specific to the
systems running LSM.
+config HAVE_HARDENED_USERCOPY_ALLOCATOR
+ bool
+ help
+ The heap allocator implements __check_heap_object() for
+ validating memory ranges against heap object sizes in
+ support of CONFIG_HARDENED_USERCOPY.
+
+config HAVE_ARCH_HARDENED_USERCOPY
+ bool
+ help
+ The architecture supports CONFIG_HARDENED_USERCOPY by
+ calling check_object_size() just before performing the
+ userspace copies in the low level implementation of
+ copy_to_user() and copy_from_user().
+
+config HARDENED_USERCOPY
+ bool "Harden memory copies between kernel and userspace"
+ depends on HAVE_ARCH_HARDENED_USERCOPY
+ help
+ This option checks for obviously wrong memory regions when
+ copying memory to/from the kernel (via copy_to_user() and
+ copy_from_user() functions) by rejecting memory ranges that
+ are larger than the specified heap object, span multiple
+ separately allocates pages, are not on the process stack,
+ or are part of the kernel text. This kills entire classes
+ of heap overflow exploits and similar kernel memory exposures.
+
source security/selinux/Kconfig
source security/smack/Kconfig
source security/tomoyo/Kconfig
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Baruch Siach <baruch@tkos.co.il> |
|---|---|
| Date | 2016-07-07 07:40 +0200 |
| Subject | Re: [PATCH 1/9] mm: Hardened usercopy |
| Message-ID | <rSacy-2D8-21@gated-at.bofh.it> |
| In reply to | #1437977 |
Hi Kees,
On Wed, Jul 06, 2016 at 03:25:20PM -0700, Kees Cook wrote:
> +#ifdef CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR
Should be CONFIG_HARDENED_USERCOPY to match the slab/slub implementation
condition.
> +const char *__check_heap_object(const void *ptr, unsigned long n,
> + struct page *page);
> +#else
> +static inline const char *__check_heap_object(const void *ptr,
> + unsigned long n,
> + struct page *page)
> +{
> + return NULL;
> +}
> +#endif
baruch
--
http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- baruch@tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -
[toc] | [prev] | [next] | [standalone]
| From | Kees Cook <keescook@chromium.org> |
|---|---|
| Date | 2016-07-07 19:30 +0200 |
| Subject | Re: [PATCH 1/9] mm: Hardened usercopy |
| Message-ID | <rSlhE-1y4-1@gated-at.bofh.it> |
| In reply to | #1438176 |
On Thu, Jul 7, 2016 at 1:37 AM, Baruch Siach <baruch@tkos.co.il> wrote:
> Hi Kees,
>
> On Wed, Jul 06, 2016 at 03:25:20PM -0700, Kees Cook wrote:
>> +#ifdef CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR
>
> Should be CONFIG_HARDENED_USERCOPY to match the slab/slub implementation
> condition.
>
>> +const char *__check_heap_object(const void *ptr, unsigned long n,
>> + struct page *page);
>> +#else
>> +static inline const char *__check_heap_object(const void *ptr,
>> + unsigned long n,
>> + struct page *page)
>> +{
>> + return NULL;
>> +}
>> +#endif
Hmm, I think what I have is correct: if the allocator supports the
heap object checking, it defines __check_heap_object as existing via
CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR. If usercopy checking is done
at all is controlled by CONFIG_HARDENED_USERCOPY.
I.e. you can have the other usercopy checks even if your allocator
doesn't support object size checking.
-Kees
--
Kees Cook
Chrome OS & Brillo Security
[toc] | [prev] | [next] | [standalone]
| From | Baruch Siach <baruch@tkos.co.il> |
|---|---|
| Date | 2016-07-07 20:50 +0200 |
| Subject | Re: [PATCH 1/9] mm: Hardened usercopy |
| Message-ID | <rSmx5-2ew-63@gated-at.bofh.it> |
| In reply to | #1438763 |
Hi Kees,
On Thu, Jul 07, 2016 at 01:25:21PM -0400, Kees Cook wrote:
> On Thu, Jul 7, 2016 at 1:37 AM, Baruch Siach <baruch@tkos.co.il> wrote:
> > On Wed, Jul 06, 2016 at 03:25:20PM -0700, Kees Cook wrote:
> >> +#ifdef CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR
> >
> > Should be CONFIG_HARDENED_USERCOPY to match the slab/slub implementation
> > condition.
> >
> >> +const char *__check_heap_object(const void *ptr, unsigned long n,
> >> + struct page *page);
> >> +#else
> >> +static inline const char *__check_heap_object(const void *ptr,
> >> + unsigned long n,
> >> + struct page *page)
> >> +{
> >> + return NULL;
> >> +}
> >> +#endif
>
> Hmm, I think what I have is correct: if the allocator supports the
> heap object checking, it defines __check_heap_object as existing via
> CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR. If usercopy checking is done
> at all is controlled by CONFIG_HARDENED_USERCOPY.
>
> I.e. you can have the other usercopy checks even if your allocator
> doesn't support object size checking.
Right. I missed the fact that usercopy.c build also depends on
CONFIG_HARDENED_USERCOPY. Sorry for the noise.
baruch
--
http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- baruch@tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -
[toc] | [prev] | [next] | [standalone]
| From | Thomas Gleixner <tglx@linutronix.de> |
|---|---|
| Date | 2016-07-07 09:50 +0200 |
| Subject | Re: [PATCH 1/9] mm: Hardened usercopy |
| Message-ID | <rScem-3Vx-21@gated-at.bofh.it> |
| In reply to | #1437977 |
On Wed, 6 Jul 2016, Kees Cook wrote:
> +
> +#if defined(CONFIG_FRAME_POINTER) && defined(CONFIG_X86)
> + const void *frame = NULL;
> + const void *oldframe;
> +#endif
That's ugly
> +
> + /* Object is not on the stack at all. */
> + if (obj + len <= stack || stackend <= obj)
> + return 0;
> +
> + /*
> + * Reject: object partially overlaps the stack (passing the
> + * the check above means at least one end is within the stack,
> + * so if this check fails, the other end is outside the stack).
> + */
> + 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
I'd rather make that a weak function returning 1 which can be replaced by
x86 for CONFIG_FRAME_POINTER=y. That also allows other architectures to
implement their specific frame checks.
Thanks,
tglx
[toc] | [prev] | [next] | [standalone]
| From | Kees Cook <keescook@chromium.org> |
|---|---|
| Date | 2016-07-07 19:40 +0200 |
| Subject | Re: [PATCH 1/9] mm: Hardened usercopy |
| Message-ID | <rSlrk-1Bj-13@gated-at.bofh.it> |
| In reply to | #1438248 |
On Thu, Jul 7, 2016 at 3:42 AM, Thomas Gleixner <tglx@linutronix.de> wrote:
> On Wed, 6 Jul 2016, Kees Cook wrote:
>> +
>> +#if defined(CONFIG_FRAME_POINTER) && defined(CONFIG_X86)
>> + const void *frame = NULL;
>> + const void *oldframe;
>> +#endif
>
> That's ugly
Yeah, I'd like to have this be controlled by a specific CONFIG, like I
invented for the linear mapping, but I wasn't sure what was the best
approach.
>
>> +
>> + /* Object is not on the stack at all. */
>> + if (obj + len <= stack || stackend <= obj)
>> + return 0;
>> +
>> + /*
>> + * Reject: object partially overlaps the stack (passing the
>> + * the check above means at least one end is within the stack,
>> + * so if this check fails, the other end is outside the stack).
>> + */
>> + 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
>
> I'd rather make that a weak function returning 1 which can be replaced by
> x86 for CONFIG_FRAME_POINTER=y. That also allows other architectures to
> implement their specific frame checks.
Yeah, though I prefer CONFIG-controlled stuff over weak functions, but
I agree, something like arch_check_stack_frame(...) or similar. I'll
build something for this on the next revision.
-Kees
--
Kees Cook
Chrome OS & Brillo Security
[toc] | [prev] | [next] | [standalone]
| From | Thomas Gleixner <tglx@linutronix.de> |
|---|---|
| Date | 2016-07-07 21:40 +0200 |
| Subject | Re: [PATCH 1/9] mm: Hardened usercopy |
| Message-ID | <rSnjr-2Pm-1@gated-at.bofh.it> |
| In reply to | #1438774 |
On Thu, 7 Jul 2016, Kees Cook wrote: > On Thu, Jul 7, 2016 at 3:42 AM, Thomas Gleixner <tglx@linutronix.de> wrote: > > I'd rather make that a weak function returning 1 which can be replaced by > > x86 for CONFIG_FRAME_POINTER=y. That also allows other architectures to > > implement their specific frame checks. > > Yeah, though I prefer CONFIG-controlled stuff over weak functions, but > I agree, something like arch_check_stack_frame(...) or similar. I'll > build something for this on the next revision. I'm fine with CONFIG_CONTROLLED as long as the ifdeffery is limited to header files. Thanks, tglx
[toc] | [prev] | [next] | [standalone]
| From | Arnd Bergmann <arnd@arndb.de> |
|---|---|
| Date | 2016-07-07 10:10 +0200 |
| Subject | Re: [PATCH 1/9] mm: Hardened usercopy |
| Message-ID | <rScxJ-4ic-65@gated-at.bofh.it> |
| In reply to | #1437977 |
On Wednesday, July 6, 2016 3:25:20 PM CEST Kees Cook wrote:
> This is the start of porting PAX_USERCOPY into the mainline kernel. This
> is the first set of features, controlled by CONFIG_HARDENED_USERCOPY. The
> work is based on code by PaX Team and Brad Spengler, and an earlier port
> from Casey Schaufler. Additional non-slab page tests are from Rik van Riel.
>
> This patch contains the logic for validating several conditions when
> performing copy_to_user() and copy_from_user() on the kernel object
> being copied to/from:
> - address range doesn't wrap around
> - address range isn't NULL or zero-allocated (with a non-zero copy size)
> - if on the slab allocator:
> - object size must be less than or equal to copy size (when check is
> implemented in the allocator, which appear in subsequent patches)
> - otherwise, object must not span page allocations
> - if on the stack
> - object must not extend before/after the current process task
> - object must be contained by the current stack frame (when there is
> arch/build support for identifying stack frames)
> - object must not overlap with kernel text
>
> Signed-off-by: Kees Cook <keescook@chromium.org>
Nice!
I have a few further thoughts, most of which have probably been
considered before:
> +static inline const char *check_bogus_address(const void *ptr, unsigned long n)
> +{
> + /* Reject if object wraps past end of memory. */
> + if (ptr + n < ptr)
> + return "<wrapped address>";
> +
> + /* Reject if NULL or ZERO-allocation. */
> + if (ZERO_OR_NULL_PTR(ptr))
> + return "<null>";
> +
> + return NULL;
> +}
This checks against address (void*)16, but I guess on most architectures the
lowest possible kernel address is much higher. While there may not be much
that to exploit if the expected kernel address points to userland, forbidding
any obviously incorrect address that is outside of the kernel may be easier.
Even on architectures like s390 that start the kernel memory at (void *)0x0,
the lowest address to which we may want to do a copy_to_user would be much
higher than (void*)0x16.
> +
> + /* Allow kernel rodata region (if not marked as Reserved). */
> + if (ptr >= (const void *)__start_rodata &&
> + end <= (const void *)__end_rodata)
> + return NULL;
Should we explicitly forbid writing to rodata, or is it enough to
rely on page protection here?
> + /* Allow kernel bss region (if not marked as Reserved). */
> + if (ptr >= (const void *)__bss_start &&
> + end <= (const void *)__bss_stop)
> + return NULL;
accesses to .data/.rodata/.bss are probably not performance critical,
so we could go further here and check the kallsyms table to ensure
that we are not spanning multiple symbols here.
For stuff that is performance critical, should there be a way to
opt out of the checks, or do we assume it already uses functions
that avoid the checks? I looked at the file and network I/O path
briefly and they seem to use kmap_atomic() to get to the user pages
at least in some of the common cases (but I may well be missing
important ones).
Arnd
[toc] | [prev] | [next] | [standalone]
| From | Kees Cook <keescook@chromium.org> |
|---|---|
| Date | 2016-07-07 19:40 +0200 |
| Subject | Re: [PATCH 1/9] mm: Hardened usercopy |
| Message-ID | <rSlrk-1Bj-15@gated-at.bofh.it> |
| In reply to | #1438297 |
On Thu, Jul 7, 2016 at 4:01 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Wednesday, July 6, 2016 3:25:20 PM CEST Kees Cook wrote:
>> This is the start of porting PAX_USERCOPY into the mainline kernel. This
>> is the first set of features, controlled by CONFIG_HARDENED_USERCOPY. The
>> work is based on code by PaX Team and Brad Spengler, and an earlier port
>> from Casey Schaufler. Additional non-slab page tests are from Rik van Riel.
>>
>> This patch contains the logic for validating several conditions when
>> performing copy_to_user() and copy_from_user() on the kernel object
>> being copied to/from:
>> - address range doesn't wrap around
>> - address range isn't NULL or zero-allocated (with a non-zero copy size)
>> - if on the slab allocator:
>> - object size must be less than or equal to copy size (when check is
>> implemented in the allocator, which appear in subsequent patches)
>> - otherwise, object must not span page allocations
>> - if on the stack
>> - object must not extend before/after the current process task
>> - object must be contained by the current stack frame (when there is
>> arch/build support for identifying stack frames)
>> - object must not overlap with kernel text
>>
>> Signed-off-by: Kees Cook <keescook@chromium.org>
>
> Nice!
>
> I have a few further thoughts, most of which have probably been
> considered before:
>
>> +static inline const char *check_bogus_address(const void *ptr, unsigned long n)
>> +{
>> + /* Reject if object wraps past end of memory. */
>> + if (ptr + n < ptr)
>> + return "<wrapped address>";
>> +
>> + /* Reject if NULL or ZERO-allocation. */
>> + if (ZERO_OR_NULL_PTR(ptr))
>> + return "<null>";
>> +
>> + return NULL;
>> +}
>
> This checks against address (void*)16, but I guess on most architectures the
> lowest possible kernel address is much higher. While there may not be much
> that to exploit if the expected kernel address points to userland, forbidding
> any obviously incorrect address that is outside of the kernel may be easier.
>
> Even on architectures like s390 that start the kernel memory at (void *)0x0,
> the lowest address to which we may want to do a copy_to_user would be much
> higher than (void*)0x16.
Yeah, that's worth exploring, but given the shenanigans around
set_fs(), I'd like to leave this as-is, and we can add to these checks
as we remove as much of the insane usage of set_fs().
>> +
>> + /* Allow kernel rodata region (if not marked as Reserved). */
>> + if (ptr >= (const void *)__start_rodata &&
>> + end <= (const void *)__end_rodata)
>> + return NULL;
>
> Should we explicitly forbid writing to rodata, or is it enough to
> rely on page protection here?
Hm, interesting. That's a very small check to add. My knee-jerk is to
just leave it up to page protection. I'm on the fence. :)
>
>> + /* Allow kernel bss region (if not marked as Reserved). */
>> + if (ptr >= (const void *)__bss_start &&
>> + end <= (const void *)__bss_stop)
>> + return NULL;
>
> accesses to .data/.rodata/.bss are probably not performance critical,
> so we could go further here and check the kallsyms table to ensure
> that we are not spanning multiple symbols here.
Oh, interesting! Yeah, would you be willing to put together that patch
and test it? I wonder if there are any cases where there are
legitimate usercopys across multiple symbols.
> For stuff that is performance critical, should there be a way to
> opt out of the checks, or do we assume it already uses functions
> that avoid the checks? I looked at the file and network I/O path
> briefly and they seem to use kmap_atomic() to get to the user pages
> at least in some of the common cases (but I may well be missing
> important ones).
I don't want to start with an exemption here, so until such a case is
found, I'd rather leave this as-is. That said, the primary protection
here tends to be buggy lengths (which is why put/get_user() is
untouched). For constant-sized copies, some checks could be skipped.
In the second part of this protection (what I named
CONFIG_HARDENED_USERCOPY_WHITELIST in the RFC version of this series),
there are cases where we want to skip the whitelist checking since it
is for a constant-sized copy the code understands is okay to pull out
of an otherwise disallowed allocator object.
-Kees
--
Kees Cook
Chrome OS & Brillo Security
[toc] | [prev] | [next] | [standalone]
| From | Arnd Bergmann <arnd@arndb.de> |
|---|---|
| Date | 2016-07-08 11:30 +0200 |
| Subject | Re: [PATCH 1/9] mm: Hardened usercopy |
| Message-ID | <rSAgG-2PC-11@gated-at.bofh.it> |
| In reply to | #1438773 |
On Thursday, July 7, 2016 1:37:43 PM CEST Kees Cook wrote:
> >
> >> + /* Allow kernel bss region (if not marked as Reserved). */
> >> + if (ptr >= (const void *)__bss_start &&
> >> + end <= (const void *)__bss_stop)
> >> + return NULL;
> >
> > accesses to .data/.rodata/.bss are probably not performance critical,
> > so we could go further here and check the kallsyms table to ensure
> > that we are not spanning multiple symbols here.
>
> Oh, interesting! Yeah, would you be willing to put together that patch
> and test it?
Not at the moment, sorry.
I've given it a closer look and unfortunately realized that kallsyms
today only covers .text and .init.text, so it's currently useless because
those sections are already disallowed.
We could extend kallsyms to also cover all other sections, but doing
that right will likely cause a number of problems (most likely
kallsyms size mismatch) that will have to be debugged first.\
I think it's doable but time-consuming. The check function should
actually be trivial:
static bool usercopy_spans_multiple_symbols(void *ptr, size_t len)
{
unsigned long size, offset;
if (kallsyms_lookup_size_offset((unsigned long)ptr, &size, &offset))
return 0; /* no symbol found or kallsyms disabled */
if (size - offset <= len)
return 0; /* range is within one symbol */
return 1;
}
This part would also be trivial:
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index 1f22a186c18c..e0f37212e2a9 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -50,6 +50,11 @@ static struct addr_range text_ranges[] = {
{ "_sinittext", "_einittext" },
{ "_stext_l1", "_etext_l1" }, /* Blackfin on-chip L1 inst SRAM */
{ "_stext_l2", "_etext_l2" }, /* Blackfin on-chip L2 SRAM */
+#ifdef CONFIG_HARDENED_USERCOPY
+ { "_sdata", "_edata" },
+ { "__bss_start", "__bss_stop" },
+ { "__start_rodata", "__end_rodata" },
+#endif
};
#define text_range_text (&text_ranges[0])
#define text_range_inittext (&text_ranges[1])
but I fear that if you actually try that, things start falling apart
in a big way, so I didn't try ;-)
> I wonder if there are any cases where there are
> legitimate usercopys across multiple symbols.
The only possible use case I can think of is for reading out the entire
kernel memory from /dev/kmem, but your other checks in here already
define that as illegitimate. On that subject, we probably want to
make CONFIG_DEVKMEM mutually exclusive with CONFIG_HARDENED_USERCOPY.
Arnd
[toc] | [prev] | [next] | [standalone]
| From | Rik van Riel <riel@redhat.com> |
|---|---|
| Date | 2016-07-07 18:30 +0200 |
| Subject | Re: [PATCH 1/9] mm: Hardened usercopy |
| Message-ID | <rSklz-X0-3@gated-at.bofh.it> |
| In reply to | #1437977 |
[Multipart message — attachments visible in raw view] — view raw
On Wed, 2016-07-06 at 15:25 -0700, Kees Cook wrote:
> This is the start of porting PAX_USERCOPY into the mainline kernel.
> This
> is the first set of features, controlled by CONFIG_HARDENED_USERCOPY.
> The
> work is based on code by PaX Team and Brad Spengler, and an earlier
> port
> from Casey Schaufler. Additional non-slab page tests are from Rik van
> Riel.
Feel free to add my S-O-B for the code I wrote. The rest
looks good, too.
There may be some room for optimization later on, by putting
the most likely branches first, annotating with likely/unlikely,
etc, but I suspect the less likely checks are already towards
the ends of the functions.
Signed-off-by: Rik van Riel <riel@redhat.com>
> This patch contains the logic for validating several conditions when
> performing copy_to_user() and copy_from_user() on the kernel object
> being copied to/from:
> - address range doesn't wrap around
> - address range isn't NULL or zero-allocated (with a non-zero copy
> size)
> - if on the slab allocator:
> - object size must be less than or equal to copy size (when check
> is
> implemented in the allocator, which appear in subsequent patches)
> - otherwise, object must not span page allocations
> - if on the stack
> - object must not extend before/after the current process task
> - object must be contained by the current stack frame (when there
> is
> arch/build support for identifying stack frames)
> - object must not overlap with kernel text
>
> Signed-off-by: Kees Cook <keescook@chromium.org>
> ---
> arch/Kconfig | 7 ++
> include/linux/slab.h | 12 +++
> include/linux/thread_info.h | 15 +++
> mm/Makefile | 4 +
> mm/usercopy.c | 239
> ++++++++++++++++++++++++++++++++++++++++++++
> security/Kconfig | 27 +++++
> 6 files changed, 304 insertions(+)
> create mode 100644 mm/usercopy.c
>
> diff --git a/arch/Kconfig b/arch/Kconfig
> index d794384a0404..3ea04d8dcf62 100644
> --- a/arch/Kconfig
> +++ b/arch/Kconfig
> @@ -424,6 +424,13 @@ config CC_STACKPROTECTOR_STRONG
>
> endchoice
>
> +config HAVE_ARCH_LINEAR_KERNEL_MAPPING
> + bool
> + help
> + An architecture should select this if it has a secondary
> linear
> + mapping of the kernel text. This is used to verify that
> kernel
> + text exposures are not visible under
> CONFIG_HARDENED_USERCOPY.
> +
> config HAVE_CONTEXT_TRACKING
> bool
> help
> diff --git a/include/linux/slab.h b/include/linux/slab.h
> index aeb3e6d00a66..96a16a3fb7cb 100644
> --- a/include/linux/slab.h
> +++ b/include/linux/slab.h
> @@ -155,6 +155,18 @@ void kfree(const void *);
> void kzfree(const void *);
> size_t ksize(const void *);
>
> +#ifdef CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR
> +const char *__check_heap_object(const void *ptr, unsigned long n,
> + struct page *page);
> +#else
> +static inline const char *__check_heap_object(const void *ptr,
> + unsigned long n,
> + struct page *page)
> +{
> + return NULL;
> +}
> +#endif
> +
> /*
> * Some archs want to perform DMA into kmalloc caches and need a
> guaranteed
> * alignment larger than the alignment of a 64-bit integer.
> diff --git a/include/linux/thread_info.h
> b/include/linux/thread_info.h
> index b4c2a485b28a..a02200db9c33 100644
> --- a/include/linux/thread_info.h
> +++ b/include/linux/thread_info.h
> @@ -146,6 +146,21 @@ static inline bool
> test_and_clear_restore_sigmask(void)
> #error "no set_restore_sigmask() provided and default one won't
> work"
> #endif
>
> +#ifdef CONFIG_HARDENED_USERCOPY
> +extern void __check_object_size(const void *ptr, unsigned long n,
> + bool to_user);
> +
> +static inline void check_object_size(const void *ptr, unsigned long
> n,
> + bool to_user)
> +{
> + __check_object_size(ptr, n, to_user);
> +}
> +#else
> +static inline void check_object_size(const void *ptr, unsigned long
> n,
> + bool to_user)
> +{ }
> +#endif /* CONFIG_HARDENED_USERCOPY */
> +
> #endif /* __KERNEL__ */
>
> #endif /* _LINUX_THREAD_INFO_H */
> diff --git a/mm/Makefile b/mm/Makefile
> index 78c6f7dedb83..32d37247c7e5 100644
> --- a/mm/Makefile
> +++ b/mm/Makefile
> @@ -21,6 +21,9 @@ KCOV_INSTRUMENT_memcontrol.o := n
> KCOV_INSTRUMENT_mmzone.o := n
> KCOV_INSTRUMENT_vmstat.o := n
>
> +# Since __builtin_frame_address does work as used, disable the
> warning.
> +CFLAGS_usercopy.o += $(call cc-disable-warning, frame-address)
> +
> mmu-y := nommu.o
> mmu-$(CONFIG_MMU) := gup.o highmem.o memory.o mincore.o \
> mlock.o mmap.o mprotect.o mremap.o
> msync.o rmap.o \
> @@ -99,3 +102,4 @@ obj-$(CONFIG_USERFAULTFD) += userfaultfd.o
> obj-$(CONFIG_IDLE_PAGE_TRACKING) += page_idle.o
> obj-$(CONFIG_FRAME_VECTOR) += frame_vector.o
> obj-$(CONFIG_DEBUG_PAGE_REF) += debug_page_ref.o
> +obj-$(CONFIG_HARDENED_USERCOPY) += usercopy.o
> diff --git a/mm/usercopy.c b/mm/usercopy.c
> new file mode 100644
> index 000000000000..ad2765dd6dc4
> --- /dev/null
> +++ b/mm/usercopy.c
> @@ -0,0 +1,239 @@
> +/*
> + * This implements the various checks for CONFIG_HARDENED_USERCOPY*,
> + * which are designed to protect kernel memory from needless
> exposure
> + * and overwrite under many unintended conditions. This code is
> based
> + * on PAX_USERCOPY, which is:
> + *
> + * Copyright (C) 2001-2016 PaX Team, Bradley Spengler, Open Source
> + * Security Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/mm.h>
> +#include <linux/slab.h>
> +#include <asm/sections.h>
> +
> +/*
> + * Checks if a given pointer and length is contained by the current
> + * stack frame (if possible).
> + *
> + * 0: not at all on the stack
> + * 1: fully on the stack (when can't do frame-checking)
> + * 2: fully inside the current stack frame
> + * -1: error condition (invalid stack position or bad stack
> frame)
> + */
> +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
> +
> + /* Object is not on the stack at all. */
> + if (obj + len <= stack || stackend <= obj)
> + return 0;
> +
> + /*
> + * Reject: object partially overlaps the stack (passing the
> + * the check above means at least one end is within the
> stack,
> + * so if this check fails, the other end is outside the
> stack).
> + */
> + 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 void report_usercopy(const void *ptr, unsigned long len,
> + bool to_user, const char *type)
> +{
> + pr_emerg("kernel memory %s attempt detected %s %p (%s) (%lu
> bytes)\n",
> + to_user ? "exposure" : "overwrite",
> + to_user ? "from" : "to", ptr, type ? : "unknown",
> len);
> + dump_stack();
> + do_group_exit(SIGKILL);
> +}
> +
> +/* Returns true if any portion of [ptr,ptr+n) over laps with
> [low,high). */
> +static bool overlaps(const void *ptr, unsigned long n, unsigned long
> low,
> + unsigned long high)
> +{
> + unsigned long check_low = (uintptr_t)ptr;
> + unsigned long check_high = check_low + n;
> +
> + /* Does not overlap if entirely above or entirely below. */
> + if (check_low >= high || check_high < low)
> + return false;
> +
> + return true;
> +}
> +
> +/* Is this address range in the kernel text area? */
> +static inline const char *check_kernel_text_object(const void *ptr,
> + unsigned long n)
> +{
> + unsigned long textlow = (unsigned long)_stext;
> + unsigned long texthigh = (unsigned long)_etext;
> +
> + if (overlaps(ptr, n, textlow, texthigh))
> + return "<kernel text>";
> +
> +#ifdef HAVE_ARCH_LINEAR_KERNEL_MAPPING
> + /* Check against linear mapping as well. */
> + if (overlaps(ptr, n, (unsigned long)__va(__pa(textlow)),
> + (unsigned long)__va(__pa(texthigh))))
> + return "<linear kernel text>";
> +#endif
> +
> + return NULL;
> +}
> +
> +static inline const char *check_bogus_address(const void *ptr,
> unsigned long n)
> +{
> + /* Reject if object wraps past end of memory. */
> + if (ptr + n < ptr)
> + return "<wrapped address>";
> +
> + /* Reject if NULL or ZERO-allocation. */
> + if (ZERO_OR_NULL_PTR(ptr))
> + return "<null>";
> +
> + return NULL;
> +}
> +
> +static inline const char *check_heap_object(const void *ptr,
> unsigned long n)
> +{
> + struct page *page, *endpage;
> + const void *end = ptr + n - 1;
> +
> + if (!virt_addr_valid(ptr))
> + return NULL;
> +
> + page = virt_to_head_page(ptr);
> +
> + /* Check slab allocator for flags and size. */
> + if (PageSlab(page))
> + return __check_heap_object(ptr, n, page);
> +
> + /* Is the object wholly within one base page? */
> + if (likely(((unsigned long)ptr & (unsigned long)PAGE_MASK)
> ==
> + ((unsigned long)end & (unsigned long)PAGE_MASK)))
> + return NULL;
> +
> + /* Allow if start and end are inside the same compound page.
> */
> + endpage = virt_to_head_page(end);
> + if (likely(endpage == page))
> + return NULL;
> +
> + /* Allow special areas, device memory, and sometimes kernel
> data. */
> + if (PageReserved(page) && PageReserved(endpage))
> + return NULL;
> +
> + /*
> + * Sometimes the kernel data regions are not marked
> Reserved. And
> + * sometimes [_sdata,_edata) does not cover rodata and/or
> bss,
> + * so check each range explicitly.
> + */
> +
> + /* Allow kernel data region (if not marked as Reserved). */
> + if (ptr >= (const void *)_sdata && end <= (const void
> *)_edata)
> + return NULL;
> +
> + /* Allow kernel rodata region (if not marked as Reserved).
> */
> + if (ptr >= (const void *)__start_rodata &&
> + end <= (const void *)__end_rodata)
> + return NULL;
> +
> + /* Allow kernel bss region (if not marked as Reserved). */
> + if (ptr >= (const void *)__bss_start &&
> + end <= (const void *)__bss_stop)
> + return NULL;
> +
> + /* Uh oh. The "object" spans several independently allocated
> pages. */
> + return "<spans multiple pages>";
> +}
> +
> +/*
> + * Validates that the given object is one of:
> + * - known safe heap object
> + * - known safe stack object
> + * - not in kernel text
> + */
> +void __check_object_size(const void *ptr, unsigned long n, bool
> to_user)
> +{
> + const char *err;
> +
> + /* Skip all tests if size is zero. */
> + if (!n)
> + return;
> +
> + /* Check for invalid addresses. */
> + err = check_bogus_address(ptr, n);
> + if (err)
> + goto report;
> +
> + /* Check for bad heap object. */
> + err = check_heap_object(ptr, n);
> + if (err)
> + goto report;
> +
> + /* Check for bad stack object. */
> + switch (check_stack_object(ptr, n)) {
> + case 0:
> + /* Object is not touching the current process stack.
> */
> + break;
> + case 1:
> + case 2:
> + /*
> + * Object is either in the correct frame (when it
> + * is possible to check) or just generally on the
> + * process stack (when frame checking not
> available).
> + */
> + return;
> + default:
> + err = "<process stack>";
> + goto report;
> + }
> +
> + /* Check for object in kernel to avoid text exposure. */
> + err = check_kernel_text_object(ptr, n);
> + if (!err)
> + return;
> +
> +report:
> + report_usercopy(ptr, n, to_user, err);
> +}
> +EXPORT_SYMBOL(__check_object_size);
> diff --git a/security/Kconfig b/security/Kconfig
> index 176758cdfa57..63340ad0b9f9 100644
> --- a/security/Kconfig
> +++ b/security/Kconfig
> @@ -118,6 +118,33 @@ config LSM_MMAP_MIN_ADDR
> this low address space will need the permission specific
> to the
> systems running LSM.
>
> +config HAVE_HARDENED_USERCOPY_ALLOCATOR
> + bool
> + help
> + The heap allocator implements __check_heap_object() for
> + validating memory ranges against heap object sizes in
> + support of CONFIG_HARDENED_USERCOPY.
> +
> +config HAVE_ARCH_HARDENED_USERCOPY
> + bool
> + help
> + The architecture supports CONFIG_HARDENED_USERCOPY by
> + calling check_object_size() just before performing the
> + userspace copies in the low level implementation of
> + copy_to_user() and copy_from_user().
> +
> +config HARDENED_USERCOPY
> + bool "Harden memory copies between kernel and userspace"
> + depends on HAVE_ARCH_HARDENED_USERCOPY
> + help
> + This option checks for obviously wrong memory regions when
> + copying memory to/from the kernel (via copy_to_user() and
> + copy_from_user() functions) by rejecting memory ranges
> that
> + are larger than the specified heap object, span multiple
> + separately allocates pages, are not on the process stack,
> + or are part of the kernel text. This kills entire classes
> + of heap overflow exploits and similar kernel memory
> exposures.
> +
> source security/selinux/Kconfig
> source security/smack/Kconfig
> source security/tomoyo/Kconfig
--
All Rights Reversed.
[toc] | [prev] | [next] | [standalone]
| From | Rik van Riel <riel@redhat.com> |
|---|---|
| Date | 2016-07-07 18:40 +0200 |
| Subject | Re: [PATCH 1/9] mm: Hardened usercopy |
| Message-ID | <rSkvf-ZX-3@gated-at.bofh.it> |
| In reply to | #1437977 |
[Multipart message — attachments visible in raw view] — view raw
On Wed, 2016-07-06 at 15:25 -0700, Kees Cook wrote:
>
> + /* Allow kernel rodata region (if not marked as Reserved).
> */
> + if (ptr >= (const void *)__start_rodata &&
> + end <= (const void *)__end_rodata)
> + return NULL;
>
One comment here.
__check_object_size gets "to_user" as an argument.
It may make sense to pass that to check_heap_object, and
only allow copy_to_user from rodata, never copy_from_user,
since that section should be read only.
> +void __check_object_size(const void *ptr, unsigned long n, bool
> to_user)
> +{
>
--
All Rights Reversed.
[toc] | [prev] | [next] | [standalone]
| From | Kees Cook <keescook@chromium.org> |
|---|---|
| Date | 2016-07-07 19:50 +0200 |
| Subject | Re: [PATCH 1/9] mm: Hardened usercopy |
| Message-ID | <rSlB0-1Eu-17@gated-at.bofh.it> |
| In reply to | #1438715 |
On Thu, Jul 7, 2016 at 12:35 PM, Rik van Riel <riel@redhat.com> wrote:
> On Wed, 2016-07-06 at 15:25 -0700, Kees Cook wrote:
>>
>> + /* Allow kernel rodata region (if not marked as Reserved).
>> */
>> + if (ptr >= (const void *)__start_rodata &&
>> + end <= (const void *)__end_rodata)
>> + return NULL;
>>
> One comment here.
>
> __check_object_size gets "to_user" as an argument.
>
> It may make sense to pass that to check_heap_object, and
> only allow copy_to_user from rodata, never copy_from_user,
> since that section should be read only.
Well, that's two votes for this extra check, but I'm still not sure
since it may already be allowed by the Reserved check, but I can
reorder things to _reject_ on rodata writes before the Reserved check,
etc.
I'll see what could work here...
-Kees
>
>> +void __check_object_size(const void *ptr, unsigned long n, bool
>> to_user)
>> +{
>>
>
> --
>
> All Rights Reversed.
--
Kees Cook
Chrome OS & Brillo Security
[toc] | [prev] | [next] | [standalone]
| From | Kees Cook <keescook@chromium.org> |
|---|---|
| Date | 2016-07-07 00:30 +0200 |
| Subject | [PATCH 2/9] x86/uaccess: Enable hardened usercopy |
| Message-ID | <rS3uq-6IW-33@gated-at.bofh.it> |
| In reply to | #1437975 |
Enables CONFIG_HARDENED_USERCOPY checks on x86. This is done both in
copy_*_user() and __copy_*_user() because copy_*_user() actually calls
down to _copy_*_user() and not __copy_*_user().
Based on code from PaX and grsecurity.
Signed-off-by: Kees Cook <keescook@chromium.org>
---
arch/x86/Kconfig | 2 ++
arch/x86/include/asm/uaccess.h | 10 ++++++----
arch/x86/include/asm/uaccess_32.h | 2 ++
arch/x86/include/asm/uaccess_64.h | 2 ++
4 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 0a7b885964ba..2a66b73a996d 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -80,11 +80,13 @@ config X86
select HAVE_ALIGNED_STRUCT_PAGE if SLUB
select HAVE_AOUT if X86_32
select HAVE_ARCH_AUDITSYSCALL
+ select HAVE_ARCH_HARDENED_USERCOPY
select HAVE_ARCH_HUGE_VMAP if X86_64 || X86_PAE
select HAVE_ARCH_JUMP_LABEL
select HAVE_ARCH_KASAN if X86_64 && SPARSEMEM_VMEMMAP
select HAVE_ARCH_KGDB
select HAVE_ARCH_KMEMCHECK
+ select HAVE_ARCH_LINEAR_KERNEL_MAPPING if X86_64
select HAVE_ARCH_MMAP_RND_BITS if MMU
select HAVE_ARCH_MMAP_RND_COMPAT_BITS if MMU && COMPAT
select HAVE_ARCH_SECCOMP_FILTER
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index 2982387ba817..aa9cc58409c6 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -742,9 +742,10 @@ copy_from_user(void *to, const void __user *from, unsigned long n)
* case, and do only runtime checking for non-constant sizes.
*/
- if (likely(sz < 0 || sz >= n))
+ if (likely(sz < 0 || sz >= n)) {
+ check_object_size(to, n, false);
n = _copy_from_user(to, from, n);
- else if(__builtin_constant_p(n))
+ } else if(__builtin_constant_p(n))
copy_from_user_overflow();
else
__copy_from_user_overflow(sz, n);
@@ -762,9 +763,10 @@ copy_to_user(void __user *to, const void *from, unsigned long n)
might_fault();
/* See the comment in copy_from_user() above. */
- if (likely(sz < 0 || sz >= n))
+ if (likely(sz < 0 || sz >= n)) {
+ check_object_size(from, n, true);
n = _copy_to_user(to, from, n);
- else if(__builtin_constant_p(n))
+ } else if(__builtin_constant_p(n))
copy_to_user_overflow();
else
__copy_to_user_overflow(sz, n);
diff --git a/arch/x86/include/asm/uaccess_32.h b/arch/x86/include/asm/uaccess_32.h
index 4b32da24faaf..7d3bdd1ed697 100644
--- a/arch/x86/include/asm/uaccess_32.h
+++ b/arch/x86/include/asm/uaccess_32.h
@@ -37,6 +37,7 @@ 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)
{
+ check_object_size(from, n, true);
return __copy_to_user_ll(to, from, n);
}
@@ -95,6 +96,7 @@ static __always_inline unsigned long
__copy_from_user(void *to, const void __user *from, unsigned long n)
{
might_fault();
+ check_object_size(to, n, false);
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 2eac2aa3e37f..673059a109fe 100644
--- a/arch/x86/include/asm/uaccess_64.h
+++ b/arch/x86/include/asm/uaccess_64.h
@@ -54,6 +54,7 @@ int __copy_from_user_nocheck(void *dst, const void __user *src, unsigned size)
{
int ret = 0;
+ check_object_size(dst, size, false);
if (!__builtin_constant_p(size))
return copy_user_generic(dst, (__force void *)src, size);
switch (size) {
@@ -119,6 +120,7 @@ int __copy_to_user_nocheck(void __user *dst, const void *src, unsigned size)
{
int ret = 0;
+ check_object_size(src, size, true);
if (!__builtin_constant_p(size))
return copy_user_generic((__force void *)dst, src, size);
switch (size) {
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Kees Cook <keescook@chromium.org> |
|---|---|
| Date | 2016-07-07 00:30 +0200 |
| Subject | [PATCH 7/9] sparc/uaccess: Enable hardened usercopy |
| Message-ID | <rS3uq-6IW-35@gated-at.bofh.it> |
| In reply to | #1437975 |
Enables CONFIG_HARDENED_USERCOPY checks on sparc.
Based on code from PaX and grsecurity.
Signed-off-by: Kees Cook <keescook@chromium.org>
---
arch/sparc/Kconfig | 1 +
arch/sparc/include/asm/uaccess_32.h | 14 ++++++++++----
arch/sparc/include/asm/uaccess_64.h | 11 +++++++++--
3 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index 546293d9e6c5..59b09600dd32 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -43,6 +43,7 @@ config SPARC
select OLD_SIGSUSPEND
select ARCH_HAS_SG_CHAIN
select CPU_NO_EFFICIENT_FFS
+ select HAVE_ARCH_HARDENED_USERCOPY
config SPARC32
def_bool !64BIT
diff --git a/arch/sparc/include/asm/uaccess_32.h b/arch/sparc/include/asm/uaccess_32.h
index 57aca2792d29..341a5a133f48 100644
--- a/arch/sparc/include/asm/uaccess_32.h
+++ b/arch/sparc/include/asm/uaccess_32.h
@@ -248,22 +248,28 @@ 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)
{
- if (n && __access_ok((unsigned long) to, n))
+ 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
return n;
}
static inline unsigned long __copy_to_user(void __user *to, const void *from, unsigned long n)
{
+ if (!__builtin_constant_p(n))
+ check_object_size(from, n, true);
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)
{
- if (n && __access_ok((unsigned long) from, n))
+ 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
return n;
}
diff --git a/arch/sparc/include/asm/uaccess_64.h b/arch/sparc/include/asm/uaccess_64.h
index e9a51d64974d..8bda94fab8e8 100644
--- a/arch/sparc/include/asm/uaccess_64.h
+++ b/arch/sparc/include/asm/uaccess_64.h
@@ -210,8 +210,12 @@ 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)
{
- unsigned long ret = ___copy_from_user(to, from, size);
+ unsigned long ret;
+ if (!__builtin_constant_p(size))
+ check_object_size(to, size, false);
+
+ ret = ___copy_from_user(to, from, size);
if (unlikely(ret))
ret = copy_from_user_fixup(to, from, size);
@@ -227,8 +231,11 @@ 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)
{
- unsigned long ret = ___copy_to_user(to, from, size);
+ unsigned long ret;
+ if (!__builtin_constant_p(size))
+ check_object_size(from, size, true);
+ ret = ___copy_to_user(to, from, size);
if (unlikely(ret))
ret = copy_to_user_fixup(to, from, size);
return ret;
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Christian Borntraeger <borntraeger@de.ibm.com> |
|---|---|
| Date | 2016-07-07 09:40 +0200 |
| Message-ID | <rSc4G-3S8-37@gated-at.bofh.it> |
| In reply to | #1437975 |
On 07/07/2016 12:25 AM, Kees Cook wrote: > Hi, > > This is a start of the mainline port of PAX_USERCOPY[1]. After I started > writing tests (now in lkdtm in -next) for Casey's earlier port[2], I > kept tweaking things further and further until I ended up with a whole > new patch series. To that end, I took Rik's feedback and made a number > of other changes and clean-ups as well. > > Based on my understanding, PAX_USERCOPY was designed to catch a few > classes of flaws around the use of copy_to_user()/copy_from_user(). These > changes don't touch get_user() and put_user(), since these operate on > constant sized lengths, and tend to be much less vulnerable. There > are effectively three distinct protections in the whole series, > each of which I've given a separate CONFIG, though this patch set is > only the first of the three intended protections. (Generally speaking, > PAX_USERCOPY covers what I'm calling CONFIG_HARDENED_USERCOPY (this) and > CONFIG_HARDENED_USERCOPY_WHITELIST (future), and PAX_USERCOPY_SLABS covers > CONFIG_HARDENED_USERCOPY_SPLIT_KMALLOC (future).) > > This series, which adds CONFIG_HARDENED_USERCOPY, checks that objects > being copied to/from userspace meet certain criteria: > - if address is a heap object, the size must not exceed the object's > allocated size. (This will catch all kinds of heap overflow flaws.) > - if address range is in the current process stack, it must be within the > current stack frame (if such checking is possible) or at least entirely > within the current process's stack. (This could catch large lengths that > would have extended beyond the current process stack, or overflows if > their length extends back into the original stack.) > - if the address range is part of kernel data, rodata, or bss, allow it. > - if address range is page-allocated, that it doesn't span multiple > allocations. > - if address is within the kernel text, reject it. > - everything else is accepted > > The patches in the series are: > - The core copy_to/from_user() checks, without the slab object checks: > 1- mm: Hardened usercopy > - Per-arch enablement of the protection: > 2- x86/uaccess: Enable hardened usercopy > 3- ARM: uaccess: Enable hardened usercopy > 4- arm64/uaccess: Enable hardened usercopy > 5- ia64/uaccess: Enable hardened usercopy > 6- powerpc/uaccess: Enable hardened usercopy > 7- sparc/uaccess: Enable hardened usercopy Was there a reason why you did not change s390?
[toc] | [prev] | [next] | [standalone]
| From | Kees Cook <keescook@chromium.org> |
|---|---|
| Date | 2016-07-07 19:30 +0200 |
| Message-ID | <rSlhE-1y4-13@gated-at.bofh.it> |
| In reply to | #1438236 |
On Thu, Jul 7, 2016 at 3:30 AM, Christian Borntraeger <borntraeger@de.ibm.com> wrote: > On 07/07/2016 12:25 AM, Kees Cook wrote: >> Hi, >> >> This is a start of the mainline port of PAX_USERCOPY[1]. After I started >> writing tests (now in lkdtm in -next) for Casey's earlier port[2], I >> kept tweaking things further and further until I ended up with a whole >> new patch series. To that end, I took Rik's feedback and made a number >> of other changes and clean-ups as well. >> >> Based on my understanding, PAX_USERCOPY was designed to catch a few >> classes of flaws around the use of copy_to_user()/copy_from_user(). These >> changes don't touch get_user() and put_user(), since these operate on >> constant sized lengths, and tend to be much less vulnerable. There >> are effectively three distinct protections in the whole series, >> each of which I've given a separate CONFIG, though this patch set is >> only the first of the three intended protections. (Generally speaking, >> PAX_USERCOPY covers what I'm calling CONFIG_HARDENED_USERCOPY (this) and >> CONFIG_HARDENED_USERCOPY_WHITELIST (future), and PAX_USERCOPY_SLABS covers >> CONFIG_HARDENED_USERCOPY_SPLIT_KMALLOC (future).) >> >> This series, which adds CONFIG_HARDENED_USERCOPY, checks that objects >> being copied to/from userspace meet certain criteria: >> - if address is a heap object, the size must not exceed the object's >> allocated size. (This will catch all kinds of heap overflow flaws.) >> - if address range is in the current process stack, it must be within the >> current stack frame (if such checking is possible) or at least entirely >> within the current process's stack. (This could catch large lengths that >> would have extended beyond the current process stack, or overflows if >> their length extends back into the original stack.) >> - if the address range is part of kernel data, rodata, or bss, allow it. >> - if address range is page-allocated, that it doesn't span multiple >> allocations. >> - if address is within the kernel text, reject it. >> - everything else is accepted >> >> The patches in the series are: >> - The core copy_to/from_user() checks, without the slab object checks: >> 1- mm: Hardened usercopy >> - Per-arch enablement of the protection: >> 2- x86/uaccess: Enable hardened usercopy >> 3- ARM: uaccess: Enable hardened usercopy >> 4- arm64/uaccess: Enable hardened usercopy >> 5- ia64/uaccess: Enable hardened usercopy >> 6- powerpc/uaccess: Enable hardened usercopy >> 7- sparc/uaccess: Enable hardened usercopy > > Was there a reason why you did not change s390? No reason -- just didn't have a good build setup for testing it. (Everything but arm64 was already in grsecurity, and I was able to build-test arm64 when I added it there.) I would love to include s390 too! -Kees -- Kees Cook Chrome OS & Brillo Security
[toc] | [prev] | [next] | [standalone]
| From | Ingo Molnar <mingo@kernel.org> |
|---|---|
| Date | 2016-07-08 10:50 +0200 |
| Message-ID | <rSzDX-2k6-7@gated-at.bofh.it> |
| In reply to | #1437975 |
* Kees Cook <keescook@chromium.org> wrote: > - I couldn't detect a measurable performance change with these features > enabled. Kernel build times were unchanged, hackbench was unchanged, > etc. I think we could flip this to "on by default" at some point. Could you please try to find some syscall workload that does many small user copies and thus excercises this code path aggressively? If that measurement works out fine then I'd prefer to enable these security checks by default. Thaks, Ingo
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | linux.kernel
csiph-web