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(); \
|
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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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" \
|
||||||
|
|
|
||||||
|
|
@ -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)); \
|
||||||
} \
|
} \
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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))); \
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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__
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
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
|
#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;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue