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

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'core-bugs-2025-12-01' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull bug handling infrastructure updates from Ingo Molnar:
"Core updates:

- Improve WARN(), which has vararg printf like arguments, to work
with the x86 #UD based WARN-optimizing infrastructure by hiding the
format in the bug_table and replacing this first argument with the
address of the bug-table entry, while making the actual function
that's called a UD1 instruction (Peter Zijlstra)

- Introduce the CONFIG_DEBUG_BUGVERBOSE_DETAILED Kconfig switch (Ingo
Molnar, s390 support by Heiko Carstens)

Fixes and cleanups:

- bugs/s390: Remove private WARN_ON() implementation (Heiko Carstens)

- <asm/bugs.h>: Make i386 use GENERIC_BUG_RELATIVE_POINTERS (Peter
Zijlstra)"

* tag 'core-bugs-2025-12-01' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (31 commits)
x86/bugs: Make i386 use GENERIC_BUG_RELATIVE_POINTERS
x86/bug: Fix BUG_FORMAT vs KASLR
x86_64/bug: Inline the UD1
x86/bug: Implement WARN_ONCE()
x86_64/bug: Implement __WARN_printf()
x86/bug: Use BUG_FORMAT for DEBUG_BUGVERBOSE_DETAILED
x86/bug: Add BUG_FORMAT basics
bug: Allow architectures to provide __WARN_printf()
bug: Implement WARN_ON() using __WARN_FLAGS()
bug: Add report_bug_entry()
bug: Add BUG_FORMAT_ARGS infrastructure
bug: Clean up CONFIG_GENERIC_BUG_RELATIVE_POINTERS
bug: Add BUG_FORMAT infrastructure
x86: Rework __bug_table helpers
bugs/s390: Remove private WARN_ON() implementation
bugs/core: Reorganize fields in the first line of WARNING output, add ->comm[] output
bugs/sh: Concatenate 'cond_str' with '__FILE__' in __WARN_FLAGS(), to extend WARN_ON/BUG_ON output
bugs/parisc: Concatenate 'cond_str' with '__FILE__' in __WARN_FLAGS(), to extend WARN_ON/BUG_ON output
bugs/riscv: Concatenate 'cond_str' with '__FILE__' in __BUG_FLAGS(), to extend WARN_ON/BUG_ON output
bugs/riscv: Pass in 'cond_str' to __BUG_FLAGS()
...

