Core kernel bug handling infrastructure changes for v6.19:

- 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)
 
 Signed-off-by: Ingo Molnar <mingo@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAmktffcRHG1pbmdvQGtl
 cm5lbC5vcmcACgkQEnMQ0APhK1gLCQ//ZMHTpv6UF+xY4Kq3rdVFJWRR7Prs2hDn
 4V/cFHu7Xb2lRwpJDBNWnbiEkLbGR5qlWD+0CpAMK4mGuL9VjnoeflEzXxOPP9W1
 7SWw1HDc6NkjHM3BNnvkPQbzWF4RL4ZGs4Lbb1nv7pjoTSdBMXNrD5RL+iNmfHHd
 QfOG9ZiRyD5A/b07bfyjIXNaeC/Hot+FeVXTMfD7/vCfc2ywhL2Sm5G/igY/shTY
 Una7q8sbFDD/bFFtWSR2eFQeHQQfT6c/Pu39ZcAIdTlLk1FtZ+A2wcRtwYv/FdPV
 6KDOAxZK7fLMHoCdNTswsg+LbazhABOb/V1J7TaHq2tF/PeyN+B+ucxVY2KFcxQJ
 V5eG5crMrUIL22QO/UaT3dPWxGbJYkNlAWl416tAKdgXA52W4OsPd+k4DGjeP569
 KogAy3SY0D/80v1QN2HcFEJMvr7W2SukxtErqdtA5OKt/ZevB49lGqZoBecqASDO
 QjI1K0yLKnb+erbMIpCpNj+o/Fr1JQgWbYVpwipL2GON4an6vrowimGTsUG7qqxN
 Hwb7IaTNnWQ/4iyCkVV44q6Ln1gyP2hz5Lnzo7QJINUdzp98UjJLAEK4NRG44T4L
 p0t5NMxnvREpAv35rwy03xhmITYeQMOqzN9JEBBvegQyld5ghbThxI7g9BDzcXyL
 Bd0mF7WOV9M=
 =bMUL
 -----END PGP SIGNATURE-----

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()
  ...
This commit is contained in:
Linus Torvalds 2025-12-01 21:33:01 -08:00
commit 4a26e7032d
17 changed files with 480 additions and 181 deletions

View File

@ -19,7 +19,7 @@
unreachable(); \ unreachable(); \
} while (0) } while (0)
#define __WARN_FLAGS(flags) __BUG_FLAGS(BUGFLAG_WARNING|(flags)) #define __WARN_FLAGS(cond_str, flags) __BUG_FLAGS(BUGFLAG_WARNING|(flags))
#define HAVE_ARCH_BUG #define HAVE_ARCH_BUG

View File

@ -11,7 +11,7 @@
#else #else
#define __BUGVERBOSE_LOCATION(file, line) \ #define __BUGVERBOSE_LOCATION(file, line) \
.pushsection .rodata.str, "aMS", @progbits, 1; \ .pushsection .rodata.str, "aMS", @progbits, 1; \
10002: .string file; \ 10002: .ascii file "\0"; \
.popsection; \ .popsection; \
\ \
.long 10002b - .; \ .long 10002b - .; \
@ -20,39 +20,38 @@
#endif #endif
#ifndef CONFIG_GENERIC_BUG #ifndef CONFIG_GENERIC_BUG
#define __BUG_ENTRY(flags) #define __BUG_ENTRY(cond_str, flags)
#else #else
#define __BUG_ENTRY(flags) \ #define __BUG_ENTRY(cond_str, flags) \
.pushsection __bug_table, "aw"; \ .pushsection __bug_table, "aw"; \
.align 2; \ .align 2; \
10000: .long 10001f - .; \ 10000: .long 10001f - .; \
_BUGVERBOSE_LOCATION(__FILE__, __LINE__) \ _BUGVERBOSE_LOCATION(WARN_CONDITION_STR(cond_str) __FILE__, __LINE__) \
.short flags; \ .short flags; \
.popsection; \ .popsection; \
10001: 10001:
#endif #endif
#define ASM_BUG_FLAGS(flags) \ #define ASM_BUG_FLAGS(cond_str, flags) \
__BUG_ENTRY(flags) \ __BUG_ENTRY(cond_str, flags) \
break BRK_BUG; break BRK_BUG;
#define ASM_BUG() ASM_BUG_FLAGS(0) #define ASM_BUG() ASM_BUG_FLAGS("", 0)
#define __BUG_FLAGS(flags, extra) \ #define __BUG_FLAGS(cond_str, flags, extra) \
asm_inline volatile (__stringify(ASM_BUG_FLAGS(flags)) \ asm_inline volatile (__stringify(ASM_BUG_FLAGS(cond_str, flags)) extra);
extra);
#define __WARN_FLAGS(flags) \ #define __WARN_FLAGS(cond_str, flags) \
do { \ do { \
instrumentation_begin(); \ instrumentation_begin(); \
__BUG_FLAGS(BUGFLAG_WARNING|(flags), ANNOTATE_REACHABLE(10001b));\ __BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags), ANNOTATE_REACHABLE(10001b));\
instrumentation_end(); \ instrumentation_end(); \
} while (0) } while (0)
#define BUG() \ #define BUG() \
do { \ do { \
instrumentation_begin(); \ instrumentation_begin(); \
__BUG_FLAGS(0, ""); \ __BUG_FLAGS("", 0, ""); \
unreachable(); \ unreachable(); \
} while (0) } while (0)

View File

