diff options
Diffstat (limited to 'sys-kernel/cairn-sources/files/5.9.6/hardened-patches/0057-slub-add-multi-purpose-random-canaries.patch')
| -rw-r--r-- | sys-kernel/cairn-sources/files/5.9.6/hardened-patches/0057-slub-add-multi-purpose-random-canaries.patch | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/sys-kernel/cairn-sources/files/5.9.6/hardened-patches/0057-slub-add-multi-purpose-random-canaries.patch b/sys-kernel/cairn-sources/files/5.9.6/hardened-patches/0057-slub-add-multi-purpose-random-canaries.patch new file mode 100644 index 000000000000..7c33605e7a1f --- /dev/null +++ b/sys-kernel/cairn-sources/files/5.9.6/hardened-patches/0057-slub-add-multi-purpose-random-canaries.patch @@ -0,0 +1,264 @@ +From 566c24b99229d4036ea95d461831761556f8ce4a Mon Sep 17 00:00:00 2001 +From: Daniel Micay <danielmicay@gmail.com> +Date: Wed, 3 May 2017 16:16:58 -0400 +Subject: [PATCH 057/108] slub: add multi-purpose random canaries + +From the configuration option: + + Place canaries at the end of kernel slab allocations, sacrificing + some performance and memory usage for security. + + Canaries can detect some forms of heap corruption when allocations + are freed and as part of the HARDENED_USERCOPY feature. It provides + basic use-after-free detection for HARDENED_USERCOPY. + + Canaries absorb small overflows (rendering them harmless), mitigate + non-NUL terminated C string overflows on 64-bit via a guaranteed zero + byte and provide basic double-free detection. + +Signed-off-by: Daniel Micay <danielmicay@gmail.com> +--- + include/linux/slub_def.h | 5 +++ + init/Kconfig | 17 ++++++++++ + mm/slab.h | 2 +- + mm/slub.c | 69 ++++++++++++++++++++++++++++++++++++++-- + 4 files changed, 89 insertions(+), 4 deletions(-) + +diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h +index 1be0ed5befa1..c71cf30b5987 100644 +--- a/include/linux/slub_def.h ++++ b/include/linux/slub_def.h +@@ -113,6 +113,11 @@ struct kmem_cache { + unsigned long random; + #endif + ++#ifdef CONFIG_SLAB_CANARY ++ unsigned long random_active; ++ unsigned long random_inactive; ++#endif ++ + #ifdef CONFIG_NUMA + /* + * Defragmentation by allocating from a remote node. +diff --git a/init/Kconfig b/init/Kconfig +index 28f20aeebc4d..7902a8106ddf 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1932,6 +1932,23 @@ config SLAB_FREELIST_HARDENED + sanity-checking than others. This option is most effective with + CONFIG_SLUB. + ++config SLAB_CANARY ++ depends on SLUB ++ depends on !SLAB_MERGE_DEFAULT ++ bool "SLAB canaries" ++ default y ++ help ++ Place canaries at the end of kernel slab allocations, sacrificing ++ some performance and memory usage for security. ++ ++ Canaries can detect some forms of heap corruption when allocations ++ are freed and as part of the HARDENED_USERCOPY feature. It provides ++ basic use-after-free detection for HARDENED_USERCOPY. ++ ++ Canaries absorb small overflows (rendering them harmless), mitigate ++ non-NUL terminated C string overflows on 64-bit via a guaranteed zero ++ byte and provide basic double-free detection. ++ + config SHUFFLE_PAGE_ALLOCATOR + bool "Page allocator randomization" + default SLAB_FREELIST_RANDOM && ACPI_NUMA +diff --git a/mm/slab.h b/mm/slab.h +index 0f8f83971fc1..af30573d6ce6 100644 +--- a/mm/slab.h ++++ b/mm/slab.h +@@ -498,7 +498,7 @@ static inline size_t slab_ksize(const struct kmem_cache *s) + * back there or track user information then we can + * only use the space before that information. + */ +- if (s->flags & (SLAB_TYPESAFE_BY_RCU | SLAB_STORE_USER)) ++ if ((s->flags & (SLAB_TYPESAFE_BY_RCU | SLAB_STORE_USER)) || IS_ENABLED(CONFIG_SLAB_CANARY)) + return s->inuse; + /* + * Else we can use all the padding etc for the allocation +diff --git a/mm/slub.c b/mm/slub.c +index 3b56a69f58cb..c6c6685b6f81 100644 +--- a/mm/slub.c ++++ b/mm/slub.c +@@ -569,6 +569,33 @@ static inline unsigned int get_info_end(struct kmem_cache *s) + return s->inuse; + } + ++#ifdef CONFIG_SLAB_CANARY ++static inline unsigned long *get_canary(struct kmem_cache *s, void *object) ++{ ++ return object + get_info_end(s); ++} ++ ++static inline unsigned long get_canary_value(const void *canary, unsigned long value) ++{ ++ return (value ^ (unsigned long)canary) & CANARY_MASK; ++} ++ ++static inline void set_canary(struct kmem_cache *s, void *object, unsigned long value) ++{ ++ unsigned long *canary = get_canary(s, object); ++ *canary = get_canary_value(canary, value); ++} ++ ++static inline void check_canary(struct kmem_cache *s, void *object, unsigned long value) ++{ ++ unsigned long *canary = get_canary(s, object); ++ BUG_ON(*canary != get_canary_value(canary, value)); ++} ++#else ++#define set_canary(s, object, value) ++#define check_canary(s, object, value) ++#endif ++ + static struct track *get_track(struct kmem_cache *s, void *object, + enum track_item alloc) + { +@@ -576,6 +603,9 @@ static struct track *get_track(struct kmem_cache *s, void *object, + + p = object + get_info_end(s); + ++ if (IS_ENABLED(CONFIG_SLAB_CANARY)) ++ p = (void *)p + sizeof(void *); ++ + return p + alloc; + } + +@@ -717,6 +747,9 @@ static void print_trailer(struct kmem_cache *s, struct page *page, u8 *p) + + off = get_info_end(s); + ++ if (IS_ENABLED(CONFIG_SLAB_CANARY)) ++ off += sizeof(void *); ++ + if (s->flags & SLAB_STORE_USER) + off += 2 * sizeof(struct track); + +@@ -825,8 +858,9 @@ static int check_bytes_and_report(struct kmem_cache *s, struct page *page, + * Meta data starts here. + * + * A. Free pointer (if we cannot overwrite object on free) +- * B. Tracking data for SLAB_STORE_USER +- * C. Padding to reach required alignment boundary or at mininum ++ * B. Canary for SLAB_CANARY ++ * C. Tracking data for SLAB_STORE_USER ++ * D. Padding to reach required alignment boundary or at mininum + * one word if debugging is on to be able to detect writes + * before the word boundary. + * +@@ -844,6 +878,9 @@ static int check_pad_bytes(struct kmem_cache *s, struct page *page, u8 *p) + { + unsigned long off = get_info_end(s); /* The end of info */ + ++ if (IS_ENABLED(CONFIG_SLAB_CANARY)) ++ off += sizeof(void *); ++ + if (s->flags & SLAB_STORE_USER) + /* We also have user information there */ + off += 2 * sizeof(struct track); +@@ -1567,6 +1604,8 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s, + object = next; + next = get_freepointer(s, object); + ++ check_canary(s, object, s->random_active); ++ + if (slab_want_init_on_free(s)) { + /* + * Clear the object and the metadata, but don't touch +@@ -1580,6 +1619,9 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s, + if (!IS_ENABLED(CONFIG_SLAB_SANITIZE_VERIFY) && s->ctor) + s->ctor(object); + } ++ ++ set_canary(s, object, s->random_inactive); ++ + /* If object's reuse doesn't have to be delayed */ + if (!slab_free_hook(s, object)) { + /* Move object to the new freelist */ +@@ -1611,6 +1653,7 @@ static void *setup_object(struct kmem_cache *s, struct page *page, + void *object) + { + setup_object_debug(s, page, object); ++ set_canary(s, object, s->random_inactive); + object = kasan_init_slab_obj(s, object); + if (unlikely(s->ctor) && !has_sanitize_verify(s)) { + kasan_unpoison_object_data(s, object); +@@ -2912,6 +2955,11 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s, + } else if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object) + memset(object, 0, s->object_size); + ++ if (object) { ++ check_canary(s, object, s->random_inactive); ++ set_canary(s, object, s->random_active); ++ } ++ + slab_post_alloc_hook(s, objcg, gfpflags, 1, &object); + + return object; +@@ -3298,7 +3346,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, + void **p) + { + struct kmem_cache_cpu *c; +- int i; ++ int i, k; + struct obj_cgroup *objcg = NULL; + + /* memcg and kmem_cache debug support */ +@@ -3368,6 +3416,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, + memset(p[j], 0, s->object_size); + } + ++ for (k = 0; k < i; k++) { ++ check_canary(s, p[k], s->random_inactive); ++ set_canary(s, p[k], s->random_active); ++ } ++ + /* memcg and kmem_cache debug support */ + slab_post_alloc_hook(s, objcg, flags, size, p); + return i; +@@ -3569,6 +3622,7 @@ static void early_kmem_cache_node_alloc(int node) + init_object(kmem_cache_node, n, SLUB_RED_ACTIVE); + init_tracking(kmem_cache_node, n); + #endif ++ set_canary(kmem_cache_node, n, kmem_cache_node->random_active); + n = kasan_kmalloc(kmem_cache_node, n, sizeof(struct kmem_cache_node), + GFP_KERNEL); + page->freelist = get_freepointer(kmem_cache_node, n); +@@ -3749,6 +3803,9 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order) + s->offset = ALIGN(freepointer_area / 2, sizeof(void *)); + } + ++ if (IS_ENABLED(CONFIG_SLAB_CANARY)) ++ size += sizeof(void *); ++ + #ifdef CONFIG_SLUB_DEBUG + if (flags & SLAB_STORE_USER) + /* +@@ -3822,6 +3879,10 @@ static int kmem_cache_open(struct kmem_cache *s, slab_flags_t flags) + #ifdef CONFIG_SLAB_FREELIST_HARDENED + s->random = get_random_long(); + #endif ++#ifdef CONFIG_SLAB_CANARY ++ s->random_active = get_random_long(); ++ s->random_inactive = get_random_long(); ++#endif + + if (!calculate_sizes(s, -1)) + goto error; +@@ -4095,6 +4156,8 @@ void __check_heap_object(const void *ptr, unsigned long n, struct page *page, + offset -= s->red_left_pad; + } + ++ check_canary(s, (void *)ptr - offset, s->random_active); ++ + /* Allow address range falling entirely within usercopy region. */ + if (offset >= s->useroffset && + offset - s->useroffset <= s->usersize && +-- +2.29.2 + |
