diff options
author | Kees Cook <keescook@chromium.org> | 2016-06-23 18:24:05 -0400 |
---|---|---|
committer | Kees Cook <keescook@chromium.org> | 2016-07-26 17:43:54 -0400 |
commit | ed18adc1cdd00a5c55a20fbdaed4804660772281 (patch) | |
tree | 652ad77b2d5e9ed24eb4782c2b270c7e7b4fc0c1 /mm | |
parent | 04385fc5e8fffed84425d909a783c0f0c587d847 (diff) |
mm: SLUB hardened usercopy support
Under CONFIG_HARDENED_USERCOPY, this adds object size checking to the
SLUB allocator to catch any copies that may span objects. Includes a
redzone handling fix discovered by Michael Ellerman.
Based on code from PaX and grsecurity.
Signed-off-by: Kees Cook <keescook@chromium.org>
Tested-by: Michael Ellerman <mpe@ellerman.id.au>
Reviwed-by: Laura Abbott <labbott@redhat.com>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/slub.c | 40 |
1 files changed, 40 insertions, 0 deletions
@@ -3614,6 +3614,46 @@ void *__kmalloc_node(size_t size, gfp_t flags, int node) | |||
3614 | EXPORT_SYMBOL(__kmalloc_node); | 3614 | EXPORT_SYMBOL(__kmalloc_node); |
3615 | #endif | 3615 | #endif |
3616 | 3616 | ||
3617 | #ifdef CONFIG_HARDENED_USERCOPY | ||
3618 | /* | ||
3619 | * Rejects objects that are incorrectly sized. | ||
3620 | * | ||
3621 | * Returns NULL if check passes, otherwise const char * to name of cache | ||
3622 | * to indicate an error. | ||
3623 | */ | ||
3624 | const char *__check_heap_object(const void *ptr, unsigned long n, | ||
3625 | struct page *page) | ||
3626 | { | ||
3627 | struct kmem_cache *s; | ||
3628 | unsigned long offset; | ||
3629 | size_t object_size; | ||
3630 | |||
3631 | /* Find object and usable object size. */ | ||
3632 | s = page->slab_cache; | ||
3633 | object_size = slab_ksize(s); | ||
3634 | |||
3635 | /* Reject impossible pointers. */ | ||
3636 | if (ptr < page_address(page)) | ||
3637 | return s->name; | ||
3638 | |||
3639 | /* Find offset within object. */ | ||
3640 | offset = (ptr - page_address(page)) % s->size; | ||
3641 | |||
3642 | /* Adjust for redzone and reject if within the redzone. */ | ||
3643 | if (kmem_cache_debug(s) && s->flags & SLAB_RED_ZONE) { | ||
3644 | if (offset < s->red_left_pad) | ||
3645 | return s->name; | ||
3646 | offset -= s->red_left_pad; | ||
3647 | } | ||
3648 | |||
3649 | /* Allow address range falling entirely within object size. */ | ||
3650 | if (offset <= object_size && n <= object_size - offset) | ||
3651 | return NULL; | ||
3652 | |||
3653 | return s->name; | ||
3654 | } | ||
3655 | #endif /* CONFIG_HARDENED_USERCOPY */ | ||
3656 | |||
3617 | static size_t __ksize(const void *object) | 3657 | static size_t __ksize(const void *object) |
3618 | { | 3658 | { |
3619 | struct page *page; | 3659 | struct page *page; |