| @@ -0,0 +1,184 @@ | ||
| From patchwork Mon Apr 23 12:54:57 2018 | ||
| Content-Type: text/plain; charset="utf-8" | ||
| MIME-Version: 1.0 | ||
| Content-Transfer-Encoding: 7bit | ||
| X-Patchwork-Submitter: "igor.stoppa@gmail.com" <igor.stoppa@gmail.com> | ||
| X-Patchwork-Id: 10356979 | ||
| Return-Path: | ||
| <kernel-hardening-return-13096-patchwork-kernel-hardening=patchwork.kernel.org@lists.openwall.com> | ||
| Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org | ||
| [172.30.200.125]) | ||
| by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id | ||
| 18EBF601D3 for <patchwork-kernel-hardening@patchwork.kernel.org>; | ||
| Mon, 23 Apr 2018 12:57:14 +0000 (UTC) | ||
| Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) | ||
| by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 08A6428AB0 | ||
| for <patchwork-kernel-hardening@patchwork.kernel.org>; | ||
| Mon, 23 Apr 2018 12:57:14 +0000 (UTC) | ||
| Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) | ||
| id F107228ABE; Mon, 23 Apr 2018 12:57:13 +0000 (UTC) | ||
| X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on | ||
| pdx-wl-mail.web.codeaurora.org | ||
| X-Spam-Level: | ||
| X-Spam-Status: No, score=-5.3 required=2.0 tests=BAYES_00,DKIM_SIGNED, | ||
| DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, MAILING_LIST_MULTI, | ||
| RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 | ||
| Received: from mother.openwall.net (mother.openwall.net [195.42.179.200]) | ||
| by mail.wl.linuxfoundation.org (Postfix) with SMTP id ED8C228AC6 | ||
| for <patchwork-kernel-hardening@patchwork.kernel.org>; | ||
| Mon, 23 Apr 2018 12:57:12 +0000 (UTC) | ||
| Received: (qmail 24261 invoked by uid 550); 23 Apr 2018 12:56:07 -0000 | ||
| Mailing-List: contact kernel-hardening-help@lists.openwall.com; run by ezmlm | ||
| Precedence: bulk | ||
| List-Post: <mailto:kernel-hardening@lists.openwall.com> | ||
| List-Help: <mailto:kernel-hardening-help@lists.openwall.com> | ||
| List-Unsubscribe: <mailto:kernel-hardening-unsubscribe@lists.openwall.com> | ||
| List-Subscribe: <mailto:kernel-hardening-subscribe@lists.openwall.com> | ||
| List-ID: <kernel-hardening.lists.openwall.com> | ||
| Delivered-To: mailing list kernel-hardening@lists.openwall.com | ||
| Received: (qmail 24136 invoked from network); 23 Apr 2018 12:56:04 -0000 | ||
| DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; | ||
| d=gmail.com; s=20161025; | ||
| h=from:to:cc:subject:date:message-id:in-reply-to:references; | ||
| bh=U09LcbMGiUra9N5GfBrZ9Z2qeNHUrpNbiO9P5fSzuZo=; | ||
| b=Xq0nRWIzsNFVfRgaixwK4d1eP8JPeOGZar3QT2Iz76LE9wA2SfQNH/T6SRrJeVGs2D | ||
| L7WLLC/wM6UlMKdCt+MmlzClr3quHmDnsIL2zERqQc6FVGkVwImJY5AJZPW3Xzu2aRcS | ||
| PIyS06ne8RCfkNJyXm1/VDCwmvNjiXTZo+dMtwX4ngKECAJZEInYy7tXpaGns8LrfLuN | ||
| Yq1Zs5rzBYN90zeQ8nUtdc9ZjyIc3yIKkrF6BGBbGt/GJCg1zweFKUDcnUzIR6p10AGe | ||
| hNK3piYoAlOGBLBDiGJcf6MjxYJdeV7HwIgD6M9ldoZxrPFHMatYRBFJPP66FW05hYio | ||
| hKyg== | ||
| X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; | ||
| d=1e100.net; s=20161025; | ||
| h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to | ||
| :references; | ||
| bh=U09LcbMGiUra9N5GfBrZ9Z2qeNHUrpNbiO9P5fSzuZo=; | ||
| b=ldVooaUdWm4BaFmsvrCtzURIVyeNPNQ9AEF2mXMNplTqfdK4VFHL18+N1ODIXWSdR5 | ||
| tEqYDuVqNO5BrIKqVR4dTmiG/rZo2n3FFCOD8jcojoXyeE+tkZw7S0vxR6I3Oa80ABIk | ||
| LDWflZQ+19T1Aql6GnBa3Yv5Y3k48lZ4OelguIJgHQExAFhY0dyDYZO+ly2GHfGKJIIZ | ||
| N1swpQD9sTmv3G/PbXVkc5IKcKHw9pqvuY65zmoQ9DGtU1Z5YlIOWAormEz10TEBr5HQ | ||
| GUvPpORE8320PpG7oafbPoFC/V0d2Z4+rbMIb7p446tPeQLEtz3GHvwkJr3YScJz8k97 | ||
| FTeQ== | ||
| X-Gm-Message-State: ALQs6tCT4AXCcF+ooR7reR4pyY0eqj1d3iOTe6EgsPJUUQOk4tEdOCOz | ||
| 8Jm+V2XVlzSQw5eNBs4IXqg= | ||
| X-Google-Smtp-Source: | ||
| AB8JxZpjg7PDkDI2ZW0SR+RASgkcPtvVpwZRUiri7WbP3zghif8OJQ+lWVd5SfHFymB09G1o1gijMg== | ||
| X-Received: by 2002:a6b:c882:: with SMTP id | ||
| y124-v6mr22567384iof.64.1524488152791; | ||
| Mon, 23 Apr 2018 05:55:52 -0700 (PDT) | ||
| From: Igor Stoppa <igor.stoppa@gmail.com> | ||
| X-Google-Original-From: Igor Stoppa <igor.stoppa@huawei.com> | ||
| To: willy@infradead.org, keescook@chromium.org, paul@paul-moore.com, | ||
| sds@tycho.nsa.gov, mhocko@kernel.org, corbet@lwn.net | ||
| Cc: labbott@redhat.com, linux-cc=david@fromorbit.com, | ||
| --cc=rppt@linux.vnet.ibm.com, --security-module@vger.kernel.org, | ||
| linux-mm@kvack.org, linux-kernel@vger.kernel.org, | ||
| kernel-hardening@lists.openwall.com, igor.stoppa@gmail.com, | ||
| Igor Stoppa <igor.stoppa@huawei.com> | ||
| Subject: [PATCH 8/9] Preliminary self test for pmalloc rare write | ||
| Date: Mon, 23 Apr 2018 16:54:57 +0400 | ||
| Message-Id: <20180423125458.5338-9-igor.stoppa@huawei.com> | ||
| X-Mailer: git-send-email 2.14.1 | ||
| In-Reply-To: <20180423125458.5338-1-igor.stoppa@huawei.com> | ||
| References: <20180423125458.5338-1-igor.stoppa@huawei.com> | ||
| X-Virus-Scanned: ClamAV using ClamSMTP | ||
|
|
||
| Try to alter locked but modifiable pools. | ||
| The test neds some cleanup and expansion. | ||
| It is provided primarily as reference. | ||
|
|
||
| Signed-off-by: Igor Stoppa <igor.stoppa@huawei.com> | ||
| --- | ||
| mm/test_pmalloc.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ | ||
| 1 file changed, 75 insertions(+) | ||
|
|
||
| diff --git a/mm/test_pmalloc.c b/mm/test_pmalloc.c | ||
| index c8835207a400..e8e945e4a4a3 100644 | ||
| --- a/mm/test_pmalloc.c | ||
| +++ b/mm/test_pmalloc.c | ||
| @@ -122,6 +122,80 @@ static void test_oovm(void) | ||
| pmalloc_destroy_pool(pool); | ||
| } | ||
|
|
||
| +#define REGION_SIZE (PAGE_SIZE / 4) | ||
| +#define REGION_NUMBERS 12 | ||
| +static inline void fill_region(char *addr, char c) | ||
| +{ | ||
| + size_t i; | ||
| + | ||
| + for (i = 0; i < REGION_SIZE - 1; i++) | ||
| + addr[i] = c; | ||
| + addr[i] = '\0'; | ||
| +} | ||
| + | ||
| +static inline void init_regions(char *array) | ||
| +{ | ||
| + size_t i; | ||
| + | ||
| + for (i = 0; i < REGION_NUMBERS; i++) | ||
| + fill_region(array + REGION_SIZE * i, i + 'A'); | ||
| +} | ||
| + | ||
| +static inline void show_regions(char *array) | ||
| +{ | ||
| + size_t i; | ||
| + | ||
| + for (i = 0; i < REGION_NUMBERS; i++) | ||
| + pr_info("%s", array + REGION_SIZE * i); | ||
| +} | ||
| + | ||
| +static inline void init_big_injection(char *big_injection) | ||
| +{ | ||
| + size_t i; | ||
| + | ||
| + for (i = 0; i < PAGE_SIZE * 3; i++) | ||
| + big_injection[i] = 'X'; | ||
| +} | ||
| + | ||
| +/* Verify rewritable feature. */ | ||
| +static int test_rare_write(void) | ||
| +{ | ||
| + struct pmalloc_pool *pool; | ||
| + char *array; | ||
| + char injection[] = "123456789"; | ||
| + unsigned short size = sizeof(injection); | ||
| + char *big_injection; | ||
| + | ||
| + | ||
| + pr_notice("Test pmalloc_rare_write()"); | ||
| + pool = pmalloc_create_pool(PMALLOC_RW); | ||
| + array = pzalloc(pool, REGION_SIZE * REGION_NUMBERS); | ||
| + init_regions(array); | ||
| + pmalloc_protect_pool(pool); | ||
| + pr_info("------------------------------------------------------"); | ||
| + pmalloc_rare_write(pool, array, injection, size); | ||
| + pmalloc_rare_write(pool, array + REGION_SIZE, injection, size); | ||
| + pmalloc_rare_write(pool, | ||
| + array + 5 * REGION_SIZE / 2 - size / 2, | ||
| + injection, size); | ||
| + pmalloc_rare_write(pool, array + 3 * REGION_SIZE - size / 2, | ||
| + injection, size); | ||
| + show_regions(array); | ||
| + pmalloc_destroy_pool(pool); | ||
| + pr_info("------------------------------------------------------"); | ||
| + pool = pmalloc_create_pool(PMALLOC_RW); | ||
| + array = pzalloc(pool, REGION_SIZE * REGION_NUMBERS); | ||
| + init_regions(array); | ||
| + pmalloc_protect_pool(pool); | ||
| + big_injection = vmalloc(PAGE_SIZE * 3); | ||
| + init_big_injection(big_injection); | ||
| + pmalloc_rare_write(pool, array + REGION_SIZE / 2, big_injection, | ||
| + PAGE_SIZE * 2); | ||
| + show_regions(array); | ||
| + pr_info("------------------------------------------------------"); | ||
| + return 0; | ||
| +} | ||
| + | ||
| /** | ||
| * test_pmalloc() -main entry point for running the test cases | ||
| */ | ||
| @@ -135,4 +209,5 @@ void test_pmalloc(void) | ||
| test_is_pmalloc_object()))) | ||
| return; | ||
| test_oovm(); | ||
| + test_rare_write(); | ||
| } |
| @@ -0,0 +1,63 @@ | ||
| From 37e980100c3a4c3906f39f5bbeba89087f5c12c1 Mon Sep 17 00:00:00 2001 | ||
| From: Alexander Popov <alex.popov@linux.com> | ||
| Date: Wed, 11 Jul 2018 23:36:35 +0300 | ||
| Subject: [PATCH 1/6] gcc-plugins: Clean up the cgraph_create_edge* macros | ||
|
|
||
| Drop useless redefinitions of cgraph_create_edge* macros. Drop the unused | ||
| nest argument. Also support gcc-8, which doesn't have freq argument. | ||
|
|
||
| Signed-off-by: Alexander Popov <alex.popov@linux.com> | ||
| Message-Id: <1531341400-12077-2-git-send-email-alex.popov@linux.com> | ||
| Signed-off-by: Steve Arnold <nerdboy@gentoo.org> | ||
| --- | ||
| scripts/gcc-plugins/gcc-common.h | 26 ++++++++++++++++---------- | ||
| 1 file changed, 16 insertions(+), 10 deletions(-) | ||
|
|
||
| diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h | ||
| index f46750053377..552d5efd7cb7 100644 | ||
| --- a/scripts/gcc-plugins/gcc-common.h | ||
| +++ b/scripts/gcc-plugins/gcc-common.h | ||
| @@ -392,13 +392,6 @@ static inline struct cgraph_node *cgraph_alias_target(struct cgraph_node *n) | ||
| } | ||
| #endif | ||
|
|
||
| -#if BUILDING_GCC_VERSION >= 4007 && BUILDING_GCC_VERSION <= 4009 | ||
| -#define cgraph_create_edge(caller, callee, call_stmt, count, freq, nest) \ | ||
| - cgraph_create_edge((caller), (callee), (call_stmt), (count), (freq)) | ||
| -#define cgraph_create_edge_including_clones(caller, callee, old_call_stmt, call_stmt, count, freq, nest, reason) \ | ||
| - cgraph_create_edge_including_clones((caller), (callee), (old_call_stmt), (call_stmt), (count), (freq), (reason)) | ||
| -#endif | ||
| - | ||
| #if BUILDING_GCC_VERSION <= 4008 | ||
| #define ENTRY_BLOCK_PTR_FOR_FN(FN) ENTRY_BLOCK_PTR_FOR_FUNCTION(FN) | ||
| #define EXIT_BLOCK_PTR_FOR_FN(FN) EXIT_BLOCK_PTR_FOR_FUNCTION(FN) | ||
| @@ -723,10 +716,23 @@ static inline const char *get_decl_section_name(const_tree decl) | ||
| #define varpool_get_node(decl) varpool_node::get(decl) | ||
| #define dump_varpool_node(file, node) (node)->dump(file) | ||
|
|
||
| -#define cgraph_create_edge(caller, callee, call_stmt, count, freq, nest) \ | ||
| +#if BUILDING_GCC_VERSION >= 8000 | ||
| +#define cgraph_create_edge(caller, callee, call_stmt, count, freq) \ | ||
| + (caller)->create_edge((callee), (call_stmt), (count)) | ||
| + | ||
| +#define cgraph_create_edge_including_clones(caller, callee, \ | ||
| + old_call_stmt, call_stmt, count, freq, reason) \ | ||
| + (caller)->create_edge_including_clones((callee), \ | ||
| + (old_call_stmt), (call_stmt), (count), (reason)) | ||
| +#else | ||
| +#define cgraph_create_edge(caller, callee, call_stmt, count, freq) \ | ||
| (caller)->create_edge((callee), (call_stmt), (count), (freq)) | ||
| -#define cgraph_create_edge_including_clones(caller, callee, old_call_stmt, call_stmt, count, freq, nest, reason) \ | ||
| - (caller)->create_edge_including_clones((callee), (old_call_stmt), (call_stmt), (count), (freq), (reason)) | ||
| + | ||
| +#define cgraph_create_edge_including_clones(caller, callee, \ | ||
| + old_call_stmt, call_stmt, count, freq, reason) \ | ||
| + (caller)->create_edge_including_clones((callee), \ | ||
| + (old_call_stmt), (call_stmt), (count), (freq), (reason)) | ||
| +#endif | ||
|
|
||
| typedef struct cgraph_node *cgraph_node_ptr; | ||
| typedef struct cgraph_edge *cgraph_edge_p; | ||
| -- | ||
| 2.16.1 | ||
|
|
| @@ -0,0 +1,378 @@ | ||
| From 60631b561a755c20590eef1ecc5d01e7bf66406e Mon Sep 17 00:00:00 2001 | ||
| From: Alexander Popov <alex.popov@linux.com> | ||
| Date: Wed, 11 Jul 2018 23:36:36 +0300 | ||
| Subject: [PATCH 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the | ||
| end of syscalls | ||
|
|
||
| The STACKLEAK feature erases the kernel stack before returning from | ||
| syscalls. That reduces the information which kernel stack leak bugs can | ||
| reveal and blocks some uninitialized stack variable attacks. Moreover, | ||
| STACKLEAK blocks kernel stack depth overflow caused by alloca(), aka | ||
| Stack Clash attack. | ||
|
|
||
| This commit introduces the code filling the used part of the kernel | ||
| stack with a poison value before returning to userspace. Full | ||
| STACKLEAK feature also contains the gcc plugin which comes in a | ||
| separate commit. | ||
|
|
||
| The STACKLEAK feature is ported from grsecurity/PaX. More information at: | ||
| https://grsecurity.net/ | ||
| https://pax.grsecurity.net/ | ||
|
|
||
| This code is modified from Brad Spengler/PaX Team's code in the last | ||
| public patch of grsecurity/PaX based on our understanding of the code. | ||
| Changes or omissions from the original code are ours and don't reflect | ||
| the original grsecurity/PaX code. | ||
|
|
||
| Signed-off-by: Alexander Popov <alex.popov@linux.com> | ||
| Acked-by: Thomas Gleixner <tglx@linutronix.de> | ||
| Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com> | ||
| Message-Id: <1531341400-12077-3-git-send-email-alex.popov@linux.com> | ||
| Signed-off-by: Steve Arnold <nerdboy@gentoo.org> | ||
| --- | ||
| Documentation/x86/x86_64/mm.txt | 2 ++ | ||
| arch/Kconfig | 27 ++++++++++++++++++ | ||
| arch/x86/Kconfig | 1 + | ||
| arch/x86/entry/calling.h | 14 +++++++++ | ||
| arch/x86/entry/entry_32.S | 7 +++++ | ||
| arch/x86/entry/entry_64.S | 3 ++ | ||
| arch/x86/entry/entry_64_compat.S | 5 ++++ | ||
| include/linux/sched.h | 4 +++ | ||
| include/linux/stackleak.h | 23 +++++++++++++++ | ||
| kernel/Makefile | 4 +++ | ||
| kernel/fork.c | 3 ++ | ||
| kernel/stackleak.c | 61 ++++++++++++++++++++++++++++++++++++++++ | ||
| 12 files changed, 154 insertions(+) | ||
| create mode 100644 include/linux/stackleak.h | ||
| create mode 100644 kernel/stackleak.c | ||
|
|
||
| diff --git a/Documentation/x86/x86_64/mm.txt b/Documentation/x86/x86_64/mm.txt | ||
| index 5432a96d31ff..600bc2afa27d 100644 | ||
| --- a/Documentation/x86/x86_64/mm.txt | ||
| +++ b/Documentation/x86/x86_64/mm.txt | ||
| @@ -24,6 +24,7 @@ ffffffffa0000000 - fffffffffeffffff (1520 MB) module mapping space | ||
| [fixmap start] - ffffffffff5fffff kernel-internal fixmap range | ||
| ffffffffff600000 - ffffffffff600fff (=4 kB) legacy vsyscall ABI | ||
| ffffffffffe00000 - ffffffffffffffff (=2 MB) unused hole | ||
| +STACKLEAK_POISON value in this last hole: ffffffffffff4111 | ||
|
|
||
| Virtual memory map with 5 level page tables: | ||
|
|
||
| @@ -50,6 +51,7 @@ ffffffffa0000000 - fffffffffeffffff (1520 MB) module mapping space | ||
| [fixmap start] - ffffffffff5fffff kernel-internal fixmap range | ||
| ffffffffff600000 - ffffffffff600fff (=4 kB) legacy vsyscall ABI | ||
| ffffffffffe00000 - ffffffffffffffff (=2 MB) unused hole | ||
| +STACKLEAK_POISON value in this last hole: ffffffffffff4111 | ||
|
|
||
| Architecture defines a 64-bit virtual address. Implementations can support | ||
| less. Currently supported are 48- and 57-bit virtual addresses. Bits 63 | ||
| diff --git a/arch/Kconfig b/arch/Kconfig | ||
| index 75dd23acf133..bc5186fcdf59 100644 | ||
| --- a/arch/Kconfig | ||
| +++ b/arch/Kconfig | ||
| @@ -401,6 +401,13 @@ config SECCOMP_FILTER | ||
|
|
||
| See Documentation/prctl/seccomp_filter.txt for details. | ||
|
|
||
| +config HAVE_ARCH_STACKLEAK | ||
| + bool | ||
| + help | ||
| + An architecture should select this if it has the code which | ||
| + fills the used part of the kernel stack with the STACKLEAK_POISON | ||
| + value before returning from system calls. | ||
| + | ||
| config HAVE_GCC_PLUGINS | ||
| bool | ||
| help | ||
| @@ -535,6 +542,26 @@ config GCC_PLUGIN_RANDSTRUCT_PERFORMANCE | ||
| in structures. This reduces the performance hit of RANDSTRUCT | ||
| at the cost of weakened randomization. | ||
|
|
||
| +config GCC_PLUGIN_STACKLEAK | ||
| + bool "Erase the kernel stack before returning from syscalls" | ||
| + depends on GCC_PLUGINS | ||
| + depends on HAVE_ARCH_STACKLEAK | ||
| + help | ||
| + This option makes the kernel erase the kernel stack before | ||
| + returning from system calls. That reduces the information which | ||
| + kernel stack leak bugs can reveal and blocks some uninitialized | ||
| + stack variable attacks. This option also blocks kernel stack depth | ||
| + overflow caused by alloca(), aka Stack Clash attack. | ||
| + | ||
| + The tradeoff is the performance impact: on a single CPU system kernel | ||
| + compilation sees a 1% slowdown, other systems and workloads may vary | ||
| + and you are advised to test this feature on your expected workload | ||
| + before deploying it. | ||
| + | ||
| + This plugin was ported from grsecurity/PaX. More information at: | ||
| + * https://grsecurity.net/ | ||
| + * https://pax.grsecurity.net/ | ||
| + | ||
| config HAVE_CC_STACKPROTECTOR | ||
| bool | ||
| help | ||
| diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig | ||
| index c07f492b871a..74b8f5c1d805 100644 | ||
| --- a/arch/x86/Kconfig | ||
| +++ b/arch/x86/Kconfig | ||
| @@ -121,6 +121,7 @@ config X86 | ||
| select HAVE_ARCH_COMPAT_MMAP_BASES if MMU && COMPAT | ||
| select HAVE_ARCH_SECCOMP_FILTER | ||
| select HAVE_ARCH_THREAD_STRUCT_WHITELIST | ||
| + select HAVE_ARCH_STACKLEAK | ||
| select HAVE_ARCH_TRACEHOOK | ||
| select HAVE_ARCH_TRANSPARENT_HUGEPAGE | ||
| select HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD if X86_64 | ||
| diff --git a/arch/x86/entry/calling.h b/arch/x86/entry/calling.h | ||
| index 352e70cd33e8..20d0885b00fb 100644 | ||
| --- a/arch/x86/entry/calling.h | ||
| +++ b/arch/x86/entry/calling.h | ||
| @@ -329,8 +329,22 @@ For 32-bit we have the following conventions - kernel is built with | ||
|
|
||
| #endif | ||
|
|
||
| +.macro STACKLEAK_ERASE_NOCLOBBER | ||
| +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK | ||
| + PUSH_AND_CLEAR_REGS | ||
| + call stackleak_erase | ||
| + POP_REGS | ||
| +#endif | ||
| +.endm | ||
| + | ||
| #endif /* CONFIG_X86_64 */ | ||
|
|
||
| +.macro STACKLEAK_ERASE | ||
| +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK | ||
| + call stackleak_erase | ||
| +#endif | ||
| +.endm | ||
| + | ||
| /* | ||
| * This does 'call enter_from_user_mode' unless we can avoid it based on | ||
| * kernel config or using the static jump infrastructure. | ||
| diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S | ||
| index bef8e2b202a8..71e0323e235a 100644 | ||
| --- a/arch/x86/entry/entry_32.S | ||
| +++ b/arch/x86/entry/entry_32.S | ||
| @@ -46,6 +46,8 @@ | ||
| #include <asm/frame.h> | ||
| #include <asm/nospec-branch.h> | ||
|
|
||
| +#include "calling.h" | ||
| + | ||
| .section .entry.text, "ax" | ||
|
|
||
| /* | ||
| @@ -298,6 +300,7 @@ ENTRY(ret_from_fork) | ||
| /* When we fork, we trace the syscall return in the child, too. */ | ||
| movl %esp, %eax | ||
| call syscall_return_slowpath | ||
| + STACKLEAK_ERASE | ||
| jmp restore_all | ||
|
|
||
| /* kernel thread */ | ||
| @@ -458,6 +461,8 @@ ENTRY(entry_SYSENTER_32) | ||
| ALTERNATIVE "testl %eax, %eax; jz .Lsyscall_32_done", \ | ||
| "jmp .Lsyscall_32_done", X86_FEATURE_XENPV | ||
|
|
||
| + STACKLEAK_ERASE | ||
| + | ||
| /* Opportunistic SYSEXIT */ | ||
| TRACE_IRQS_ON /* User mode traces as IRQs on. */ | ||
| movl PT_EIP(%esp), %edx /* pt_regs->ip */ | ||
| @@ -544,6 +549,8 @@ ENTRY(entry_INT80_32) | ||
| call do_int80_syscall_32 | ||
| .Lsyscall_32_done: | ||
|
|
||
| + STACKLEAK_ERASE | ||
| + | ||
| restore_all: | ||
| TRACE_IRQS_IRET | ||
| .Lrestore_all_notrace: | ||
| diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S | ||
| index 3166b9674429..e0b29f2bb267 100644 | ||
| --- a/arch/x86/entry/entry_64.S | ||
| +++ b/arch/x86/entry/entry_64.S | ||
| @@ -329,6 +329,8 @@ syscall_return_via_sysret: | ||
| * We are on the trampoline stack. All regs except RDI are live. | ||
| * We can do future final exit work right here. | ||
| */ | ||
| + STACKLEAK_ERASE_NOCLOBBER | ||
| + | ||
| SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi | ||
|
|
||
| popq %rdi | ||
| @@ -687,6 +689,7 @@ GLOBAL(swapgs_restore_regs_and_return_to_usermode) | ||
| * We are on the trampoline stack. All regs except RDI are live. | ||
| * We can do future final exit work right here. | ||
| */ | ||
| + STACKLEAK_ERASE_NOCLOBBER | ||
|
|
||
| SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi | ||
|
|
||
| diff --git a/arch/x86/entry/entry_64_compat.S b/arch/x86/entry/entry_64_compat.S | ||
| index 7d0df78db727..8eaf8952c408 100644 | ||
| --- a/arch/x86/entry/entry_64_compat.S | ||
| +++ b/arch/x86/entry/entry_64_compat.S | ||
| @@ -261,6 +261,11 @@ GLOBAL(entry_SYSCALL_compat_after_hwframe) | ||
|
|
||
| /* Opportunistic SYSRET */ | ||
| sysret32_from_system_call: | ||
| + /* | ||
| + * We are not going to return to userspace from the trampoline | ||
| + * stack. So let's erase the thread stack right now. | ||
| + */ | ||
| + STACKLEAK_ERASE | ||
| TRACE_IRQS_ON /* User mode traces as IRQs on. */ | ||
| movq RBX(%rsp), %rbx /* pt_regs->rbx */ | ||
| movq RBP(%rsp), %rbp /* pt_regs->rbp */ | ||
| diff --git a/include/linux/sched.h b/include/linux/sched.h | ||
| index ca3f3eae8980..050906fc255c 100644 | ||
| --- a/include/linux/sched.h | ||
| +++ b/include/linux/sched.h | ||
| @@ -1168,6 +1168,10 @@ struct task_struct { | ||
| void *security; | ||
| #endif | ||
|
|
||
| +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK | ||
| + unsigned long lowest_stack; | ||
| +#endif | ||
| + | ||
| /* | ||
| * New fields for task_struct should be added above here, so that | ||
| * they are included in the randomized portion of task_struct. | ||
| diff --git a/include/linux/stackleak.h b/include/linux/stackleak.h | ||
| new file mode 100644 | ||
| index 000000000000..d2560159e68b | ||
| --- /dev/null | ||
| +++ b/include/linux/stackleak.h | ||
| @@ -0,0 +1,23 @@ | ||
| +/* SPDX-License-Identifier: GPL-2.0 */ | ||
| +#ifndef _LINUX_STACKLEAK_H | ||
| +#define _LINUX_STACKLEAK_H | ||
| + | ||
| +#include <linux/sched.h> | ||
| +#include <linux/sched/task_stack.h> | ||
| + | ||
| +/* | ||
| + * Check that the poison value points to the unused hole in the | ||
| + * virtual memory map for your platform. | ||
| + */ | ||
| +#define STACKLEAK_POISON -0xBEEF | ||
| + | ||
| +#define STACKLEAK_SEARCH_DEPTH 128 | ||
| + | ||
| +static inline void stackleak_task_init(struct task_struct *t) | ||
| +{ | ||
| +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK | ||
| + t->lowest_stack = (unsigned long)end_of_stack(t) + sizeof(unsigned long); | ||
| +#endif | ||
| +} | ||
| + | ||
| +#endif | ||
| diff --git a/kernel/Makefile b/kernel/Makefile | ||
| index f85ae5dfa474..a530f77a0ab7 100644 | ||
| --- a/kernel/Makefile | ||
| +++ b/kernel/Makefile | ||
| @@ -114,6 +114,10 @@ obj-$(CONFIG_TORTURE_TEST) += torture.o | ||
|
|
||
| obj-$(CONFIG_HAS_IOMEM) += memremap.o | ||
|
|
||
| +obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak.o | ||
| +KASAN_SANITIZE_stackleak.o := n | ||
| +KCOV_INSTRUMENT_stackleak.o := n | ||
| + | ||
| $(obj)/configs.o: $(obj)/config_data.h | ||
|
|
||
| targets += config_data.gz | ||
| diff --git a/kernel/fork.c b/kernel/fork.c | ||
| index a5d21c42acfc..85956e9710af 100644 | ||
| --- a/kernel/fork.c | ||
| +++ b/kernel/fork.c | ||
| @@ -91,6 +91,7 @@ | ||
| #include <linux/kcov.h> | ||
| #include <linux/livepatch.h> | ||
| #include <linux/thread_info.h> | ||
| +#include <linux/stackleak.h> | ||
|
|
||
| #include <asm/pgtable.h> | ||
| #include <asm/pgalloc.h> | ||
| @@ -1804,6 +1805,8 @@ static __latent_entropy struct task_struct *copy_process( | ||
| if (retval) | ||
| goto bad_fork_cleanup_io; | ||
|
|
||
| + stackleak_task_init(p); | ||
| + | ||
| if (pid != &init_struct_pid) { | ||
| pid = alloc_pid(p->nsproxy->pid_ns_for_children); | ||
| if (IS_ERR(pid)) { | ||
| diff --git a/kernel/stackleak.c b/kernel/stackleak.c | ||
| new file mode 100644 | ||
| index 000000000000..ba2abdf3a569 | ||
| --- /dev/null | ||
| +++ b/kernel/stackleak.c | ||
| @@ -0,0 +1,61 @@ | ||
| +// SPDX-License-Identifier: GPL-2.0 | ||
| +/* | ||
| + * This code fills the used part of the kernel stack with a poison value | ||
| + * before returning to userspace. It's part of the STACKLEAK feature | ||
| + * ported from grsecurity/PaX. | ||
| + * | ||
| + * Author: Alexander Popov <alex.popov@linux.com> | ||
| + * | ||
| + * STACKLEAK reduces the information which kernel stack leak bugs can | ||
| + * reveal and blocks some uninitialized stack variable attacks. Moreover, | ||
| + * STACKLEAK blocks stack depth overflow caused by alloca(), aka Stack Clash | ||
| + * attack. | ||
| + */ | ||
| + | ||
| +#include <linux/stackleak.h> | ||
| + | ||
| +asmlinkage void stackleak_erase(void) | ||
| +{ | ||
| + /* It would be nice not to have 'kstack_ptr' and 'boundary' on stack */ | ||
| + unsigned long kstack_ptr = current->lowest_stack; | ||
| + unsigned long boundary = kstack_ptr & ~(THREAD_SIZE - 1); | ||
| + unsigned int poison_count = 0; | ||
| + const unsigned int depth = STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long); | ||
| + | ||
| + /* Search for the poison value in the kernel stack */ | ||
| + while (kstack_ptr > boundary && poison_count <= depth) { | ||
| + if (*(unsigned long *)kstack_ptr == STACKLEAK_POISON) | ||
| + poison_count++; | ||
| + else | ||
| + poison_count = 0; | ||
| + | ||
| + kstack_ptr -= sizeof(unsigned long); | ||
| + } | ||
| + | ||
| + /* | ||
| + * One 'long int' at the bottom of the thread stack is reserved and | ||
| + * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK=y). | ||
| + */ | ||
| + if (kstack_ptr == boundary) | ||
| + kstack_ptr += sizeof(unsigned long); | ||
| + | ||
| + /* | ||
| + * Now write the poison value to the kernel stack. Start from | ||
| + * 'kstack_ptr' and move up till the new 'boundary'. We assume that | ||
| + * the stack pointer doesn't change when we write poison. | ||
| + */ | ||
| + if (on_thread_stack()) | ||
| + boundary = current_stack_pointer; | ||
| + else | ||
| + boundary = current_top_of_stack(); | ||
| + | ||
| + BUG_ON(boundary - kstack_ptr >= THREAD_SIZE); | ||
| + | ||
| + while (kstack_ptr < boundary) { | ||
| + *(unsigned long *)kstack_ptr = STACKLEAK_POISON; | ||
| + kstack_ptr += sizeof(unsigned long); | ||
| + } | ||
| + | ||
| + /* Reset the 'lowest_stack' value for the next syscall */ | ||
| + current->lowest_stack = current_top_of_stack() - THREAD_SIZE/64; | ||
| +} | ||
| -- | ||
| 2.16.1 | ||
|
|
| @@ -0,0 +1,227 @@ | ||
| From fb27f8ee56cd90b120928c1d72f2d72b9822ad3a Mon Sep 17 00:00:00 2001 | ||
| From: Alexander Popov <alex.popov@linux.com> | ||
| Date: Wed, 11 Jul 2018 23:36:38 +0300 | ||
| Subject: [PATCH 4/6] lkdtm: Add a test for STACKLEAK | ||
|
|
||
| Introduce lkdtm tests for the STACKLEAK feature. | ||
|
|
||
| First, all of them check that the current task stack is properly erased | ||
| (filled with STACKLEAK_POISON). | ||
|
|
||
| STACKLEAK_DEEP_RECURSION tests that exhausting the current task stack | ||
| with deep recursion is detected by CONFIG_VMAP_STACK (which is implied | ||
| by CONFIG_GCC_PLUGIN_STACKLEAK). | ||
|
|
||
| STACKLEAK_BIG_ALLOCA and STACKLEAK_RECURSION_WITH_ALLOCA test that | ||
| alloca() calls which overflow the kernel stack hit BUG()/panic() in | ||
| stackleak_check_alloca(). | ||
|
|
||
| Signed-off-by: Alexander Popov <alex.popov@linux.com> | ||
| Signed-off-by: Tycho Andersen <tycho@tycho.ws> | ||
| Message-Id: <1531341400-12077-5-git-send-email-alex.popov@linux.com> | ||
| Signed-off-by: Steve Arnold <nerdboy@gentoo.org> | ||
| --- | ||
| drivers/misc/lkdtm/Makefile | 2 + | ||
| drivers/misc/lkdtm/core.c | 3 + | ||
| drivers/misc/lkdtm/lkdtm.h | 5 ++ | ||
| drivers/misc/lkdtm/stackleak.c | 146 +++++++++++++++++++++++++++++++++++++++++ | ||
| 4 files changed, 156 insertions(+) | ||
| create mode 100644 drivers/misc/lkdtm/stackleak.c | ||
|
|
||
| diff --git a/drivers/misc/lkdtm/Makefile b/drivers/misc/lkdtm/Makefile | ||
| index 3370a4138e94..951c984de61a 100644 | ||
| --- a/drivers/misc/lkdtm/Makefile | ||
| +++ b/drivers/misc/lkdtm/Makefile | ||
| @@ -8,7 +8,9 @@ lkdtm-$(CONFIG_LKDTM) += perms.o | ||
| lkdtm-$(CONFIG_LKDTM) += refcount.o | ||
| lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o | ||
| lkdtm-$(CONFIG_LKDTM) += usercopy.o | ||
| +lkdtm-$(CONFIG_LKDTM) += stackleak.o | ||
|
|
||
| +KASAN_SANITIZE_stackleak.o := n | ||
| KCOV_INSTRUMENT_rodata.o := n | ||
|
|
||
| OBJCOPYFLAGS := | ||
| diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c | ||
| index 2154d1bfd18b..9d0324a2e7e7 100644 | ||
| --- a/drivers/misc/lkdtm/core.c | ||
| +++ b/drivers/misc/lkdtm/core.c | ||
| @@ -183,6 +183,9 @@ static const struct crashtype crashtypes[] = { | ||
| CRASHTYPE(USERCOPY_STACK_FRAME_FROM), | ||
| CRASHTYPE(USERCOPY_STACK_BEYOND), | ||
| CRASHTYPE(USERCOPY_KERNEL), | ||
| + CRASHTYPE(STACKLEAK_BIG_ALLOCA), | ||
| + CRASHTYPE(STACKLEAK_DEEP_RECURSION), | ||
| + CRASHTYPE(STACKLEAK_RECURSION_WITH_ALLOCA), | ||
| }; | ||
|
|
||
|
|
||
| diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h | ||
| index 9e513dcfd809..865a6c371bfa 100644 | ||
| --- a/drivers/misc/lkdtm/lkdtm.h | ||
| +++ b/drivers/misc/lkdtm/lkdtm.h | ||
| @@ -83,4 +83,9 @@ void lkdtm_USERCOPY_STACK_FRAME_FROM(void); | ||
| void lkdtm_USERCOPY_STACK_BEYOND(void); | ||
| void lkdtm_USERCOPY_KERNEL(void); | ||
|
|
||
| +/* lkdtm_stackleak.c */ | ||
| +void lkdtm_STACKLEAK_BIG_ALLOCA(void); | ||
| +void lkdtm_STACKLEAK_DEEP_RECURSION(void); | ||
| +void lkdtm_STACKLEAK_RECURSION_WITH_ALLOCA(void); | ||
| + | ||
| #endif | ||
| diff --git a/drivers/misc/lkdtm/stackleak.c b/drivers/misc/lkdtm/stackleak.c | ||
| new file mode 100644 | ||
| index 000000000000..5aecdec71623 | ||
| --- /dev/null | ||
| +++ b/drivers/misc/lkdtm/stackleak.c | ||
| @@ -0,0 +1,146 @@ | ||
| +// SPDX-License-Identifier: GPL-2.0 | ||
| +/* | ||
| + * This code tests several aspects of the STACKLEAK feature: | ||
| + * - the current task stack is properly erased (filled with STACKLEAK_POISON); | ||
| + * - exhausting the current task stack with deep recursion is detected by | ||
| + * CONFIG_VMAP_STACK (which is implied by CONFIG_GCC_PLUGIN_STACKLEAK); | ||
| + * - alloca() calls which overflow the kernel stack hit BUG()/panic() in | ||
| + * stackleak_check_alloca(). | ||
| + * | ||
| + * Authors: | ||
| + * Alexander Popov <alex.popov@linux.com> | ||
| + * Tycho Andersen <tycho@tycho.ws> | ||
| + */ | ||
| + | ||
| +#include "lkdtm.h" | ||
| +#include <linux/stackleak.h> | ||
| + | ||
| +static noinline bool stack_is_erased(void) | ||
| +{ | ||
| + unsigned long *sp, left, found, i; | ||
| + const unsigned long check_depth = | ||
| + STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long); | ||
| + | ||
| + /* | ||
| + * For the details about the alignment of the poison values, see | ||
| + * the comment in stackleak_track_stack(). | ||
| + */ | ||
| + sp = PTR_ALIGN(&i, sizeof(unsigned long)); | ||
| + | ||
| + left = ((unsigned long)sp & (THREAD_SIZE - 1)) / sizeof(unsigned long); | ||
| + sp--; | ||
| + | ||
| + /* | ||
| + * One 'long int' at the bottom of the thread stack is reserved | ||
| + * and not poisoned. | ||
| + */ | ||
| + if (left > 1) | ||
| + left--; | ||
| + else | ||
| + return false; | ||
| + | ||
| + pr_info("checking unused part of the thread stack (%lu bytes)...\n", | ||
| + left * sizeof(unsigned long)); | ||
| + | ||
| + /* | ||
| + * Search for 'check_depth' poison values in a row (just like | ||
| + * stackleak_erase() does). | ||
| + */ | ||
| + for (i = 0, found = 0; i < left && found <= check_depth; i++) { | ||
| + if (*(sp - i) == STACKLEAK_POISON) | ||
| + found++; | ||
| + else | ||
| + found = 0; | ||
| + } | ||
| + | ||
| + if (found <= check_depth) { | ||
| + pr_err("FAIL: thread stack is not erased (checked %lu bytes)\n", | ||
| + i * sizeof(unsigned long)); | ||
| + return false; | ||
| + } | ||
| + | ||
| + pr_info("first %lu bytes are unpoisoned\n", | ||
| + (i - found) * sizeof(unsigned long)); | ||
| + | ||
| + /* The rest of thread stack should be erased */ | ||
| + for (; i < left; i++) { | ||
| + if (*(sp - i) != STACKLEAK_POISON) { | ||
| + pr_err("FAIL: thread stack is NOT properly erased\n"); | ||
| + return false; | ||
| + } | ||
| + } | ||
| + | ||
| + pr_info("the rest of the thread stack is properly erased\n"); | ||
| + return true; | ||
| +} | ||
| + | ||
| +static noinline void do_alloca(unsigned long size) | ||
| +{ | ||
| + char buf[size]; | ||
| + | ||
| + /* So this doesn't get inlined or optimized out */ | ||
| + snprintf(buf, size, "testing alloca...\n"); | ||
| +} | ||
| + | ||
| +void lkdtm_STACKLEAK_BIG_ALLOCA(void) | ||
| +{ | ||
| + if (!stack_is_erased()) | ||
| + return; | ||
| + | ||
| + pr_info("try a small alloca() of 16 bytes...\n"); | ||
| + do_alloca(16); | ||
| + pr_info("small alloca() is successful\n"); | ||
| + | ||
| + pr_info("try alloca() over the thread stack boundary...\n"); | ||
| + do_alloca(THREAD_SIZE); | ||
| + pr_err("FAIL: alloca() over the thread stack boundary is NOT detected\n"); | ||
| +} | ||
| + | ||
| +static noinline unsigned long recursion(unsigned long prev_sp, bool with_alloca) | ||
| +{ | ||
| + char buf[400]; | ||
| + unsigned long sp = (unsigned long)&sp; | ||
| + | ||
| + snprintf(buf, sizeof(buf), "testing deep recursion...\n"); | ||
| + | ||
| + if (with_alloca) | ||
| + do_alloca(400); | ||
| + | ||
| + if (prev_sp < sp + THREAD_SIZE) | ||
| + sp = recursion(prev_sp, with_alloca); | ||
| + | ||
| + return sp; | ||
| +} | ||
| + | ||
| +void lkdtm_STACKLEAK_DEEP_RECURSION(void) | ||
| +{ | ||
| + unsigned long sp = (unsigned long)&sp; | ||
| + | ||
| + if (!stack_is_erased()) | ||
| + return; | ||
| + | ||
| + /* | ||
| + * Overflow the thread stack using deep recursion. It should hit the | ||
| + * guard page provided by CONFIG_VMAP_STACK (which is implied by | ||
| + * CONFIG_GCC_PLUGIN_STACKLEAK). | ||
| + */ | ||
| + pr_info("try to overflow the thread stack using deep recursion...\n"); | ||
| + pr_err("FAIL: stack depth overflow (%lu bytes) is not detected\n", | ||
| + sp - recursion(sp, 0)); | ||
| +} | ||
| + | ||
| +void lkdtm_STACKLEAK_RECURSION_WITH_ALLOCA(void) | ||
| +{ | ||
| + unsigned long sp = (unsigned long)&sp; | ||
| + | ||
| + if (!stack_is_erased()) | ||
| + return; | ||
| + | ||
| + /* | ||
| + * Overflow the thread stack using deep recursion with alloca. | ||
| + * It should hit BUG()/panic() in stackleak_check_alloca(). | ||
| + */ | ||
| + pr_info("try to overflow the thread stack using recursion & alloca\n"); | ||
| + recursion(sp, 1); | ||
| + pr_err("FAIL: stack depth overflow is not detected\n"); | ||
| +} | ||
| -- | ||
| 2.16.1 | ||
|
|
| @@ -0,0 +1,124 @@ | ||
| From 639ebb03fa61e899c21ed2684e40725612458127 Mon Sep 17 00:00:00 2001 | ||
| From: Steve Arnold <nerdboy@gentoo.org> | ||
| Date: Fri, 3 Aug 2018 14:39:08 -0700 | ||
| Subject: [PATCH 5/6] fs/proc: Show STACKLEAK metrics in the /proc file system | ||
|
|
||
| Introduce CONFIG_STACKLEAK_METRICS providing STACKLEAK information about | ||
| tasks via the /proc file system. In particular, /proc/<pid>/stack_depth | ||
| shows the maximum kernel stack consumption for the current and previous | ||
| syscalls. Although this information is not precise, it can be useful for | ||
| estimating the STACKLEAK performance impact for your workloads. | ||
|
|
||
| Signed-off-by: Alexander Popov <alex.popov@linux.com> | ||
| Signed-off-by: Steve Arnold <nerdboy@gentoo.org> | ||
| --- | ||
| arch/Kconfig | 12 ++++++++++++ | ||
| fs/proc/base.c | 18 ++++++++++++++++++ | ||
| include/linux/sched.h | 1 + | ||
| include/linux/stackleak.h | 3 +++ | ||
| kernel/stackleak.c | 4 ++++ | ||
| 5 files changed, 38 insertions(+) | ||
|
|
||
| diff --git a/arch/Kconfig b/arch/Kconfig | ||
| index ac0811ef6d70..3e3bdb433e0c 100644 | ||
| --- a/arch/Kconfig | ||
| +++ b/arch/Kconfig | ||
| @@ -576,6 +576,18 @@ config STACKLEAK_TRACK_MIN_SIZE | ||
| a stack frame size greater than or equal to this parameter. | ||
| If unsure, leave the default value 100. | ||
|
|
||
| +config STACKLEAK_METRICS | ||
| + bool "Show STACKLEAK metrics in the /proc file system" | ||
| + depends on GCC_PLUGIN_STACKLEAK | ||
| + depends on PROC_FS | ||
| + help | ||
| + If this is set, STACKLEAK metrics for every task are available in | ||
| + the /proc file system. In particular, /proc/<pid>/stack_depth | ||
| + shows the maximum kernel stack consumption for the current and | ||
| + previous syscalls. Although this information is not precise, it | ||
| + can be useful for estimating the STACKLEAK performance impact for | ||
| + your workloads. | ||
| + | ||
| config HAVE_CC_STACKPROTECTOR | ||
| bool | ||
| help | ||
| diff --git a/fs/proc/base.c b/fs/proc/base.c | ||
| index 1a76d751cf3c..5a97edf20c0f 100644 | ||
| --- a/fs/proc/base.c | ||
| +++ b/fs/proc/base.c | ||
| @@ -2924,6 +2924,21 @@ static int proc_pid_patch_state(struct seq_file *m, struct pid_namespace *ns, | ||
| } | ||
| #endif /* CONFIG_LIVEPATCH */ | ||
|
|
||
| +#ifdef CONFIG_STACKLEAK_METRICS | ||
| +static int proc_stack_depth(struct seq_file *m, struct pid_namespace *ns, | ||
| + struct pid *pid, struct task_struct *task) | ||
| +{ | ||
| + unsigned long prev_depth = THREAD_SIZE - | ||
| + (task->prev_lowest_stack & (THREAD_SIZE - 1)); | ||
| + unsigned long depth = THREAD_SIZE - | ||
| + (task->lowest_stack & (THREAD_SIZE - 1)); | ||
| + | ||
| + seq_printf(m, "previous stack depth: %lu\nstack depth: %lu\n", | ||
| + prev_depth, depth); | ||
| + return 0; | ||
| +} | ||
| +#endif /* CONFIG_STACKLEAK_METRICS */ | ||
| + | ||
| /* | ||
| * Thread groups | ||
| */ | ||
| @@ -3025,6 +3040,9 @@ static const struct pid_entry tgid_base_stuff[] = { | ||
| #ifdef CONFIG_LIVEPATCH | ||
| ONE("patch_state", S_IRUSR, proc_pid_patch_state), | ||
| #endif | ||
| +#ifdef CONFIG_STACKLEAK_METRICS | ||
| + ONE("stack_depth", S_IRUGO, proc_stack_depth), | ||
| +#endif | ||
| }; | ||
|
|
||
| static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx) | ||
| diff --git a/include/linux/sched.h b/include/linux/sched.h | ||
| index 050906fc255c..3456d7cc32aa 100644 | ||
| --- a/include/linux/sched.h | ||
| +++ b/include/linux/sched.h | ||
| @@ -1170,6 +1170,7 @@ struct task_struct { | ||
|
|
||
| #ifdef CONFIG_GCC_PLUGIN_STACKLEAK | ||
| unsigned long lowest_stack; | ||
| + unsigned long prev_lowest_stack; | ||
| #endif | ||
|
|
||
| /* | ||
| diff --git a/include/linux/stackleak.h b/include/linux/stackleak.h | ||
| index d2560159e68b..b62f1330c9ff 100644 | ||
| --- a/include/linux/stackleak.h | ||
| +++ b/include/linux/stackleak.h | ||
| @@ -17,6 +17,9 @@ static inline void stackleak_task_init(struct task_struct *t) | ||
| { | ||
| #ifdef CONFIG_GCC_PLUGIN_STACKLEAK | ||
| t->lowest_stack = (unsigned long)end_of_stack(t) + sizeof(unsigned long); | ||
| +# ifdef CONFIG_STACKLEAK_METRICS | ||
| + t->prev_lowest_stack = t->lowest_stack; | ||
| +# endif | ||
| #endif | ||
| } | ||
|
|
||
| diff --git a/kernel/stackleak.c b/kernel/stackleak.c | ||
| index 259b6fbefa5f..90f73187eac3 100644 | ||
| --- a/kernel/stackleak.c | ||
| +++ b/kernel/stackleak.c | ||
| @@ -39,6 +39,10 @@ asmlinkage void stackleak_erase(void) | ||
| if (kstack_ptr == boundary) | ||
| kstack_ptr += sizeof(unsigned long); | ||
|
|
||
| +#ifdef CONFIG_STACKLEAK_METRICS | ||
| + current->prev_lowest_stack = kstack_ptr; | ||
| +#endif | ||
| + | ||
| /* | ||
| * Now write the poison value to the kernel stack. Start from | ||
| * 'kstack_ptr' and move up till the new 'boundary'. We assume that | ||
| -- | ||
| 2.16.1 | ||
|
|
| @@ -0,0 +1,60 @@ | ||
| From ad9873f821956da5ef5d9fe5f2560e29e84f0527 Mon Sep 17 00:00:00 2001 | ||
| From: Alexander Popov <alex.popov@linux.com> | ||
| Date: Wed, 11 Jul 2018 23:36:40 +0300 | ||
| Subject: [PATCH 6/6] doc: self-protection: Add information about STACKLEAK | ||
| feature | ||
|
|
||
| Add information about STACKLEAK feature to "Stack depth overflow" and | ||
| "Memory poisoning" sections of self-protection.rst. | ||
|
|
||
| Signed-off-by: Alexander Popov <alex.popov@linux.com> | ||
| Message-Id: <1531341400-12077-7-git-send-email-alex.popov@linux.com> | ||
| Signed-off-by: Steve Arnold <nerdboy@gentoo.org> | ||
| --- | ||
| Documentation/security/self-protection.rst | 23 ++++++++++++++--------- | ||
| 1 file changed, 14 insertions(+), 9 deletions(-) | ||
|
|
||
| diff --git a/Documentation/security/self-protection.rst b/Documentation/security/self-protection.rst | ||
| index 0f53826c78b9..b685f18041a0 100644 | ||
| --- a/Documentation/security/self-protection.rst | ||
| +++ b/Documentation/security/self-protection.rst | ||
| @@ -165,10 +165,15 @@ Stack depth overflow | ||
| A less well understood attack is using a bug that triggers the | ||
| kernel to consume stack memory with deep function calls or large stack | ||
| allocations. With this attack it is possible to write beyond the end of | ||
| -the kernel's preallocated stack space and into sensitive structures. Two | ||
| -important changes need to be made for better protections: moving the | ||
| -sensitive thread_info structure elsewhere, and adding a faulting memory | ||
| -hole at the bottom of the stack to catch these overflows. | ||
| +the kernel's preallocated stack space and into sensitive structures. | ||
| +The combination of the following measures gives better protection: | ||
| + | ||
| +* moving the sensitive thread_info structure off the stack | ||
| + (``CONFIG_THREAD_INFO_IN_TASK``); | ||
| +* adding a faulting memory hole at the bottom of the stack to catch | ||
| + these overflows (``CONFIG_VMAP_STACK``); | ||
| +* runtime checking that alloca() calls don't overstep the stack boundary | ||
| + (``CONFIG_GCC_PLUGIN_STACKLEAK``). | ||
|
|
||
| Heap memory integrity | ||
| --------------------- | ||
| @@ -302,11 +307,11 @@ sure structure holes are cleared. | ||
| Memory poisoning | ||
| ---------------- | ||
|
|
||
| -When releasing memory, it is best to poison the contents (clear stack on | ||
| -syscall return, wipe heap memory on a free), to avoid reuse attacks that | ||
| -rely on the old contents of memory. This frustrates many uninitialized | ||
| -variable attacks, stack content exposures, heap content exposures, and | ||
| -use-after-free attacks. | ||
| +When releasing memory, it is best to poison the contents, to avoid reuse | ||
| +attacks that rely on the old contents of memory. E.g., clear stack on a | ||
| +syscall return (``CONFIG_GCC_PLUGIN_STACKLEAK``), wipe heap memory on a | ||
| +free. This frustrates many uninitialized variable attacks, stack content | ||
| +exposures, heap content exposures, and use-after-free attacks. | ||
|
|
||
| Destination tracking | ||
| -------------------- | ||
| -- | ||
| 2.16.1 | ||
|
|
| @@ -0,0 +1,63 @@ | ||
| # Copyright 1999-2018 Gentoo Foundation | ||
| # Distributed under the terms of the GNU General Public License v2 | ||
|
|
||
| EAPI="6" | ||
| UNIPATCH_STRICTORDER="1" | ||
| ETYPE="sources" | ||
| K_WANT_GENPATCHES="base extras experimental" | ||
| K_GENPATCHES_VER="17" | ||
|
|
||
| SPLASH_PATCH="linux-4.15-bootsplash-patches-and-Makefile-fix.patch" | ||
| SPL_PATCH_URI="mirror://gentoo/${SPLASH_PATCH}.gz" | ||
|
|
||
| inherit kernel-2 | ||
| detect_version | ||
| detect_arch | ||
|
|
||
| KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86" | ||
| HOMEPAGE="https://dev.gentoo.org/~mpagano/genpatches | ||
| https://github.com/philmmanjaro/linux-bootsplash" | ||
| IUSE="experimental" | ||
|
|
||
| DESCRIPTION="Full sources including the Gentoo patchset for the ${KV_MAJOR}.${KV_MINOR} kernel tree" | ||
| SRC_URI="${KERNEL_URI} ${GENPATCHES_URI} ${ARCH_URI} ${SPL_PATCH_URI}" | ||
|
|
||
| K_EXTRAELOG="This is the bleeding-edge mainline gentoo-sources kernel | ||
| with the linux bootsplash patches on top (see tools/bootsplash). You | ||
| need to enable CONFIG_BOOTSPLASH and build the splash file using the | ||
| script in the above directory (a sample tux bootsplash is provided). | ||
| Then install the splash file as shown and add the cmdline parameter." | ||
|
|
||
| src_unpack() { | ||
| # need to unpack manually due to patch reqs below | ||
| unpack ${SPLASH_PATCH}.gz | ||
|
|
||
| kernel-2_src_unpack | ||
| } | ||
|
|
||
| src_prepare() { | ||
| ebegin "Applying kernel bootsplash patches" | ||
| EPATCH_OPTS="-F3" | ||
| epatch "${WORKDIR}"/${SPLASH_PATCH} || die "splash patch failed!" | ||
| cp "${FILESDIR}"/*.gif "${S}"/tools/bootsplash/ | ||
| epatch "${FILESDIR}"/4.15.14/* | ||
| eend $? || return | ||
|
|
||
| kernel-2_src_prepare | ||
| } | ||
|
|
||
| pkg_postinst() { | ||
| kernel-2_pkg_postinst | ||
| einfo "" | ||
| einfo "To configure the bootsplash, copy the file and add cmdline:" | ||
| einfo "" | ||
| einfo " Splash-file under /lib/firmware/mypath/myfile" | ||
| einfo " Cmdline: bootsplash.bootfile=mypath/myfile" | ||
| einfo "" | ||
| einfo "For more info on this patchset, and how to report problems, see:" | ||
| einfo "${HOMEPAGE}" | ||
| } | ||
|
|
||
| pkg_postrm() { | ||
| kernel-2_pkg_postrm | ||
| } |
| @@ -0,0 +1,78 @@ | ||
| # Copyright 1999-2018 Gentoo Foundation | ||
| # Distributed under the terms of the GNU General Public License v2 | ||
|
|
||
| EAPI="6" | ||
|
|
||
| ETYPE="sources" | ||
| UNIPATCH_STRICTORDER="1" | ||
| K_WANT_GENPATCHES="base extras experimental" | ||
| K_GENPATCHES_VER="12" | ||
|
|
||
| inherit eutils kernel-2 | ||
| detect_version | ||
| detect_arch | ||
|
|
||
| K_BRANCH_ID="${KV_MAJOR}.${KV_MINOR}" | ||
| SPLASH_PATCH="linux-${K_BRANCH_ID}-bootsplash-patches-for-kernel-space-fbc.patch" | ||
| SPLASH_URI="mirror://gentoo/${SPLASH_PATCH}.gz" | ||
| LOGO_PATCH="linux-4.14-bootsplash-add-gentoo-logo-build-script.patch" | ||
|
|
||
| KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86" | ||
| HOMEPAGE="https://dev.gentoo.org/~mpagano/genpatches" | ||
| IUSE="experimental" | ||
|
|
||
| DESCRIPTION="Full sources including the Gentoo patchset for the ${KV_MAJOR}.${KV_MINOR} kernel tree" | ||
| SRC_URI="${KERNEL_URI} ${GENPATCHES_URI} ${ARCH_URI} ${SPLASH_URI}" | ||
|
|
||
| RDEPEND="" | ||
| DEPEND="${RDEPEND} | ||
| >=dev-vcs/git-1.8.2.1" | ||
|
|
||
| K_EXTRAELOG="This is the bleeding-edge mainline gentoo-sources kernel | ||
| with the linux bootsplash patches on top (see tools/bootsplash). You | ||
| need to enable CONFIG_BOOTSPLASH and build the splash file using the | ||
| script in the above directory (a sample tux bootsplash is provided). | ||
| Then install the splash file as shown and add the cmdline parameter." | ||
|
|
||
| src_unpack() { | ||
| # need to unpack manually and depend on git due to patch reqs below | ||
| unpack ${SPLASH_PATCH}.gz | ||
|
|
||
| kernel-2_src_unpack | ||
| } | ||
|
|
||
| src_prepare() { | ||
| # We can't use unipatch or epatch here due to the git binary | ||
| # diffs that always cause dry-run errors (even with --force). | ||
|
|
||
| ebegin "Applying kernel bootsplash, protectable memory, and stackleak patches" | ||
| EPATCH_OPTS="-F3 -b" | ||
| epatch "${WORKDIR}"/${SPLASH_PATCH} || die "splash patch failed!" | ||
| epatch "${FILESDIR}"/${LOGO_PATCH} || die "logo patch failed!" | ||
| cp "${FILESDIR}"/*.gif "${S}"/tools/bootsplash/ | ||
|
|
||
| epatch "${FILESDIR}"/${K_BRANCH_ID}/stack/* | ||
| epatch "${FILESDIR}"/${K_BRANCH_ID}/pmem/* | ||
| eend $? || return | ||
|
|
||
| default | ||
|
|
||
| # clean up workdir so we don't install patch cruft | ||
| rm -f "${WORKDIR}"/*bootsplash-patches-for-kernel-space-fbc* | ||
| } | ||
|
|
||
| pkg_postinst() { | ||
| kernel-2_pkg_postinst | ||
| einfo "" | ||
| einfo "To configure the bootsplash, copy the file and add cmdline:" | ||
| einfo "" | ||
| einfo " Splash-file under /lib/firmware/mypath/myfile" | ||
| einfo " Cmdline: bootsplash.bootfile=mypath/myfile" | ||
| einfo "" | ||
| einfo "For more info on this patchset, and how to report problems, see:" | ||
| einfo " https://github.com/philmmanjaro/linux-bootsplash" | ||
| } | ||
|
|
||
| pkg_postrm() { | ||
| kernel-2_pkg_postrm | ||
| } |