@ -50,7 +50,7 @@
#endif #endif
#ifdef CONFIG_DEBUG_BUGVERBOSE #ifdef CONFIG_DEBUG_BUGVERBOSE
#define __WARN_FLAGS(flags) \ #define __WARN_FLAGS(cond_str, flags) \
do { \ do { \
asm volatile("\n" \ asm volatile("\n" \
"1:\t" PARISC_BUG_BREAK_ASM "\n" \ "1:\t" PARISC_BUG_BREAK_ASM "\n" \
@ -61,12 +61,12 @@
"\t.short %1, %2\n" \ "\t.short %1, %2\n" \
"\t.blockz %3-2*4-2*2\n" \ "\t.blockz %3-2*4-2*2\n" \
"\t.popsection" \ "\t.popsection" \
: : "i" (__FILE__), "i" (__LINE__), \ : : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__), \
"i" (BUGFLAG_WARNING|(flags)), \ "i" (BUGFLAG_WARNING|(flags)), \
"i" (sizeof(struct bug_entry)) ); \ "i" (sizeof(struct bug_entry)) ); \
} while(0) } while(0)
#else #else
#define __WARN_FLAGS(flags) \ #define __WARN_FLAGS(cond_str, flags) \
do { \ do { \
asm volatile("\n" \ asm volatile("\n" \
"1:\t" PARISC_BUG_BREAK_ASM "\n" \ "1:\t" PARISC_BUG_BREAK_ASM "\n" \

View File

@ -51,11 +51,11 @@
".previous\n" ".previous\n"
#endif #endif
#define BUG_ENTRY(insn, flags, ...) \ #define BUG_ENTRY(cond_str, insn, flags, ...) \
__asm__ __volatile__( \ __asm__ __volatile__( \
"1: " insn "\n" \ "1: " insn "\n" \
_EMIT_BUG_ENTRY \ _EMIT_BUG_ENTRY \
: : "i" (__FILE__), "i" (__LINE__), \ : : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__), \
"i" (flags), \ "i" (flags), \
"i" (sizeof(struct bug_entry)), \ "i" (sizeof(struct bug_entry)), \
##__VA_ARGS__) ##__VA_ARGS__)
@ -67,12 +67,12 @@
*/ */
#define BUG() do { \ #define BUG() do { \
BUG_ENTRY("twi 31, 0, 0", 0); \ BUG_ENTRY("", "twi 31, 0, 0", 0); \
unreachable(); \ unreachable(); \
} while (0) } while (0)
#define HAVE_ARCH_BUG #define HAVE_ARCH_BUG
#define __WARN_FLAGS(flags) BUG_ENTRY("twi 31, 0, 0", BUGFLAG_WARNING | (flags)) #define __WARN_FLAGS(cond_str, flags) BUG_ENTRY(cond_str, "twi 31, 0, 0", BUGFLAG_WARNING | (flags))
#ifdef CONFIG_PPC64 #ifdef CONFIG_PPC64
#define BUG_ON(x) do { \ #define BUG_ON(x) do { \
@ -80,7 +80,7 @@
if (x) \ if (x) \
BUG(); \ BUG(); \
} else { \ } else { \
BUG_ENTRY(PPC_TLNEI " %4, 0", 0, "r" ((__force long)(x))); \ BUG_ENTRY(#x, PPC_TLNEI " %4, 0", 0, "r" ((__force long)(x))); \
} \ } \
} while (0) } while (0)
@ -90,7 +90,7 @@
if (__ret_warn_on) \ if (__ret_warn_on) \
__WARN(); \ __WARN(); \
} else { \ } else { \
BUG_ENTRY(PPC_TLNEI " %4, 0", \ BUG_ENTRY(#x, PPC_TLNEI " %4, 0", \
BUGFLAG_WARNING | BUGFLAG_TAINT(TAINT_WARN), \ BUGFLAG_WARNING | BUGFLAG_TAINT(TAINT_WARN), \
"r" (__ret_warn_on)); \ "r" (__ret_warn_on)); \
} \ } \

View File

@ -60,28 +60,28 @@ typedef u32 bug_insn_t;
".org 2b + " size "\n\t" \ ".org 2b + " size "\n\t" \
".popsection" \ ".popsection" \
#define __BUG_FLAGS(flags) \ #define __BUG_FLAGS(cond_str, flags) \
do { \ do { \
__asm__ __volatile__ ( \ __asm__ __volatile__ ( \
ARCH_WARN_ASM("%0", "%1", "%2", "%3") \ ARCH_WARN_ASM("%0", "%1", "%2", "%3") \
: \ : \
: "i" (__FILE__), "i" (__LINE__), \ : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__), \
"i" (flags), \ "i" (flags), \
"i" (sizeof(struct bug_entry))); \ "i" (sizeof(struct bug_entry))); \
} while (0) } while (0)
#else /* CONFIG_GENERIC_BUG */ #else /* CONFIG_GENERIC_BUG */
#define __BUG_FLAGS(flags) do { \ #define __BUG_FLAGS(cond_str, flags) do { \
__asm__ __volatile__ ("ebreak\n"); \ __asm__ __volatile__ ("ebreak\n"); \
} while (0) } while (0)
#endif /* CONFIG_GENERIC_BUG */ #endif /* CONFIG_GENERIC_BUG */
#define BUG() do { \ #define BUG() do { \
__BUG_FLAGS(0); \ __BUG_FLAGS("", 0); \
unreachable(); \ unreachable(); \
} while (0) } while (0)
#define __WARN_FLAGS(flags) __BUG_FLAGS(BUGFLAG_WARNING|(flags)) #define __WARN_FLAGS(cond_str, flags) __BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags))
#define ARCH_WARN_REACHABLE #define ARCH_WARN_REACHABLE

View File

