Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Arnd Bergmann and committed by
Linus Torvalds
173a3efd cb6f0f34

+44 -6
+2 -1
arch/arc/include/asm/bug.h
··· 23 23 24 24 #define BUG() do { \ 25 25 pr_warn("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \ 26 - dump_stack(); \ 26 + barrier_before_unreachable(); \ 27 + __builtin_trap(); \ 27 28 } while (0) 28 29 29 30 #define HAVE_ARCH_BUG
+9 -2
arch/cris/include/arch-v10/arch/bug.h
··· 44 44 * not be used like this with newer versions of gcc. 45 45 */ 46 46 #define BUG() \ 47 + do { \ 47 48 __asm__ __volatile__ ("clear.d [" __stringify(BUG_MAGIC) "]\n\t"\ 48 49 "movu.w " __stringify(__LINE__) ",$r0\n\t"\ 49 50 "jump 0f\n\t" \ 50 51 ".section .rodata\n" \ 51 52 "0:\t.string \"" __FILE__ "\"\n\t" \ 52 - ".previous") 53 + ".previous"); \ 54 + unreachable(); \ 55 + } while (0) 53 56 #endif 54 57 55 58 #else 56 59 57 60 /* This just causes an oops. */ 58 - #define BUG() (*(int *)0 = 0) 61 + #define BUG() \ 62 + do { \ 63 + barrier_before_unreachable(); \ 64 + __builtin_trap(); \ 65 + } while (0) 59 66 60 67 #endif 61 68
+5 -1
arch/ia64/include/asm/bug.h
··· 4 4 5 5 #ifdef CONFIG_BUG 6 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 13 /* should this BUG be made generic? */ 10 14 #define HAVE_ARCH_BUG
+3
arch/m68k/include/asm/bug.h
··· 8 8 #ifndef CONFIG_SUN3 9 9 #define BUG() do { \ 10 10 pr_crit("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ 11 + barrier_before_unreachable(); \ 11 12 __builtin_trap(); \ 12 13 } while (0) 13 14 #else 14 15 #define BUG() do { \ 15 16 pr_crit("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ 17 + barrier_before_unreachable(); \ 16 18 panic("BUG!"); \ 17 19 } while (0) 18 20 #endif 19 21 #else 20 22 #define BUG() do { \ 23 + barrier_before_unreachable(); \ 21 24 __builtin_trap(); \ 22 25 } while (0) 23 26 #endif
+5 -1
arch/sparc/include/asm/bug.h
··· 9 9 void do_BUG(const char *file, int line); 10 10 #define BUG() do { \ 11 11 do_BUG(__FILE__, __LINE__); \ 12 + barrier_before_unreachable(); \ 12 13 __builtin_trap(); \ 13 14 } while (0) 14 15 #else 15 - #define BUG() __builtin_trap() 16 + #define BUG() do { \ 17 + barrier_before_unreachable(); \ 18 + __builtin_trap(); \ 19 + } while (0) 16 20 #endif 17 21 18 22 #define HAVE_ARCH_BUG
+1
include/asm-generic/bug.h
··· 52 52 #ifndef HAVE_ARCH_BUG 53 53 #define BUG() do { \ 54 54 printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \ 55 + barrier_before_unreachable(); \ 55 56 panic("BUG!"); \ 56 57 } while (0) 57 58 #endif
+14 -1
include/linux/compiler-gcc.h
··· 208 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 220 * Mark a position in code as unreachable. This can be used to 212 221 * suppress control flow warnings after asm blocks that transfer 213 222 * control elsewhere. ··· 226 217 * unreleased. Really, we need to have autoconf for the kernel. 227 218 */ 228 219 #define unreachable() \ 229 - do { annotate_unreachable(); __builtin_unreachable(); } while (0) 220 + do { \ 221 + annotate_unreachable(); \ 222 + barrier_before_unreachable(); \ 223 + __builtin_unreachable(); \ 224 + } while (0) 230 225 231 226 /* Mark a function definition as prohibited from being cloned. */ 232 227 #define __noclone __attribute__((__noclone__, __optimize__("no-tracer")))
+5
include/linux/compiler.h
··· 86 86 # define barrier_data(ptr) barrier() 87 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 94 /* Unreachable code */ 90 95 #ifdef CONFIG_STACK_VALIDATION 91 96 /*