From c607904c264da5c24d9ef98c37b8eb4170030882 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 12:09:17 -0400 Subject: [PATCH 01/34] enable CONFIG_PANIC_ON_OOPS by default Signed-off-by: Daniel Micay --- lib/Kconfig.debug | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index fa16c0f82d6e4..8aa0f58a1547a 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -916,6 +916,7 @@ endmenu # "Debug lockups and hangs" config PANIC_ON_OOPS bool "Panic on Oops" + default y help Say Y here to enable the kernel to panic when it oopses. This has the same effect as setting oops=panic on the kernel command @@ -925,7 +926,7 @@ config PANIC_ON_OOPS anything erroneous after an oops which could result in data corruption or other issues. - Say N if unsure. + Say Y if unsure. config PANIC_ON_OOPS_VALUE int From 313bc9a3c374971b4fa2f15633c400ab20531565 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 12:10:57 -0400 Subject: [PATCH 02/34] enable CONFIG_DEBUG_LIST by default Signed-off-by: Daniel Micay --- lib/Kconfig.debug | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 8aa0f58a1547a..e1cb9d088e58b 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1250,6 +1250,7 @@ config DEBUG_BUGVERBOSE config DEBUG_LIST bool "Debug linked list manipulation" depends on DEBUG_KERNEL || BUG_ON_DATA_CORRUPTION + default y help Enable this to turn on extended checks in the linked-list walking routines. From ae12fbf7527c6f4de3292ecbf176f596f9aa3886 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 19:43:38 -0400 Subject: [PATCH 03/34] enable SLAB_FREELIST_RANDOM by default --- init/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index a92f27da4a272..320d1bdef891b 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1853,7 +1853,7 @@ config SLOB endchoice config SLAB_FREELIST_RANDOM - default n + default y depends on SLAB || SLUB bool "SLAB freelist randomization" help From cdb398d96c702a77adb192bebd4c93747f48e54f Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 12:06:14 -0400 Subject: [PATCH 04/34] set kptr_restrict=2 by default Signed-off-by: Daniel Micay --- lib/vsprintf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vsprintf.c b/lib/vsprintf.c index e3bf4e0f10b56..f5a259a822610 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1470,7 +1470,7 @@ char *flags_string(char *buf, char *end, void *flags_ptr, const char *fmt) return format_flags(buf, end, flags, names); } -int kptr_restrict __read_mostly; +int kptr_restrict __read_mostly = 2; /* * Show a '%p' thing. A kernel extension is that the '%p' is followed From e32ea1ab1f26cc37858f0cddc33749ec52054008 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 11:35:35 -0400 Subject: [PATCH 05/34] add __ro_after_init to slab_nomerge Signed-off-by: Daniel Micay --- mm/slab_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/slab_common.c b/mm/slab_common.c index 09d0e849b07f4..08e0e52acafc7 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -49,7 +49,7 @@ static DECLARE_WORK(slab_caches_to_rcu_destroy_work, * Merge control. If this is set then no merging of slab caches will occur. * (Could be removed. This was introduced to pacify the merge skeptics.) */ -static int slab_nomerge; +static int __ro_after_init slab_nomerge; static int __init setup_slab_nomerge(char *str) { From b835fbc123e8f13e0cc06b6b1e5954ef9ebb52df Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 11:44:21 -0400 Subject: [PATCH 06/34] add a SLAB_HARDENED configuration option Signed-off-by: Daniel Micay --- init/Kconfig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/init/Kconfig b/init/Kconfig index 320d1bdef891b..439066fb96bd1 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1861,6 +1861,14 @@ config SLAB_FREELIST_RANDOM security feature reduces the predictability of the kernel slab allocator against heap overflows. +config SLAB_HARDENED + default y + depends on SLAB_FREELIST_RANDOM && SLUB + bool "Hardened SLAB infrastructure" + help + Make minor performance sacrifices to harden the kernel slab + allocator. + config SLUB_CPU_PARTIAL default y depends on SLUB && SMP From 8f36fe91836808948856a8e307a2352d71e0e1c3 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 11:37:48 -0400 Subject: [PATCH 07/34] disable slab merging Signed-off-by: Daniel Micay --- mm/slab_common.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mm/slab_common.c b/mm/slab_common.c index 08e0e52acafc7..c60545facd1da 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -49,7 +49,11 @@ static DECLARE_WORK(slab_caches_to_rcu_destroy_work, * Merge control. If this is set then no merging of slab caches will occur. * (Could be removed. This was introduced to pacify the merge skeptics.) */ +#ifdef CONFIG_SLAB_HARDENED +static int __ro_after_init slab_nomerge = 1; +#else static int __ro_after_init slab_nomerge; +#endif static int __init setup_slab_nomerge(char *str) { From b2383074feee7cb0f567c0e4808e6389fac7b098 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 11:38:54 -0400 Subject: [PATCH 08/34] add missing cache_from_obj !PageSlab check Taken from PaX. Signed-off-by: Daniel Micay --- mm/slab.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mm/slab.h b/mm/slab.h index 65e7c3fcac727..30006c51fe6ed 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -384,6 +384,9 @@ static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x) return s; page = virt_to_head_page(x); +#ifdef CONFIG_SLAB_HARDENED + BUG_ON(!PageSlab(page)); +#endif cachep = page->slab_cache; if (slab_equal_or_root(cachep, s)) return cachep; From 133cc263a1b0a98452f4dfb2b2b38a977a66fbd3 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Thu, 30 Mar 2017 23:05:53 -0400 Subject: [PATCH 09/34] real slab_equal_or_root check for !MEMCG_KMEM Signed-off-by: Daniel Micay --- mm/slab.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mm/slab.h b/mm/slab.h index 30006c51fe6ed..a7526d60cd58e 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -327,7 +327,11 @@ static inline bool is_root_cache(struct kmem_cache *s) static inline bool slab_equal_or_root(struct kmem_cache *s, struct kmem_cache *p) { +#ifdef CONFIG_SLAB_HARDENED + return p == s; +#else return true; +#endif } static inline const char *cache_name(struct kmem_cache *s) From e45250a70671073209ccae238282aea687183701 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 11:50:53 -0400 Subject: [PATCH 10/34] bug on kmem_cache_free with the wrong cache At least when CONFIG_BUG_ON_DATA_CORRUPTION is enabled. Signed-off-by: Daniel Micay --- mm/slab.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mm/slab.h b/mm/slab.h index a7526d60cd58e..5940713d98422 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -397,7 +397,11 @@ static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x) pr_err("%s: Wrong slab cache. %s but object is from %s\n", __func__, s->name, cachep->name); +#ifdef CONFIG_BUG_ON_DATA_CORRUPTION + BUG_ON(1); +#else WARN_ON_ONCE(1); +#endif return s; } From 6e098efede55b7e0e335658dd1533808a50b3463 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 11:54:49 -0400 Subject: [PATCH 11/34] always perform cache_from_obj consistency checks Signed-off-by: Daniel Micay --- mm/slab.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mm/slab.h b/mm/slab.h index 5940713d98422..5f649511ec17b 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -383,7 +383,8 @@ static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x) * to not do even the assignment. In that case, slab_equal_or_root * will also be a constant. */ - if (!memcg_kmem_enabled() && + if (IS_ENABLED(CONFIG_SLAB_HARDENED) && + !memcg_kmem_enabled() && !unlikely(s->flags & SLAB_CONSISTENCY_CHECKS)) return s; From eb54ce720c61a88d03221405659e54a93c06e36b Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 11:57:35 -0400 Subject: [PATCH 12/34] bug on !PageSlab && !PageCompound in ksize Signed-off-by: Daniel Micay --- mm/slub.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mm/slub.c b/mm/slub.c index 7f4bc7027ed53..c2eb193a6263b 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -3844,7 +3844,11 @@ static size_t __ksize(const void *object) page = virt_to_head_page(object); if (unlikely(!PageSlab(page))) { +#ifdef CONFIG_BUG_ON_DATA_CORRUPTION + BUG_ON(!PageCompound(page)); +#else WARN_ON(!PageCompound(page)); +#endif return PAGE_SIZE << compound_order(page); } From 6efe84cdb88f73e8b8c59b59a8ea46fa4b1bdab1 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 12:02:56 -0400 Subject: [PATCH 13/34] add kmalloc alloc_size attributes Signed-off-by: Daniel Micay --- include/linux/slab.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index 3c37a8c519215..35940cfd14e7d 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -327,7 +327,7 @@ static __always_inline int kmalloc_index(size_t size) } #endif /* !CONFIG_SLOB */ -void *__kmalloc(size_t size, gfp_t flags) __assume_kmalloc_alignment __malloc; +void *__kmalloc(size_t size, gfp_t flags) __assume_kmalloc_alignment __malloc __attribute__((alloc_size(1))); void *kmem_cache_alloc(struct kmem_cache *, gfp_t flags) __assume_slab_alignment __malloc; void kmem_cache_free(struct kmem_cache *, void *); @@ -351,7 +351,7 @@ static __always_inline void kfree_bulk(size_t size, void **p) } #ifdef CONFIG_NUMA -void *__kmalloc_node(size_t size, gfp_t flags, int node) __assume_kmalloc_alignment __malloc; +void *__kmalloc_node(size_t size, gfp_t flags, int node) __assume_kmalloc_alignment __malloc __attribute__((alloc_size(1))); void *kmem_cache_alloc_node(struct kmem_cache *, gfp_t flags, int node) __assume_slab_alignment __malloc; #else static __always_inline void *__kmalloc_node(size_t size, gfp_t flags, int node) @@ -475,7 +475,7 @@ static __always_inline void *kmalloc_large(size_t size, gfp_t flags) * for general use, and so are not documented here. For a full list of * potential flags, always refer to linux/gfp.h. */ -static __always_inline void *kmalloc(size_t size, gfp_t flags) +static __always_inline __attribute__((alloc_size(1))) void *kmalloc(size_t size, gfp_t flags) { if (__builtin_constant_p(size)) { if (size > KMALLOC_MAX_CACHE_SIZE) @@ -515,7 +515,7 @@ static __always_inline int kmalloc_size(int n) return 0; } -static __always_inline void *kmalloc_node(size_t size, gfp_t flags, int node) +static __always_inline __attribute__((alloc_size(1))) void *kmalloc_node(size_t size, gfp_t flags, int node) { #ifndef CONFIG_SLOB if (__builtin_constant_p(size) && From d342da362c5f852c1666dce461bc82521b6711e4 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 12:04:03 -0400 Subject: [PATCH 14/34] add vmalloc alloc_size attributes Signed-off-by: Daniel Micay --- include/linux/vmalloc.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index d68edffbf142c..764d10707d87e 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -67,19 +67,19 @@ static inline void vmalloc_init(void) } #endif -extern void *vmalloc(unsigned long size); -extern void *vzalloc(unsigned long size); -extern void *vmalloc_user(unsigned long size); -extern void *vmalloc_node(unsigned long size, int node); -extern void *vzalloc_node(unsigned long size, int node); -extern void *vmalloc_exec(unsigned long size); -extern void *vmalloc_32(unsigned long size); -extern void *vmalloc_32_user(unsigned long size); -extern void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot); +extern void *vmalloc(unsigned long size) __attribute__((alloc_size(1))); +extern void *vzalloc(unsigned long size) __attribute__((alloc_size(1))); +extern void *vmalloc_user(unsigned long size) __attribute__((alloc_size(1))); +extern void *vmalloc_node(unsigned long size, int node) __attribute__((alloc_size(1))); +extern void *vzalloc_node(unsigned long size, int node) __attribute__((alloc_size(1))); +extern void *vmalloc_exec(unsigned long size) __attribute__((alloc_size(1))); +extern void *vmalloc_32(unsigned long size) __attribute__((alloc_size(1))); +extern void *vmalloc_32_user(unsigned long size) __attribute__((alloc_size(1))); +extern void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot) __attribute__((alloc_size(1))); extern void *__vmalloc_node_range(unsigned long size, unsigned long align, unsigned long start, unsigned long end, gfp_t gfp_mask, pgprot_t prot, unsigned long vm_flags, int node, - const void *caller); + const void *caller) __attribute__((alloc_size(1))); extern void vfree(const void *addr); extern void vfree_atomic(const void *addr); From e1f59d43bf7b89801bcb87c415c7b5f5d0c7f727 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 12:17:18 -0400 Subject: [PATCH 15/34] arm64: zero the leading stack canary byte Signed-off-by: Daniel Micay --- arch/arm64/include/asm/stackprotector.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/include/asm/stackprotector.h b/arch/arm64/include/asm/stackprotector.h index fe5e287dc56b7..818d1ad9440a4 100644 --- a/arch/arm64/include/asm/stackprotector.h +++ b/arch/arm64/include/asm/stackprotector.h @@ -31,6 +31,9 @@ static __always_inline void boot_init_stack_canary(void) get_random_bytes(&canary, sizeof(canary)); canary ^= LINUX_VERSION_CODE; + /* Sacrifice 8 bits of entropy to mitigate non-terminated C string overflows */ + canary &= ~(unsigned long)0xff; + current->stack_canary = canary; __stack_chk_guard = current->stack_canary; } From 624349a253b3c6090a798d741731cbc4052d773a Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 12:30:03 -0400 Subject: [PATCH 16/34] x86_64: zero the leading stack canary byte Signed-off-by: Daniel Micay --- arch/x86/include/asm/stackprotector.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/x86/include/asm/stackprotector.h b/arch/x86/include/asm/stackprotector.h index 58505f01962f3..04e408163a901 100644 --- a/arch/x86/include/asm/stackprotector.h +++ b/arch/x86/include/asm/stackprotector.h @@ -75,6 +75,11 @@ static __always_inline void boot_init_stack_canary(void) tsc = rdtsc(); canary += tsc + (tsc << 32UL); +#ifdef CONFIG_X86_64 + /* Sacrifice 8 bits of entropy to mitigate non-terminated C string overflows */ + canary &= ~(unsigned long)0xff; +#endif + current->stack_canary = canary; #ifdef CONFIG_X86_64 this_cpu_write(irq_stack_union.stack_canary, canary); From 0926d3f6fcec40b35e0291041858492680633c3e Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 12:28:59 -0400 Subject: [PATCH 17/34] use get_random_long for the per-task stack canary Signed-off-by: Daniel Micay --- kernel/fork.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/fork.c b/kernel/fork.c index 6c463c80e93de..a546bc725a17a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -536,7 +536,7 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) set_task_stack_end_magic(tsk); #ifdef CONFIG_CC_STACKPROTECTOR - tsk->stack_canary = get_random_int(); + tsk->stack_canary = get_random_long(); #endif /* From daaf36f7849d3533a84b3e19a0b73e3c8da68a9d Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 12:31:44 -0400 Subject: [PATCH 18/34] zero leading per-task stack canary byte on 64-bit Signed-off-by: Daniel Micay --- kernel/fork.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/fork.c b/kernel/fork.c index a546bc725a17a..5700346af96bd 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -537,6 +537,11 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) #ifdef CONFIG_CC_STACKPROTECTOR tsk->stack_canary = get_random_long(); + +#ifdef CONFIG_64BIT + /* Sacrifice 8 bits of entropy to mitigate non-terminated C string overflows */ + memset(&tsk->stack_canary, 0, 1); +#endif #endif /* From e1d45862a2f5c07611c46565acbcc9ec52510109 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 13:02:17 -0400 Subject: [PATCH 19/34] add slub free list XOR encryption Based on the grsecurity feature, but with a per-cache random value. Signed-off-by: Daniel Micay --- include/linux/slub_def.h | 4 ++++ mm/slub.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index 07ef550c66270..0258d6d74e9c4 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -93,6 +93,10 @@ struct kmem_cache { #endif #endif +#ifdef CONFIG_SLAB_HARDENED + unsigned long random; +#endif + #ifdef CONFIG_NUMA /* * Defragmentation by allocating from a remote node. diff --git a/mm/slub.c b/mm/slub.c index c2eb193a6263b..df617d6fdf8fb 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -240,28 +241,53 @@ static inline void stat(const struct kmem_cache *s, enum stat_item si) static inline void *get_freepointer(struct kmem_cache *s, void *object) { +#ifdef CONFIG_SLAB_HARDENED + unsigned long freepointer_addr = (unsigned long)object + s->offset; + return (void *)(*(unsigned long *)freepointer_addr ^ s->random ^ freepointer_addr); +#else return *(void **)(object + s->offset); +#endif } static void prefetch_freepointer(const struct kmem_cache *s, void *object) { +#ifdef CONFIG_SLAB_HARDENED + unsigned long freepointer_addr = (unsigned long)object + s->offset; + if (object) { + void **freepointer_ptr = (void **)(*(unsigned long *)freepointer_addr ^ s->random ^ freepointer_addr); + prefetch(freepointer_ptr); + } +#else prefetch(object + s->offset); +#endif } static inline void *get_freepointer_safe(struct kmem_cache *s, void *object) { + unsigned long __maybe_unused freepointer_addr; void *p; if (!debug_pagealloc_enabled()) return get_freepointer(s, object); +#if CONFIG_SLAB_HARDENED + freepointer_addr = (unsigned long)object + s->offset; + probe_kernel_read(&p, (void **)freepointer_addr, sizeof(p)); + return (void *)((unsigned long)p ^ s->random ^ freepointer_addr); +#else probe_kernel_read(&p, (void **)(object + s->offset), sizeof(p)); return p; +#endif } static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp) { +#ifdef CONFIG_SLAB_HARDENED + unsigned long freepointer_addr = (unsigned long)object + s->offset; + *(void **)freepointer_addr = (void *)((unsigned long)fp ^ s->random ^ freepointer_addr); +#else *(void **)(object + s->offset) = fp; +#endif } /* Loop over all objects in a slab */ @@ -3535,6 +3561,9 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order) static int kmem_cache_open(struct kmem_cache *s, unsigned long flags) { s->flags = kmem_cache_flags(s->size, flags, s->name, s->ctor); +#ifdef CONFIG_SLAB_HARDENED + s->random = get_random_long(); +#endif s->reserved = 0; if (need_reserve_slab_rcu && (s->flags & SLAB_DESTROY_BY_RCU)) From 7048d1bc5b076b3466261250427b0b33b77a2c1c Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Sat, 8 Apr 2017 22:36:42 -0400 Subject: [PATCH 20/34] add fortified string.h functions GNU C __builtin_*_chk intrinsics are avoided because they're only designed to detect write overflows and are overly complex. A single inline branch works for everything but strncat while those intrinsics would force the creation of a bunch of extra non-inline wrappers that aren't able to receive the detected source buffer size. As a future improvement, the fortified string functions can place a limit on reads from the source. Signed-off-by: Daniel Micay --- arch/x86/boot/compressed/misc.c | 5 + include/linux/string.h | 161 ++++++++++++++++++++++++++++++++ lib/string.c | 8 ++ security/Kconfig | 7 ++ 4 files changed, 181 insertions(+) diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c index b3c5a5f030ced..43691238a21d2 100644 --- a/arch/x86/boot/compressed/misc.c +++ b/arch/x86/boot/compressed/misc.c @@ -409,3 +409,8 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap, debug_putstr("done.\nBooting the kernel.\n"); return output; } + +void fortify_panic(const char *name) +{ + error("detected buffer overflow"); +} diff --git a/include/linux/string.h b/include/linux/string.h index 26b6f6a66f835..3bd429c9593ad 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -169,4 +169,165 @@ static inline const char *kbasename(const char *path) return tail ? tail + 1 : path; } +#define __FORTIFY_INLINE extern __always_inline __attribute__((gnu_inline)) +#define __RENAME(x) __asm__(#x) + +void fortify_panic(const char *name) __noreturn __cold; +void __buffer_overflow(void) __compiletime_error("buffer overflow"); + +#if !defined(__NO_FORTIFY) && defined(__OPTIMIZE__) && defined(CONFIG_FORTIFY_SOURCE) +__FORTIFY_INLINE char *strcpy(char *p, const char *q) +{ + size_t p_size = __builtin_object_size(p, 0); + if (p_size == (size_t)-1) + return __builtin_strcpy(p, q); + if (strlcpy(p, q, p_size) >= p_size) + fortify_panic(__func__); + return p; +} + +__FORTIFY_INLINE char *strncpy(char *p, const char *q, __kernel_size_t size) +{ + size_t p_size = __builtin_object_size(p, 0); + if (__builtin_constant_p(size) && p_size < size) + __buffer_overflow(); + if (p_size < size) + fortify_panic(__func__); + return __builtin_strncpy(p, q, size); +} + +__FORTIFY_INLINE char *strcat(char *p, const char *q) +{ + size_t p_size = __builtin_object_size(p, 0); + if (p_size == (size_t)-1) + return __builtin_strcat(p, q); + if (strlcat(p, q, p_size) >= p_size) + fortify_panic(__func__); + return p; +} + +__FORTIFY_INLINE char *strncat(char *p, const char *q, __kernel_size_t count) +{ + size_t p_len, copy_len; + size_t p_size = __builtin_object_size(p, 0); + if (p_size == (size_t)-1) + return __builtin_strncat(p, q, count); + p_len = __builtin_strlen(p); + copy_len = strnlen(q, count); + if (p_size < p_len + copy_len + 1) + fortify_panic(__func__); + __builtin_memcpy(p + p_len, q, copy_len); + p[p_len + copy_len] = '\0'; + return p; +} + +__FORTIFY_INLINE __kernel_size_t strlen(const char *p) +{ + __kernel_size_t ret; + size_t p_size = __builtin_object_size(p, 0); + if (p_size == (size_t)-1) + return __builtin_strlen(p); + ret = strnlen(p, p_size); + if (p_size <= ret) + fortify_panic(__func__); + return ret; +} + +extern __kernel_size_t __real_strnlen(const char *, __kernel_size_t) __RENAME(strnlen); +__FORTIFY_INLINE __kernel_size_t strnlen(const char *p, __kernel_size_t maxlen) +{ + size_t p_size = __builtin_object_size(p, 0); + __kernel_size_t ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size); + if (p_size <= ret) + fortify_panic(__func__); + return ret; +} + +__FORTIFY_INLINE void *memset(void *p, int c, __kernel_size_t size) +{ + size_t p_size = __builtin_object_size(p, 0); + if (__builtin_constant_p(size) && p_size < size) + __buffer_overflow(); + if (p_size < size) + fortify_panic(__func__); + return __builtin_memset(p, c, size); +} + +__FORTIFY_INLINE void *memcpy(void *p, const void *q, __kernel_size_t size) +{ + size_t p_size = __builtin_object_size(p, 0); + size_t q_size = __builtin_object_size(q, 0); + if (__builtin_constant_p(size) && (p_size < size || q_size < size)) + __buffer_overflow(); + if (p_size < size || q_size < size) + fortify_panic(__func__); + return __builtin_memcpy(p, q, size); +} + +__FORTIFY_INLINE void *memmove(void *p, const void *q, __kernel_size_t size) +{ + size_t p_size = __builtin_object_size(p, 0); + size_t q_size = __builtin_object_size(q, 0); + if (__builtin_constant_p(size) && (p_size < size || q_size < size)) + __buffer_overflow(); + if (p_size < size || q_size < size) + fortify_panic(__func__); + return __builtin_memmove(p, q, size); +} + +extern void *__real_memscan(void *, int, __kernel_size_t) __RENAME(memscan); +__FORTIFY_INLINE void *memscan(void *p, int c, __kernel_size_t size) +{ + size_t p_size = __builtin_object_size(p, 0); + if (__builtin_constant_p(size) && p_size < size) + __buffer_overflow(); + if (p_size < size) + fortify_panic(__func__); + return __real_memscan(p, c, size); +} + +__FORTIFY_INLINE int memcmp(const void *p, const void *q, __kernel_size_t size) +{ + size_t p_size = __builtin_object_size(p, 0); + size_t q_size = __builtin_object_size(q, 0); + if (__builtin_constant_p(size) && (p_size < size || q_size < size)) + __buffer_overflow(); + if (p_size < size || q_size < size) + fortify_panic(__func__); + return __builtin_memcmp(p, q, size); +} + +__FORTIFY_INLINE void *memchr(const void *p, int c, __kernel_size_t size) +{ + size_t p_size = __builtin_object_size(p, 0); + if (__builtin_constant_p(size) && p_size < size) + __buffer_overflow(); + if (p_size < size) + fortify_panic(__func__); + return __builtin_memchr(p, c, size); +} + +void *__real_memchr_inv(const void *s, int c, size_t n) __RENAME(memchr_inv); +__FORTIFY_INLINE void *memchr_inv(const void *p, int c, size_t size) +{ + size_t p_size = __builtin_object_size(p, 0); + if (__builtin_constant_p(size) && p_size < size) + __buffer_overflow(); + if (p_size < size) + fortify_panic(__func__); + return __real_memchr_inv(p, c, size); +} + +extern void *__real_kmemdup(const void *src, size_t len, gfp_t gfp) __RENAME(kmemdup); +__FORTIFY_INLINE void *kmemdup(const void *p, size_t size, gfp_t gfp) +{ + size_t p_size = __builtin_object_size(p, 0); + if (__builtin_constant_p(size) && p_size < size) + __buffer_overflow(); + if (p_size < size) + fortify_panic(__func__); + return __real_kmemdup(p, size, gfp); +} +#endif + #endif /* _LINUX_STRING_H_ */ diff --git a/lib/string.c b/lib/string.c index ed83562a53ae5..f93cc4386f880 100644 --- a/lib/string.c +++ b/lib/string.c @@ -19,6 +19,8 @@ * - Kissed strtok() goodbye */ +#define __NO_FORTIFY + #include #include #include @@ -952,3 +954,9 @@ char *strreplace(char *s, char old, char new) return s; } EXPORT_SYMBOL(strreplace); + +void fortify_panic(const char *name) +{ + panic("detected buffer overflow in %s", name); +} +EXPORT_SYMBOL(fortify_panic); diff --git a/security/Kconfig b/security/Kconfig index eea22ba3cea8a..245b705ca2262 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -158,6 +158,13 @@ config HARDENED_USERCOPY_PAGESPAN been removed. This config is intended to be used only while trying to find such users. +config FORTIFY_SOURCE + bool "Harden common functions against buffer overflows" + default y + help + Detect overflows of buffers in common functions where the compiler + can determine the buffer size. + config STATIC_USERMODEHELPER bool "Force all usermode helper calls through a single binary" help From 4764563b7550d236a161357cb398cee39f2836a7 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Sun, 9 Apr 2017 03:04:26 -0400 Subject: [PATCH 21/34] work around undefined memcmp in arch/arm64/kernel/vdso.c --- arch/arm64/kernel/vdso.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index 41b6e31f8f556..f8abefe5c376e 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -125,7 +125,7 @@ static int __init vdso_init(void) struct page **vdso_pagelist; unsigned long pfn; - if (memcmp(&vdso_start, "\177ELF", 4)) { + if (__builtin_memcmp(&vdso_start, "\177ELF", 4)) { pr_err("vDSO is not a valid ELF object!\n"); return -EINVAL; } From 7a841c14f00ebd305d6d85f60c960ae4d7324254 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 13:50:30 -0400 Subject: [PATCH 22/34] work around undefined memcmp in kernel/kexec_file.c --- kernel/kexec_file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c index b118735fea9da..326bcdcd5db36 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c @@ -889,7 +889,7 @@ int kexec_load_purgatory(struct kimage *image, unsigned long min, pi->ehdr = (Elf_Ehdr *)kexec_purgatory; - if (memcmp(pi->ehdr->e_ident, ELFMAG, SELFMAG) != 0 + if (__builtin_memcmp(pi->ehdr->e_ident, ELFMAG, SELFMAG) != 0 || pi->ehdr->e_type != ET_REL || !elf_check_arch(pi->ehdr) || pi->ehdr->e_shentsize != sizeof(Elf_Shdr)) From 46e3c062fefd6d09824a6e8268ccd07a995518d3 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 13:57:08 -0400 Subject: [PATCH 23/34] work around undefined memcpy in drivers/net/ethernet/brocade/bna/bnad_ethtool.c --- drivers/net/ethernet/brocade/bna/bnad_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c index 286593922139e..38e7a445a6818 100644 --- a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c +++ b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c @@ -547,7 +547,7 @@ bnad_get_strings(struct net_device *netdev, u32 stringset, u8 *string) for (i = 0; i < BNAD_ETHTOOL_STATS_NUM; i++) { BUG_ON(!(strlen(bnad_net_stats_strings[i]) < ETH_GSTRING_LEN)); - memcpy(string, bnad_net_stats_strings[i], + __builtin_memcpy(string, bnad_net_stats_strings[i], ETH_GSTRING_LEN); string += ETH_GSTRING_LEN; } From 9e2e0bd1d31d9830d6b33a6d249c0fab03a48a35 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 14:14:48 -0400 Subject: [PATCH 24/34] work around undefined memcpy in drivers/net/ethernet/brocade/bna/bfa_ioc.c --- drivers/net/ethernet/brocade/bna/bfa_ioc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc.c b/drivers/net/ethernet/brocade/bna/bfa_ioc.c index 0f6811860ad51..a9c4a77d50b02 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_ioc.c +++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.c @@ -2845,7 +2845,7 @@ bfa_ioc_get_adapter_optrom_ver(struct bfa_ioc *ioc, char *optrom_ver) static void bfa_ioc_get_adapter_manufacturer(struct bfa_ioc *ioc, char *manufacturer) { - memcpy(manufacturer, BFA_MFG_NAME, BFA_ADAPTER_MFG_NAME_LEN); + __builtin_memcpy(manufacturer, BFA_MFG_NAME, BFA_ADAPTER_MFG_NAME_LEN); } static void From 3222859c7bbf9b2bae32a4b9eccc9fea4816291d Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 14:25:47 -0400 Subject: [PATCH 25/34] work around undefined memcpy calls in drivers/net/ethernet/qlogic/qlge/qlge_dbg.c --- drivers/net/ethernet/qlogic/qlge/qlge_dbg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c b/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c index 829be21f97b21..2e3c0aa13437e 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c @@ -765,7 +765,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) sizeof(struct mpi_coredump_global_header); mpi_coredump->mpi_global_header.imageSize = sizeof(struct ql_mpi_coredump); - memcpy(mpi_coredump->mpi_global_header.idString, "MPI Coredump", + __builtin_memcpy(mpi_coredump->mpi_global_header.idString, "MPI Coredump", sizeof(mpi_coredump->mpi_global_header.idString)); /* Get generic NIC reg dump */ @@ -1255,7 +1255,7 @@ static void ql_gen_reg_dump(struct ql_adapter *qdev, sizeof(struct mpi_coredump_global_header); mpi_coredump->mpi_global_header.imageSize = sizeof(struct ql_reg_dump); - memcpy(mpi_coredump->mpi_global_header.idString, "MPI Coredump", + __builtin_memcpy(mpi_coredump->mpi_global_header.idString, "MPI Coredump", sizeof(mpi_coredump->mpi_global_header.idString)); From 0638b182fb3907cddf7abf0f70a79ee60375bcf5 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 14:36:06 -0400 Subject: [PATCH 26/34] work around undefined memcpy in drivers/net/wireless/marvell/libertas/mesh.c --- drivers/net/wireless/marvell/libertas/mesh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/libertas/mesh.c b/drivers/net/wireless/marvell/libertas/mesh.c index d0c881dd58467..754075e7d0e90 100644 --- a/drivers/net/wireless/marvell/libertas/mesh.c +++ b/drivers/net/wireless/marvell/libertas/mesh.c @@ -1177,7 +1177,7 @@ void lbs_mesh_ethtool_get_strings(struct net_device *dev, switch (stringset) { case ETH_SS_STATS: for (i = 0; i < MESH_STATS_NUM; i++) { - memcpy(s + i * ETH_GSTRING_LEN, + __builtin_memcpy(s + i * ETH_GSTRING_LEN, mesh_stat_strings[i], ETH_GSTRING_LEN); } From 81f5f22c47fa339540bfda303a191983308de455 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 14:42:24 -0400 Subject: [PATCH 27/34] work around undefined memcpy in drivers/net/wireless/ray_cs.c --- drivers/net/wireless/ray_cs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index b94479441b0c7..270b8dce4d434 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -597,7 +597,7 @@ static void init_startup_params(ray_dev_t *local) * a_beacon_period = hops a_beacon_period = KuS *//* 64ms = 010000 */ if (local->fw_ver == 0x55) { - memcpy((UCHAR *) &local->sparm.b4, b4_default_startup_parms, + __builtin_memcpy((UCHAR *) &local->sparm.b4, b4_default_startup_parms, sizeof(struct b4_startup_params)); /* Translate sane kus input values to old build 4/5 format */ /* i = hop time in uS truncated to 3 bytes */ From bfe45e03db2cb2aadd90772b8e8d82ef4d34feac Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 14:50:45 -0400 Subject: [PATCH 28/34] work around undefined memcpy in drivers/scsi/csiostor/csio_lnode.c --- drivers/scsi/csiostor/csio_lnode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/csiostor/csio_lnode.c b/drivers/scsi/csiostor/csio_lnode.c index c00b2ff72b551..3ae2b41aa3596 100644 --- a/drivers/scsi/csiostor/csio_lnode.c +++ b/drivers/scsi/csiostor/csio_lnode.c @@ -245,7 +245,7 @@ csio_append_attrib(uint8_t **ptr, uint16_t type, uint8_t *val, uint16_t len) len += 4; /* includes attribute type and length */ len = (len + 3) & ~3; /* should be multiple of 4 bytes */ ae->len = htons(len); - memcpy(ae->value, val, len); + __builtin_memcpy(ae->value, val, len); *ptr += len; } From e20aadaae5d0e686bac439e0f526c01397615478 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 16:40:04 -0400 Subject: [PATCH 29/34] add basic full slab sanitization Signed-off-by: Daniel Micay --- init/Kconfig | 10 ++++++++++ mm/slub.c | 15 +++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/init/Kconfig b/init/Kconfig index 439066fb96bd1..5c6adff57eb26 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1869,6 +1869,16 @@ config SLAB_HARDENED Make minor performance sacrifices to harden the kernel slab allocator. +config SLAB_SANITIZE + default y + depends on SLUB + bool "Sanitize SLAB allocations" + help + Zero fill slab allocations on free, reducing the lifetime of + sensitive data and helping to mitigate use-after-free bugs. + + For slabs with debug poisoning enabling, this has no impact. + config SLUB_CPU_PARTIAL default y depends on SLUB && SMP diff --git a/mm/slub.c b/mm/slub.c index df617d6fdf8fb..2796b6d4b2f95 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2947,6 +2947,21 @@ static __always_inline void do_slab_free(struct kmem_cache *s, void *tail_obj = tail ? : head; struct kmem_cache_cpu *c; unsigned long tid; + + if (IS_ENABLED(CONFIG_SLAB_SANITIZE) && !(s->flags & (SLAB_DESTROY_BY_RCU | SLAB_POISON))) { + int offset = s->offset ? 0 : sizeof(void *); + void *x = head; + + while (1) { + memset(x + offset, 0, s->object_size - offset); + if (s->ctor) + s->ctor(x); + if (x == tail_obj) + break; + x = get_freepointer(s, x); + } + } + redo: /* * Determine the currently cpus per cpu slab. From c8e0e67ddd9e8cc3ad78c152e6b13caf2cba8f04 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 3 May 2017 16:16:58 -0400 Subject: [PATCH 30/34] 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 --- include/linux/slub_def.h | 5 +++ init/Kconfig | 16 ++++++++ mm/slab.h | 2 +- mm/slub.c | 80 +++++++++++++++++++++++++++++++++++++--- 4 files changed, 96 insertions(+), 7 deletions(-) diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index 0258d6d74e9c4..e42aafcbe2f69 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -97,6 +97,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 5c6adff57eb26..1928240362c9d 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1869,6 +1869,22 @@ config SLAB_HARDENED Make minor performance sacrifices to harden the kernel slab allocator. +config SLAB_CANARY + default y + depends on SLUB + bool "SLAB canaries" + 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 SLAB_SANITIZE default y depends on SLUB diff --git a/mm/slab.h b/mm/slab.h index 5f649511ec17b..15dd511f509df 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -427,7 +427,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_DESTROY_BY_RCU | SLAB_STORE_USER)) + if ((s->flags & (SLAB_DESTROY_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 2796b6d4b2f95..eb972bae4e6ba 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -290,6 +290,36 @@ static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp) #endif } +#ifdef CONFIG_64BIT +static const unsigned long canary_mask = ~0xFFUL; +#else +static const unsigned long canary_mask = ~0UL; +#endif + +static inline unsigned long *get_canary(struct kmem_cache *s, void *object) +{ + if (s->offset) + return object + s->offset + sizeof(void *); + else + return object + s->inuse; +} + +static inline void set_canary(struct kmem_cache *s, void *object, unsigned long value) +{ + if (IS_ENABLED(CONFIG_SLAB_CANARY)) { + unsigned long *canary = get_canary(s, object); + *canary = (value ^ (unsigned long)canary) & canary_mask; + } +} + +static inline void check_canary(struct kmem_cache *s, void *object, unsigned long value) +{ + if (IS_ENABLED(CONFIG_SLAB_CANARY)) { + unsigned long *canary = get_canary(s, object); + BUG_ON(*canary != ((value ^ (unsigned long)canary) & canary_mask)); + } +} + /* Loop over all objects in a slab */ #define for_each_object(__p, __s, __addr, __objects) \ for (__p = fixup_red_left(__s, __addr); \ @@ -543,6 +573,9 @@ static struct track *get_track(struct kmem_cache *s, void *object, else p = object + s->inuse; + if (IS_ENABLED(CONFIG_SLAB_CANARY)) + p = (void *)p + sizeof(void *); + return p + alloc; } @@ -681,6 +714,9 @@ static void print_trailer(struct kmem_cache *s, struct page *page, u8 *p) else off = s->inuse; + if (IS_ENABLED(CONFIG_SLAB_CANARY)) + off += sizeof(void *); + if (s->flags & SLAB_STORE_USER) off += 2 * sizeof(struct track); @@ -810,6 +846,9 @@ static int check_pad_bytes(struct kmem_cache *s, struct page *page, u8 *p) /* Freepointer is placed after the object. */ off += sizeof(void *); + 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); @@ -1411,6 +1450,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); kasan_init_slab_obj(s, object); if (unlikely(s->ctor)) { kasan_unpoison_object_data(s, object); @@ -2741,6 +2781,11 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s, if (unlikely(gfpflags & __GFP_ZERO) && 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, gfpflags, 1, &object); return object; @@ -2947,15 +2992,23 @@ static __always_inline void do_slab_free(struct kmem_cache *s, void *tail_obj = tail ? : head; struct kmem_cache_cpu *c; unsigned long tid; + bool sanitize = IS_ENABLED(CONFIG_SLAB_SANITIZE) && !(s->flags & (SLAB_DESTROY_BY_RCU | SLAB_POISON)); - if (IS_ENABLED(CONFIG_SLAB_SANITIZE) && !(s->flags & (SLAB_DESTROY_BY_RCU | SLAB_POISON))) { - int offset = s->offset ? 0 : sizeof(void *); + if (IS_ENABLED(CONFIG_SLAB_CANARY) || sanitize) { + __maybe_unused int offset = s->offset ? 0 : sizeof(void *); void *x = head; while (1) { - memset(x + offset, 0, s->object_size - offset); - if (s->ctor) - s->ctor(x); + if (IS_ENABLED(CONFIG_SLAB_CANARY)) { + check_canary(s, x, s->random_active); + set_canary(s, x, s->random_inactive); + } + + if (sanitize) { + memset(x + offset, 0, s->object_size - offset); + if (s->ctor) + s->ctor(x); + } if (x == tail_obj) break; x = get_freepointer(s, x); @@ -3140,7 +3193,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; /* memcg and kmem_cache debug support */ s = slab_pre_alloc_hook(s, flags); @@ -3184,6 +3237,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, flags, size, p); return i; @@ -3387,6 +3445,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); kasan_kmalloc(kmem_cache_node, n, sizeof(struct kmem_cache_node), GFP_KERNEL); init_kmem_cache_node(n); @@ -3510,6 +3569,9 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order) size += sizeof(void *); } + if (IS_ENABLED(CONFIG_SLAB_CANARY)) + size += sizeof(void *); + #ifdef CONFIG_SLUB_DEBUG if (flags & SLAB_STORE_USER) /* @@ -3578,6 +3640,10 @@ static int kmem_cache_open(struct kmem_cache *s, unsigned long flags) s->flags = kmem_cache_flags(s->size, flags, s->name, s->ctor); #ifdef CONFIG_SLAB_HARDENED s->random = get_random_long(); +#endif +#ifdef CONFIG_SLAB_CANARY + s->random_active = get_random_long(); + s->random_inactive = get_random_long(); #endif s->reserved = 0; @@ -3870,6 +3936,8 @@ const char *__check_heap_object(const void *ptr, unsigned long n, offset -= s->red_left_pad; } + check_canary(s, (void *)ptr - offset, s->random_active); + /* Allow address range falling entirely within object size. */ if (offset <= object_size && n <= object_size - offset) return NULL; From 753806217b318982fe947623ae5cb6f9a7216ccc Mon Sep 17 00:00:00 2001 From: Andy Johnson Date: Wed, 3 May 2017 17:47:10 -0700 Subject: [PATCH 31/34] initial commit of extracted grsecurity chroot hardening --- fs/fcntl.c | 4 +- fs/fhandle.c | 3 +- fs/fs_struct.c | 11 +- fs/namei.c | 29 +- fs/namespace.c | 11 +- fs/open.c | 13 +- fs/proc/proc_sysctl.c | 27 ++ include/linux/dcache.h | 5 + include/linux/hardened.h | 43 +++ include/linux/hardened_internal.h | 79 +++++ include/linux/sched.h | 3 + include/linux/shm.h | 5 + include/linux/uidgid.h | 6 + init/main.c | 13 + ipc/shm.c | 12 +- kernel/capability.c | 11 +- kernel/fork.c | 3 + kernel/pid.c | 17 +- kernel/sched/core.c | 5 +- kernel/signal.c | 23 ++ kernel/sys.c | 7 + net/unix/af_unix.c | 7 + security/Kconfig | 11 + security/Makefile | 2 + security/hardened/Kconfig | 211 +++++++++++++ security/hardened/Makefile | 7 + security/hardened/chroot.c | 489 ++++++++++++++++++++++++++++++ security/hardened/init.c | 84 +++++ security/hardened/sysctl.c | 137 +++++++++ 29 files changed, 1263 insertions(+), 15 deletions(-) create mode 100644 include/linux/hardened.h create mode 100644 include/linux/hardened_internal.h create mode 100644 security/hardened/Kconfig create mode 100644 security/hardened/Makefile create mode 100644 security/hardened/chroot.c create mode 100644 security/hardened/init.c create mode 100644 security/hardened/sysctl.c diff --git a/fs/fcntl.c b/fs/fcntl.c index be8fbe289087e..e6635f31354b0 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -23,7 +23,7 @@ #include #include #include - +#include #include #include #include @@ -104,6 +104,8 @@ void __f_setown(struct file *filp, struct pid *pid, enum pid_type type, int force) { security_file_set_fowner(filp); + if (handle_chroot_fowner(pid, type)) + return; f_modown(filp, pid, type, force); } EXPORT_SYMBOL(__f_setown); diff --git a/fs/fhandle.c b/fs/fhandle.c index 5559168d56373..405e0570e78ee 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "internal.h" #include "mount.h" @@ -175,7 +176,7 @@ static int handle_to_path(int mountdirfd, struct file_handle __user *ufh, * the directory. Ideally we would like CAP_DAC_SEARCH. * But we don't have that */ - if (!capable(CAP_DAC_READ_SEARCH)) { + if (!capable(CAP_DAC_READ_SEARCH) || !chroot_fhandle()) { retval = -EPERM; goto out_err; } diff --git a/fs/fs_struct.c b/fs/fs_struct.c index be0250788b737..3695b6ecc5def 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "internal.h" /* @@ -16,14 +17,18 @@ void set_fs_root(struct fs_struct *fs, const struct path *path) struct path old_root; path_get(path); + inc_chroot_refcnts(path->dentry, path->mnt); spin_lock(&fs->lock); write_seqcount_begin(&fs->seq); old_root = fs->root; fs->root = *path; + set_chroot_entries(current, path); write_seqcount_end(&fs->seq); spin_unlock(&fs->lock); - if (old_root.dentry) + if (old_root.dentry) { + dec_chroot_refcnts(old_root.dentry, old_root.mnt); path_put(&old_root); + } } /* @@ -86,6 +91,7 @@ void chroot_fs_refs(const struct path *old_root, const struct path *new_root) void free_fs_struct(struct fs_struct *fs) { + dec_chroot_refcnts(fs->root.dentry, fs->root.mnt); path_put(&fs->root); path_put(&fs->pwd); kmem_cache_free(fs_cachep, fs); @@ -100,6 +106,7 @@ void exit_fs(struct task_struct *tsk) task_lock(tsk); spin_lock(&fs->lock); tsk->fs = NULL; + clear_chroot_entries(tsk); kill = !--fs->users; spin_unlock(&fs->lock); task_unlock(tsk); @@ -125,6 +132,7 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old) fs->pwd = old->pwd; path_get(&fs->pwd); spin_unlock(&old->lock); + inc_chroot_refcnts(fs->root.dentry, fs->root.mnt); } return fs; } @@ -142,6 +150,7 @@ int unshare_fs_struct(void) spin_lock(&fs->lock); kill = !--fs->users; current->fs = new_fs; + set_chroot_entries(current, &new_fs->root); spin_unlock(&fs->lock); task_unlock(current); diff --git a/fs/namei.c b/fs/namei.c index 19dcf62133cc9..3de194a8bb92b 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -38,7 +38,7 @@ #include #include #include - +#include #include "internal.h" #include "mount.h" @@ -2277,6 +2277,10 @@ static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path if (!err) err = complete_walk(nd); + if (!err && !(nd->flags & LOOKUP_PARENT)) { + err = chroot_pathat(nd->dfd, nd->path.dentry, nd->path.mnt, nd->flags); + } + if (!err && nd->flags & LOOKUP_DIRECTORY) if (!d_can_lookup(nd->path.dentry)) err = -ENOTDIR; @@ -2324,7 +2328,9 @@ static int path_parentat(struct nameidata *nd, unsigned flags, return PTR_ERR(s); err = link_path_walk(s, nd); if (!err) - err = complete_walk(nd); + err = complete_walk(nd); + if (!err) + err = chroot_pathat(nd->dfd, nd->path.dentry, nd->path.mnt, nd->flags); if (!err) { *parent = nd->path; nd->path.mnt = NULL; @@ -3159,6 +3165,10 @@ static int lookup_open(struct nameidata *nd, struct path *path, /* Negative dentry, just create the file */ if (!dentry->d_inode && (open_flag & O_CREAT)) { + error = chroot_pathat(nd->dfd, dentry, nd->path.mnt, nd->flags); + if (error) + goto out_dput; + *opened |= FILE_CREATED; audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE); if (!dir_inode->i_op->create) { @@ -3323,6 +3333,10 @@ static int do_last(struct nameidata *nd, error = complete_walk(nd); if (error) return error; + error = chroot_pathat(nd->dfd, nd->path.dentry, nd->path.mnt, nd->flags); + if (error) + goto out; + audit_inode(nd->name, nd->path.dentry, 0); error = -EISDIR; if ((open_flag & O_CREAT) && d_is_dir(nd->path.dentry)) @@ -3716,6 +3730,10 @@ SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode, if (!IS_POSIXACL(path.dentry->d_inode)) mode &= ~current_umask(); + if (handle_chroot_mknod(dentry, path.mnt, mode)) { + error = -EPERM; + goto out; + } error = security_path_mknod(&path, dentry, mode, dev); if (error) goto out; @@ -4555,6 +4573,13 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, error = -ENOTEMPTY; if (new_dentry == trap) goto exit5; + if (bad_chroot_rename(old_dentry, old_path.mnt, new_dentry, new_path.mnt)) { + /* use EXDEV error to cause 'mv' to switch to an alternative + * method for usability + */ + error = -EXDEV; + goto exit5; + } error = security_path_rename(&old_path, old_dentry, &new_path, new_dentry, flags); diff --git a/fs/namespace.c b/fs/namespace.c index cc1375eff88c7..1d25f170e73c3 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -26,7 +26,7 @@ #include #include #include - +#include #include "pnode.h" #include "internal.h" @@ -2812,6 +2812,11 @@ long do_mount(const char *dev_name, const char __user *dir_name, if (flags & MS_RDONLY) mnt_flags |= MNT_READONLY; + if (handle_chroot_mount(path.dentry, path.mnt, dev_name)) { + retval = -EPERM; + goto dput_out; + } + /* The default atime for remount is preservation */ if ((flags & MS_REMOUNT) && ((flags & (MS_NOATIME | MS_NODIRATIME | MS_RELATIME | @@ -3133,6 +3138,10 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, error = security_sb_pivotroot(&old, &new); if (error) goto out2; + if (handle_chroot_pivot()) { + error = -EPERM; + goto out2; + } get_fs_root(current->fs, &root); old_mp = lock_mount(&old); diff --git a/fs/open.c b/fs/open.c index 949cef29c3bba..41a553b0f1bd2 100644 --- a/fs/open.c +++ b/fs/open.c @@ -31,7 +31,7 @@ #include #include #include - +#include #include "internal.h" int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, @@ -473,6 +473,8 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd) goto out_putf; error = inode_permission(inode, MAY_EXEC | MAY_CHDIR); + if (!error && !chroot_fchdir(f.file->f_path.dentry, f.file->f_path.mnt)) + error = -EPERM; if (!error) set_fs_pwd(current->fs, &f.file->f_path); out_putf: @@ -501,8 +503,11 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename) error = security_path_chroot(&path); if (error) goto dput_and_out; + if (handle_chroot_chroot(path.dentry, path.mnt)) + goto dput_and_out; set_fs_root(current->fs, &path); + handle_chroot_chdir(&path); error = 0; dput_and_out: path_put(&path); @@ -526,6 +531,12 @@ static int chmod_common(const struct path *path, umode_t mode) return error; retry_deleg: inode_lock(inode); + + if (handle_chroot_chmod(path->dentry, path->mnt, mode)) { + error = -EACCES; + goto out_unlock; + } + error = security_path_chmod(path, mode); if (error) goto out_unlock; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index d04ea43499096..95a095e712578 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -12,8 +12,11 @@ #include #include #include +#include #include "internal.h" +extern int gr_handle_chroot_sysctl(const int op); + static const struct dentry_operations proc_sys_dentry_operations; static const struct file_operations proc_sys_file_operations; static const struct inode_operations proc_sys_inode_operations; @@ -557,6 +560,7 @@ static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf, struct inode *inode = file_inode(filp); struct ctl_table_header *head = grab_header(inode); struct ctl_table *table = PROC_I(inode)->sysctl_entry; + int op = write ? MAY_WRITE : MAY_READ; ssize_t error; size_t res; @@ -576,6 +580,29 @@ static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf, if (!table->proc_handler) goto out; +#ifdef CONFIG_HARDENED + error = -EPERM; + if (handle_chroot_sysctl(op)) + goto out; + /* NOTE: review code below to see if more of this would be useful for security other than acl + dget(filp->f_path.dentry); + if (gr_handle_sysctl_mod((const char *)filp->f_path.dentry->d_parent->d_name.name, table->procname, op)) { + dput(filp->f_path.dentry); + goto out; + } + dput(filp->f_path.dentry); + if (!gr_acl_handle_open(filp->f_path.dentry, filp->f_path.mnt, op)) + goto out; + if (write) { + if (current->nsproxy->net_ns != table->extra2) { + if (!capable(CAP_SYS_ADMIN)) + goto out; + } else if (!ns_capable(current->nsproxy->net_ns->user_ns, CAP_NET_ADMIN)) + goto out; + } + */ +#endif + /* careful: calling conventions are nasty here */ res = count; error = table->proc_handler(table, write, buf, &res, ppos); diff --git a/include/linux/dcache.h b/include/linux/dcache.h index d2e38dc6172c0..7d3bb0cc68975 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -103,6 +103,11 @@ struct dentry { struct list_head d_lru; /* LRU list */ wait_queue_head_t *d_wait; /* in-lookup ones only */ }; + +#ifdef CONFIG_HARDENED_CHROOT_RENAME + atomic_t chroot_refcnt; /* tracks use of directory in chroot */ +#endif + struct list_head d_child; /* child of parent list */ struct list_head d_subdirs; /* our children */ /* diff --git a/include/linux/hardened.h b/include/linux/hardened.h new file mode 100644 index 0000000000000..e24568621769f --- /dev/null +++ b/include/linux/hardened.h @@ -0,0 +1,43 @@ +#ifndef HARDENED_H +#define HARDENED_H +#include +#include +#include +#include + +int pid_is_chrooted(struct task_struct *p); +int handle_chroot_fowner(struct pid *pid, enum pid_type type); +int handle_chroot_nice(void); +int handle_chroot_sysctl(const int op); +int handle_chroot_setpriority(struct task_struct *p, + const int niceval); +int chroot_fchdir(struct dentry *u_dentry, struct vfsmount *u_mnt); +int chroot_pathat(int dfd, struct dentry *u_dentry, struct vfsmount *u_mnt, unsigned flags); +int chroot_fhandle(void); +int handle_chroot_chroot(const struct dentry *dentry, + const struct vfsmount *mnt); +void handle_chroot_chdir(const struct path *path); +int handle_chroot_chmod(const struct dentry *dentry, + const struct vfsmount *mnt, const int mode); +int handle_chroot_mknod(const struct dentry *dentry, + const struct vfsmount *mnt, const int mode); +int handle_chroot_mount(const struct dentry *dentry, + const struct vfsmount *mnt, + const char *dev_name); +int handle_chroot_pivot(void); +int handle_chroot_unix(const pid_t pid); + +void set_chroot_entries(struct task_struct *task, const struct path *path); +void clear_chroot_entries(struct task_struct *task); +int chroot_is_capable(const int cap); +int task_chroot_is_capable(const struct task_struct *task, const struct cred *cred, const int cap); +void inc_chroot_refcnts(struct dentry *dentry, struct vfsmount *mnt); +void dec_chroot_refcnts(struct dentry *dentry, struct vfsmount *mnt); +int bad_chroot_rename(struct dentry *olddentry, struct vfsmount *oldmnt, + struct dentry *newdentry, struct vfsmount *newmnt); + +#ifdef CONFIG_HARDENED_CHROOT_FINDTASK +extern int hardened_enable_chroot_findtask; +#endif + +#endif diff --git a/include/linux/hardened_internal.h b/include/linux/hardened_internal.h new file mode 100644 index 0000000000000..ca9393fc5da68 --- /dev/null +++ b/include/linux/hardened_internal.h @@ -0,0 +1,79 @@ +#ifndef __HARDENED_INTERNAL_H +#define __HARDENED_INTERNAL_H + +#ifdef CONFIG_HARDENED + +#include +#include +#include + +void hardened_handle_alertkill(struct task_struct *task); +char *hardened_to_filename(const struct dentry *dentry, + const struct vfsmount *mnt); +char *hardened_to_filename1(const struct dentry *dentry, + const struct vfsmount *mnt); +char *hardened_to_filename2(const struct dentry *dentry, + const struct vfsmount *mnt); +char *hardened_to_filename3(const struct dentry *dentry, + const struct vfsmount *mnt); + +extern int hardened_enable_chroot_shmat; +extern int hardened_enable_chroot_mount; +extern int hardened_enable_chroot_double; +extern int hardened_enable_chroot_pivot; +extern int hardened_enable_chroot_chdir; +extern int hardened_enable_chroot_chmod; +extern int hardened_enable_chroot_mknod; +extern int hardened_enable_chroot_fchdir; +extern int hardened_enable_chroot_nice; +//extern int hardened_enable_chroot_execlog; +extern int hardened_enable_chroot_caps; +extern int hardened_enable_chroot_rename; +extern int hardened_enable_chroot_sysctl; +extern int hardened_enable_chroot_unix; + +#define hardened_task_fullpath(tsk) ((tsk)->exec_file ? \ + hardened_to_filename2((tsk)->exec_file->f_path.dentry, \ + (tsk)->exec_file->f_path.mnt) : "/") + +#define hardened_parent_task_fullpath(tsk) ((tsk)->real_parent->exec_file ? \ + hardened_to_filename3((tsk)->real_parent->exec_file->f_path.dentry, \ + (tsk)->real_parent->exec_file->f_path.mnt) : "/") + +#define hardened_task_fullpath0(tsk) ((tsk)->exec_file ? \ + hardened_to_filename((tsk)->exec_file->f_path.dentry, \ + (tsk)->exec_file->f_path.mnt) : "/") + +#define hardened_parent_task_fullpath0(tsk) ((tsk)->real_parent->exec_file ? \ + hardened_to_filename1((tsk)->real_parent->exec_file->f_path.dentry, \ + (tsk)->real_parent->exec_file->f_path.mnt) : "/") + +#define proc_is_chrooted(tsk_a) ((tsk_a)->is_chrooted) + +#define have_same_root(tsk_a,tsk_b) ((tsk_a)->chroot_dentry == (tsk_b)->chroot_dentry) + +static inline bool gr_is_same_file(const struct file *file1, const struct file *file2) +{ + if (file1 && file2) { + const struct inode *inode1 = file1->f_path.dentry->d_inode; + const struct inode *inode2 = file2->f_path.dentry->d_inode; + if (inode1->i_ino == inode2->i_ino && inode1->i_sb->s_dev == inode2->i_sb->s_dev) + return true; + } + + return false; +} + +#define HARDENED_CHROOT_CAPS {{ \ + CAP_TO_MASK(CAP_LINUX_IMMUTABLE) | CAP_TO_MASK(CAP_NET_ADMIN) | \ + CAP_TO_MASK(CAP_SYS_MODULE) | CAP_TO_MASK(CAP_SYS_RAWIO) | \ + CAP_TO_MASK(CAP_SYS_PACCT) | CAP_TO_MASK(CAP_SYS_ADMIN) | \ + CAP_TO_MASK(CAP_SYS_BOOT) | CAP_TO_MASK(CAP_SYS_TIME) | \ + CAP_TO_MASK(CAP_NET_RAW) | CAP_TO_MASK(CAP_SYS_TTY_CONFIG) | \ + CAP_TO_MASK(CAP_IPC_OWNER) | CAP_TO_MASK(CAP_SETFCAP), \ + CAP_TO_MASK(CAP_SYSLOG) | CAP_TO_MASK(CAP_MAC_ADMIN) }} + + +#endif + +#endif diff --git a/include/linux/sched.h b/include/linux/sched.h index 4cf9a59a4d08e..77daa1464591d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1042,6 +1042,8 @@ struct task_struct { /* A live task holds one reference: */ atomic_t stack_refcount; #endif + struct dentry *chroot_dentry; + u8 is_chrooted; /* CPU-specific state of this task: */ struct thread_struct thread; @@ -1384,6 +1386,7 @@ static inline struct thread_info *task_thread_info(struct task_struct *task) */ extern struct task_struct *find_task_by_vpid(pid_t nr); +extern struct task_struct *find_task_by_vpid_unrestricted(pid_t nr); extern struct task_struct *find_task_by_pid_ns(pid_t nr, struct pid_namespace *ns); extern int wake_up_state(struct task_struct *tsk, unsigned int state); diff --git a/include/linux/shm.h b/include/linux/shm.h index 04e8818296251..937a35c77bda4 100644 --- a/include/linux/shm.h +++ b/include/linux/shm.h @@ -22,6 +22,11 @@ struct shmid_kernel /* private to the kernel */ /* The task created the shm object. NULL if the task is dead. */ struct task_struct *shm_creator; struct list_head shm_clist; /* list by creator */ + +#ifdef CONFIG_HARDENED + u64 shm_createtime; + pid_t shm_lapid; +#endif }; /* shm_mode upper byte flags */ diff --git a/include/linux/uidgid.h b/include/linux/uidgid.h index 25e9d92163408..5a2a0d08d6fb0 100644 --- a/include/linux/uidgid.h +++ b/include/linux/uidgid.h @@ -117,6 +117,12 @@ static inline bool gid_valid(kgid_t gid) return __kgid_val(gid) != (gid_t) -1; } +#define GLOBAL_UID(x) from_kuid_munged(&init_user_ns, (x)) +#define GLOBAL_GID(x) from_kgid_munged(&init_user_ns, (x)) +#define is_global_root(x) uid_eq((x), GLOBAL_ROOT_UID) +#define is_global_nonroot(x) (!uid_eq((x), GLOBAL_ROOT_UID)) +#define is_global_nonroot_gid(x) (!gid_eq((x), GLOBAL_ROOT_GID)) + #ifdef CONFIG_USER_NS extern kuid_t make_kuid(struct user_namespace *from, uid_t uid); diff --git a/init/main.c b/init/main.c index b0c11cbf5ddf8..0db73cda82ace 100644 --- a/init/main.c +++ b/init/main.c @@ -101,6 +101,8 @@ extern void init_IRQ(void); extern void fork_init(void); extern void radix_tree_init(void); +extern void hardened_init(void); + /* * Debug helper: via this flag we know that we are in 'early bootup code' * where only the boot processor is running with IRQ disabled. This means @@ -925,6 +927,10 @@ static int try_to_run_init_process(const char *init_filename) return ret; } +#ifdef CONFIG_HARDENED_CHROOT_INITRD +extern int init_ran; +#endif + static noinline void __init kernel_init_freeable(void); #if defined(CONFIG_STRICT_KERNEL_RWX) || defined(CONFIG_STRICT_MODULE_RWX) @@ -974,6 +980,11 @@ static int __ref kernel_init(void *unused) ramdisk_execute_command, ret); } +#ifdef CONFIG_HARDENED_CHROOT_INITRD + /* if no initrd was used, be extra sure we enforce chroot restrictions */ + init_ran = 1; +#endif + /* * We try each of these until one succeeds. * @@ -1053,6 +1064,8 @@ static noinline void __init kernel_init_freeable(void) prepare_namespace(); } + hardened_init(); + /* * Ok, we have completed the initial bootup, and * we're essentially up and running. Get rid of the diff --git a/ipc/shm.c b/ipc/shm.c index 481d2a9c298ab..24f8e940fa2b9 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -72,6 +72,11 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp); static int sysvipc_shm_proc_show(struct seq_file *s, void *it); #endif +#ifdef CONFIG_HARDENED +extern int chroot_shmat(const pid_t shm_cprid, const pid_t shm_lapid, + const u64 shm_createtime); +#endif + void shm_init_ns(struct ipc_namespace *ns) { ns->shm_ctlmax = SHMMAX; @@ -1176,7 +1181,12 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, err = -EIDRM; goto out_unlock; } - +#ifdef CONFIG_HARDENED + if ( !chroot_shmat(shp->shm_cprid, shp->shm_lapid, shp->shm_createtime)) { + err = -EACCES; + goto out_unlock; + } +#endif path = shp->shm_file->f_path; path_get(&path); shp->shm_nattch++; diff --git a/kernel/capability.c b/kernel/capability.c index f97fe77ceb88a..1ad8114e87ae1 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -18,6 +18,7 @@ #include #include #include +#include /* * Leveraged for setting/resetting capabilities @@ -298,7 +299,8 @@ bool has_ns_capability(struct task_struct *t, int ret; rcu_read_lock(); - ret = security_capable(__task_cred(t), ns, cap); + ret = security_capable(__task_cred(t), ns, cap) == 0 && + task_chroot_is_capable(t, __task_cred(t), cap); rcu_read_unlock(); return (ret == 0); @@ -339,7 +341,8 @@ bool has_ns_capability_noaudit(struct task_struct *t, int ret; rcu_read_lock(); - ret = security_capable_noaudit(__task_cred(t), ns, cap); + ret = security_capable_noaudit(__task_cred(t), ns, cap) == 0 && + task_chroot_is_capable(t, __task_cred(t), cap); rcu_read_unlock(); return (ret == 0); @@ -371,8 +374,8 @@ static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit) BUG(); } - capable = audit ? security_capable(current_cred(), ns, cap) : - security_capable_noaudit(current_cred(), ns, cap); + capable = audit ? (security_capable(current_cred(), ns, cap) == 0 && chroot_is_capable(cap)) : + (security_capable_noaudit(current_cred(), ns, cap) == 0 && chroot_is_capable(cap)); if (capable == 0) { current->flags |= PF_SUPERPRIV; return true; diff --git a/kernel/fork.c b/kernel/fork.c index 5700346af96bd..7bfbcc6104291 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -97,6 +97,8 @@ #include +#include + #define CREATE_TRACE_POINTS #include @@ -2335,6 +2337,7 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags) fs = current->fs; spin_lock(&fs->lock); current->fs = new_fs; + set_chroot_entries(current, ¤t->fs->root); if (--fs->users) new_fs = NULL; else diff --git a/kernel/pid.c b/kernel/pid.c index 0143ac0ddceb9..eb4ef9f156401 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -39,6 +39,7 @@ #include #include #include +#include #define pid_hashfn(nr, ns) \ hash_long((unsigned long)nr + (unsigned long)ns, pidhash_shift) @@ -450,9 +451,16 @@ EXPORT_SYMBOL(pid_task); */ struct task_struct *find_task_by_pid_ns(pid_t nr, struct pid_namespace *ns) { + struct task_struct *task; + RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "find_task_by_pid_ns() needs rcu_read_lock() protection"); - return pid_task(find_pid_ns(nr, ns), PIDTYPE_PID); + task = pid_task(find_pid_ns(nr, ns), PIDTYPE_PID); + + if (pid_is_chrooted(task)) + return NULL; + + return task; } struct task_struct *find_task_by_vpid(pid_t vnr) @@ -460,6 +468,13 @@ struct task_struct *find_task_by_vpid(pid_t vnr) return find_task_by_pid_ns(vnr, task_active_pid_ns(current)); } +struct task_struct *find_task_by_vpid_unrestricted(pid_t vnr) +{ + RCU_LOCKDEP_WARN(!rcu_read_lock_held(), + "find_task_by_pid_ns() needs rcu_read_lock() protection"); + return pid_task(find_pid_ns(vnr, task_active_pid_ns(current)), PIDTYPE_PID); +} + struct pid *get_task_pid(struct task_struct *task, enum pid_type type) { struct pid *pid; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 3b31fc05a0f1e..5fdb2c3bb95f6 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -3867,8 +3868,8 @@ SYSCALL_DEFINE1(nice, int, increment) nice = task_nice(current) + increment; nice = clamp_val(nice, MIN_NICE, MAX_NICE); - if (increment < 0 && !can_nice(current, nice)) - return -EPERM; + if (increment < 0 && (!can_nice(current, nice) || handle_chroot_nice())) + return -EPERM; retval = security_task_setnice(current, nice); if (retval) diff --git a/kernel/signal.c b/kernel/signal.c index 7e59ebc2c25e6..a9bc1595c9079 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -39,6 +39,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -717,6 +718,23 @@ static int kill_ok_by_cred(struct task_struct *t) return 0; } +int +handle_signal(const struct task_struct *p, const int sig) +{ +#ifdef CONFIG_HARDENED + // NOTE: need to review gr_check_protected_task to see if it should be extracted + /* ignore the 0 signal for protected task checks */ + /*if (task_pid_nr(current) > 1 && sig && gr_check_protected_task(p)) { + gr_log_sig_task(GR_DONT_AUDIT, GR_SIG_ACL_MSG, p, sig); + return -EPERM; + } else*/ if (pid_is_chrooted((struct task_struct *)p)) { + return -EPERM; + } +#endif + return 0; +} + + /* * Bad permissions for sending the signal * - the caller must hold the RCU read lock @@ -753,6 +771,11 @@ static int check_kill_permission(int sig, struct siginfo *info, } } + if ((info == SEND_SIG_NOINFO || info->si_code != SI_TKILL || + sig != (SIGRTMIN+1) || task_tgid_vnr(t) != info->si_pid) + && handle_signal(t, sig)) + return -EPERM; + return security_task_kill(t, info, sig, 0); } diff --git a/kernel/sys.c b/kernel/sys.c index 7ff6d1b10ceca..c40ae97bdfc24 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -68,6 +68,8 @@ #include #include +#include + #ifndef SET_UNALIGN_CTL # define SET_UNALIGN_CTL(a, b) (-EINVAL) #endif @@ -167,6 +169,11 @@ static int set_one_prio(struct task_struct *p, int niceval, int error) error = -EACCES; goto out; } + if (handle_chroot_setpriority(p, niceval)) { + error = -EACCES; + goto out; + } + no_nice = security_task_setnice(p, niceval); if (no_nice) { error = no_nice; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 928691c434087..ac5129c540484 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -118,6 +118,7 @@ #include #include #include +#include struct hlist_head unix_socket_table[2 * UNIX_HASH_SIZE]; EXPORT_SYMBOL_GPL(unix_socket_table); @@ -942,6 +943,12 @@ static struct sock *unix_find_other(struct net *net, if (u) { struct dentry *dentry; dentry = unix_sk(u)->path.dentry; + + if (!handle_chroot_unix(pid_vnr(u->sk_peer_pid))) { + err = -EPERM; + sock_put(u); + goto fail; + } if (dentry) touch_atime(&unix_sk(u)->path); } else diff --git a/security/Kconfig b/security/Kconfig index 245b705ca2262..6441c38992ae1 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -4,6 +4,17 @@ menu "Security options" +menu "Hardened" + +config HARDENED + bool "Hardened" + help + If you say Y here, you will be able to configure kernel hardening features. + +source security/hardened/Kconfig + +endmenu + source security/keys/Kconfig config SECURITY_DMESG_RESTRICT diff --git a/security/Makefile b/security/Makefile index f2d71cdb8e19b..2e38f1fbcb5f4 100644 --- a/security/Makefile +++ b/security/Makefile @@ -9,6 +9,7 @@ subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor subdir-$(CONFIG_SECURITY_YAMA) += yama subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin +subdir-$(CONFIG_HARDENED) += hardened # always enable default capabilities obj-y += commoncap.o @@ -24,6 +25,7 @@ obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/ obj-$(CONFIG_SECURITY_YAMA) += yama/ obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/ +obj-$(CONFIG_HARDENED) += hardened/ obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o # Object integrity file lists diff --git a/security/hardened/Kconfig b/security/hardened/Kconfig new file mode 100644 index 0000000000000..9024d87f91cc6 --- /dev/null +++ b/security/hardened/Kconfig @@ -0,0 +1,211 @@ +# +# Hardened patchset configuration +# +menu "Chroot" +depends on HARDENED + +config HARDENED_CHROOT + bool "Chroot jail restrictions" + default y + help + This functionality has been extracted from the Grsecurity patchset. All + credit for this functionality should be attributed to Open Source Security, inc. + The extraction of this feature was from the open source test patch for kernel version + 4.9.24. + + If you say Y here, you will be able to choose several options that will + make breaking out of a chrooted jail much more difficult. If you + encounter no software incompatibilities with the following options, it + is recommended that you enable each one. + + Note that the chroot restrictions are not intended to apply to "chroots" + to directories that are simple bind mounts of the global root filesystem. + For several other reasons, a user shouldn't expect any significant + security by performing such a chroot. + +config HARDENED_CHROOT_MOUNT + bool "Deny mounts" + default y + depends on HARDENED_CHROOT + help + If you say Y here, processes inside a chroot will not be able to + mount or remount filesystems. If the sysctl option is enabled, a + sysctl option with name "chroot_deny_mount" is created. + +config HARDENED_CHROOT_DOUBLE + bool "Deny double-chroots" + default y + depends on HARDENED_CHROOT + help + If you say Y here, processes inside a chroot will not be able to chroot + again outside the chroot. This is a widely used method of breaking + out of a chroot jail and should not be allowed. If the sysctl + option is enabled, a sysctl option with name + "chroot_deny_chroot" is created. + +config HARDENED_CHROOT_PIVOT + bool "Deny pivot_root in chroot" + default y + depends on HARDENED_CHROOT + help + If you say Y here, processes inside a chroot will not be able to use + a function called pivot_root() that was introduced in Linux 2.3.41. It + works similar to chroot in that it changes the root filesystem. This + function could be misused in a chrooted process to attempt to break out + of the chroot, and therefore should not be allowed. If the sysctl + option is enabled, a sysctl option with name "chroot_deny_pivot" is + created. + +config HARDENED_CHROOT_CHDIR + bool "Enforce chdir(\"/\") on all chroots" + default y + depends on HARDENED_CHROOT + help + If you say Y here, the current working directory of all newly-chrooted + applications will be set to the the root directory of the chroot. + The man page on chroot(2) states: + Note that this call does not change the current working + directory, so that `.' can be outside the tree rooted at + `/'. In particular, the super-user can escape from a + `chroot jail' by doing `mkdir foo; chroot foo; cd ..'. + + It is recommended that you say Y here, since it's not known to break + any software. If the sysctl option is enabled, a sysctl option with + name "chroot_enforce_chdir" is created. + +config HARDENED_CHROOT_CHMOD + bool "Deny (f)chmod +s" + default y + depends on HARDENED_CHROOT + help + If you say Y here, processes inside a chroot will not be able to chmod + or fchmod files to make them have suid or sgid bits. This protects + against another published method of breaking a chroot. If the sysctl + option is enabled, a sysctl option with name "chroot_deny_chmod" is + created. + +config HARDENED_CHROOT_FCHDIR + bool "Deny fchdir and fhandle out of chroot" + default y + depends on HARDENED_CHROOT + help + If you say Y here, a well-known method of breaking chroots by fchdir'ing + to a file descriptor of the chrooting process that points to a directory + outside the filesystem will be stopped. This option also prevents use of + the recently-created syscall for opening files by a guessable "file handle" + inside a chroot, as well as accessing relative paths outside of a + directory passed in via file descriptor with openat and similar syscalls. + If the sysctl option is enabled, a sysctl option with name "chroot_deny_fchdir" + is created. + +config HARDENED_CHROOT_MKNOD + bool "Deny mknod" + default y + depends on HARDENED_CHROOT + help + If you say Y here, processes inside a chroot will not be allowed to + mknod. The problem with using mknod inside a chroot is that it + would allow an attacker to create a device entry that is the same + as one on the physical root of your system, which could range from + anything from the console device to a device for your harddrive (which + they could then use to wipe the drive or steal data). It is recommended + that you say Y here, unless you run into software incompatibilities. + If the sysctl option is enabled, a sysctl option with name + "chroot_deny_mknod" is created. + +config HARDENED_CHROOT_SHMAT + bool "Deny shmat() out of chroot" + default y + depends on HARDENED_CHROOT + help + If you say Y here, processes inside a chroot will not be able to attach + to shared memory segments that were created outside of the chroot jail. + It is recommended that you say Y here. If the sysctl option is enabled, + a sysctl option with name "chroot_deny_shmat" is created. + +config HARDENED_CHROOT_UNIX + bool "Deny access to abstract AF_UNIX sockets out of chroot" + default y + depends on HARDENED_CHROOT + help + If you say Y here, processes inside a chroot will not be able to + connect to abstract (meaning not belonging to a filesystem) Unix + domain sockets that were bound outside of a chroot. It is recommended + that you say Y here. If the sysctl option is enabled, a sysctl option + with name "chroot_deny_unix" is created. + +config HARDENED_CHROOT_FINDTASK + bool "Protect outside processes" + default y + depends on HARDENED_CHROOT + help + If you say Y here, processes inside a chroot will not be able to + kill, send signals with fcntl, ptrace, capget, getpgid, setpgid, + getsid, or view any process outside of the chroot. If the sysctl + option is enabled, a sysctl option with name "chroot_findtask" is + created. + +config HARDENED_CHROOT_NICE + bool "Restrict priority changes" + default y + depends on HARDENED_CHROOT + help + If you say Y here, processes inside a chroot will not be able to raise + the priority of processes in the chroot, or alter the priority of + processes outside the chroot. This provides more security than simply + removing CAP_SYS_NICE from the process' capability set. If the + sysctl option is enabled, a sysctl option with name "chroot_restrict_nice" + is created. + +config HARDENED_CHROOT_SYSCTL + bool "Deny sysctl writes" + default y + depends on HARDENED_CHROOT + help + If you say Y here, an attacker in a chroot will not be able to + write to sysctl entries, either by sysctl(2) or through a /proc + interface. It is strongly recommended that you say Y here. If the + sysctl option is enabled, a sysctl option with name + "chroot_deny_sysctl" is created. + +config HARDENED_CHROOT_RENAME + bool "Deny bad renames" + default y + depends on HARDENED_CHROOT + help + If you say Y here, an attacker in a chroot will not be able to + abuse the ability to create double chroots to break out of the + chroot by exploiting a race condition between a rename of a directory + within a chroot against an open of a symlink with relative path + components. This feature will likewise prevent an accomplice outside + a chroot from enabling a user inside the chroot to break out and make + use of their credentials on the global filesystem. Enabling this + feature is essential to prevent root users from breaking out of a + chroot. If the sysctl option is enabled, a sysctl option with name + "chroot_deny_bad_rename" is created. + +config HARDENED_CHROOT_CAPS + bool "Capability restrictions" + default y + depends on HARDENED_CHROOT + help + If you say Y here, the capabilities on all processes within a + chroot jail will be lowered to stop module insertion, raw i/o, + system and net admin tasks, rebooting the system, modifying immutable + files, modifying IPC owned by another, and changing the system time. + This is left an option because it can break some apps. Disable this + if your chrooted apps are having problems performing those kinds of + tasks. If the sysctl option is enabled, a sysctl option with + name "chroot_caps" is created. + +config HARDENED_CHROOT_INITRD + bool "Exempt initrd tasks from restrictions" + default y + depends on HARDENED_CHROOT && BLK_DEV_INITRD + help + If you say Y here, tasks started prior to init will be exempted from + grsecurity's chroot restrictions. This option is mainly meant to + resolve Plymouth's performing privileged operations unnecessarily + in a chroot. + +endmenu diff --git a/security/hardened/Makefile b/security/hardened/Makefile new file mode 100644 index 0000000000000..2e92ed4845abb --- /dev/null +++ b/security/hardened/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the HARDENED patch set +# + +obj-$(CONFIG_HARDENED) := hardened.o + +hardened-y := init.o sysctl.o chroot.o diff --git a/security/hardened/chroot.c b/security/hardened/chroot.c new file mode 100644 index 0000000000000..87ad1d6cf7359 --- /dev/null +++ b/security/hardened/chroot.c @@ -0,0 +1,489 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "../fs/mount.h" +#include +#include +#include +#include + +#ifdef CONFIG_HARDENED_CHROOT_INITRD +int init_ran; +#endif + +void inc_chroot_refcnts(struct dentry *dentry, struct vfsmount *mnt) +{ +#ifdef CONFIG_HARDENED_CHROOT_RENAME + struct dentry *tmpd = dentry; + + read_seqlock_excl(&mount_lock); + write_seqlock(&rename_lock); + + while (tmpd != mnt->mnt_root) { + atomic_inc(&tmpd->chroot_refcnt); + tmpd = tmpd->d_parent; + } + atomic_inc(&tmpd->chroot_refcnt); + + write_sequnlock(&rename_lock); + read_sequnlock_excl(&mount_lock); +#endif +} + +void dec_chroot_refcnts(struct dentry *dentry, struct vfsmount *mnt) +{ +#ifdef CONFIG_HARDENED_CHROOT_RENAME + struct dentry *tmpd = dentry; + + read_seqlock_excl(&mount_lock); + write_seqlock(&rename_lock); + + while (tmpd != mnt->mnt_root) { + atomic_dec(&tmpd->chroot_refcnt); + tmpd = tmpd->d_parent; + } + atomic_dec(&tmpd->chroot_refcnt); + + write_sequnlock(&rename_lock); + read_sequnlock_excl(&mount_lock); +#endif +} + +#ifdef CONFIG_HARDENED_CHROOT_RENAME +static struct dentry *get_closest_chroot(struct dentry *dentry) +{ + write_seqlock(&rename_lock); + do { + if (atomic_read(&dentry->chroot_refcnt)) { + write_sequnlock(&rename_lock); + return dentry; + } + dentry = dentry->d_parent; + } while (!IS_ROOT(dentry)); + write_sequnlock(&rename_lock); + return NULL; +} +#endif + +int bad_chroot_rename(struct dentry *olddentry, struct vfsmount *oldmnt, + struct dentry *newdentry, struct vfsmount *newmnt) +{ +#ifdef CONFIG_HARDENED_CHROOT_RENAME + struct dentry *chroot; + + if (unlikely(!hardened_enable_chroot_rename)) + return 0; + + if (likely(!proc_is_chrooted(current) && is_global_root(current_uid()))) + return 0; + + chroot = get_closest_chroot(olddentry); + + if (chroot == NULL) + return 0; + + if (is_subdir(newdentry, chroot)) + return 0; + + // NOTE: add non-grsec specific logging? + //log_fs_generic(GR_DONT_AUDIT, GR_CHROOT_RENAME_MSG, olddentry, oldmnt); + + return 1; +#else + return 0; +#endif +} + +void set_chroot_entries(struct task_struct *task, const struct path *path) +{ + if (task_pid_nr(task) > 1 && path->dentry != init_task.fs->root.dentry && + path->dentry != task->nsproxy->mnt_ns->root->mnt.mnt_root +#ifdef CONFIG_HARDENED_CHROOT_INITRD + && init_ran +#endif + ) + task->is_chrooted = 1; + else { +#ifdef CONFIG_HARDENED_CHROOT_INITRD + if (task_pid_nr(task) == 1 && !init_ran) + init_ran = 1; +#endif + task->is_chrooted = 0; + } + + task->chroot_dentry = path->dentry; + return; +} + +void clear_chroot_entries(struct task_struct *task) +{ + task->is_chrooted = 0; + task->chroot_dentry = NULL; + return; +} + +EXPORT_SYMBOL_GPL(handle_chroot_unix); + +int +handle_chroot_unix(const pid_t pid) +{ +#ifdef CONFIG_HARDENED_CHROOT_UNIX + struct task_struct *p; + + if (unlikely(!hardened_enable_chroot_unix)) + return 1; + + if (likely(!proc_is_chrooted(current))) + return 1; + + rcu_read_lock(); + read_lock(&tasklist_lock); + p = find_task_by_vpid_unrestricted(pid); + if (unlikely(p && !have_same_root(current, p))) { + read_unlock(&tasklist_lock); + rcu_read_unlock(); + //log_noargs(GR_DONT_AUDIT, GR_UNIX_CHROOT_MSG); + return 0; + } + read_unlock(&tasklist_lock); + rcu_read_unlock(); +#endif + return 1; +} + +int +handle_chroot_nice(void) +{ +#ifdef CONFIG_HARDENED_CHROOT_NICE + if (hardened_enable_chroot_nice && proc_is_chrooted(current)) { + //log_noargs(GR_DONT_AUDIT, GR_NICE_CHROOT_MSG); + return -EPERM; + } +#endif + return 0; +} + +int +handle_chroot_setpriority(struct task_struct *p, const int niceval) +{ +#ifdef CONFIG_HARDENED_CHROOT_NICE + if (hardened_enable_chroot_nice && (niceval < task_nice(p)) + && proc_is_chrooted(current)) { + //log_str_int(GR_DONT_AUDIT, GR_PRIORITY_CHROOT_MSG, p->comm, task_pid_nr(p)); + return -EACCES; + } +#endif + return 0; +} + +int +handle_chroot_fowner(struct pid *pid, enum pid_type type) +{ +#ifdef CONFIG_HARDENED_CHROOT_FINDTASK + struct task_struct *p; + int ret = 0; + if (!hardened_enable_chroot_findtask || !proc_is_chrooted(current) || !pid) + return ret; + + read_lock(&tasklist_lock); + do_each_pid_task(pid, type, p) { + if (!have_same_root(current, p)) { + ret = 1; + goto out; + } + } while_each_pid_task(pid, type, p); +out: + read_unlock(&tasklist_lock); + return ret; +#endif + return 0; +} + +int +pid_is_chrooted(struct task_struct *p) +{ +#ifdef CONFIG_HARDENED_CHROOT_FINDTASK + if (!hardened_enable_chroot_findtask || !proc_is_chrooted(current) || p == NULL) + return 0; + + if ((p->exit_state & (EXIT_ZOMBIE | EXIT_DEAD)) || + !have_same_root(current, p)) { + return 1; + } +#endif + return 0; +} + +EXPORT_SYMBOL_GPL(pid_is_chrooted); + +#if defined(CONFIG_HARDENED_CHROOT_DOUBLE) || defined(CONFIG_HARDENED_CHROOT_FCHDIR) +int is_outside_chroot(const struct dentry *u_dentry, const struct vfsmount *u_mnt) +{ + struct path path, currentroot; + int ret = 0; + + path.dentry = (struct dentry *)u_dentry; + path.mnt = (struct vfsmount *)u_mnt; + get_fs_root(current->fs, ¤troot); + if (path_is_under(&path, ¤troot)) + ret = 1; + path_put(¤troot); + + return ret; +} +#endif + +int +chroot_fchdir(struct dentry *u_dentry, struct vfsmount *u_mnt) +{ +#ifdef CONFIG_HARDENED_CHROOT_FCHDIR + if (!hardened_enable_chroot_fchdir) + return 1; + + if (!proc_is_chrooted(current)) + return 1; + else if (!is_outside_chroot(u_dentry, u_mnt)) { + //log_fs_generic(GR_DONT_AUDIT, GR_CHROOT_FCHDIR_MSG, u_dentry, u_mnt); + return 0; + } +#endif + return 1; +} + +int +chroot_pathat(int dfd, struct dentry *u_dentry, struct vfsmount *u_mnt, unsigned flags) +{ +#ifdef CONFIG_HARDENED_CHROOT_FCHDIR + struct fd f; + struct path fd_path; + struct path file_path; + + if (!hardened_enable_chroot_fchdir) + return 0; + + if (!proc_is_chrooted(current) || dfd == -1 || dfd == AT_FDCWD) + return 0; + + if (flags & LOOKUP_RCU) + return -ECHILD; + + f = fdget_raw(dfd); + if (!f.file) + return 0; + + fd_path = f.file->f_path; + path_get(&fd_path); + fdput(f); + + file_path.dentry = u_dentry; + file_path.mnt = u_mnt; + + if (!is_outside_chroot(u_dentry, u_mnt) && !path_is_under(&file_path, &fd_path)) { + path_put(&fd_path); + //log_fs_generic(GR_DONT_AUDIT, GR_CHROOT_PATHAT_MSG, u_dentry, u_mnt); + return -ENOENT; + } + path_put(&fd_path); +#endif + return 0; +} + +int +chroot_fhandle(void) +{ +#ifdef CONFIG_HARDENED_CHROOT_FCHDIR + if (!hardened_enable_chroot_fchdir) + return 1; + + if (!proc_is_chrooted(current)) + return 1; + else { + //log_noargs(GR_DONT_AUDIT, GR_CHROOT_FHANDLE_MSG); + return 0; + } +#endif + return 1; +} + +int +chroot_shmat(const pid_t shm_cprid, const pid_t shm_lapid, + const u64 shm_createtime) +{ +#ifdef CONFIG_HARDENED_CHROOT_SHMAT + struct task_struct *p; + + if (unlikely(!hardened_enable_chroot_shmat)) + return 1; + + if (likely(!proc_is_chrooted(current))) + return 1; + + rcu_read_lock(); + read_lock(&tasklist_lock); + + if ((p = find_task_by_vpid_unrestricted(shm_cprid))) { + if (time_before_eq64(p->start_time, shm_createtime)) { + if (have_same_root(current, p)) { + goto allow; + } else { + read_unlock(&tasklist_lock); + rcu_read_unlock(); + //log_noargs(GR_DONT_AUDIT, GR_SHMAT_CHROOT_MSG); + return 0; + } + } + /* creator exited, pid reuse, fall through to next check */ + } + if ((p = find_task_by_vpid_unrestricted(shm_lapid))) { + if (unlikely(!have_same_root(current, p))) { + read_unlock(&tasklist_lock); + rcu_read_unlock(); + //log_noargs(GR_DONT_AUDIT, GR_SHMAT_CHROOT_MSG); + return 0; + } + } + +allow: + read_unlock(&tasklist_lock); + rcu_read_unlock(); +#endif + return 1; +} + +void +log_chroot_exec(const struct dentry *dentry, const struct vfsmount *mnt) +{ +#ifdef CONFIG_HARDENED_CHROOT_EXECLOG + if (hardened_enable_chroot_execlog && proc_is_chrooted(current)) + //log_fs_generic(GR_DO_AUDIT, GR_EXEC_CHROOT_MSG, dentry, mnt); +#endif + return; +} + +int +handle_chroot_mknod(const struct dentry *dentry, + const struct vfsmount *mnt, const int mode) +{ +#ifdef CONFIG_HARDENED_CHROOT_MKNOD + if (hardened_enable_chroot_mknod && !S_ISFIFO(mode) && !S_ISREG(mode) && + proc_is_chrooted(current)) { + //log_fs_generic(GR_DONT_AUDIT, GR_MKNOD_CHROOT_MSG, dentry, mnt); + return -EPERM; + } +#endif + return 0; +} + +int +handle_chroot_mount(const struct dentry *dentry, + const struct vfsmount *mnt, const char *dev_name) +{ +#ifdef CONFIG_HARDENED_CHROOT_MOUNT + if (hardened_enable_chroot_mount && proc_is_chrooted(current)) { + //log_str_fs(GR_DONT_AUDIT, GR_MOUNT_CHROOT_MSG, dev_name ? dev_name : "none", dentry, mnt); + return -EPERM; + } +#endif + return 0; +} + +int +handle_chroot_pivot(void) +{ +#ifdef CONFIG_HARDENED_CHROOT_PIVOT + if (hardened_enable_chroot_pivot && proc_is_chrooted(current)) { + //log_noargs(GR_DONT_AUDIT, GR_PIVOT_CHROOT_MSG); + return -EPERM; + } +#endif + return 0; +} + +int +handle_chroot_chroot(const struct dentry *dentry, const struct vfsmount *mnt) +{ +#ifdef CONFIG_HARDENED_CHROOT_DOUBLE + if (hardened_enable_chroot_double && proc_is_chrooted(current) && + !is_outside_chroot(dentry, mnt)) { + //log_fs_generic(GR_DONT_AUDIT, GR_CHROOT_CHROOT_MSG, dentry, mnt); + return -EPERM; + } +#endif + return 0; +} + +// NOTE: only used in logging which we aren't extracting atm +//extern const char *captab_log[]; +//extern int captab_log_entries; + +EXPORT_SYMBOL_GPL(task_chroot_is_capable); + +int +task_chroot_is_capable(const struct task_struct *task, const struct cred *cred, const int cap) +{ +#ifdef CONFIG_HARDENED_CHROOT_CAPS + if (hardened_enable_chroot_caps && proc_is_chrooted(task)) { + kernel_cap_t chroot_caps = HARDENED_CHROOT_CAPS; + if (cap_raised(chroot_caps, cap)) { + /*if (cap_raised(cred->cap_effective, cap) && cap < captab_log_entries) { + //log_cap(GR_DONT_AUDIT, GR_CAP_CHROOT_MSG, task, captab_log[cap]); + }*/ + return 0; + } + } +#endif + return 1; +} + +EXPORT_SYMBOL_GPL(chroot_is_capable) + +int +chroot_is_capable(const int cap) +{ +#ifdef CONFIG_HARDENED_CHROOT_CAPS + return task_chroot_is_capable(current, current_cred(), cap); +#endif + return 1; +} + +int +handle_chroot_sysctl(const int op) +{ +#ifdef CONFIG_HARDENED_CHROOT_SYSCTL + if (hardened_enable_chroot_sysctl && (op & MAY_WRITE) && + proc_is_chrooted(current)) + return -EACCES; +#endif + return 0; +} + +void +handle_chroot_chdir(const struct path *path) +{ +#ifdef CONFIG_HARDENED_CHROOT_CHDIR + if (hardened_enable_chroot_chdir) + set_fs_pwd(current->fs, path); +#endif + return; +} + +int +handle_chroot_chmod(const struct dentry *dentry, + const struct vfsmount *mnt, const int mode) +{ +#ifdef CONFIG_HARDENED_CHROOT_CHMOD + /* allow chmod +s on directories, but not files */ + if (hardened_enable_chroot_chmod && !d_is_dir(dentry) && + ((mode & S_ISUID) || ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) && + proc_is_chrooted(current)) { + //log_fs_generic(GR_DONT_AUDIT, GR_CHMOD_CHROOT_MSG, dentry, mnt); + return -EPERM; + } +#endif + return 0; +} diff --git a/security/hardened/init.c b/security/hardened/init.c new file mode 100644 index 0000000000000..338d2b3ebb427 --- /dev/null +++ b/security/hardened/init.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include +#include + +int hardened_enable_chroot_findtask; +int hardened_enable_chroot_mount; +int hardened_enable_chroot_shmat; +int hardened_enable_chroot_fchdir; +int hardened_enable_chroot_double; +int hardened_enable_chroot_pivot; +int hardened_enable_chroot_chdir; +int hardened_enable_chroot_chmod; +int hardened_enable_chroot_mknod; +int hardened_enable_chroot_nice; +int hardened_enable_chroot_execlog; +int hardened_enable_chroot_caps; +int hardened_enable_chroot_rename; +int hardened_enable_chroot_sysctl; +int hardened_enable_chroot_unix; + +/* +DEFINE_SPINLOCK(hardened_alert_lock); +unsigned long hardened_alert_wtime = 0; +unsigned long hardened_alert_fyet = 0; + +DEFINE_SPINLOCK(hardened_audit_lock); + +DEFINE_RWLOCK(hardened_exec_file_lock); +*/ + +void __init +hardened_init(void) +{ +#ifdef CONFIG_HARDENED_CHROOT_FINDTASK + hardened_enable_chroot_findtask = 1; +#endif +#ifdef CONFIG_HARDENED_CHROOT_UNIX + hardened_enable_chroot_unix = 1; +#endif +#ifdef CONFIG_HARDENED_CHROOT_MOUNT + hardened_enable_chroot_mount = 1; +#endif +#ifdef CONFIG_HARDENED_CHROOT_FCHDIR + hardened_enable_chroot_fchdir = 1; +#endif +#ifdef CONFIG_HARDENED_CHROOT_SHMAT + hardened_enable_chroot_shmat = 1; +#endif +#ifdef CONFIG_HARDENED_AUDIT_PTRACE + hardened_enable_audit_ptrace = 1; +#endif +#ifdef CONFIG_HARDENED_CHROOT_DOUBLE + hardened_enable_chroot_double = 1; +#endif +#ifdef CONFIG_HARDENED_CHROOT_PIVOT + hardened_enable_chroot_pivot = 1; +#endif +#ifdef CONFIG_HARDENED_CHROOT_CHDIR + hardened_enable_chroot_chdir = 1; +#endif +#ifdef CONFIG_HARDENED_CHROOT_CHMOD + hardened_enable_chroot_chmod = 1; +#endif +#ifdef CONFIG_HARDENED_CHROOT_MKNOD + hardened_enable_chroot_mknod = 1; +#endif +#ifdef CONFIG_HARDENED_CHROOT_NICE + hardened_enable_chroot_nice = 1; +#endif +#ifdef CONFIG_HARDENED_CHROOT_CAPS + hardened_enable_chroot_caps = 1; +#endif +#ifdef CONFIG_HARDENED_CHROOT_RENAME + hardened_enable_chroot_rename = 1; +#endif +#ifdef CONFIG_HARDENED_CHROOT_SYSCTL + hardened_enable_chroot_sysctl = 1; +#endif + return; +} diff --git a/security/hardened/sysctl.c b/security/hardened/sysctl.c new file mode 100644 index 0000000000000..388deaaf8f9ba --- /dev/null +++ b/security/hardened/sysctl.c @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include + +struct ctl_table hardened_table[] = { +#ifdef CONFIG_HARDENED_CHROOT +#ifdef CONFIG_GRKERNSEC_CHROOT_SHMAT + { + .procname = "chroot_deny_shmat", + .data = &grsec_hardened_enable_chroot_shmat, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = &proc_dointvec_secure, + }, +#endif +#ifdef CONFIG_GRKERNSEC_CHROOT_UNIX + { + .procname = "chroot_deny_unix", + .data = &grsec_hardened_enable_chroot_unix, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = &proc_dointvec_secure, + }, +#endif +#ifdef CONFIG_GRKERNSEC_CHROOT_MOUNT + { + .procname = "chroot_deny_mount", + .data = &grsec_hardened_enable_chroot_mount, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = &proc_dointvec_secure, + }, +#endif +#ifdef CONFIG_GRKERNSEC_CHROOT_FCHDIR + { + .procname = "chroot_deny_fchdir", + .data = &grsec_hardened_enable_chroot_fchdir, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = &proc_dointvec_secure, + }, +#endif +#ifdef CONFIG_GRKERNSEC_CHROOT_DOUBLE + { + .procname = "chroot_deny_chroot", + .data = &grsec_hardened_enable_chroot_double, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = &proc_dointvec_secure, + }, +#endif +#ifdef CONFIG_GRKERNSEC_CHROOT_PIVOT + { + .procname = "chroot_deny_pivot", + .data = &grsec_hardened_enable_chroot_pivot, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = &proc_dointvec_secure, + }, +#endif +#ifdef CONFIG_GRKERNSEC_CHROOT_CHDIR + { + .procname = "chroot_enforce_chdir", + .data = &grsec_hardened_enable_chroot_chdir, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = &proc_dointvec_secure, + }, +#endif +#ifdef CONFIG_GRKERNSEC_CHROOT_CHMOD + { + .procname = "chroot_deny_chmod", + .data = &grsec_hardened_enable_chroot_chmod, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = &proc_dointvec_secure, + }, +#endif +#ifdef CONFIG_GRKERNSEC_CHROOT_MKNOD + { + .procname = "chroot_deny_mknod", + .data = &grsec_hardened_enable_chroot_mknod, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = &proc_dointvec_secure, + }, +#endif +#ifdef CONFIG_GRKERNSEC_CHROOT_NICE + { + .procname = "chroot_restrict_nice", + .data = &grsec_hardened_enable_chroot_nice, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = &proc_dointvec_secure, + }, +#endif +#ifdef CONFIG_GRKERNSEC_CHROOT_CAPS + { + .procname = "chroot_caps", + .data = &grsec_hardened_enable_chroot_caps, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = &proc_dointvec_secure, + }, +#endif +#ifdef CONFIG_GRKERNSEC_CHROOT_RENAME + { + .procname = "chroot_deny_bad_rename", + .data = &grsec_hardened_enable_chroot_rename, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = &proc_dointvec_secure, + }, +#endif +#ifdef CONFIG_GRKERNSEC_CHROOT_SYSCTL + { + .procname = "chroot_deny_sysctl", + .data = &grsec_hardened_enable_chroot_sysctl, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = &proc_dointvec_secure, + }, +#endif +#ifdef CONFIG_GRKERNSEC_CHROOT_FINDTASK + { + .procname = "chroot_findtask", + .data = &grsec_hardened_enable_chroot_findtask, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = &proc_dointvec_secure, + }, +#endif + { } +}; +#endif From b5d93158978f57258bb44e983100cff01b0216c2 Mon Sep 17 00:00:00 2001 From: Andy Johnson Date: Thu, 4 May 2017 11:21:34 -0700 Subject: [PATCH 32/34] corrected error from a squash merge, left GRKERNSEC and grsec_, instead of replacing them with new equivalents --- security/hardened/sysctl.c | 56 +++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/security/hardened/sysctl.c b/security/hardened/sysctl.c index 388deaaf8f9ba..eb00d8b085d11 100644 --- a/security/hardened/sysctl.c +++ b/security/hardened/sysctl.c @@ -6,127 +6,127 @@ struct ctl_table hardened_table[] = { #ifdef CONFIG_HARDENED_CHROOT -#ifdef CONFIG_GRKERNSEC_CHROOT_SHMAT +#ifdef CONFIG_HARDENED_CHROOT_SHMAT { .procname = "chroot_deny_shmat", - .data = &grsec_hardened_enable_chroot_shmat, + .data = &hardened_enable_chroot_shmat, .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec_secure, }, #endif -#ifdef CONFIG_GRKERNSEC_CHROOT_UNIX +#ifdef CONFIG_HARDENED_CHROOT_UNIX { .procname = "chroot_deny_unix", - .data = &grsec_hardened_enable_chroot_unix, + .data = &hardened_enable_chroot_unix, .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec_secure, }, #endif -#ifdef CONFIG_GRKERNSEC_CHROOT_MOUNT +#ifdef CONFIG_HARDENED_CHROOT_MOUNT { .procname = "chroot_deny_mount", - .data = &grsec_hardened_enable_chroot_mount, + .data = &hardened_enable_chroot_mount, .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec_secure, }, #endif -#ifdef CONFIG_GRKERNSEC_CHROOT_FCHDIR +#ifdef CONFIG_HARDENED_CHROOT_FCHDIR { .procname = "chroot_deny_fchdir", - .data = &grsec_hardened_enable_chroot_fchdir, + .data = &hardened_enable_chroot_fchdir, .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec_secure, }, #endif -#ifdef CONFIG_GRKERNSEC_CHROOT_DOUBLE +#ifdef CONFIG_HARDENED_CHROOT_DOUBLE { .procname = "chroot_deny_chroot", - .data = &grsec_hardened_enable_chroot_double, + .data = &hardened_enable_chroot_double, .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec_secure, }, #endif -#ifdef CONFIG_GRKERNSEC_CHROOT_PIVOT +#ifdef CONFIG_HARDENED_CHROOT_PIVOT { .procname = "chroot_deny_pivot", - .data = &grsec_hardened_enable_chroot_pivot, + .data = &hardened_enable_chroot_pivot, .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec_secure, }, #endif -#ifdef CONFIG_GRKERNSEC_CHROOT_CHDIR +#ifdef CONFIG_HARDENED_CHROOT_CHDIR { .procname = "chroot_enforce_chdir", - .data = &grsec_hardened_enable_chroot_chdir, + .data = &hardened_enable_chroot_chdir, .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec_secure, }, #endif -#ifdef CONFIG_GRKERNSEC_CHROOT_CHMOD +#ifdef CONFIG_HARDENED_CHROOT_CHMOD { .procname = "chroot_deny_chmod", - .data = &grsec_hardened_enable_chroot_chmod, + .data = &hardened_enable_chroot_chmod, .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec_secure, }, #endif -#ifdef CONFIG_GRKERNSEC_CHROOT_MKNOD +#ifdef CONFIG_HARDENED_CHROOT_MKNOD { .procname = "chroot_deny_mknod", - .data = &grsec_hardened_enable_chroot_mknod, + .data = &hardened_enable_chroot_mknod, .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec_secure, }, #endif -#ifdef CONFIG_GRKERNSEC_CHROOT_NICE +#ifdef CONFIG_HARDENED_CHROOT_NICE { .procname = "chroot_restrict_nice", - .data = &grsec_hardened_enable_chroot_nice, + .data = &hardened_enable_chroot_nice, .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec_secure, }, #endif -#ifdef CONFIG_GRKERNSEC_CHROOT_CAPS +#ifdef CONFIG_HARDENED_CHROOT_CAPS { .procname = "chroot_caps", - .data = &grsec_hardened_enable_chroot_caps, + .data = &hardened_enable_chroot_caps, .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec_secure, }, #endif -#ifdef CONFIG_GRKERNSEC_CHROOT_RENAME +#ifdef CONFIG_HARDENED_CHROOT_RENAME { .procname = "chroot_deny_bad_rename", - .data = &grsec_hardened_enable_chroot_rename, + .data = &hardened_enable_chroot_rename, .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec_secure, }, #endif -#ifdef CONFIG_GRKERNSEC_CHROOT_SYSCTL +#ifdef CONFIG_HARDENED_CHROOT_SYSCTL { .procname = "chroot_deny_sysctl", - .data = &grsec_hardened_enable_chroot_sysctl, + .data = &hardened_enable_chroot_sysctl, .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec_secure, }, #endif -#ifdef CONFIG_GRKERNSEC_CHROOT_FINDTASK +#ifdef CONFIG_HARDENED_CHROOT_FINDTASK { .procname = "chroot_findtask", - .data = &grsec_hardened_enable_chroot_findtask, + .data = &hardened_enable_chroot_findtask, .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec_secure, From 5e51c714794ccb34e40e3bbcdfb4b726fa8e88b7 Mon Sep 17 00:00:00 2001 From: Andy Johnson Date: Thu, 4 May 2017 11:28:17 -0700 Subject: [PATCH 33/34] missed another rename in squash merge for handle_chroot_sysctl --- fs/proc/proc_sysctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 95a095e712578..0845efc9e3799 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -15,7 +15,7 @@ #include #include "internal.h" -extern int gr_handle_chroot_sysctl(const int op); +extern int handle_chroot_sysctl(const int op); static const struct dentry_operations proc_sys_dentry_operations; static const struct file_operations proc_sys_file_operations; From 8e43a359600d0331208f435c1520c2a0703d8d82 Mon Sep 17 00:00:00 2001 From: Andy Johnson Date: Thu, 4 May 2017 11:43:29 -0700 Subject: [PATCH 34/34] another rename correction missed in initial merge --- include/linux/hardened_internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/hardened_internal.h b/include/linux/hardened_internal.h index ca9393fc5da68..8ba3171d8870d 100644 --- a/include/linux/hardened_internal.h +++ b/include/linux/hardened_internal.h @@ -52,7 +52,7 @@ extern int hardened_enable_chroot_unix; #define have_same_root(tsk_a,tsk_b) ((tsk_a)->chroot_dentry == (tsk_b)->chroot_dentry) -static inline bool gr_is_same_file(const struct file *file1, const struct file *file2) +static inline bool is_same_file(const struct file *file1, const struct file *file2) { if (file1 && file2) { const struct inode *inode1 = file1->f_path.dentry->d_inode;