@ -2,69 +2,55 @@
#ifndef _ASM_S390_BUG_H #ifndef _ASM_S390_BUG_H
#define _ASM_S390_BUG_H #define _ASM_S390_BUG_H
#include <linux/compiler.h> #include <linux/stringify.h>
#ifdef CONFIG_BUG #ifndef CONFIG_DEBUG_BUGVERBOSE
#define _BUGVERBOSE_LOCATION(file, line)
#else
#define __BUGVERBOSE_LOCATION(file, line) \
.pushsection .rodata.str, "aMS", @progbits, 1; \
10002: .ascii file "\0"; \
.popsection; \
\
.long 10002b - .; \
.short line;
#define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line)
#endif
#ifdef CONFIG_DEBUG_BUGVERBOSE #ifndef CONFIG_GENERIC_BUG
#define __BUG_ENTRY(cond_str, flags)
#else
#define __BUG_ENTRY(cond_str, flags) \
.pushsection __bug_table, "aw"; \
.align 4; \
10000: .long 10001f - .; \
_BUGVERBOSE_LOCATION(WARN_CONDITION_STR(cond_str) __FILE__, __LINE__) \
.short flags; \
.popsection; \
10001:
#endif
#define __EMIT_BUG(x) do { \ #define ASM_BUG_FLAGS(cond_str, flags) \
asm_inline volatile( \ __BUG_ENTRY(cond_str, flags) \
"0: mc 0,0\n" \ mc 0,0
".section .rodata.str,\"aMS\",@progbits,1\n" \
"1: .asciz \""__FILE__"\"\n" \ #define ASM_BUG() ASM_BUG_FLAGS("", 0)
".previous\n" \
".section __bug_table,\"aw\"\n" \ #define __BUG_FLAGS(cond_str, flags) \
"2: .long 0b-.\n" \ asm_inline volatile(__stringify(ASM_BUG_FLAGS(cond_str, flags)));
" .long 1b-.\n" \
" .short %0,%1\n" \ #define __WARN_FLAGS(cond_str, flags) \
" .org 2b+%2\n" \ do { \
".previous\n" \ __BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags)); \
: : "i" (__LINE__), \
"i" (x), \
"i" (sizeof(struct bug_entry))); \
} while (0) } while (0)
#else /* CONFIG_DEBUG_BUGVERBOSE */ #define BUG() \
do { \
#define __EMIT_BUG(x) do { \ __BUG_FLAGS("", 0); \
asm_inline volatile( \ unreachable(); \
"0: mc 0,0\n" \
".section __bug_table,\"aw\"\n" \
"1: .long 0b-.\n" \
" .short %0\n" \
" .org 1b+%1\n" \
".previous\n" \
: : "i" (x), \
"i" (sizeof(struct bug_entry))); \
} while (0) } while (0)
#endif /* CONFIG_DEBUG_BUGVERBOSE */
#define BUG() do { \
__EMIT_BUG(0); \
unreachable(); \
} while (0)
#define __WARN_FLAGS(flags) do { \
__EMIT_BUG(BUGFLAG_WARNING|(flags)); \
} while (0)
#define WARN_ON(x) ({ \
int __ret_warn_on = !!(x); \
if (__builtin_constant_p(__ret_warn_on)) { \
if (__ret_warn_on) \
__WARN(); \
} else { \
if (unlikely(__ret_warn_on)) \
__WARN(); \
} \
unlikely(__ret_warn_on); \
})
#define HAVE_ARCH_BUG #define HAVE_ARCH_BUG
#define HAVE_ARCH_WARN_ON
#endif /* CONFIG_BUG */
#include <asm-generic/bug.h> #include <asm-generic/bug.h>

View File

