mirror of https://github.com/torvalds/linux.git
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:
commit
4a26e7032d
|
|
@ -19,7 +19,7 @@
|
|||
unreachable(); \
|
||||
} 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
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
#else
|
||||
#define __BUGVERBOSE_LOCATION(file, line) \
|
||||
.pushsection .rodata.str, "aMS", @progbits, 1; \
|
||||
10002: .string file; \
|
||||
10002: .ascii file "\0"; \
|
||||
.popsection; \
|
||||
\
|
||||
.long 10002b - .; \
|
||||
|
|
@ -20,39 +20,38 @@
|
|||
#endif
|
||||
|
||||
#ifndef CONFIG_GENERIC_BUG
|
||||
#define __BUG_ENTRY(flags)
|
||||
#define __BUG_ENTRY(cond_str, flags)
|
||||
#else
|
||||
#define __BUG_ENTRY(flags) \
|
||||
#define __BUG_ENTRY(cond_str, flags) \
|
||||
.pushsection __bug_table, "aw"; \
|
||||
.align 2; \
|
||||
10000: .long 10001f - .; \
|
||||
_BUGVERBOSE_LOCATION(__FILE__, __LINE__) \
|
||||
.short flags; \
|
||||
_BUGVERBOSE_LOCATION(WARN_CONDITION_STR(cond_str) __FILE__, __LINE__) \
|
||||
.short flags; \
|
||||
.popsection; \
|
||||
10001:
|
||||
#endif
|
||||
|
||||
#define ASM_BUG_FLAGS(flags) \
|
||||
__BUG_ENTRY(flags) \
|
||||
#define ASM_BUG_FLAGS(cond_str, flags) \
|
||||
__BUG_ENTRY(cond_str, flags) \
|
||||
break BRK_BUG;
|
||||
|
||||
#define ASM_BUG() ASM_BUG_FLAGS(0)
|
||||
#define ASM_BUG() ASM_BUG_FLAGS("", 0)
|
||||
|
||||
#define __BUG_FLAGS(flags, extra) \
|
||||
asm_inline volatile (__stringify(ASM_BUG_FLAGS(flags)) \
|
||||
extra);
|
||||
#define __BUG_FLAGS(cond_str, flags, extra) \
|
||||
asm_inline volatile (__stringify(ASM_BUG_FLAGS(cond_str, flags)) extra);
|
||||
|
||||
#define __WARN_FLAGS(flags) \
|
||||
#define __WARN_FLAGS(cond_str, flags) \
|
||||
do { \
|
||||
instrumentation_begin(); \
|
||||
__BUG_FLAGS(BUGFLAG_WARNING|(flags), ANNOTATE_REACHABLE(10001b));\
|
||||
__BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags), ANNOTATE_REACHABLE(10001b));\
|
||||
instrumentation_end(); \
|
||||
} while (0)
|
||||
|
||||
#define BUG() \
|
||||
do { \
|
||||
instrumentation_begin(); \
|
||||
__BUG_FLAGS(0, ""); \
|
||||
__BUG_FLAGS("", 0, ""); \
|
||||
unreachable(); \
|
||||
} while (0)
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_BUGVERBOSE
|
||||
#define __WARN_FLAGS(flags) \
|
||||
#define __WARN_FLAGS(cond_str, flags) \
|
||||
do { \
|
||||
asm volatile("\n" \
|
||||
"1:\t" PARISC_BUG_BREAK_ASM "\n" \
|
||||
|
|
@ -61,12 +61,12 @@
|
|||
"\t.short %1, %2\n" \
|
||||
"\t.blockz %3-2*4-2*2\n" \
|
||||
"\t.popsection" \
|
||||
: : "i" (__FILE__), "i" (__LINE__), \
|
||||
: : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__), \
|
||||
"i" (BUGFLAG_WARNING|(flags)), \
|
||||
"i" (sizeof(struct bug_entry)) ); \
|
||||
} while(0)
|
||||
#else
|
||||
#define __WARN_FLAGS(flags) \
|
||||
#define __WARN_FLAGS(cond_str, flags) \
|
||||
do { \
|
||||
asm volatile("\n" \
|
||||
"1:\t" PARISC_BUG_BREAK_ASM "\n" \
|
||||
|
|
|
|||
|
|
@ -51,11 +51,11 @@
|
|||
".previous\n"
|
||||
#endif
|
||||
|
||||
#define BUG_ENTRY(insn, flags, ...) \
|
||||
#define BUG_ENTRY(cond_str, insn, flags, ...) \
|
||||
__asm__ __volatile__( \
|
||||
"1: " insn "\n" \
|
||||
_EMIT_BUG_ENTRY \
|
||||
: : "i" (__FILE__), "i" (__LINE__), \
|
||||
: : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__), \
|
||||
"i" (flags), \
|
||||
"i" (sizeof(struct bug_entry)), \
|
||||
##__VA_ARGS__)
|
||||
|
|
@ -67,12 +67,12 @@
|
|||
*/
|
||||
|
||||
#define BUG() do { \
|
||||
BUG_ENTRY("twi 31, 0, 0", 0); \
|
||||
BUG_ENTRY("", "twi 31, 0, 0", 0); \
|
||||
unreachable(); \
|
||||
} while (0)
|
||||
#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
|
||||
#define BUG_ON(x) do { \
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
if (x) \
|
||||
BUG(); \
|
||||
} 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)
|
||||
|
||||
|
|
@ -90,7 +90,7 @@
|
|||
if (__ret_warn_on) \
|
||||
__WARN(); \
|
||||
} else { \
|
||||
BUG_ENTRY(PPC_TLNEI " %4, 0", \
|
||||
BUG_ENTRY(#x, PPC_TLNEI " %4, 0", \
|
||||
BUGFLAG_WARNING | BUGFLAG_TAINT(TAINT_WARN), \
|
||||
"r" (__ret_warn_on)); \
|
||||
} \
|
||||
|
|
|
|||
|
|
@ -60,28 +60,28 @@ typedef u32 bug_insn_t;
|
|||
".org 2b + " size "\n\t" \
|
||||
".popsection" \
|
||||
|
||||
#define __BUG_FLAGS(flags) \
|
||||
#define __BUG_FLAGS(cond_str, flags) \
|
||||
do { \
|
||||
__asm__ __volatile__ ( \
|
||||
ARCH_WARN_ASM("%0", "%1", "%2", "%3") \
|
||||
: \
|
||||
: "i" (__FILE__), "i" (__LINE__), \
|
||||
: "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__), \
|
||||
"i" (flags), \
|
||||
"i" (sizeof(struct bug_entry))); \
|
||||
} while (0)
|
||||
|
||||
#else /* CONFIG_GENERIC_BUG */
|
||||
#define __BUG_FLAGS(flags) do { \
|
||||
#define __BUG_FLAGS(cond_str, flags) do { \
|
||||
__asm__ __volatile__ ("ebreak\n"); \
|
||||
} while (0)
|
||||
#endif /* CONFIG_GENERIC_BUG */
|
||||
|
||||
#define BUG() do { \
|
||||
__BUG_FLAGS(0); \
|
||||
__BUG_FLAGS("", 0); \
|
||||
unreachable(); \
|
||||
} 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
|
||||
|
||||
|
|
|
|||
|
|
@ -2,69 +2,55 @@
|
|||
#ifndef _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 { \
|
||||
asm_inline volatile( \
|
||||
"0: mc 0,0\n" \
|
||||
".section .rodata.str,\"aMS\",@progbits,1\n" \
|
||||
"1: .asciz \""__FILE__"\"\n" \
|
||||
".previous\n" \
|
||||
".section __bug_table,\"aw\"\n" \
|
||||
"2: .long 0b-.\n" \
|
||||
" .long 1b-.\n" \
|
||||
" .short %0,%1\n" \
|
||||
" .org 2b+%2\n" \
|
||||
".previous\n" \
|
||||
: : "i" (__LINE__), \
|
||||
"i" (x), \
|
||||
"i" (sizeof(struct bug_entry))); \
|
||||
#define ASM_BUG_FLAGS(cond_str, flags) \
|
||||
__BUG_ENTRY(cond_str, flags) \
|
||||
mc 0,0
|
||||
|
||||
#define ASM_BUG() ASM_BUG_FLAGS("", 0)
|
||||
|
||||
#define __BUG_FLAGS(cond_str, flags) \
|
||||
asm_inline volatile(__stringify(ASM_BUG_FLAGS(cond_str, flags)));
|
||||
|
||||
#define __WARN_FLAGS(cond_str, flags) \
|
||||
do { \
|
||||
__BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags)); \
|
||||
} while (0)
|
||||
|
||||
#else /* CONFIG_DEBUG_BUGVERBOSE */
|
||||
|
||||
#define __EMIT_BUG(x) do { \
|
||||
asm_inline volatile( \
|
||||
"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))); \
|
||||
#define BUG() \
|
||||
do { \
|
||||
__BUG_FLAGS("", 0); \
|
||||
unreachable(); \
|
||||
} 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_WARN_ON
|
||||
#endif /* CONFIG_BUG */
|
||||
|
||||
#include <asm-generic/bug.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -52,14 +52,14 @@ do { \
|
|||
unreachable(); \
|
||||
} while (0)
|
||||
|
||||
#define __WARN_FLAGS(flags) \
|
||||
#define __WARN_FLAGS(cond_str, flags) \
|
||||
do { \
|
||||
__asm__ __volatile__ ( \
|
||||
"1:\t.short %O0\n" \
|
||||
_EMIT_BUG_ENTRY \
|
||||
: \
|
||||
: "n" (TRAPA_BUG_OPCODE), \
|
||||
"i" (__FILE__), \
|
||||
"i" (WARN_CONDITION_STR(cond_str) __FILE__), \
|
||||
"i" (__LINE__), \
|
||||
"i" (BUGFLAG_WARNING|(flags)), \
|
||||
"i" (sizeof(struct bug_entry))); \
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ config GENERIC_CSUM
|
|||
config GENERIC_BUG
|
||||
def_bool y
|
||||
depends on BUG
|
||||
select GENERIC_BUG_RELATIVE_POINTERS if X86_64
|
||||
select GENERIC_BUG_RELATIVE_POINTERS
|
||||
|
||||
config GENERIC_BUG_RELATIVE_POINTERS
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -32,6 +32,14 @@ SYM_FUNC_END(write_ibpb)
|
|||
/* For KVM */
|
||||
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
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@
|
|||
#include <linux/objtool.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().
|
||||
*/
|
||||
|
|
@ -31,53 +36,77 @@
|
|||
#define BUG_UD2 0xfffe
|
||||
#define BUG_UD1 0xfffd
|
||||
#define BUG_UD1_UBSAN 0xfffc
|
||||
#define BUG_UD1_WARN 0xfffb
|
||||
#define BUG_UDB 0xffd6
|
||||
#define BUG_LOCK 0xfff0
|
||||
|
||||
#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
|
||||
#define __BUG_ENTRY(file, line, flags) \
|
||||
"2:\t" __BUG_REL("1b") "\t# bug_entry::bug_addr\n" \
|
||||
"\t" __BUG_REL(file) "\t# bug_entry::file\n" \
|
||||
"\t.word " line "\t# bug_entry::line\n" \
|
||||
"\t.word " flags "\t# bug_entry::flags\n"
|
||||
#define __BUG_ENTRY_VERBOSE(file, line) \
|
||||
"\t.long " file " - .\t# bug_entry::file\n" \
|
||||
"\t.word " line "\t# bug_entry::line\n"
|
||||
#else
|
||||
#define __BUG_ENTRY(file, line, flags) \
|
||||
"2:\t" __BUG_REL("1b") "\t# bug_entry::bug_addr\n" \
|
||||
"\t.word " flags "\t# bug_entry::flags\n"
|
||||
#define __BUG_ENTRY_VERBOSE(file, line)
|
||||
#endif
|
||||
|
||||
#define _BUG_FLAGS_ASM(ins, file, line, flags, size, extra) \
|
||||
"1:\t" ins "\n" \
|
||||
".pushsection __bug_table,\"aw\"\n" \
|
||||
#if defined(CONFIG_X86_64) || defined(CONFIG_DEBUG_BUGVERBOSE_DETAILED)
|
||||
#define HAVE_ARCH_BUG_FORMAT
|
||||
#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 \
|
||||
__BUG_ENTRY(file, line, flags) \
|
||||
"2:\n\t" \
|
||||
__BUG_ENTRY(format, file, line, flags) \
|
||||
"\t.org 2b + " size "\n" \
|
||||
".popsection\n" \
|
||||
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 { \
|
||||
asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c0", \
|
||||
"%c1", "%c2", "%c3", extra) \
|
||||
: : "i" (__FILE__), "i" (__LINE__), \
|
||||
"i" (flags), \
|
||||
"i" (sizeof(struct bug_entry))); \
|
||||
asm_inline volatile("1:\t" ins "\n" \
|
||||
_BUG_FLAGS_ASM("%c[fmt]", "%c[file]", \
|
||||
"%c[line]", "%c[fl]", \
|
||||
"%c[size]", extra) \
|
||||
: : [fmt] "i" (WARN_CONDITION_STR(cond_str)), \
|
||||
[file] "i" (__FILE__), \
|
||||
[line] "i" (__LINE__), \
|
||||
[fl] "i" (flags), \
|
||||
[size] "i" (sizeof(struct bug_entry))); \
|
||||
} while (0)
|
||||
|
||||
#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
|
||||
|
||||
#define _BUG_FLAGS(ins, flags, extra) asm volatile(ins)
|
||||
#define _BUG_FLAGS(cond_str, ins, flags, extra) asm volatile(ins)
|
||||
|
||||
#endif /* CONFIG_GENERIC_BUG */
|
||||
|
||||
|
|
@ -85,7 +114,7 @@ do { \
|
|||
#define BUG() \
|
||||
do { \
|
||||
instrumentation_begin(); \
|
||||
_BUG_FLAGS(ASM_UD2, 0, ""); \
|
||||
_BUG_FLAGS("", ASM_UD2, 0, ""); \
|
||||
__builtin_unreachable(); \
|
||||
} while (0)
|
||||
|
||||
|
|
@ -98,14 +127,69 @@ do { \
|
|||
|
||||
#define ARCH_WARN_REACHABLE ANNOTATE_REACHABLE(1b)
|
||||
|
||||
#define __WARN_FLAGS(flags) \
|
||||
do { \
|
||||
__auto_type __flags = BUGFLAG_WARNING|(flags); \
|
||||
instrumentation_begin(); \
|
||||
_BUG_FLAGS(ASM_UD2, __flags, ARCH_WARN_REACHABLE); \
|
||||
instrumentation_end(); \
|
||||
#define __WARN_FLAGS(cond_str, flags) \
|
||||
do { \
|
||||
__auto_type __flags = BUGFLAG_WARNING|(flags); \
|
||||
instrumentation_begin(); \
|
||||
_BUG_FLAGS(cond_str, ASM_UD2, __flags, ARCH_WARN_REACHABLE); \
|
||||
instrumentation_end(); \
|
||||
} 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>
|
||||
|
||||
#endif /* _ASM_X86_BUG_H */
|
||||
|
|
|
|||
|
|
@ -26,6 +26,11 @@ static const u8 xor5rax[] = { 0x2e, 0x2e, 0x2e, 0x31, 0xc0 };
|
|||
|
||||
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 */
|
||||
{
|
||||
u8 ret = 0;
|
||||
|
|
@ -69,7 +74,10 @@ static void __ref __static_call_transform(void *insn, enum insn_type type,
|
|||
emulate = code;
|
||||
code = &xor5rax;
|
||||
}
|
||||
|
||||
if (func == &__WARN_trap) {
|
||||
emulate = code;
|
||||
code = &warninsn;
|
||||
}
|
||||
break;
|
||||
|
||||
case NOP:
|
||||
|
|
@ -128,7 +136,8 @@ static void __static_call_validate(u8 *insn, bool tail, bool tramp)
|
|||
} else {
|
||||
if (opcode == CALL_INSN_OPCODE ||
|
||||
!memcmp(insn, x86_nops[5], 5) ||
|
||||
!memcmp(insn, xor5rax, 5))
|
||||
!memcmp(insn, xor5rax, 5) ||
|
||||
!memcmp(insn, warninsn, 5))
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/kexec.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/task_stack.h>
|
||||
#include <linux/static_call.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/init.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{10}: 67 0f b9 40 10 ud1 0x10(%eax),%eax
|
||||
* 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)
|
||||
{
|
||||
unsigned long start = addr;
|
||||
u8 v, reg, rm, rex = 0;
|
||||
int type = BUG_UD1;
|
||||
bool lock = false;
|
||||
u8 v;
|
||||
|
||||
if (addr < TASK_SIZE_MAX)
|
||||
return BUG_NONE;
|
||||
|
||||
v = *(u8 *)(addr++);
|
||||
if (v == INSN_ASOP)
|
||||
for (;;) {
|
||||
v = *(u8 *)(addr++);
|
||||
if (v == INSN_ASOP)
|
||||
continue;
|
||||
|
||||
if (v == INSN_LOCK) {
|
||||
lock = true;
|
||||
v = *(u8 *)(addr++);
|
||||
if (v == INSN_LOCK) {
|
||||
lock = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((v & 0xf0) == 0x40) {
|
||||
rex = v;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
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)
|
||||
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 */
|
||||
switch (X86_MODRM_MOD(v)) {
|
||||
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;
|
||||
|
||||
case 1: *imm = *(s8 *)addr;
|
||||
addr += 1;
|
||||
if (rm == 0) /* (%eax) */
|
||||
type = BUG_UD1_UBSAN;
|
||||
break;
|
||||
|
||||
case 2: *imm = *(s32 *)addr;
|
||||
addr += 4;
|
||||
if (rm == 0) /* (%eax) */
|
||||
type = BUG_UD1_UBSAN;
|
||||
break;
|
||||
|
||||
case 3: break;
|
||||
|
|
@ -176,12 +204,76 @@ __always_inline int decode_bug(unsigned long addr, s32 *imm, int *len)
|
|||
/* record instruction length */
|
||||
*len = addr - start;
|
||||
|
||||
if (X86_MODRM_REG(v) == 0) /* EAX */
|
||||
return BUG_UD1_UBSAN;
|
||||
|
||||
return BUG_UD1;
|
||||
return type;
|
||||
}
|
||||
|
||||
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
|
||||
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();
|
||||
|
||||
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:
|
||||
if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) {
|
||||
handled = true;
|
||||
|
|
|
|||
|
|
@ -13,10 +13,19 @@
|
|||
#define BUGFLAG_ONCE (1 << 1)
|
||||
#define BUGFLAG_DONE (1 << 2)
|
||||
#define BUGFLAG_NO_CUT_HERE (1 << 3) /* CUT_HERE already sent */
|
||||
#define BUGFLAG_ARGS (1 << 4)
|
||||
#define BUGFLAG_TAINT(taint) ((taint) << 8)
|
||||
#define BUG_GET_TAINT(bug) ((bug)->flags >> 8)
|
||||
#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__
|
||||
#include <linux/panic.h>
|
||||
#include <linux/printk.h>
|
||||
|
|
@ -29,19 +38,20 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
|
|||
|
||||
#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
|
||||
struct bug_entry {
|
||||
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
|
||||
unsigned long bug_addr;
|
||||
#else
|
||||
signed int bug_addr_disp;
|
||||
BUG_REL(unsigned long, bug_addr);
|
||||
#ifdef HAVE_ARCH_BUG_FORMAT
|
||||
BUG_REL(const char *, format);
|
||||
#endif
|
||||
#ifdef CONFIG_DEBUG_BUGVERBOSE
|
||||
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
|
||||
const char *file;
|
||||
#else
|
||||
signed int file_disp;
|
||||
#endif
|
||||
BUG_REL(const char *, file);
|
||||
unsigned short line;
|
||||
#endif
|
||||
unsigned short flags;
|
||||
|
|
@ -92,28 +102,50 @@ void warn_slowpath_fmt(const char *file, const int line, unsigned taint,
|
|||
const char *fmt, ...);
|
||||
extern __printf(1, 2) void __warn_printk(const char *fmt, ...);
|
||||
|
||||
#ifndef __WARN_FLAGS
|
||||
#define __WARN() __WARN_printf(TAINT_WARN, NULL)
|
||||
#ifdef __WARN_FLAGS
|
||||
#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 { \
|
||||
instrumentation_begin(); \
|
||||
warn_slowpath_fmt(__FILE__, __LINE__, taint, arg); \
|
||||
instrumentation_end(); \
|
||||
} while (0)
|
||||
#else
|
||||
#define __WARN() __WARN_FLAGS(BUGFLAG_TAINT(TAINT_WARN))
|
||||
#define __WARN_printf(taint, arg...) do { \
|
||||
instrumentation_begin(); \
|
||||
__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
|
||||
|
||||
#ifndef __WARN
|
||||
#define __WARN() __WARN_printf(TAINT_WARN, NULL)
|
||||
#endif
|
||||
|
||||
/* 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)
|
||||
#endif
|
||||
|
||||
#ifndef WARN_ONCE
|
||||
#define WARN_ONCE(condition, format...) \
|
||||
DO_ONCE_LITE_IF(condition, WARN, 1, format)
|
||||
#endif
|
||||
|
||||
#define WARN_TAINT_ONCE(condition, taint, format...) \
|
||||
DO_ONCE_LITE_IF(condition, WARN_TAINT, 1, taint, format)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ void bug_get_file_line(struct bug_entry *bug, const char **file,
|
|||
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_entry(struct bug_entry *bug, struct pt_regs *regs);
|
||||
|
||||
/* These are defined by the architecture */
|
||||
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;
|
||||
|
||||
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,
|
||||
unsigned int *line)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -873,13 +873,15 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
|
|||
|
||||
disable_trace_on_warning();
|
||||
|
||||
if (file)
|
||||
pr_warn("WARNING: CPU: %d PID: %d at %s:%d %pS\n",
|
||||
raw_smp_processor_id(), current->pid, file, line,
|
||||
caller);
|
||||
else
|
||||
pr_warn("WARNING: CPU: %d PID: %d at %pS\n",
|
||||
raw_smp_processor_id(), current->pid, caller);
|
||||
if (file) {
|
||||
pr_warn("WARNING: %s:%d at %pS, CPU#%d: %s/%d\n",
|
||||
file, line, caller,
|
||||
raw_smp_processor_id(), current->comm, current->pid);
|
||||
} else {
|
||||
pr_warn("WARNING: at %pS, CPU#%d: %s/%d\n",
|
||||
caller,
|
||||
raw_smp_processor_id(), current->comm, current->pid);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#ifndef __clang__
|
||||
|
|
|
|||
|
|
@ -206,6 +206,16 @@ config DEBUG_BUGVERBOSE
|
|||
of the BUG call as well as the EIP and oops trace. This aids
|
||||
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"
|
||||
|
||||
config DEBUG_KERNEL
|
||||
|
|
|
|||
90
lib/bug.c
90
lib/bug.c
|
|
@ -139,6 +139,29 @@ void bug_get_file_line(struct bug_entry *bug, const char **file,
|
|||
#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 *bug;
|
||||
|
|
@ -150,26 +173,51 @@ struct bug_entry *find_bug(unsigned long 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;
|
||||
const char *file;
|
||||
unsigned line, warning, once, done;
|
||||
if (!fmt)
|
||||
return;
|
||||
|
||||
if (!is_valid_bugaddr(bugaddr))
|
||||
return BUG_TRAP_TYPE_NONE;
|
||||
#ifdef HAVE_ARCH_BUG_FORMAT_ARGS
|
||||
if (regs) {
|
||||
struct arch_va_list _args;
|
||||
va_list *args = __warn_args(&_args, regs);
|
||||
|
||||
bug = find_bug(bugaddr);
|
||||
if (!bug)
|
||||
return BUG_TRAP_TYPE_NONE;
|
||||
if (args) {
|
||||
vprintk(fmt, *args);
|
||||
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();
|
||||
|
||||
bug_get_file_line(bug, &file, &line);
|
||||
fmt = bug_get_format(bug);
|
||||
|
||||
warning = (bug->flags & BUGFLAG_WARNING) != 0;
|
||||
once = (bug->flags & BUGFLAG_ONCE) != 0;
|
||||
done = (bug->flags & BUGFLAG_DONE) != 0;
|
||||
warning = bug->flags & BUGFLAG_WARNING;
|
||||
once = bug->flags & BUGFLAG_ONCE;
|
||||
done = bug->flags & BUGFLAG_DONE;
|
||||
no_cut = bug->flags & BUGFLAG_NO_CUT_HERE;
|
||||
has_args = bug->flags & BUGFLAG_ARGS;
|
||||
|
||||
if (warning && once) {
|
||||
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
|
||||
* 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);
|
||||
__warn_printf(fmt, has_args ? regs : NULL);
|
||||
}
|
||||
|
||||
if (warning) {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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 ret;
|
||||
bool rcu = false;
|
||||
|
||||
rcu = warn_rcu_enter();
|
||||
ret = __report_bug(bugaddr, regs);
|
||||
ret = __report_bug(NULL, bugaddr, regs);
|
||||
warn_rcu_exit(rcu);
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
Loading…
Reference in New Issue