From patchwork Thu Dec 19 19:35:59 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 3384061 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id C0F7AC0D4A for ; Thu, 19 Dec 2013 19:39:06 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 6B342206B7 for ; Thu, 19 Dec 2013 19:39:05 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id DB739206B5 for ; Thu, 19 Dec 2013 19:39:03 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VtjQE-0001UW-B2; Thu, 19 Dec 2013 19:38:14 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1VtjQ3-0003oQ-0J; Thu, 19 Dec 2013 19:38:03 +0000 Received: from smtp.outflux.net ([2001:19d0:2:6:c0de:0:736d:7470]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VtjPb-0003k3-F0 for linux-arm-kernel@lists.infradead.org; Thu, 19 Dec 2013 19:37:39 +0000 Received: from www.outflux.net (serenity-end.outflux.net [10.2.0.2]) by vinyl.outflux.net (8.14.4/8.14.4/Debian-2ubuntu2.1) with ESMTP id rBJJa62p000688; Thu, 19 Dec 2013 11:36:06 -0800 From: Kees Cook To: linux-kernel@vger.kernel.org Subject: [PATCH v5 2/2] provide -fstack-protector-strong build option Date: Thu, 19 Dec 2013 11:35:59 -0800 Message-Id: <1387481759-14535-3-git-send-email-keescook@chromium.org> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1387481759-14535-1-git-send-email-keescook@chromium.org> References: <1387481759-14535-1-git-send-email-keescook@chromium.org> X-MIMEDefang-Filter: outflux$Revision: 1.316 $ X-HELO: www.outflux.net X-Scanned-By: MIMEDefang 2.71 on 10.2.0.1 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20131219_143735_855676_F9CB7CA4 X-CRM114-Status: GOOD ( 24.26 ) X-Spam-Score: -2.4 (--) Cc: Michal Marek , Stephen Rothwell , James Hogan , Russell King , Andrew Morton , linux-kbuild@vger.kernel.org, linux-sh@vger.kernel.org, x86@kernel.org, Ralf Baechle , Paul Mundt , linux-mips@linux-mips.org, "H. Peter Anvin" , Thomas Gleixner , Shawn Guo , Ingo Molnar , linux-arm-kernel@lists.infradead.org, keescook@chromium.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.7 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This changes the stack protector config option into a choice of "None", "Regular", and "Strong". For "Strong", the kernel is built with -fstack-protector-strong (gcc 4.9 and later). This options increases the coverage of the stack protector without the heavy performance hit of -fstack-protector-all. For reference, the stack protector options available in gcc are: -fstack-protector-all: Adds the stack-canary saving prefix and stack-canary checking suffix to _all_ function entry and exit. Results in substantial use of stack space for saving the canary for deep stack users (e.g. historically xfs), and measurable (though shockingly still low) performance hit due to all the saving/checking. Really not suitable for sane systems, and was entirely removed as an option from the kernel many years ago. -fstack-protector: Adds the canary save/check to functions that define an 8 (--param=ssp-buffer-size=N, N=8 by default) or more byte local char array. Traditionally, stack overflows happened with string-based manipulations, so this was a way to find those functions. Very few total functions actually get the canary; no measurable performance or size overhead. -fstack-protector-strong Adds the canary for a wider set of functions, since it's not just those with strings that have ultimately been vulnerable to stack-busting. With this superset, more functions end up with a canary, but it still remains small compared to all functions with no measurable change in performance. Based on the original design document, a function gets the canary when it contains any of: - local variable's address used as part of the RHS of an assignment or function argument - local variable is an array (or union containing an array), regardless of array type or length - uses register local variables https://docs.google.com/a/google.com/document/d/1xXBH6rRZue4f296vGt9YQcuLVQHeE516stHwt8M9xyU Comparison of "size" and "objdump" output when built with gcc-4.9 in three configurations: - defconfig 11430641 text size 36110 function bodies - defconfig + CONFIG_CC_STACKPROTECTOR 11468490 text size (+0.33%) 1015 of 36110 functions stack-protected (2.81%) - defconfig + CONFIG_CC_STACKPROTECTOR_STRONG via this patch 11692790 text size (+2.24%) 7401 of 36110 functions stack-protected (20.5%) With -strong, ARM's compressed boot code now triggers stack protection, so a static guard was added. Since this is only used during decompression and was never used before, the exposure here is very small. Once it switches to the full kernel, the stack guard is back to normal. Chrome OS has been using -fstack-protector-strong for its kernel builds for the last 8 months with no problems. Signed-off-by: Kees Cook --- v5: - add function counts to help text - switch from Makefile error to warning to not kill silentoldconfig v4: - add objdump analysis to comparison v3: - split off type of stack protection as a distinct config v2: - added description of all stack protector options - added size comparisons for Ubuntu and defconfig --- Makefile | 8 ++++++- arch/Kconfig | 50 ++++++++++++++++++++++++++++++++++++--- arch/arm/boot/compressed/misc.c | 14 +++++++++++ 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 84fb5cd092d2..5271b9623aa3 100644 --- a/Makefile +++ b/Makefile @@ -596,12 +596,18 @@ KBUILD_CFLAGS += $(call cc-option,-Wframe-larger-than=${CONFIG_FRAME_WARN}) endif # Handle stack protector mode. -ifdef CONFIG_CC_STACKPROTECTOR +ifdef CONFIG_CC_STACKPROTECTOR_REGULAR stackp-flag := -fstack-protector ifeq ($(call cc-option, $(stackp-flag)),) $(warning Cannot use CONFIG_CC_STACKPROTECTOR: \ -fstack-protector not supported by compiler)) endif +else ifdef CONFIG_CC_STACKPROTECTOR_STRONG + stackp-flag := -fstack-protector-strong + ifeq ($(call cc-option, $(stackp-flag)),) + $(warning Cannot use CONFIG_CC_STACKPROTECTOR_STRONG: \ + -fstack-protector-strong not supported by compiler) + endif else # Force off for distro compilers that enable stack protector by default. stackp-flag := $(call cc-option, -fno-stack-protector) diff --git a/arch/Kconfig b/arch/Kconfig index 24e026d83072..d6411a3af71e 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -344,10 +344,17 @@ config HAVE_CC_STACKPROTECTOR - it has implemented a stack canary (e.g. __stack_chk_guard) config CC_STACKPROTECTOR - bool "Enable -fstack-protector buffer overflow detection" + def_bool n + help + Set when a stack-protector mode is enabled, so that the build + can enable kernel-side support for the GCC feature. + +choice + prompt "Stack Protector buffer overflow detection" depends on HAVE_CC_STACKPROTECTOR + default CC_STACKPROTECTOR_NONE help - This option turns on the -fstack-protector GCC feature. This + This option turns on the "stack-protector" GCC feature. This feature puts, at the beginning of functions, a canary value on the stack just before the return address, and validates the value just before actually returning. Stack based buffer @@ -355,8 +362,45 @@ config CC_STACKPROTECTOR overwrite the canary, which gets detected and the attack is then neutralized via a kernel panic. +config CC_STACKPROTECTOR_NONE + bool "None" + help + Disable "stack-protector" GCC feature. + +config CC_STACKPROTECTOR_REGULAR + bool "Regular" + select CC_STACKPROTECTOR + help + Functions will have the stack-protector canary logic added if they + have an 8-byte or larger character array on the stack. + This feature requires gcc version 4.2 or above, or a distribution - gcc with the feature backported. + gcc with the feature backported ("-fstack-protector"). + + On an x86 "defconfig" build, this feature adds canary checks to + about 3% of all kernel functions, which increases kernel code size + by about 0.3%. + +config CC_STACKPROTECTOR_STRONG + bool "Strong" + select CC_STACKPROTECTOR + help + Functions will have the stack-protector canary logic added in any + of the following conditions: + - local variable's address used as part of the RHS of an + assignment or function argument + - local variable is an array (or union containing an array), + regardless of array type or length + - uses register local variables + + This feature requires gcc version 4.9 or above, or a distribution + gcc with the feature backported ("-fstack-protector-strong"). + + On an x86 "defconfig" build, this feature adds canary checks to + about 20% of all kernel functions, which increases the kernel code + size by about 2%. + +endchoice config HAVE_CONTEXT_TRACKING bool diff --git a/arch/arm/boot/compressed/misc.c b/arch/arm/boot/compressed/misc.c index 31bd43b82095..d4f891f56996 100644 --- a/arch/arm/boot/compressed/misc.c +++ b/arch/arm/boot/compressed/misc.c @@ -127,6 +127,18 @@ asmlinkage void __div0(void) error("Attempting division by 0!"); } +unsigned long __stack_chk_guard; + +void __stack_chk_guard_setup(void) +{ + __stack_chk_guard = 0x000a0dff; +} + +void __stack_chk_fail(void) +{ + error("stack-protector: Kernel stack is corrupted\n"); +} + extern int do_decompress(u8 *input, int len, u8 *output, void (*error)(char *x)); @@ -137,6 +149,8 @@ decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p, { int ret; + __stack_chk_guard_setup(); + output_data = (unsigned char *)output_start; free_mem_ptr = free_mem_ptr_p; free_mem_end_ptr = free_mem_ptr_end_p;