@ -52,14 +52,14 @@ do { \
unreachable(); \ unreachable(); \
} while (0) } while (0)
#define __WARN_FLAGS(flags) \ #define __WARN_FLAGS(cond_str, flags) \
do { \ do { \
__asm__ __volatile__ ( \ __asm__ __volatile__ ( \
"1:\t.short %O0\n" \ "1:\t.short %O0\n" \
_EMIT_BUG_ENTRY \ _EMIT_BUG_ENTRY \
: \ : \
: "n" (TRAPA_BUG_OPCODE), \ : "n" (TRAPA_BUG_OPCODE), \
"i" (__FILE__), \ "i" (WARN_CONDITION_STR(cond_str) __FILE__), \
"i" (__LINE__), \ "i" (__LINE__), \
"i" (BUGFLAG_WARNING|(flags)), \ "i" (BUGFLAG_WARNING|(flags)), \
"i" (sizeof(struct bug_entry))); \ "i" (sizeof(struct bug_entry))); \

View File

@ -381,7 +381,7 @@ config GENERIC_CSUM
config GENERIC_BUG config GENERIC_BUG
def_bool y def_bool y
depends on BUG depends on BUG
select GENERIC_BUG_RELATIVE_POINTERS if X86_64 select GENERIC_BUG_RELATIVE_POINTERS
config GENERIC_BUG_RELATIVE_POINTERS config GENERIC_BUG_RELATIVE_POINTERS
bool bool

View File

@ -32,6 +32,14 @@ SYM_FUNC_END(write_ibpb)
/* For KVM */ /* For KVM */
EXPORT_SYMBOL_GPL(write_ibpb); EXPORT_SYMBOL_GPL(write_ibpb);
SYM_FUNC_START(__WARN_trap)
ANNOTATE_NOENDBR
ANNOTATE_REACHABLE
ud1 (%edx), %_ASM_ARG1
RET
SYM_FUNC_END(__WARN_trap)
EXPORT_SYMBOL(__WARN_trap)
.popsection .popsection
/* /*

View File

@ -7,6 +7,11 @@
#include <linux/objtool.h> #include <linux/objtool.h>
#include <asm/asm.h> #include <asm/asm.h>
#ifndef __ASSEMBLY__
struct bug_entry;
extern void __WARN_trap(struct bug_entry *bug, ...);
#endif
/* /*
* Despite that some emulators terminate on UD2, we use it for WARN(). * Despite that some emulators terminate on UD2, we use it for WARN().
*/ */
@ -31,53 +36,77 @@
#define BUG_UD2 0xfffe #define BUG_UD2 0xfffe
#define BUG_UD1 0xfffd #define BUG_UD1 0xfffd
#define BUG_UD1_UBSAN 0xfffc #define BUG_UD1_UBSAN 0xfffc
#define BUG_UD1_WARN 0xfffb
#define BUG_UDB 0xffd6 #define BUG_UDB 0xffd6
#define BUG_LOCK 0xfff0 #define BUG_LOCK 0xfff0
#ifdef CONFIG_GENERIC_BUG #ifdef CONFIG_GENERIC_BUG
#ifdef CONFIG_X86_32
# define __BUG_REL(val) ".long " val
#else
# define __BUG_REL(val) ".long " val " - ."
#endif
#ifdef CONFIG_DEBUG_BUGVERBOSE #ifdef CONFIG_DEBUG_BUGVERBOSE
#define __BUG_ENTRY(file, line, flags) \ #define __BUG_ENTRY_VERBOSE(file, line) \
"2:\t" __BUG_REL("1b") "\t# bug_entry::bug_addr\n" \ "\t.long " file " - .\t# bug_entry::file\n" \
"\t" __BUG_REL(file) "\t# bug_entry::file\n" \ "\t.word " line "\t# bug_entry::line\n"
"\t.word " line "\t# bug_entry::line\n" \
"\t.word " flags "\t# bug_entry::flags\n"
#else #else
#define __BUG_ENTRY(file, line, flags) \ #define __BUG_ENTRY_VERBOSE(file, line)
"2:\t" __BUG_REL("1b") "\t# bug_entry::bug_addr\n" \
"\t.word " flags "\t# bug_entry::flags\n"
#endif #endif
#define _BUG_FLAGS_ASM(ins, file, line, flags, size, extra) \ #if defined(CONFIG_X86_64) || defined(CONFIG_DEBUG_BUGVERBOSE_DETAILED)
"1:\t" ins "\n" \ #define HAVE_ARCH_BUG_FORMAT
".pushsection __bug_table,\"aw\"\n" \ #define __BUG_ENTRY_FORMAT(format) \
"\t.long " format " - .\t# bug_entry::format\n"
#else
#define __BUG_ENTRY_FORMAT(format)
#endif
#ifdef CONFIG_X86_64
#define HAVE_ARCH_BUG_FORMAT_ARGS
#endif
#define __BUG_ENTRY(format, file, line, flags) \
"\t.long 1b - ." "\t# bug_entry::bug_addr\n" \
__BUG_ENTRY_FORMAT(format) \
__BUG_ENTRY_VERBOSE(file, line) \
"\t.word " flags "\t# bug_entry::flags\n"
#define _BUG_FLAGS_ASM(format, file, line, flags, size, extra) \
".pushsection __bug_table,\"aw\"\n\t" \
ANNOTATE_DATA_SPECIAL \ ANNOTATE_DATA_SPECIAL \
__BUG_ENTRY(file, line, flags) \ "2:\n\t" \
__BUG_ENTRY(format, file, line, flags) \
"\t.org 2b + " size "\n" \ "\t.org 2b + " size "\n" \
".popsection\n" \ ".popsection\n" \
extra extra
#define _BUG_FLAGS(ins, flags, extra) \ #ifdef CONFIG_DEBUG_BUGVERBOSE_DETAILED
#define WARN_CONDITION_STR(cond_str) cond_str
#else
#define WARN_CONDITION_STR(cond_str) ""
#endif
#define _BUG_FLAGS(cond_str, ins, flags, extra) \
do { \ do { \
asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c0", \ asm_inline volatile("1:\t" ins "\n" \
"%c1", "%c2", "%c3", extra) \ _BUG_FLAGS_ASM("%c[fmt]", "%c[file]", \
: : "i" (__FILE__), "i" (__LINE__), \ "%c[line]", "%c[fl]", \
"i" (flags), \ "%c[size]", extra) \
"i" (sizeof(struct bug_entry))); \ : : [fmt] "i" (WARN_CONDITION_STR(cond_str)), \
[file] "i" (__FILE__), \
[line] "i" (__LINE__), \
[fl] "i" (flags), \
[size] "i" (sizeof(struct bug_entry))); \
} while (0) } while (0)
#define ARCH_WARN_ASM(file, line, flags, size) \ #define ARCH_WARN_ASM(file, line, flags, size) \
_BUG_FLAGS_ASM(ASM_UD2, file, line, flags, size, "") ".pushsection .rodata.str1.1, \"aMS\", @progbits, 1\n" \
"99:\n" \
"\t.string \"\"\n" \
".popsection\n" \
"1:\t " ASM_UD2 "\n" \
_BUG_FLAGS_ASM("99b", file, line, flags, size, "")
#else #else
#define _BUG_FLAGS(ins, flags, extra) asm volatile(ins) #define _BUG_FLAGS(cond_str, ins, flags, extra) asm volatile(ins)
#endif /* CONFIG_GENERIC_BUG */ #endif /* CONFIG_GENERIC_BUG */
@ -85,7 +114,7 @@ do { \
#define BUG() \ #define BUG() \
do { \ do { \
instrumentation_begin(); \ instrumentation_begin(); \
_BUG_FLAGS(ASM_UD2, 0, ""); \ _BUG_FLAGS("", ASM_UD2, 0, ""); \
__builtin_unreachable(); \ __builtin_unreachable(); \
} while (0) } while (0)
@ -98,14 +127,69 @@ do { \
#define ARCH_WARN_REACHABLE ANNOTATE_REACHABLE(1b) #define ARCH_WARN_REACHABLE ANNOTATE_REACHABLE(1b)
#define __WARN_FLAGS(flags) \ #define __WARN_FLAGS(cond_str, flags) \
do { \ do { \
__auto_type __flags = BUGFLAG_WARNING|(flags); \ __auto_type __flags = BUGFLAG_WARNING|(flags); \
instrumentation_begin(); \ instrumentation_begin(); \
_BUG_FLAGS(ASM_UD2, __flags, ARCH_WARN_REACHABLE); \ _BUG_FLAGS(cond_str, ASM_UD2, __flags, ARCH_WARN_REACHABLE); \
instrumentation_end(); \ instrumentation_end(); \
} while (0) } while (0)
#ifdef HAVE_ARCH_BUG_FORMAT_ARGS
#ifndef __ASSEMBLY__
#include <linux/static_call_types.h>
DECLARE_STATIC_CALL(WARN_trap, __WARN_trap);
struct pt_regs;
struct sysv_va_list { /* from AMD64 System V ABI */
unsigned int gp_offset;
unsigned int fp_offset;
void *overflow_arg_area;
void *reg_save_area;
};
struct arch_va_list {
unsigned long regs[6];
struct sysv_va_list args;
};
extern void *__warn_args(struct arch_va_list *args, struct pt_regs *regs);
#endif /* __ASSEMBLY__ */
#define __WARN_bug_entry(flags, format) ({ \
struct bug_entry *bug; \
asm_inline volatile("lea (2f)(%%rip), %[addr]\n1:\n" \
_BUG_FLAGS_ASM("%c[fmt]", "%c[file]", \
"%c[line]", "%c[fl]", \
"%c[size]", "") \
: [addr] "=r" (bug) \
: [fmt] "i" (format), \
[file] "i" (__FILE__), \
[line] "i" (__LINE__), \
[fl] "i" (flags), \
[size] "i" (sizeof(struct bug_entry))); \
bug; })
#define __WARN_print_arg(flags, format, arg...) \
do { \
int __flags = (flags) | BUGFLAG_WARNING | BUGFLAG_ARGS ; \
static_call_mod(WARN_trap)(__WARN_bug_entry(__flags, format), ## arg); \
asm (""); /* inhibit tail-call optimization */ \
} while (0)
#define __WARN_printf(taint, fmt, arg...) \
__WARN_print_arg(BUGFLAG_TAINT(taint), fmt, ## arg)
#define WARN_ONCE(cond, format, arg...) ({ \
int __ret_warn_on = !!(cond); \
if (unlikely(__ret_warn_on)) { \
__WARN_print_arg(BUGFLAG_ONCE|BUGFLAG_TAINT(TAINT_WARN),\
format, ## arg); \
} \
__ret_warn_on; \
})
#endif /* HAVE_ARCH_BUG_FORMAT_ARGS */
#include <asm-generic/bug.h> #include <asm-generic/bug.h>
#endif /* _ASM_X86_BUG_H */ #endif /* _ASM_X86_BUG_H */

View File

@ -26,6 +26,11 @@ static const u8 xor5rax[] = { 0x2e, 0x2e, 0x2e, 0x31, 0xc0 };
static const u8 retinsn[] = { RET_INSN_OPCODE, 0xcc, 0xcc, 0xcc, 0xcc }; static const u8 retinsn[] = { RET_INSN_OPCODE, 0xcc, 0xcc, 0xcc, 0xcc };
/*
* ud1 (%edx),%rdi -- see __WARN_trap() / decode_bug()
*/
static const u8 warninsn[] = { 0x67, 0x48, 0x0f, 0xb9, 0x3a };
static u8 __is_Jcc(u8 *insn) /* Jcc.d32 */ static u8 __is_Jcc(u8 *insn) /* Jcc.d32 */
{ {
u8 ret = 0; u8 ret = 0;
@ -69,7 +74,10 @@ static void __ref __static_call_transform(void *insn, enum insn_type type,
emulate = code; emulate = code;
code = &xor5rax; code = &xor5rax;
} }
if (func == &__WARN_trap) {
emulate = code;
code = &warninsn;
}
break; break;
case NOP: case NOP:
@ -128,7 +136,8 @@ static void __static_call_validate(u8 *insn, bool tail, bool tramp)
} else { } else {
if (opcode == CALL_INSN_OPCODE || if (opcode == CALL_INSN_OPCODE ||
!memcmp(insn, x86_nops[5], 5) || !memcmp(insn, x86_nops[5], 5) ||
!memcmp(insn, xor5rax, 5)) !memcmp(insn, xor5rax, 5) ||
!memcmp(insn, warninsn, 5))
return; return;
} }

View File

@ -31,6 +31,7 @@
#include <linux/kexec.h> #include <linux/kexec.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/sched/task_stack.h> #include <linux/sched/task_stack.h>
#include <linux/static_call.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/bug.h> #include <linux/bug.h>
@ -102,25 +103,37 @@ __always_inline int is_valid_bugaddr(unsigned long addr)
* UBSan{0}: 67 0f b9 00 ud1 (%eax),%eax * UBSan{0}: 67 0f b9 00 ud1 (%eax),%eax
* UBSan{10}: 67 0f b9 40 10 ud1 0x10(%eax),%eax * UBSan{10}: 67 0f b9 40 10 ud1 0x10(%eax),%eax
* static_call: 0f b9 cc ud1 %esp,%ecx * static_call: 0f b9 cc ud1 %esp,%ecx
* __WARN_trap: 67 48 0f b9 3a ud1 (%edx),%reg
* *
* Notably UBSAN uses EAX, static_call uses ECX. * Notable, since __WARN_trap can use all registers, the distinction between
* UD1 users is through R/M.
*/ */
__always_inline int decode_bug(unsigned long addr, s32 *imm, int *len) __always_inline int decode_bug(unsigned long addr, s32 *imm, int *len)
{ {
unsigned long start = addr; unsigned long start = addr;
u8 v, reg, rm, rex = 0;
int type = BUG_UD1;
bool lock = false; bool lock = false;
u8 v;
if (addr < TASK_SIZE_MAX) if (addr < TASK_SIZE_MAX)
return BUG_NONE; return BUG_NONE;
v = *(u8 *)(addr++); for (;;) {
if (v == INSN_ASOP)
v = *(u8 *)(addr++); v = *(u8 *)(addr++);
if (v == INSN_ASOP)
continue;
if (v == INSN_LOCK) { if (v == INSN_LOCK) {
lock = true; lock = true;
v = *(u8 *)(addr++); continue;
}
if ((v & 0xf0) == 0x40) {
rex = v;
continue;
}
break;
} }
switch (v) { switch (v) {
@ -156,18 +169,33 @@ __always_inline int decode_bug(unsigned long addr, s32 *imm, int *len)
if (X86_MODRM_MOD(v) != 3 && X86_MODRM_RM(v) == 4) if (X86_MODRM_MOD(v) != 3 && X86_MODRM_RM(v) == 4)
addr++; /* SIB */ addr++; /* SIB */
reg = X86_MODRM_REG(v) + 8*!!X86_REX_R(rex);
rm = X86_MODRM_RM(v) + 8*!!X86_REX_B(rex);
/* Decode immediate, if present */ /* Decode immediate, if present */
switch (X86_MODRM_MOD(v)) { switch (X86_MODRM_MOD(v)) {
case 0: if (X86_MODRM_RM(v) == 5) case 0: if (X86_MODRM_RM(v) == 5)
addr += 4; /* RIP + disp32 */ addr += 4; /* RIP + disp32 */
if (rm == 0) /* (%eax) */
type = BUG_UD1_UBSAN;
if (rm == 2) { /* (%edx) */
*imm = reg;
type = BUG_UD1_WARN;
}
break; break;
case 1: *imm = *(s8 *)addr; case 1: *imm = *(s8 *)addr;
addr += 1; addr += 1;
if (rm == 0) /* (%eax) */
type = BUG_UD1_UBSAN;
break; break;
case 2: *imm = *(s32 *)addr; case 2: *imm = *(s32 *)addr;
addr += 4; addr += 4;
if (rm == 0) /* (%eax) */
type = BUG_UD1_UBSAN;
break; break;
case 3: break; case 3: break;
@ -176,12 +204,76 @@ __always_inline int decode_bug(unsigned long addr, s32 *imm, int *len)
/* record instruction length */ /* record instruction length */
*len = addr - start; *len = addr - start;
if (X86_MODRM_REG(v) == 0) /* EAX */ return type;
return BUG_UD1_UBSAN;
return BUG_UD1;
} }
static inline unsigned long pt_regs_val(struct pt_regs *regs, int nr)
{
int offset = pt_regs_offset(regs, nr);
if (WARN_ON_ONCE(offset < -0))
return 0;
return *((unsigned long *)((void *)regs + offset));
}
#ifdef HAVE_ARCH_BUG_FORMAT_ARGS
DEFINE_STATIC_CALL(WARN_trap, __WARN_trap);
EXPORT_STATIC_CALL_TRAMP(WARN_trap);
/*
* Create a va_list from an exception context.
*/
void *__warn_args(struct arch_va_list *args, struct pt_regs *regs)
{
/*
* Register save area; populate with function call argument registers
*/
args->regs[0] = regs->di;
args->regs[1] = regs->si;
args->regs[2] = regs->dx;
args->regs[3] = regs->cx;
args->regs[4] = regs->r8;
args->regs[5] = regs->r9;
/*
* From the ABI document:
*
* @gp_offset - the element holds the offset in bytes from
* reg_save_area to the place where the next available general purpose
* argument register is saved. In case all argument registers have
* been exhausted, it is set to the value 48 (6*8).
*
* @fp_offset - the element holds the offset in bytes from
* reg_save_area to the place where the next available floating point
* argument is saved. In case all argument registers have been
* exhausted, it is set to the value 176 (6*8 + 8*16)
*
* @overflow_arg_area - this pointer is used to fetch arguments passed
* on the stack. It is initialized with the address of the first
* argument passed on the stack, if any, and then always updated to
* point to the start of the next argument on the stack.
*
* @reg_save_area - the element points to the start of the register
* save area.
*
* Notably the vararg starts with the second argument and there are no
* floating point arguments in the kernel.
*/
args->args.gp_offset = 1*8;
args->args.fp_offset = 6*8 + 8*16;
args->args.reg_save_area = &args->regs;
args->args.overflow_arg_area = (void *)regs->sp;
/*
* If the exception came from __WARN_trap, there is a return
* address on the stack, skip that. This is why any __WARN_trap()
* caller must inhibit tail-call optimization.
*/
if ((void *)regs->ip == &__WARN_trap)
args->args.overflow_arg_area += 8;
return &args->args;
}
#endif /* HAVE_ARCH_BUG_FORMAT */
static nokprobe_inline int static nokprobe_inline int
do_trap_no_signal(struct task_struct *tsk, int trapnr, const char *str, do_trap_no_signal(struct task_struct *tsk, int trapnr, const char *str,
@ -334,6 +426,11 @@ static noinstr bool handle_bug(struct pt_regs *regs)
raw_local_irq_enable(); raw_local_irq_enable();
switch (ud_type) { switch (ud_type) {
case BUG_UD1_WARN:
if (report_bug_entry((void *)pt_regs_val(regs, ud_imm), regs) == BUG_TRAP_TYPE_WARN)
handled = true;
break;
case BUG_UD2: case BUG_UD2:
if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) { if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) {
handled = true; handled = true;

View File

@ -13,10 +13,19 @@
#define BUGFLAG_ONCE (1 << 1) #define BUGFLAG_ONCE (1 << 1)
#define BUGFLAG_DONE (1 << 2) #define BUGFLAG_DONE (1 << 2)
#define BUGFLAG_NO_CUT_HERE (1 << 3) /* CUT_HERE already sent */ #define BUGFLAG_NO_CUT_HERE (1 << 3) /* CUT_HERE already sent */
#define BUGFLAG_ARGS (1 << 4)
#define BUGFLAG_TAINT(taint) ((taint) << 8) #define BUGFLAG_TAINT(taint) ((taint) << 8)
#define BUG_GET_TAINT(bug) ((bug)->flags >> 8) #define BUG_GET_TAINT(bug) ((bug)->flags >> 8)
#endif #endif
#ifndef WARN_CONDITION_STR
#ifdef CONFIG_DEBUG_BUGVERBOSE_DETAILED
# define WARN_CONDITION_STR(cond_str) "[" cond_str "] "
#else
# define WARN_CONDITION_STR(cond_str)
#endif
#endif /* WARN_CONDITION_STR */
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#include <linux/panic.h> #include <linux/panic.h>
#include <linux/printk.h> #include <linux/printk.h>
@ -29,19 +38,20 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
#ifdef CONFIG_BUG #ifdef CONFIG_BUG
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
#define BUG_REL(type, name) type name
#else
#define BUG_REL(type, name) signed int name##_disp
#endif
#ifdef CONFIG_GENERIC_BUG #ifdef CONFIG_GENERIC_BUG
struct bug_entry { struct bug_entry {
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS BUG_REL(unsigned long, bug_addr);
unsigned long bug_addr; #ifdef HAVE_ARCH_BUG_FORMAT
#else BUG_REL(const char *, format);
signed int bug_addr_disp;
#endif #endif
#ifdef CONFIG_DEBUG_BUGVERBOSE #ifdef CONFIG_DEBUG_BUGVERBOSE
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS BUG_REL(const char *, file);
const char *file;
#else
signed int file_disp;
#endif
unsigned short line; unsigned short line;
#endif #endif
unsigned short flags; unsigned short flags;
@ -92,28 +102,50 @@ void warn_slowpath_fmt(const char *file, const int line, unsigned taint,
const char *fmt, ...); const char *fmt, ...);
extern __printf(1, 2) void __warn_printk(const char *fmt, ...); extern __printf(1, 2) void __warn_printk(const char *fmt, ...);
#ifndef __WARN_FLAGS #ifdef __WARN_FLAGS
#define __WARN() __WARN_printf(TAINT_WARN, NULL) #define __WARN() __WARN_FLAGS("", BUGFLAG_TAINT(TAINT_WARN))
#ifndef WARN_ON
#define WARN_ON(condition) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN_FLAGS(#condition, \
BUGFLAG_TAINT(TAINT_WARN)); \
unlikely(__ret_warn_on); \
})
#endif
#ifndef WARN_ON_ONCE
#define WARN_ON_ONCE(condition) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN_FLAGS(#condition, \
BUGFLAG_ONCE | \
BUGFLAG_TAINT(TAINT_WARN)); \
unlikely(__ret_warn_on); \
})
#endif
#endif /* __WARN_FLAGS */
#if defined(__WARN_FLAGS) && !defined(__WARN_printf)
#define __WARN_printf(taint, arg...) do { \
instrumentation_begin(); \
__warn_printk(arg); \
__WARN_FLAGS("", BUGFLAG_NO_CUT_HERE | BUGFLAG_TAINT(taint));\
instrumentation_end(); \
} while (0)
#endif
#ifndef __WARN_printf
#define __WARN_printf(taint, arg...) do { \ #define __WARN_printf(taint, arg...) do { \
instrumentation_begin(); \ instrumentation_begin(); \
warn_slowpath_fmt(__FILE__, __LINE__, taint, arg); \ warn_slowpath_fmt(__FILE__, __LINE__, taint, arg); \
instrumentation_end(); \ instrumentation_end(); \
} while (0) } while (0)
#else #endif
#define __WARN() __WARN_FLAGS(BUGFLAG_TAINT(TAINT_WARN))
#define __WARN_printf(taint, arg...) do { \ #ifndef __WARN
instrumentation_begin(); \ #define __WARN() __WARN_printf(TAINT_WARN, NULL)
__warn_printk(arg); \
__WARN_FLAGS(BUGFLAG_NO_CUT_HERE | BUGFLAG_TAINT(taint));\
instrumentation_end(); \
} while (0)
#define WARN_ON_ONCE(condition) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN_FLAGS(BUGFLAG_ONCE | \
BUGFLAG_TAINT(TAINT_WARN)); \
unlikely(__ret_warn_on); \
})
#endif #endif
/* used internally by panic.c */ /* used internally by panic.c */
@ -148,8 +180,10 @@ extern __printf(1, 2) void __warn_printk(const char *fmt, ...);
DO_ONCE_LITE_IF(condition, WARN_ON, 1) DO_ONCE_LITE_IF(condition, WARN_ON, 1)
#endif #endif
#ifndef WARN_ONCE
#define WARN_ONCE(condition, format...) \ #define WARN_ONCE(condition, format...) \
DO_ONCE_LITE_IF(condition, WARN, 1, format) DO_ONCE_LITE_IF(condition, WARN, 1, format)
#endif
#define WARN_TAINT_ONCE(condition, taint, format...) \ #define WARN_TAINT_ONCE(condition, taint, format...) \
DO_ONCE_LITE_IF(condition, WARN_TAINT, 1, taint, format) DO_ONCE_LITE_IF(condition, WARN_TAINT, 1, taint, format)

