aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2018-02-21 17:45:54 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2018-02-21 18:35:43 -0500
commit173a3efd3edb2ef6ef07471397c5f542a360e9c1 (patch)
tree7a8fc8fde6d5596d16cf14343d25b0bd149d14f6
parentcb6f0f34802dd7148d930f4f8d1cce991b8c23be (diff)
bug.h: work around GCC PR82365 in BUG()
Looking at functions with large stack frames across all architectures led me discovering that BUG() suffers from the same problem as fortify_panic(), which I've added a workaround for already. In short, variables that go out of scope by calling a noreturn function or __builtin_unreachable() keep using stack space in functions afterwards. A workaround that was identified is to insert an empty assembler statement just before calling the function that doesn't return. I'm adding a macro "barrier_before_unreachable()" to document this, and insert calls to that in all instances of BUG() that currently suffer from this problem. The files that saw the largest change from this had these frame sizes before, and much less with my patch: fs/ext4/inode.c:82:1: warning: the frame size of 1672 bytes is larger than 800 bytes [-Wframe-larger-than=] fs/ext4/namei.c:434:1: warning: the frame size of 904 bytes is larger than 800 bytes [-Wframe-larger-than=] fs/ext4/super.c:2279:1: warning: the frame size of 1160 bytes is larger than 800 bytes [-Wframe-larger-than=] fs/ext4/xattr.c:146:1: warning: the frame size of 1168 bytes is larger than 800 bytes [-Wframe-larger-than=] fs/f2fs/inode.c:152:1: warning: the frame size of 1424 bytes is larger than 800 bytes [-Wframe-larger-than=] net/netfilter/ipvs/ip_vs_core.c:1195:1: warning: the frame size of 1068 bytes is larger than 800 bytes [-Wframe-larger-than=] net/netfilter/ipvs/ip_vs_core.c:395:1: warning: the frame size of 1084 bytes is larger than 800 bytes [-Wframe-larger-than=] net/netfilter/ipvs/ip_vs_ftp.c:298:1: warning: the frame size of 928 bytes is larger than 800 bytes [-Wframe-larger-than=] net/netfilter/ipvs/ip_vs_ftp.c:418:1: warning: the frame size of 908 bytes is larger than 800 bytes [-Wframe-larger-than=] net/netfilter/ipvs/ip_vs_lblcr.c:718:1: warning: the frame size of 960 bytes is larger than 800 bytes [-Wframe-larger-than=] drivers/net/xen-netback/netback.c:1500:1: warning: the frame size of 1088 bytes is larger than 800 bytes [-Wframe-larger-than=] In case of ARC and CRIS, it turns out that the BUG() implementation actually does return (or at least the compiler thinks it does), resulting in lots of warnings about uninitialized variable use and leaving noreturn functions, such as: block/cfq-iosched.c: In function 'cfq_async_queue_prio': block/cfq-iosched.c:3804:1: error: control reaches end of non-void function [-Werror=return-type] include/linux/dmaengine.h: In function 'dma_maxpq': include/linux/dmaengine.h:1123:1: error: control reaches end of non-void function [-Werror=return-type] This makes them call __builtin_trap() instead, which should normally dump the stack and kill the current process, like some of the other architectures already do. I tried adding barrier_before_unreachable() to panic() and fortify_panic() as well, but that had very little effect, so I'm not submitting that patch. Vineet said: : For ARC, it is double win. : : 1. Fixes 3 -Wreturn-type warnings : : | ../net/core/ethtool.c:311:1: warning: control reaches end of non-void function : [-Wreturn-type] : | ../kernel/sched/core.c:3246:1: warning: control reaches end of non-void function : [-Wreturn-type] : | ../include/linux/sunrpc/svc_xprt.h:180:1: warning: control reaches end of : non-void function [-Wreturn-type] : : 2. bloat-o-meter reports code size improvements as gcc elides the : generated code for stack return. Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82365 Link: http://lkml.kernel.org/r/20171219114112.939391-1-arnd@arndb.de Signed-off-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Vineet Gupta <vgupta@synopsys.com> [arch/arc] Tested-by: Vineet Gupta <vgupta@synopsys.com> [arch/arc] Cc: Mikael Starvik <starvik@axis.com> Cc: Jesper Nilsson <jesper.nilsson@axis.com> Cc: Tony Luck <tony.luck@intel.com> Cc: Fenghua Yu <fenghua.yu@intel.com> Cc: Geert Uytterhoeven <geert@linux-m68k.org> Cc: "David S. Miller" <davem@davemloft.net> Cc: Christopher Li <sparse@chrisli.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Kees Cook <keescook@chromium.org> Cc: Ingo Molnar <mingo@kernel.org> Cc: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Will Deacon <will.deacon@arm.com> Cc: "Steven Rostedt (VMware)" <rostedt@goodmis.org> Cc: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/arc/include/asm/bug.h3
-rw-r--r--arch/cris/include/arch-v10/arch/bug.h11
-rw-r--r--arch/ia64/include/asm/bug.h6
-rw-r--r--arch/m68k/include/asm/bug.h3
-rw-r--r--arch/sparc/include/asm/bug.h6
-rw-r--r--include/asm-generic/bug.h1
-rw-r--r--include/linux/compiler-gcc.h15
-rw-r--r--include/linux/compiler.h5
8 files changed, 44 insertions, 6 deletions
diff --git a/arch/arc/include/asm/bug.h b/arch/arc/include/asm/bug.h
index ea022d47896c..21ec82466d62 100644
--- a/arch/arc/include/asm/bug.h
+++ b/arch/arc/include/asm/bug.h
@@ -23,7 +23,8 @@ void die(const char *str, struct pt_regs *regs, unsigned long address);
23 23
24#define BUG() do { \ 24#define BUG() do { \
25 pr_warn("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \ 25 pr_warn("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \
26 dump_stack(); \ 26 barrier_before_unreachable(); \
27 __builtin_trap(); \
27} while (0) 28} while (0)
28 29
29#define HAVE_ARCH_BUG 30#define HAVE_ARCH_BUG
diff --git a/arch/cris/include/arch-v10/arch/bug.h b/arch/cris/include/arch-v10/arch/bug.h
index 905afeacfedf..06da9d49152a 100644
--- a/arch/cris/include/arch-v10/arch/bug.h
+++ b/arch/cris/include/arch-v10/arch/bug.h
@@ -44,18 +44,25 @@ struct bug_frame {
44 * not be used like this with newer versions of gcc. 44 * not be used like this with newer versions of gcc.
45 */ 45 */
46#define BUG() \ 46#define BUG() \
47do { \
47 __asm__ __volatile__ ("clear.d [" __stringify(BUG_MAGIC) "]\n\t"\ 48 __asm__ __volatile__ ("clear.d [" __stringify(BUG_MAGIC) "]\n\t"\
48 "movu.w " __stringify(__LINE__) ",$r0\n\t"\ 49 "movu.w " __stringify(__LINE__) ",$r0\n\t"\
49 "jump 0f\n\t" \ 50 "jump 0f\n\t" \
50 ".section .rodata\n" \ 51 ".section .rodata\n" \
51 "0:\t.string \"" __FILE__ "\"\n\t" \ 52 "0:\t.string \"" __FILE__ "\"\n\t" \
52 ".previous") 53 ".previous"); \
54 unreachable(); \
55} while (0)
53#endif 56#endif
54 57
55#else 58#else
56 59
57/* This just causes an oops. */ 60/* This just causes an oops. */
58#define BUG() (*(int *)0 = 0) 61#define BUG() \
62do { \
63 barrier_before_unreachable(); \
64 __builtin_trap(); \
65} while (0)
59 66
60#endif 67#endif
61 68
diff --git a/arch/ia64/include/asm/bug.h b/arch/ia64/include/asm/bug.h
index bd3eeb8d1cfa..66b37a532765 100644
--- a/arch/ia64/include/asm/bug.h
+++ b/arch/ia64/include/asm/bug.h
@@ -4,7 +4,11 @@
4 4
5#ifdef CONFIG_BUG 5#ifdef CONFIG_BUG
6#define ia64_abort() __builtin_trap() 6#define ia64_abort() __builtin_trap()
7#define BUG() do { printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); ia64_abort(); } while (0) 7#define BUG() do { \
8 printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \
9 barrier_before_unreachable(); \
10 ia64_abort(); \
11} while (0)
8 12
9/* should this BUG be made generic? */ 13/* should this BUG be made generic? */
10#define HAVE_ARCH_BUG 14#define HAVE_ARCH_BUG
diff --git a/arch/m68k/include/asm/bug.h b/arch/m68k/include/asm/bug.h
index b7e2bf1ba4a6..275dca1435bf 100644
--- a/arch/m68k/include/asm/bug.h
+++ b/arch/m68k/include/asm/bug.h
@@ -8,16 +8,19 @@
8#ifndef CONFIG_SUN3 8#ifndef CONFIG_SUN3
9#define BUG() do { \ 9#define BUG() do { \
10 pr_crit("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ 10 pr_crit("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \
11 barrier_before_unreachable(); \
11 __builtin_trap(); \ 12 __builtin_trap(); \
12} while (0) 13} while (0)
13#else 14#else
14#define BUG() do { \ 15#define BUG() do { \
15 pr_crit("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ 16 pr_crit("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \
17 barrier_before_unreachable(); \
16 panic("BUG!"); \ 18 panic("BUG!"); \
17} while (0) 19} while (0)
18#endif 20#endif
19#else 21#else
20#define BUG() do { \ 22#define BUG() do { \
23 barrier_before_unreachable(); \
21 __builtin_trap(); \ 24 __builtin_trap(); \
22} while (0) 25} while (0)
23#endif 26#endif
diff --git a/arch/sparc/include/asm/bug.h b/arch/sparc/include/asm/bug.h
index 6f17528356b2..ea53e418f6c0 100644
--- a/arch/sparc/include/asm/bug.h
+++ b/arch/sparc/include/asm/bug.h
@@ -9,10 +9,14 @@
9void do_BUG(const char *file, int line); 9void do_BUG(const char *file, int line);
10#define BUG() do { \ 10#define BUG() do { \
11 do_BUG(__FILE__, __LINE__); \ 11 do_BUG(__FILE__, __LINE__); \
12 barrier_before_unreachable(); \
12 __builtin_trap(); \ 13 __builtin_trap(); \
13} while (0) 14} while (0)
14#else 15#else
15#define BUG() __builtin_trap() 16#define BUG() do { \
17 barrier_before_unreachable(); \
18 __builtin_trap(); \
19} while (0)
16#endif 20#endif
17 21
18#define HAVE_ARCH_BUG 22#define HAVE_ARCH_BUG
diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h
index 963b755d19b0..a7613e1b0c87 100644
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -52,6 +52,7 @@ struct bug_entry {
52#ifndef HAVE_ARCH_BUG 52#ifndef HAVE_ARCH_BUG
53#define BUG() do { \ 53#define BUG() do { \
54 printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \ 54 printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \
55 barrier_before_unreachable(); \
55 panic("BUG!"); \ 56 panic("BUG!"); \
56} while (0) 57} while (0)
57#endif 58#endif
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 73bc63e0a1c4..901c1ccb3374 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -208,6 +208,15 @@
208#endif 208#endif
209 209
210/* 210/*
211 * calling noreturn functions, __builtin_unreachable() and __builtin_trap()
212 * confuse the stack allocation in gcc, leading to overly large stack
213 * frames, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82365
214 *
215 * Adding an empty inline assembly before it works around the problem
216 */
217#define barrier_before_unreachable() asm volatile("")
218
219/*
211 * Mark a position in code as unreachable. This can be used to 220 * Mark a position in code as unreachable. This can be used to
212 * suppress control flow warnings after asm blocks that transfer 221 * suppress control flow warnings after asm blocks that transfer
213 * control elsewhere. 222 * control elsewhere.
@@ -217,7 +226,11 @@
217 * unreleased. Really, we need to have autoconf for the kernel. 226 * unreleased. Really, we need to have autoconf for the kernel.
218 */ 227 */
219#define unreachable() \ 228#define unreachable() \
220 do { annotate_unreachable(); __builtin_unreachable(); } while (0) 229 do { \
230 annotate_unreachable(); \
231 barrier_before_unreachable(); \
232 __builtin_unreachable(); \
233 } while (0)
221 234
222/* Mark a function definition as prohibited from being cloned. */ 235/* Mark a function definition as prohibited from being cloned. */
223#define __noclone __attribute__((__noclone__, __optimize__("no-tracer"))) 236#define __noclone __attribute__((__noclone__, __optimize__("no-tracer")))
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index e835fc0423ec..ab4711c63601 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -86,6 +86,11 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
86# define barrier_data(ptr) barrier() 86# define barrier_data(ptr) barrier()
87#endif 87#endif
88 88
89/* workaround for GCC PR82365 if needed */
90#ifndef barrier_before_unreachable
91# define barrier_before_unreachable() do { } while (0)
92#endif
93
89/* Unreachable code */ 94/* Unreachable code */
90#ifdef CONFIG_STACK_VALIDATION 95#ifdef CONFIG_STACK_VALIDATION
91/* 96/*