+480 -181
+1 -1
arch/arm64/include/asm/bug.h
··· 19 19 unreachable(); \ 20 20 } while (0) 21 21 22 - #define __WARN_FLAGS(flags) __BUG_FLAGS(BUGFLAG_WARNING|(flags)) 22 + #define __WARN_FLAGS(cond_str, flags) __BUG_FLAGS(BUGFLAG_WARNING|(flags)) 23 23 24 24 #define HAVE_ARCH_BUG 25 25
+13 -14
arch/loongarch/include/asm/bug.h
··· 11 11 #else 12 12 #define __BUGVERBOSE_LOCATION(file, line) \ 13 13 .pushsection .rodata.str, "aMS", @progbits, 1; \ 14 - 10002: .string file; \ 14 + 10002: .ascii file "\0"; \ 15 15 .popsection; \ 16 16 \ 17 17 .long 10002b - .; \ ··· 20 20 #endif 21 21 22 22 #ifndef CONFIG_GENERIC_BUG 23 - #define __BUG_ENTRY(flags) 23 + #define __BUG_ENTRY(cond_str, flags) 24 24 #else 25 - #define __BUG_ENTRY(flags) \ 25 + #define __BUG_ENTRY(cond_str, flags) \ 26 26 .pushsection __bug_table, "aw"; \ 27 27 .align 2; \ 28 28 10000: .long 10001f - .; \ 29 - _BUGVERBOSE_LOCATION(__FILE__, __LINE__) \ 30 - .short flags; \ 29 + _BUGVERBOSE_LOCATION(WARN_CONDITION_STR(cond_str) __FILE__, __LINE__) \ 30 + .short flags; \ 31 31 .popsection; \ 32 32 10001: 33 33 #endif 34 34 35 - #define ASM_BUG_FLAGS(flags) \ 36 - __BUG_ENTRY(flags) \ 35 + #define ASM_BUG_FLAGS(cond_str, flags) \ 36 + __BUG_ENTRY(cond_str, flags) \ 37 37 break BRK_BUG; 38 38 39 - #define ASM_BUG() ASM_BUG_FLAGS(0) 39 + #define ASM_BUG() ASM_BUG_FLAGS("", 0) 40 40 41 - #define __BUG_FLAGS(flags, extra) \ 42 - asm_inline volatile (__stringify(ASM_BUG_FLAGS(flags)) \ 43 - extra); 41 + #define __BUG_FLAGS(cond_str, flags, extra) \ 42 + asm_inline volatile (__stringify(ASM_BUG_FLAGS(cond_str, flags)) extra); 44 43 45 - #define __WARN_FLAGS(flags) \ 44 + #define __WARN_FLAGS(cond_str, flags) \ 46 45 do { \ 47 46 instrumentation_begin(); \ 48 - __BUG_FLAGS(BUGFLAG_WARNING|(flags), ANNOTATE_REACHABLE(10001b));\ 47 + __BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags), ANNOTATE_REACHABLE(10001b));\ 49 48 instrumentation_end(); \ 50 49 } while (0) 51 50 52 51 #define BUG() \ 53 52 do { \ 54 53 instrumentation_begin(); \ 55 - __BUG_FLAGS(0, ""); \ 54 + __BUG_FLAGS("", 0, ""); \ 56 55 unreachable(); \ 57 56 } while (0) 58 57
+3 -3
arch/parisc/include/asm/bug.h
··· 50 50 #endif 51 51 52 52 #ifdef CONFIG_DEBUG_BUGVERBOSE 53 - #define __WARN_FLAGS(flags) \ 53 + #define __WARN_FLAGS(cond_str, flags) \ 54 54 do { \ 55 55 asm volatile("\n" \ 56 56 "1:\t" PARISC_BUG_BREAK_ASM "\n" \ ··· 61 61 "\t.short %1, %2\n" \ 62 62 "\t.blockz %3-2*4-2*2\n" \ 63 63 "\t.popsection" \ 64 - : : "i" (__FILE__), "i" (__LINE__), \ 64 + : : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__), \ 65 65 "i" (BUGFLAG_WARNING|(flags)), \ 66 66 "i" (sizeof(struct bug_entry)) ); \ 67 67 } while(0) 68 68 #else 69 - #define __WARN_FLAGS(flags) \ 69 + #define __WARN_FLAGS(cond_str, flags) \ 70 70 do { \ 71 71 asm volatile("\n" \ 72 72 "1:\t" PARISC_BUG_BREAK_ASM "\n" \
+6 -6
arch/powerpc/include/asm/bug.h
··· 51 51 ".previous\n" 52 52 #endif 53 53 54 - #define BUG_ENTRY(insn, flags, ...) \ 54 + #define BUG_ENTRY(cond_str, insn, flags, ...) \ 55 55 __asm__ __volatile__( \ 56 56 "1: " insn "\n" \ 57 57 _EMIT_BUG_ENTRY \ 58 - : : "i" (__FILE__), "i" (__LINE__), \ 58 + : : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__), \ 59 59 "i" (flags), \ 60 60 "i" (sizeof(struct bug_entry)), \ 61 61 ##__VA_ARGS__) ··· 67 67 */ 68 68 69 69 #define BUG() do { \ 70 - BUG_ENTRY("twi 31, 0, 0", 0); \ 70 + BUG_ENTRY("", "twi 31, 0, 0", 0); \ 71 71 unreachable(); \ 72 72 } while (0) 73 73 #define HAVE_ARCH_BUG 74 74 75 - #define __WARN_FLAGS(flags) BUG_ENTRY("twi 31, 0, 0", BUGFLAG_WARNING | (flags)) 75 + #define __WARN_FLAGS(cond_str, flags) BUG_ENTRY(cond_str, "twi 31, 0, 0", BUGFLAG_WARNING | (flags)) 76 76 77 77 #ifdef CONFIG_PPC64 78 78 #define BUG_ON(x) do { \ ··· 80 80 if (x) \ 81 81 BUG(); \ 82 82 } else { \ 83 - BUG_ENTRY(PPC_TLNEI " %4, 0", 0, "r" ((__force long)(x))); \ 83 + BUG_ENTRY(#x, PPC_TLNEI " %4, 0", 0, "r" ((__force long)(x))); \ 84 84 } \ 85 85 } while (0) 86 86 ··· 90 90 if (__ret_warn_on) \ 91 91 __WARN(); \ 92 92 } else { \ 93 - BUG_ENTRY(PPC_TLNEI " %4, 0", \ 93 + BUG_ENTRY(#x, PPC_TLNEI " %4, 0", \ 94 94 BUGFLAG_WARNING | BUGFLAG_TAINT(TAINT_WARN), \ 95 95 "r" (__ret_warn_on)); \ 96 96 } \
+5 -5
arch/riscv/include/asm/bug.h
··· 60 60 ".org 2b + " size "\n\t" \ 61 61 ".popsection" \ 62 62 63 - #define __BUG_FLAGS(flags) \ 63 + #define __BUG_FLAGS(cond_str, flags) \ 64 64 do { \ 65 65 __asm__ __volatile__ ( \ 66 66 ARCH_WARN_ASM("%0", "%1", "%2", "%3") \ 67 67 : \ 68 - : "i" (__FILE__), "i" (__LINE__), \ 68 + : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__), \ 69 69 "i" (flags), \ 70 70 "i" (sizeof(struct bug_entry))); \ 71 71 } while (0) 72 72 73 73 #else /* CONFIG_GENERIC_BUG */ 74 - #define __BUG_FLAGS(flags) do { \ 74 + #define __BUG_FLAGS(cond_str, flags) do { \ 75 75 __asm__ __volatile__ ("ebreak\n"); \ 76 76 } while (0) 77 77 #endif /* CONFIG_GENERIC_BUG */ 78 78 79 79 #define BUG() do { \ 80 - __BUG_FLAGS(0); \ 80 + __BUG_FLAGS("", 0); \ 81 81 unreachable(); \ 82 82 } while (0) 83 83 84 - #define __WARN_FLAGS(flags) __BUG_FLAGS(BUGFLAG_WARNING|(flags)) 84 + #define __WARN_FLAGS(cond_str, flags) __BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags)) 85 85 86 86 #define ARCH_WARN_REACHABLE 87 87
+41 -55
arch/s390/include/asm/bug.h
··· 2 2 #ifndef _ASM_S390_BUG_H 3 3 #define _ASM_S390_BUG_H 4 4 5 - #include <linux/compiler.h> 5 + #include <linux/stringify.h> 6 6 7 - #ifdef CONFIG_BUG 7 + #ifndef CONFIG_DEBUG_BUGVERBOSE 8 + #define _BUGVERBOSE_LOCATION(file, line) 9 + #else 10 + #define __BUGVERBOSE_LOCATION(file, line) \ 11 + .pushsection .rodata.str, "aMS", @progbits, 1; \ 12 + 10002: .ascii file "\0"; \ 13 + .popsection; \ 14 + \ 15 + .long 10002b - .; \ 16 + .short line; 17 + #define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line) 18 + #endif 8 19 9 - #ifdef CONFIG_DEBUG_BUGVERBOSE 20 + #ifndef CONFIG_GENERIC_BUG 21 + #define __BUG_ENTRY(cond_str, flags) 22 + #else 23 + #define __BUG_ENTRY(cond_str, flags) \ 24 + .pushsection __bug_table, "aw"; \ 25 + .align 4; \ 26 + 10000: .long 10001f - .; \ 27 + _BUGVERBOSE_LOCATION(WARN_CONDITION_STR(cond_str) __FILE__, __LINE__) \ 28 + .short flags; \ 29 + .popsection; \ 30 + 10001: 31 + #endif 10 32 11 - #define __EMIT_BUG(x) do { \ 12 - asm_inline volatile( \ 13 - "0: mc 0,0\n" \ 14 - ".section .rodata.str,\"aMS\",@progbits,1\n" \ 15 - "1: .asciz \""__FILE__"\"\n" \ 16 - ".previous\n" \ 17 - ".section __bug_table,\"aw\"\n" \ 18 - "2: .long 0b-.\n" \ 19 - " .long 1b-.\n" \ 20 - " .short %0,%1\n" \ 21 - " .org 2b+%2\n" \ 22 - ".previous\n" \ 23 - : : "i" (__LINE__), \ 24 - "i" (x), \ 25 - "i" (sizeof(struct bug_entry))); \ 33 + #define ASM_BUG_FLAGS(cond_str, flags) \ 34 + __BUG_ENTRY(cond_str, flags) \ 35 + mc 0,0 36 + 37 + #define ASM_BUG() ASM_BUG_FLAGS("", 0) 38 + 39 + #define __BUG_FLAGS(cond_str, flags) \ 40 + asm_inline volatile(__stringify(ASM_BUG_FLAGS(cond_str, flags))); 41 + 42 + #define __WARN_FLAGS(cond_str, flags) \ 43 + do { \ 44 + __BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags)); \ 26 45 } while (0) 27 46 28 - #else /* CONFIG_DEBUG_BUGVERBOSE */ 29 - 30 - #define __EMIT_BUG(x) do { \ 31 - asm_inline volatile( \ 32 - "0: mc 0,0\n" \ 33 - ".section __bug_table,\"aw\"\n" \ 34 - "1: .long 0b-.\n" \ 35 - " .short %0\n" \ 36 - " .org 1b+%1\n" \ 37 - ".previous\n" \ 38 - : : "i" (x), \ 39 - "i" (sizeof(struct bug_entry))); \ 47 + #define BUG() \ 48 + do { \ 49 + __BUG_FLAGS("", 0); \ 50 + unreachable(); \ 40 51 } while (0) 41 - 42 - #endif /* CONFIG_DEBUG_BUGVERBOSE */ 43 - 44 - #define BUG() do { \ 45 - __EMIT_BUG(0); \ 46 - unreachable(); \ 47 - } while (0) 48 - 49 - #define __WARN_FLAGS(flags) do { \ 50 - __EMIT_BUG(BUGFLAG_WARNING|(flags)); \ 51 - } while (0) 52 - 53 - #define WARN_ON(x) ({ \ 54 - int __ret_warn_on = !!(x); \ 55 - if (__builtin_constant_p(__ret_warn_on)) { \ 56 - if (__ret_warn_on) \ 57 - __WARN(); \ 58 - } else { \ 59 - if (unlikely(__ret_warn_on)) \ 60 - __WARN(); \ 61 - } \ 62 - unlikely(__ret_warn_on); \ 63 - }) 64 52 65 53 #define HAVE_ARCH_BUG 66 - #define HAVE_ARCH_WARN_ON 67 - #endif /* CONFIG_BUG */ 68 54 69 55 #include <asm-generic/bug.h> 70 56
+2 -2
arch/sh/include/asm/bug.h
··· 52 52 unreachable(); \ 53 53 } while (0) 54 54 55 - #define __WARN_FLAGS(flags) \ 55 + #define __WARN_FLAGS(cond_str, flags) \ 56 56 do { \ 57 57 __asm__ __volatile__ ( \ 58 58 "1:\t.short %O0\n" \ 59 59 _EMIT_BUG_ENTRY \ 60 60 : \ 61 61 : "n" (TRAPA_BUG_OPCODE), \ 62 - "i" (__FILE__), \ 62 + "i" (WARN_CONDITION_STR(cond_str) __FILE__), \ 63 63 "i" (__LINE__), \ 64 64 "i" (BUGFLAG_WARNING|(flags)), \ 65 65 "i" (sizeof(struct bug_entry))); \
+1 -1
arch/x86/Kconfig
··· 381 381 config GENERIC_BUG 382 382 def_bool y 383 383 depends on BUG 384 - select GENERIC_BUG_RELATIVE_POINTERS if X86_64 384 + select GENERIC_BUG_RELATIVE_POINTERS 385 385 386 386 config GENERIC_BUG_RELATIVE_POINTERS 387 387 bool
+8
arch/x86/entry/entry.S
··· 32 32 /* For KVM */ 33 33 EXPORT_SYMBOL_GPL(write_ibpb); 34 34 35 + SYM_FUNC_START(__WARN_trap) 36 + ANNOTATE_NOENDBR 37 + ANNOTATE_REACHABLE 38 + ud1 (%edx), %_ASM_ARG1 39 + RET 40 + SYM_FUNC_END(__WARN_trap) 41 + EXPORT_SYMBOL(__WARN_trap) 42 + 35 43 .popsection 36 44 37 45 /*
+117 -33
arch/x86/include/asm/bug.h
··· 7 7 #include <linux/objtool.h> 8 8 #include <asm/asm.h> 9 9 10 + #ifndef __ASSEMBLY__ 11 + struct bug_entry; 12 + extern void __WARN_trap(struct bug_entry *bug, ...); 13 + #endif 14 + 10 15 /* 11 16 * Despite that some emulators terminate on UD2, we use it for WARN(). 12 17 */ ··· 36 31 #define BUG_UD2 0xfffe 37 32 #define BUG_UD1 0xfffd 38 33 #define BUG_UD1_UBSAN 0xfffc 34 + #define BUG_UD1_WARN 0xfffb 39 35 #define BUG_UDB 0xffd6 40 36 #define BUG_LOCK 0xfff0 41 37 42 38 #ifdef CONFIG_GENERIC_BUG 43 39 44 - #ifdef CONFIG_X86_32 45 - # define __BUG_REL(val) ".long " val 46 - #else 47 - # define __BUG_REL(val) ".long " val " - ." 48 - #endif 49 - 50 40 #ifdef CONFIG_DEBUG_BUGVERBOSE 51 - #define __BUG_ENTRY(file, line, flags) \ 52 - "2:\t" __BUG_REL("1b") "\t# bug_entry::bug_addr\n" \ 53 - "\t" __BUG_REL(file) "\t# bug_entry::file\n" \ 54 - "\t.word " line "\t# bug_entry::line\n" \ 55 - "\t.word " flags "\t# bug_entry::flags\n" 41 + #define __BUG_ENTRY_VERBOSE(file, line) \ 42 + "\t.long " file " - .\t# bug_entry::file\n" \ 43 + "\t.word " line "\t# bug_entry::line\n" 56 44 #else 57 - #define __BUG_ENTRY(file, line, flags) \ 58 - "2:\t" __BUG_REL("1b") "\t# bug_entry::bug_addr\n" \ 59 - "\t.word " flags "\t# bug_entry::flags\n" 45 + #define __BUG_ENTRY_VERBOSE(file, line) 60 46 #endif 61 47 62 - #define _BUG_FLAGS_ASM(ins, file, line, flags, size, extra) \ 63 - "1:\t" ins "\n" \ 64 - ".pushsection __bug_table,\"aw\"\n" \ 48 + #if defined(CONFIG_X86_64) || defined(CONFIG_DEBUG_BUGVERBOSE_DETAILED) 49 + #define HAVE_ARCH_BUG_FORMAT 50 + #define __BUG_ENTRY_FORMAT(format) \ 51 + "\t.long " format " - .\t# bug_entry::format\n" 52 + #else 53 + #define __BUG_ENTRY_FORMAT(format) 54 + #endif 55 + 56 + #ifdef CONFIG_X86_64 57 + #define HAVE_ARCH_BUG_FORMAT_ARGS 58 + #endif 59 + 60 + #define __BUG_ENTRY(format, file, line, flags) \ 61 + "\t.long 1b - ." "\t# bug_entry::bug_addr\n" \ 62 + __BUG_ENTRY_FORMAT(format) \ 63 + __BUG_ENTRY_VERBOSE(file, line) \ 64 + "\t.word " flags "\t# bug_entry::flags\n" 65 + 66 + #define _BUG_FLAGS_ASM(format, file, line, flags, size, extra) \ 67 + ".pushsection __bug_table,\"aw\"\n\t" \ 65 68 ANNOTATE_DATA_SPECIAL \ 66 - __BUG_ENTRY(file, line, flags) \ 69 + "2:\n\t" \ 70 + __BUG_ENTRY(format, file, line, flags) \ 67 71 "\t.org 2b + " size "\n" \ 68 72 ".popsection\n" \ 69 73 extra 70 74 71 - #define _BUG_FLAGS(ins, flags, extra) \ 75 + #ifdef CONFIG_DEBUG_BUGVERBOSE_DETAILED 76 + #define WARN_CONDITION_STR(cond_str) cond_str 77 + #else 78 + #define WARN_CONDITION_STR(cond_str) "" 79 + #endif 80 + 81 + #define _BUG_FLAGS(cond_str, ins, flags, extra) \ 72 82 do { \ 73 - asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c0", \ 74 - "%c1", "%c2", "%c3", extra) \ 75 - : : "i" (__FILE__), "i" (__LINE__), \ 76 - "i" (flags), \ 77 - "i" (sizeof(struct bug_entry))); \ 83 + asm_inline volatile("1:\t" ins "\n" \ 84 + _BUG_FLAGS_ASM("%c[fmt]", "%c[file]", \ 85 + "%c[line]", "%c[fl]", \ 86 + "%c[size]", extra) \ 87 + : : [fmt] "i" (WARN_CONDITION_STR(cond_str)), \ 88 + [file] "i" (__FILE__), \ 89 + [line] "i" (__LINE__), \ 90 + [fl] "i" (flags), \ 91 + [size] "i" (sizeof(struct bug_entry))); \ 78 92 } while (0) 79 93 80 94 #define ARCH_WARN_ASM(file, line, flags, size) \ 81 - _BUG_FLAGS_ASM(ASM_UD2, file, line, flags, size, "") 95 + ".pushsection .rodata.str1.1, \"aMS\", @progbits, 1\n" \ 96 + "99:\n" \ 97 + "\t.string \"\"\n" \ 98 + ".popsection\n" \ 99 + "1:\t " ASM_UD2 "\n" \ 100 + _BUG_FLAGS_ASM("99b", file, line, flags, size, "") 82 101 83 102 #else 84 103 85 - #define _BUG_FLAGS(ins, flags, extra) asm volatile(ins) 104 + #define _BUG_FLAGS(cond_str, ins, flags, extra) asm volatile(ins) 86 105 87 106 #endif /* CONFIG_GENERIC_BUG */ 88 107 ··· 114 85 #define BUG() \ 115 86 do { \ 116 87 instrumentation_begin(); \ 117 - _BUG_FLAGS(ASM_UD2, 0, ""); \ 88 + _BUG_FLAGS("", ASM_UD2, 0, ""); \ 118 89 __builtin_unreachable(); \ 119 90 } while (0) 120 91 ··· 127 98 128 99 #define ARCH_WARN_REACHABLE ANNOTATE_REACHABLE(1b) 129 100 130 - #define __WARN_FLAGS(flags) \ 131 - do { \ 132 - __auto_type __flags = BUGFLAG_WARNING|(flags); \ 133 - instrumentation_begin(); \ 134 - _BUG_FLAGS(ASM_UD2, __flags, ARCH_WARN_REACHABLE); \ 135 - instrumentation_end(); \ 101 + #define __WARN_FLAGS(cond_str, flags) \ 102 + do { \ 103 + __auto_type __flags = BUGFLAG_WARNING|(flags); \ 104 + instrumentation_begin(); \ 105 + _BUG_FLAGS(cond_str, ASM_UD2, __flags, ARCH_WARN_REACHABLE); \ 106 + instrumentation_end(); \ 136 107 } while (0) 108 + 109 + #ifdef HAVE_ARCH_BUG_FORMAT_ARGS 110 + 111 + #ifndef __ASSEMBLY__ 112 + #include <linux/static_call_types.h> 113 + DECLARE_STATIC_CALL(WARN_trap, __WARN_trap); 114 + 115 + struct pt_regs; 116 + struct sysv_va_list { /* from AMD64 System V ABI */ 117 + unsigned int gp_offset; 118 + unsigned int fp_offset; 119 + void *overflow_arg_area; 120 + void *reg_save_area; 121 + }; 122 + struct arch_va_list { 123 + unsigned long regs[6]; 124 + struct sysv_va_list args; 125 + }; 126 + extern void *__warn_args(struct arch_va_list *args, struct pt_regs *regs); 127 + #endif /* __ASSEMBLY__ */ 128 + 129 + #define __WARN_bug_entry(flags, format) ({ \ 130 + struct bug_entry *bug; \ 131 + asm_inline volatile("lea (2f)(%%rip), %[addr]\n1:\n" \ 132 + _BUG_FLAGS_ASM("%c[fmt]", "%c[file]", \ 133 + "%c[line]", "%c[fl]", \ 134 + "%c[size]", "") \ 135 + : [addr] "=r" (bug) \ 136 + : [fmt] "i" (format), \ 137 + [file] "i" (__FILE__), \ 138 + [line] "i" (__LINE__), \ 139 + [fl] "i" (flags), \ 140 + [size] "i" (sizeof(struct bug_entry))); \ 141 + bug; }) 142 + 143 + #define __WARN_print_arg(flags, format, arg...) \ 144 + do { \ 145 + int __flags = (flags) | BUGFLAG_WARNING | BUGFLAG_ARGS ; \ 146 + static_call_mod(WARN_trap)(__WARN_bug_entry(__flags, format), ## arg); \ 147 + asm (""); /* inhibit tail-call optimization */ \ 148 + } while (0) 149 + 150 + #define __WARN_printf(taint, fmt, arg...) \ 151 + __WARN_print_arg(BUGFLAG_TAINT(taint), fmt, ## arg) 152 + 153 + #define WARN_ONCE(cond, format, arg...) ({ \ 154 + int __ret_warn_on = !!(cond); \ 155 + if (unlikely(__ret_warn_on)) { \ 156 + __WARN_print_arg(BUGFLAG_ONCE|BUGFLAG_TAINT(TAINT_WARN),\ 157 + format, ## arg); \ 158 + } \ 159 + __ret_warn_on; \ 160 + }) 161 + 162 + #endif /* HAVE_ARCH_BUG_FORMAT_ARGS */ 137 163 138 164 #include <asm-generic/bug.h> 139 165
+11 -2
arch/x86/kernel/static_call.c
··· 26 26 27 27 static const u8 retinsn[] = { RET_INSN_OPCODE, 0xcc, 0xcc, 0xcc, 0xcc }; 28 28 29 + /* 30 + * ud1 (%edx),%rdi -- see __WARN_trap() / decode_bug() 31 + */ 32 + static const u8 warninsn[] = { 0x67, 0x48, 0x0f, 0xb9, 0x3a }; 33 + 29 34 static u8 __is_Jcc(u8 *insn) /* Jcc.d32 */ 30 35 { 31 36 u8 ret = 0; ··· 74 69 emulate = code; 75 70 code = &xor5rax; 76 71 } 77 - 72 + if (func == &__WARN_trap) { 73 + emulate = code; 74 + code = &warninsn; 75 + } 78 76 break; 79 77 80 78 case NOP: ··· 136 128 } else { 137 129 if (opcode == CALL_INSN_OPCODE || 138 130 !memcmp(insn, x86_nops[5], 5) || 139 - !memcmp(insn, xor5rax, 5)) 131 + !memcmp(insn, xor5rax, 5) || 132 + !memcmp(insn, warninsn, 5)) 140 133 return; 141 134 } 142 135
+109 -12
arch/x86/kernel/traps.c
··· 31 31 #include <linux/kexec.h> 32 32 #include <linux/sched.h> 33 33 #include <linux/sched/task_stack.h> 34 + #include <linux/static_call.h> 34 35 #include <linux/timer.h> 35 36 #include <linux/init.h> 36 37 #include <linux/bug.h> ··· 103 102 * UBSan{0}: 67 0f b9 00 ud1 (%eax),%eax 104 103 * UBSan{10}: 67 0f b9 40 10 ud1 0x10(%eax),%eax 105 104 * static_call: 0f b9 cc ud1 %esp,%ecx 105 + * __WARN_trap: 67 48 0f b9 3a ud1 (%edx),%reg 106 106 * 107 - * Notably UBSAN uses EAX, static_call uses ECX. 107 + * Notable, since __WARN_trap can use all registers, the distinction between 108 + * UD1 users is through R/M. 108 109 */ 109 110 __always_inline int decode_bug(unsigned long addr, s32 *imm, int *len) 110 111 { 111 112 unsigned long start = addr; 113 + u8 v, reg, rm, rex = 0; 114 + int type = BUG_UD1; 112 115 bool lock = false; 113 - u8 v; 114 116 115 117 if (addr < TASK_SIZE_MAX) 116 118 return BUG_NONE; 117 119 118 - v = *(u8 *)(addr++); 119 - if (v == INSN_ASOP) 120 + for (;;) { 120 121 v = *(u8 *)(addr++); 122 + if (v == INSN_ASOP) 123 + continue; 121 124 122 - if (v == INSN_LOCK) { 123 - lock = true; 124 - v = *(u8 *)(addr++); 125 + if (v == INSN_LOCK) { 126 + lock = true; 127 + continue; 128 + } 129 + 130 + if ((v & 0xf0) == 0x40) { 131 + rex = v; 132 + continue; 133 + } 134 + 135 + break; 125 136 } 126 137 127 138 switch (v) { ··· 169 156 if (X86_MODRM_MOD(v) != 3 && X86_MODRM_RM(v) == 4) 170 157 addr++; /* SIB */ 171 158 159 + reg = X86_MODRM_REG(v) + 8*!!X86_REX_R(rex); 160 + rm = X86_MODRM_RM(v) + 8*!!X86_REX_B(rex); 161 + 172 162 /* Decode immediate, if present */ 173 163 switch (X86_MODRM_MOD(v)) { 174 164 case 0: if (X86_MODRM_RM(v) == 5) 175 - addr += 4; /* RIP + disp32 */ 165 + addr += 4; /* RIP + disp32 */ 166 + 167 + if (rm == 0) /* (%eax) */ 168 + type = BUG_UD1_UBSAN; 169 + 170 + if (rm == 2) { /* (%edx) */ 171 + *imm = reg; 172 + type = BUG_UD1_WARN; 173 + } 176 174 break; 177 175 178 176 case 1: *imm = *(s8 *)addr; 179 177 addr += 1; 178 + if (rm == 0) /* (%eax) */ 179 + type = BUG_UD1_UBSAN; 180 180 break; 181 181 182 182 case 2: *imm = *(s32 *)addr; 183 183 addr += 4; 184 + if (rm == 0) /* (%eax) */ 185 + type = BUG_UD1_UBSAN; 184 186 break; 185 187 186 188 case 3: break; ··· 204 176 /* record instruction length */ 205 177 *len = addr - start; 206 178 207 - if (X86_MODRM_REG(v) == 0) /* EAX */ 208 - return BUG_UD1_UBSAN; 209 - 210 - return BUG_UD1; 179 + return type; 211 180 } 212 181 182 + static inline unsigned long pt_regs_val(struct pt_regs *regs, int nr) 183 + { 184 + int offset = pt_regs_offset(regs, nr); 185 + if (WARN_ON_ONCE(offset < -0)) 186 + return 0; 187 + return *((unsigned long *)((void *)regs + offset)); 188 + } 189 + 190 + #ifdef HAVE_ARCH_BUG_FORMAT_ARGS 191 + DEFINE_STATIC_CALL(WARN_trap, __WARN_trap); 192 + EXPORT_STATIC_CALL_TRAMP(WARN_trap); 193 + 194 + /* 195 + * Create a va_list from an exception context. 196 + */ 197 + void *__warn_args(struct arch_va_list *args, struct pt_regs *regs) 198 + { 199 + /* 200 + * Register save area; populate with function call argument registers 201 + */ 202 + args->regs[0] = regs->di; 203 + args->regs[1] = regs->si; 204 + args->regs[2] = regs->dx; 205 + args->regs[3] = regs->cx; 206 + args->regs[4] = regs->r8; 207 + args->regs[5] = regs->r9; 208 + 209 + /* 210 + * From the ABI document: 211 + * 212 + * @gp_offset - the element holds the offset in bytes from 213 + * reg_save_area to the place where the next available general purpose 214 + * argument register is saved. In case all argument registers have 215 + * been exhausted, it is set to the value 48 (6*8). 216 + * 217 + * @fp_offset - the element holds the offset in bytes from 218 + * reg_save_area to the place where the next available floating point 219 + * argument is saved. In case all argument registers have been 220 + * exhausted, it is set to the value 176 (6*8 + 8*16) 221 + * 222 + * @overflow_arg_area - this pointer is used to fetch arguments passed 223 + * on the stack. It is initialized with the address of the first 224 + * argument passed on the stack, if any, and then always updated to 225 + * point to the start of the next argument on the stack. 226 + * 227 + * @reg_save_area - the element points to the start of the register 228 + * save area. 229 + * 230 + * Notably the vararg starts with the second argument and there are no 231 + * floating point arguments in the kernel. 232 + */ 233 + args->args.gp_offset = 1*8; 234 + args->args.fp_offset = 6*8 + 8*16; 235 + args->args.reg_save_area = &args->regs; 236 + args->args.overflow_arg_area = (void *)regs->sp; 237 + 238 + /* 239 + * If the exception came from __WARN_trap, there is a return 240 + * address on the stack, skip that. This is why any __WARN_trap() 241 + * caller must inhibit tail-call optimization. 242 + */ 243 + if ((void *)regs->ip == &__WARN_trap) 244 + args->args.overflow_arg_area += 8; 245 + 246 + return &args->args; 247 + } 248 + #endif /* HAVE_ARCH_BUG_FORMAT */ 213 249 214 250 static nokprobe_inline int 215 251 do_trap_no_signal(struct task_struct *tsk, int trapnr, const char *str, ··· 426 334 raw_local_irq_enable(); 427 335 428 336 switch (ud_type) { 337 + case BUG_UD1_WARN: 338 + if (report_bug_entry((void *)pt_regs_val(regs, ud_imm), regs) == BUG_TRAP_TYPE_WARN) 339 + handled = true; 340 + break; 341 + 429 342 case BUG_UD2: 430 343 if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) { 431 344 handled = true;
+60 -26
include/asm-generic/bug.h
··· 13 13 #define BUGFLAG_ONCE (1 << 1) 14 14 #define BUGFLAG_DONE (1 << 2) 15 15 #define BUGFLAG_NO_CUT_HERE (1 << 3) /* CUT_HERE already sent */ 16 + #define BUGFLAG_ARGS (1 << 4) 16 17 #define BUGFLAG_TAINT(taint) ((taint) << 8) 17 18 #define BUG_GET_TAINT(bug) ((bug)->flags >> 8) 18 19 #endif 20 + 21 + #ifndef WARN_CONDITION_STR 22 + #ifdef CONFIG_DEBUG_BUGVERBOSE_DETAILED 23 + # define WARN_CONDITION_STR(cond_str) "[" cond_str "] " 24 + #else 25 + # define WARN_CONDITION_STR(cond_str) 26 + #endif 27 + #endif /* WARN_CONDITION_STR */ 19 28 20 29 #ifndef __ASSEMBLY__ 21 30 #include <linux/panic.h> ··· 38 29 39 30 #ifdef CONFIG_BUG 40 31 32 + #ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS 33 + #define BUG_REL(type, name) type name 34 + #else 35 + #define BUG_REL(type, name) signed int name##_disp 36 + #endif 37 + 41 38 #ifdef CONFIG_GENERIC_BUG 42 39 struct bug_entry { 43 - #ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS 44 - unsigned long bug_addr; 45 - #else 46 - signed int bug_addr_disp; 40 + BUG_REL(unsigned long, bug_addr); 41 + #ifdef HAVE_ARCH_BUG_FORMAT 42 + BUG_REL(const char *, format); 47 43 #endif 48 44 #ifdef CONFIG_DEBUG_BUGVERBOSE 49 - #ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS 50 - const char *file; 51 - #else 52 - signed int file_disp; 53 - #endif 45 + BUG_REL(const char *, file); 54 46 unsigned short line; 55 47 #endif 56 48 unsigned short flags; ··· 102 92 const char *fmt, ...); 103 93 extern __printf(1, 2) void __warn_printk(const char *fmt, ...); 104 94 105 - #ifndef __WARN_FLAGS 106 - #define __WARN() __WARN_printf(TAINT_WARN, NULL) 95 + #ifdef __WARN_FLAGS 96 + #define __WARN() __WARN_FLAGS("", BUGFLAG_TAINT(TAINT_WARN)) 97 + 98 + #ifndef WARN_ON 99 + #define WARN_ON(condition) ({ \ 100 + int __ret_warn_on = !!(condition); \ 101 + if (unlikely(__ret_warn_on)) \ 102 + __WARN_FLAGS(#condition, \ 103 + BUGFLAG_TAINT(TAINT_WARN)); \ 104 + unlikely(__ret_warn_on); \ 105 + }) 106 + #endif 107 + 108 + #ifndef WARN_ON_ONCE 109 + #define WARN_ON_ONCE(condition) ({ \ 110 + int __ret_warn_on = !!(condition); \ 111 + if (unlikely(__ret_warn_on)) \ 112 + __WARN_FLAGS(#condition, \ 113 + BUGFLAG_ONCE | \ 114 + BUGFLAG_TAINT(TAINT_WARN)); \ 115 + unlikely(__ret_warn_on); \ 116 + }) 117 + #endif 118 + #endif /* __WARN_FLAGS */ 119 + 120 + #if defined(__WARN_FLAGS) && !defined(__WARN_printf) 121 + #define __WARN_printf(taint, arg...) do { \ 122 + instrumentation_begin(); \ 123 + __warn_printk(arg); \ 124 + __WARN_FLAGS("", BUGFLAG_NO_CUT_HERE | BUGFLAG_TAINT(taint));\ 125 + instrumentation_end(); \ 126 + } while (0) 127 + #endif 128 + 129 + #ifndef __WARN_printf 107 130 #define __WARN_printf(taint, arg...) do { \ 108 131 instrumentation_begin(); \ 109 132 warn_slowpath_fmt(__FILE__, __LINE__, taint, arg); \ 110 133 instrumentation_end(); \ 111 134 } while (0) 112 - #else 113 - #define __WARN() __WARN_FLAGS(BUGFLAG_TAINT(TAINT_WARN)) 114 - #define __WARN_printf(taint, arg...) do { \ 115 - instrumentation_begin(); \ 116 - __warn_printk(arg); \ 117 - __WARN_FLAGS(BUGFLAG_NO_CUT_HERE | BUGFLAG_TAINT(taint));\ 118 - instrumentation_end(); \ 119 - } while (0) 120 - #define WARN_ON_ONCE(condition) ({ \ 121 - int __ret_warn_on = !!(condition); \ 122 - if (unlikely(__ret_warn_on)) \ 123 - __WARN_FLAGS(BUGFLAG_ONCE | \ 124 - BUGFLAG_TAINT(TAINT_WARN)); \ 125 - unlikely(__ret_warn_on); \ 126 - }) 135 + #endif 136 + 137 + #ifndef __WARN 138 + #define __WARN() __WARN_printf(TAINT_WARN, NULL) 127 139 #endif 128 140 129 141 /* used internally by panic.c */ ··· 180 148 DO_ONCE_LITE_IF(condition, WARN_ON, 1) 181 149 #endif 182 150 151 + #ifndef WARN_ONCE 183 152 #define WARN_ONCE(condition, format...) \ 184 153 DO_ONCE_LITE_IF(condition, WARN, 1, format) 154 + #endif 185 155 186 156 #define WARN_TAINT_ONCE(condition, taint, format...) \ 187 157 DO_ONCE_LITE_IF(condition, WARN_TAINT, 1, taint, format)
+8
include/linux/bug.h
··· 42 42 struct bug_entry *find_bug(unsigned long bugaddr); 43 43 44 44 enum bug_trap_type report_bug(unsigned long bug_addr, struct pt_regs *regs); 45 + enum bug_trap_type report_bug_entry(struct bug_entry *bug, struct pt_regs *regs); 45 46 46 47 /* These are defined by the architecture */ 47 48 int is_valid_bugaddr(unsigned long addr); ··· 63 62 } 64 63 65 64 struct bug_entry; 65 + 66 + static inline enum bug_trap_type 67 + report_bug_entry(struct bug_entry *bug, struct pt_regs *regs) 68 + { 69 + return BUG_TRAP_TYPE_BUG; 70 + } 71 + 66 72 static inline void bug_get_file_line(struct bug_entry *bug, const char **file, 67 73 unsigned int *line) 68 74 {
+9 -7
kernel/panic.c
··· 873 873 874 874 disable_trace_on_warning(); 875 875 876 - if (file) 877 - pr_warn("WARNING: CPU: %d PID: %d at %s:%d %pS\n", 878 - raw_smp_processor_id(), current->pid, file, line, 879 - caller); 880 - else 881 - pr_warn("WARNING: CPU: %d PID: %d at %pS\n", 882 - raw_smp_processor_id(), current->pid, caller); 876 + if (file) { 877 + pr_warn("WARNING: %s:%d at %pS, CPU#%d: %s/%d\n", 878 + file, line, caller, 879 + raw_smp_processor_id(), current->comm, current->pid); 880 + } else { 881 + pr_warn("WARNING: at %pS, CPU#%d: %s/%d\n", 882 + caller, 883 + raw_smp_processor_id(), current->comm, current->pid); 884 + } 883 885 884 886 #pragma GCC diagnostic push 885 887 #ifndef __clang__
+10
lib/Kconfig.debug
··· 206 206 of the BUG call as well as the EIP and oops trace. This aids 207 207 debugging but costs about 70-100K of memory. 208 208 209 + config DEBUG_BUGVERBOSE_DETAILED 210 + bool "Verbose WARN_ON_ONCE() reporting (adds 100K)" if DEBUG_BUGVERBOSE 211 + help 212 + Say Y here to make WARN_ON_ONCE() output the condition string of the 213 + warning, in addition to the file name and line number. 214 + This helps debugging, but costs about 100K of memory. 215 + 216 + Say N if unsure. 217 + 218 + 209 219 endmenu # "printk and dmesg options" 210 220 211 221 config DEBUG_KERNEL
+76 -14
lib/bug.c
··· 139 139 #endif 140 140 } 141 141 142 + static const char *bug_get_format(struct bug_entry *bug) 143 + { 144 + const char *format = NULL; 145 + #ifdef HAVE_ARCH_BUG_FORMAT 146 + #ifdef CONFIG_GENERIC_BUG_RELATIVE_POINTERS 147 + /* 148 + * Allow an architecture to: 149 + * - relative encode NULL (difficult vs KASLR); 150 + * - use a literal 0 (there are no valid objects inside 151 + * the __bug_table itself to refer to after all); 152 + * - use an empty string. 153 + */ 154 + if (bug->format_disp) 155 + format = (const char *)&bug->format_disp + bug->format_disp; 156 + if (format && format[0] == '\0') 157 + format = NULL; 158 + #else 159 + format = bug->format; 160 + #endif 161 + #endif 162 + return format; 163 + } 164 + 142 165 struct bug_entry *find_bug(unsigned long bugaddr) 143 166 { 144 167 struct bug_entry *bug; ··· 173 150 return module_find_bug(bugaddr); 174 151 } 175 152 176 - static enum bug_trap_type __report_bug(unsigned long bugaddr, struct pt_regs *regs) 153 + static void __warn_printf(const char *fmt, struct pt_regs *regs) 177 154 { 178 - struct bug_entry *bug; 179 - const char *file; 180 - unsigned line, warning, once, done; 155 + if (!fmt) 156 + return; 181 157 182 - if (!is_valid_bugaddr(bugaddr)) 183 - return BUG_TRAP_TYPE_NONE; 158 + #ifdef HAVE_ARCH_BUG_FORMAT_ARGS 159 + if (regs) { 160 + struct arch_va_list _args; 161 + va_list *args = __warn_args(&_args, regs); 184 162 185 - bug = find_bug(bugaddr); 186 - if (!bug) 187 - return BUG_TRAP_TYPE_NONE; 163 + if (args) { 164 + vprintk(fmt, *args); 165 + return; 166 + } 167 + } 168 + #endif 169 + 170 + printk("%s", fmt); 171 + } 172 + 173 + static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long bugaddr, struct pt_regs *regs) 174 + { 175 + bool warning, once, done, no_cut, has_args; 176 + const char *file, *fmt; 177 + unsigned line; 178 + 179 + if (!bug) { 180 + if (!is_valid_bugaddr(bugaddr)) 181 + return BUG_TRAP_TYPE_NONE; 182 + 183 + bug = find_bug(bugaddr); 184 + if (!bug) 185 + return BUG_TRAP_TYPE_NONE; 186 + } 188 187 189 188 disable_trace_on_warning(); 190 189 191 190 bug_get_file_line(bug, &file, &line); 191 + fmt = bug_get_format(bug); 192 192 193 - warning = (bug->flags & BUGFLAG_WARNING) != 0; 194 - once = (bug->flags & BUGFLAG_ONCE) != 0; 195 - done = (bug->flags & BUGFLAG_DONE) != 0; 193 + warning = bug->flags & BUGFLAG_WARNING; 194 + once = bug->flags & BUGFLAG_ONCE; 195 + done = bug->flags & BUGFLAG_DONE; 196 + no_cut = bug->flags & BUGFLAG_NO_CUT_HERE; 197 + has_args = bug->flags & BUGFLAG_ARGS; 196 198 197 199 if (warning && once) { 198 200 if (done) ··· 235 187 * "cut here" line now. WARN() issues its own "cut here" before the 236 188 * extra debugging message it writes before triggering the handler. 237 189 */ 238 - if ((bug->flags & BUGFLAG_NO_CUT_HERE) == 0) 190 + if (!no_cut) { 239 191 printk(KERN_DEFAULT CUT_HERE); 192 + __warn_printf(fmt, has_args ? regs : NULL); 193 + } 240 194 241 195 if (warning) { 242 196 /* this is a WARN_ON rather than BUG/BUG_ON */ ··· 256 206 return BUG_TRAP_TYPE_BUG; 257 207 } 258 208 209 + enum bug_trap_type report_bug_entry(struct bug_entry *bug, struct pt_regs *regs) 210 + { 211 + enum bug_trap_type ret; 212 + bool rcu = false; 213 + 214 + rcu = warn_rcu_enter(); 215 + ret = __report_bug(bug, 0, regs); 216 + warn_rcu_exit(rcu); 217 + 218 + return ret; 219 + } 220 + 259 221 enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) 260 222 { 261 223 enum bug_trap_type ret; 262 224 bool rcu = false; 263 225 264 226 rcu = warn_rcu_enter(); 265 - ret = __report_bug(bugaddr, regs); 227 + ret = __report_bug(NULL, bugaddr, regs); 266 228 warn_rcu_exit(rcu); 267 229 268 230 return ret;