View File

@ -42,6 +42,7 @@ void bug_get_file_line(struct bug_entry *bug, const char **file,
struct bug_entry *find_bug(unsigned long bugaddr); struct bug_entry *find_bug(unsigned long bugaddr);
enum bug_trap_type report_bug(unsigned long bug_addr, struct pt_regs *regs); enum bug_trap_type report_bug(unsigned long bug_addr, struct pt_regs *regs);
enum bug_trap_type report_bug_entry(struct bug_entry *bug, struct pt_regs *regs);
/* These are defined by the architecture */ /* These are defined by the architecture */
int is_valid_bugaddr(unsigned long addr); int is_valid_bugaddr(unsigned long addr);
@ -62,6 +63,13 @@ static inline enum bug_trap_type report_bug(unsigned long bug_addr,
} }
struct bug_entry; struct bug_entry;
static inline enum bug_trap_type
report_bug_entry(struct bug_entry *bug, struct pt_regs *regs)
{
return BUG_TRAP_TYPE_BUG;
}
static inline void bug_get_file_line(struct bug_entry *bug, const char **file, static inline void bug_get_file_line(struct bug_entry *bug, const char **file,
unsigned int *line) unsigned int *line)
{ {

View File

@ -873,13 +873,15 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
disable_trace_on_warning(); disable_trace_on_warning();
if (file) if (file) {
pr_warn("WARNING: CPU: %d PID: %d at %s:%d %pS\n", pr_warn("WARNING: %s:%d at %pS, CPU#%d: %s/%d\n",
raw_smp_processor_id(), current->pid, file, line, file, line, caller,
caller); raw_smp_processor_id(), current->comm, current->pid);
else } else {
pr_warn("WARNING: CPU: %d PID: %d at %pS\n", pr_warn("WARNING: at %pS, CPU#%d: %s/%d\n",
raw_smp_processor_id(), current->pid, caller); caller,
raw_smp_processor_id(), current->comm, current->pid);
}
#pragma GCC diagnostic push #pragma GCC diagnostic push
#ifndef __clang__ #ifndef __clang__

View File

@ -206,6 +206,16 @@ config DEBUG_BUGVERBOSE
of the BUG call as well as the EIP and oops trace. This aids of the BUG call as well as the EIP and oops trace. This aids
debugging but costs about 70-100K of memory. debugging but costs about 70-100K of memory.
config DEBUG_BUGVERBOSE_DETAILED
bool "Verbose WARN_ON_ONCE() reporting (adds 100K)" if DEBUG_BUGVERBOSE
help
Say Y here to make WARN_ON_ONCE() output the condition string of the
warning, in addition to the file name and line number.
This helps debugging, but costs about 100K of memory.
Say N if unsure.
endmenu # "printk and dmesg options" endmenu # "printk and dmesg options"
config DEBUG_KERNEL config DEBUG_KERNEL

View File

@ -139,6 +139,29 @@ void bug_get_file_line(struct bug_entry *bug, const char **file,
#endif #endif
} }
static const char *bug_get_format(struct bug_entry *bug)
{
const char *format = NULL;
#ifdef HAVE_ARCH_BUG_FORMAT
#ifdef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
/*
* Allow an architecture to:
* - relative encode NULL (difficult vs KASLR);
* - use a literal 0 (there are no valid objects inside
* the __bug_table itself to refer to after all);
* - use an empty string.
*/
if (bug->format_disp)
format = (const char *)&bug->format_disp + bug->format_disp;
if (format && format[0] == '\0')
format = NULL;
#else
format = bug->format;
#endif
#endif
return format;
}
struct bug_entry *find_bug(unsigned long bugaddr) struct bug_entry *find_bug(unsigned long bugaddr)
{ {
struct bug_entry *bug; struct bug_entry *bug;
@ -150,26 +173,51 @@ struct bug_entry *find_bug(unsigned long bugaddr)
return module_find_bug(bugaddr); return module_find_bug(bugaddr);
} }
static enum bug_trap_type __report_bug(unsigned long bugaddr, struct pt_regs *regs) static void __warn_printf(const char *fmt, struct pt_regs *regs)
{ {
struct bug_entry *bug; if (!fmt)
const char *file; return;
unsigned line, warning, once, done;
if (!is_valid_bugaddr(bugaddr)) #ifdef HAVE_ARCH_BUG_FORMAT_ARGS
return BUG_TRAP_TYPE_NONE; if (regs) {
struct arch_va_list _args;
va_list *args = __warn_args(&_args, regs);
bug = find_bug(bugaddr); if (args) {
if (!bug) vprintk(fmt, *args);
return BUG_TRAP_TYPE_NONE; return;
}
}
#endif
printk("%s", fmt);
}
static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long bugaddr, struct pt_regs *regs)
{
bool warning, once, done, no_cut, has_args;
const char *file, *fmt;
unsigned line;
if (!bug) {
if (!is_valid_bugaddr(bugaddr))
return BUG_TRAP_TYPE_NONE;
bug = find_bug(bugaddr);
if (!bug)
return BUG_TRAP_TYPE_NONE;
}
disable_trace_on_warning(); disable_trace_on_warning();
bug_get_file_line(bug, &file, &line); bug_get_file_line(bug, &file, &line);
fmt = bug_get_format(bug);
warning = (bug->flags & BUGFLAG_WARNING) != 0; warning = bug->flags & BUGFLAG_WARNING;
once = (bug->flags & BUGFLAG_ONCE) != 0; once = bug->flags & BUGFLAG_ONCE;
done = (bug->flags & BUGFLAG_DONE) != 0; done = bug->flags & BUGFLAG_DONE;
no_cut = bug->flags & BUGFLAG_NO_CUT_HERE;
has_args = bug->flags & BUGFLAG_ARGS;
if (warning && once) { if (warning && once) {
if (done) if (done)
@ -187,8 +235,10 @@ static enum bug_trap_type __report_bug(unsigned long bugaddr, struct pt_regs *re
* "cut here" line now. WARN() issues its own "cut here" before the * "cut here" line now. WARN() issues its own "cut here" before the
* extra debugging message it writes before triggering the handler. * extra debugging message it writes before triggering the handler.
*/ */
if ((bug->flags & BUGFLAG_NO_CUT_HERE) == 0) if (!no_cut) {
printk(KERN_DEFAULT CUT_HERE); printk(KERN_DEFAULT CUT_HERE);
__warn_printf(fmt, has_args ? regs : NULL);
}
if (warning) { if (warning) {
/* this is a WARN_ON rather than BUG/BUG_ON */ /* this is a WARN_ON rather than BUG/BUG_ON */
@ -206,13 +256,25 @@ static enum bug_trap_type __report_bug(unsigned long bugaddr, struct pt_regs *re
return BUG_TRAP_TYPE_BUG; return BUG_TRAP_TYPE_BUG;
} }
enum bug_trap_type report_bug_entry(struct bug_entry *bug, struct pt_regs *regs)
{
enum bug_trap_type ret;
bool rcu = false;
rcu = warn_rcu_enter();
ret = __report_bug(bug, 0, regs);
warn_rcu_exit(rcu);
return ret;
}
enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs)
{ {
enum bug_trap_type ret; enum bug_trap_type ret;
bool rcu = false; bool rcu = false;
rcu = warn_rcu_enter(); rcu = warn_rcu_enter();
ret = __report_bug(bugaddr, regs); ret = __report_bug(NULL, bugaddr, regs);
warn_rcu_exit(rcu); warn_rcu_exit(rcu);
return ret; return ret;