* [PATCH v2 00/12] x86: WARN() hackery
@ 2025-11-10 11:46 Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 01/12] x86: Rework __bug_table helpers Peter Zijlstra
` (12 more replies)
0 siblings, 13 replies; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-10 11:46 UTC (permalink / raw)
To: x86
Cc: linux-kernel, peterz, kees, acarmina, jpoimboe, mark.rutland,
torvalds, maciej.wieczor-retman
Hi!
I was going through my patch cabinet the other day and found these patches.
Mark was going to see if he could make ARM64 play ball to see if the generic
bits really are that. But clearly he's forgotten about things just as much as I
had.
We should probably just merge these, as they improbe the code-gen for WARN()
significantly on x86 and then I'll help Mark out for ARM64 when he gets around
to it and needs a hand.
Freshly rebased and somewhat tested.
As a quick reminder, this makes WARN(), which has a vararg printf like
arguments work with the #UD based WARN infra by hiding the format in the
bug_table (its a constant after all) and replacing this first argument with the
address of the bug-table entry while making the actual function that's called a
UD1 instruction.
The exception handler then create a va_list from pt_regs and vprintk() does the
rest.
Also here:
git://git.kernel.org/pub/scm/linux/kernel/git/peterz/queue.git x86/bugs
v1: https://lkml.kernel.org/r/20250607094224.104791182@infradead.org
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 01/12] x86: Rework __bug_table helpers
2025-11-10 11:46 [PATCH v2 00/12] x86: WARN() hackery Peter Zijlstra
@ 2025-11-10 11:46 ` Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 02/12] bug: Add BUG_FORMAT infrastructure Peter Zijlstra
` (11 subsequent siblings)
12 siblings, 0 replies; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-10 11:46 UTC (permalink / raw)
To: x86
Cc: linux-kernel, peterz, kees, acarmina, jpoimboe, mark.rutland,
torvalds, maciej.wieczor-retman
Rework the __bug_table helpers such that extension becomes easier.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
arch/x86/include/asm/bug.h | 37 ++++++++++++++++++++-----------------
1 file changed, 20 insertions(+), 17 deletions(-)
--- a/arch/x86/include/asm/bug.h
+++ b/arch/x86/include/asm/bug.h
@@ -37,39 +37,42 @@
#ifdef CONFIG_GENERIC_BUG
#ifdef CONFIG_X86_32
-# define __BUG_REL(val) ".long " val
+#define __BUG_REL(val) ".long " val
#else
-# define __BUG_REL(val) ".long " val " - ."
+#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" \
+#define __BUG_ENTRY_VERBOSE(file, line) \
"\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"
+ "\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_ENTRY(file, line, flags) \
+ __BUG_REL("1b") "\t# bug_entry::bug_addr\n" \
+ __BUG_ENTRY_VERBOSE(file, line) \
+ "\t.word " flags "\t# bug_entry::flags\n"
+
#define _BUG_FLAGS_ASM(ins, file, line, flags, size, extra) \
"1:\t" ins "\n" \
- ".pushsection __bug_table,\"aw\"\n" \
+ ".pushsection __bug_table,\"aw\"\n\t" \
ANNOTATE_DATA_SPECIAL \
+ "2:\n\t" \
__BUG_ENTRY(file, line, flags) \
"\t.org 2b + " size "\n" \
".popsection\n" \
extra
-#define _BUG_FLAGS(cond_str, ins, flags, extra) \
-do { \
- asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c0", \
- "%c1", "%c2", "%c3", extra) \
- : : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__), \
- "i" (flags), \
- "i" (sizeof(struct bug_entry))); \
+#define _BUG_FLAGS(cond_str, ins, flags, extra) \
+do { \
+ asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c0", \
+ "%c1", "%c2", "%c3", extra) \
+ : : "i" (WARN_CONDITION_STR(cond_str) __FILE__), \
+ "i" (__LINE__), \
+ "i" (flags), \
+ "i" (sizeof(struct bug_entry))); \
} while (0)
#define ARCH_WARN_ASM(file, line, flags, size) \
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 02/12] bug: Add BUG_FORMAT infrastructure
2025-11-10 11:46 [PATCH v2 00/12] x86: WARN() hackery Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 01/12] x86: Rework __bug_table helpers Peter Zijlstra
@ 2025-11-10 11:46 ` Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 03/12] bug: Clean up CONFIG_GENERIC_BUG_RELATIVE_POINTERS Peter Zijlstra
` (10 subsequent siblings)
12 siblings, 0 replies; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-10 11:46 UTC (permalink / raw)
To: x86
Cc: linux-kernel, peterz, kees, acarmina, jpoimboe, mark.rutland,
torvalds, maciej.wieczor-retman
Add BUG_FORMAT; an architecture opt-in feature that allows adding the
WARN_printf() format string to the bug_entry table.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
include/asm-generic/bug.h | 7 +++++++
lib/bug.c | 39 ++++++++++++++++++++++++++++++++-------
2 files changed, 39 insertions(+), 7 deletions(-)
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -36,6 +36,13 @@ struct bug_entry {
#else
signed int bug_addr_disp;
#endif
+#ifdef HAVE_ARCH_BUG_FORMAT
+#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
+ const char *format;
+#else
+ signed int format_disp;
+#endif
+#endif
#ifdef CONFIG_DEBUG_BUGVERBOSE
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
const char *file;
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -139,6 +139,19 @@ void bug_get_file_line(struct bug_entry
#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
+ format = (const char *)&bug->format_disp + bug->format_disp;
+#else
+ format = bug->format;
+#endif
+#endif
+ return format;
+}
+
struct bug_entry *find_bug(unsigned long bugaddr)
{
struct bug_entry *bug;
@@ -150,11 +163,19 @@ struct bug_entry *find_bug(unsigned long
return module_find_bug(bugaddr);
}
+static void __warn_printf(const char *fmt)
+{
+ if (!fmt)
+ return;
+
+ printk("%s", fmt);
+}
+
static enum bug_trap_type __report_bug(unsigned long bugaddr, struct pt_regs *regs)
{
- struct bug_entry *bug;
- const char *file;
- unsigned line, warning, once, done;
+ bool warning, once, done, no_cut, has_args;
+ const char *file, *fmt;
+ unsigned line;
if (!is_valid_bugaddr(bugaddr))
return BUG_TRAP_TYPE_NONE;
@@ -166,10 +187,12 @@ static enum bug_trap_type __report_bug(u
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;
if (warning && once) {
if (done)
@@ -187,8 +210,10 @@ static enum bug_trap_type __report_bug(u
* "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);
+ }
if (warning) {
/* this is a WARN_ON rather than BUG/BUG_ON */
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 03/12] bug: Clean up CONFIG_GENERIC_BUG_RELATIVE_POINTERS
2025-11-10 11:46 [PATCH v2 00/12] x86: WARN() hackery Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 01/12] x86: Rework __bug_table helpers Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 02/12] bug: Add BUG_FORMAT infrastructure Peter Zijlstra
@ 2025-11-10 11:46 ` Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 04/12] bug: Add BUG_FORMAT_ARGS infrastructure Peter Zijlstra
` (9 subsequent siblings)
12 siblings, 0 replies; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-10 11:46 UTC (permalink / raw)
To: x86
Cc: linux-kernel, peterz, kees, acarmina, jpoimboe, mark.rutland,
torvalds, maciej.wieczor-retman
Three repeated CONFIG_GENERIC_BUG_RELATIVE_POINTERS #ifdefs right
after one another yields unreadable code. Add a helper.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
include/asm-generic/bug.h | 22 ++++++++--------------
1 file changed, 8 insertions(+), 14 deletions(-)
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -30,26 +30,20 @@ void __warn(const char *file, int line,
#ifdef CONFIG_BUG
-#ifdef CONFIG_GENERIC_BUG
-struct bug_entry {
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
- unsigned long bug_addr;
+#define BUG_REL(type, name) type name
#else
- signed int bug_addr_disp;
+#define BUG_REL(type, name) signed int name##_disp
#endif
+
+#ifdef CONFIG_GENERIC_BUG
+struct bug_entry {
+ BUG_REL(unsigned long, bug_addr);
#ifdef HAVE_ARCH_BUG_FORMAT
-#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
- const char *format;
-#else
- signed int format_disp;
-#endif
+ 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;
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 04/12] bug: Add BUG_FORMAT_ARGS infrastructure
2025-11-10 11:46 [PATCH v2 00/12] x86: WARN() hackery Peter Zijlstra
` (2 preceding siblings ...)
2025-11-10 11:46 ` [PATCH v2 03/12] bug: Clean up CONFIG_GENERIC_BUG_RELATIVE_POINTERS Peter Zijlstra
@ 2025-11-10 11:46 ` Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 05/12] bug: Add report_bug_entry() Peter Zijlstra
` (8 subsequent siblings)
12 siblings, 0 replies; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-10 11:46 UTC (permalink / raw)
To: x86
Cc: linux-kernel, peterz, kees, acarmina, jpoimboe, mark.rutland,
torvalds, maciej.wieczor-retman
Add BUG_FORMAT_ARGS; when an architecture is able to provide a va_list
given pt_regs, use this to print format arguments.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
include/asm-generic/bug.h | 1 +
lib/bug.c | 17 +++++++++++++++--
2 files changed, 16 insertions(+), 2 deletions(-)
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -13,6 +13,7 @@
#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
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -163,11 +163,23 @@ struct bug_entry *find_bug(unsigned long
return module_find_bug(bugaddr);
}
-static void __warn_printf(const char *fmt)
+static void __warn_printf(const char *fmt, struct pt_regs *regs)
{
if (!fmt)
return;
+#ifdef HAVE_ARCH_BUG_FORMAT_ARGS
+ if (regs) {
+ struct arch_va_list _args;
+ va_list *args = __warn_args(&_args, regs);
+
+ if (args) {
+ vprintk(fmt, *args);
+ return;
+ }
+ }
+#endif
+
printk("%s", fmt);
}
@@ -193,6 +205,7 @@ static enum bug_trap_type __report_bug(u
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)
@@ -212,7 +225,7 @@ static enum bug_trap_type __report_bug(u
*/
if (!no_cut) {
printk(KERN_DEFAULT CUT_HERE);
- __warn_printf(fmt);
+ __warn_printf(fmt, has_args ? regs : NULL);
}
if (warning) {
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 05/12] bug: Add report_bug_entry()
2025-11-10 11:46 [PATCH v2 00/12] x86: WARN() hackery Peter Zijlstra
` (3 preceding siblings ...)
2025-11-10 11:46 ` [PATCH v2 04/12] bug: Add BUG_FORMAT_ARGS infrastructure Peter Zijlstra
@ 2025-11-10 11:46 ` Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 06/12] bug: Implement WARN_ON() using __WARN_FLAGS() Peter Zijlstra
` (7 subsequent siblings)
12 siblings, 0 replies; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-10 11:46 UTC (permalink / raw)
To: x86
Cc: linux-kernel, peterz, kees, acarmina, jpoimboe, mark.rutland,
torvalds, maciej.wieczor-retman
Add a report_bug() variant where the bug_entry is already known. This
is useful when the exception instruction is not instantiated per-site.
But instead has a single instance. In such a case the bug_entry
address might be passed along in a known register or something.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
include/linux/bug.h | 1 +
lib/bug.c | 32 +++++++++++++++++++++++---------
2 files changed, 24 insertions(+), 9 deletions(-)
--- a/include/linux/bug.h
+++ b/include/linux/bug.h
@@ -42,6 +42,7 @@ void bug_get_file_line(struct bug_entry
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);
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -183,18 +183,20 @@ static void __warn_printf(const char *fm
printk("%s", fmt);
}
-static enum bug_trap_type __report_bug(unsigned long bugaddr, struct pt_regs *regs)
+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 (!is_valid_bugaddr(bugaddr))
- return BUG_TRAP_TYPE_NONE;
-
- bug = find_bug(bugaddr);
- if (!bug)
- return BUG_TRAP_TYPE_NONE;
+ 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();
@@ -244,13 +246,25 @@ static enum bug_trap_type __report_bug(u
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;
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 06/12] bug: Implement WARN_ON() using __WARN_FLAGS()
2025-11-10 11:46 [PATCH v2 00/12] x86: WARN() hackery Peter Zijlstra
` (4 preceding siblings ...)
2025-11-10 11:46 ` [PATCH v2 05/12] bug: Add report_bug_entry() Peter Zijlstra
@ 2025-11-10 11:46 ` Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 07/12] bug: Allow architectures to provide __WARN_printf() Peter Zijlstra
` (6 subsequent siblings)
12 siblings, 0 replies; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-10 11:46 UTC (permalink / raw)
To: x86
Cc: linux-kernel, peterz, kees, acarmina, jpoimboe, mark.rutland,
torvalds, maciej.wieczor-retman
This completes 3bc3c9c3ab6d ("bugs/core: Pass down the condition
string of WARN_ON_ONCE(cond) warnings to __WARN_FLAGS()") and makes
WARN_ON() and WARN_ON_ONCE() behaviour consistent.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
include/asm-generic/bug.h | 24 +++++++++++++++++-------
1 file changed, 17 insertions(+), 7 deletions(-)
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -109,19 +109,29 @@ extern __printf(1, 2) void __warn_printk
} 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("["#condition"] ", \
- BUGFLAG_ONCE | \
- BUGFLAG_TAINT(TAINT_WARN)); \
- unlikely(__ret_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); \
+})
+
+#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
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 07/12] bug: Allow architectures to provide __WARN_printf()
2025-11-10 11:46 [PATCH v2 00/12] x86: WARN() hackery Peter Zijlstra
` (5 preceding siblings ...)
2025-11-10 11:46 ` [PATCH v2 06/12] bug: Implement WARN_ON() using __WARN_FLAGS() Peter Zijlstra
@ 2025-11-10 11:46 ` Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 08/12] x86/bug: Add BUG_FORMAT basics Peter Zijlstra
` (5 subsequent siblings)
12 siblings, 0 replies; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-10 11:46 UTC (permalink / raw)
To: x86
Cc: linux-kernel, peterz, kees, acarmina, jpoimboe, mark.rutland,
torvalds, maciej.wieczor-retman
In addition to providing __WARN_FLAGS(), allow an architecture to also
provide __WARN_printf().
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
include/asm-generic/bug.h | 37 ++++++++++++++++++++++---------------
1 file changed, 22 insertions(+), 15 deletions(-)
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -100,23 +100,9 @@ void warn_slowpath_fmt(const char *file,
const char *fmt, ...);
extern __printf(1, 2) void __warn_printk(const char *fmt, ...);
-#ifndef __WARN_FLAGS
-#define __WARN() __WARN_printf(TAINT_WARN, NULL)
-#define __WARN_printf(taint, arg...) do { \
- instrumentation_begin(); \
- warn_slowpath_fmt(__FILE__, __LINE__, taint, arg); \
- instrumentation_end(); \
- } while (0)
-#else
+#ifdef __WARN_FLAGS
#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(condition) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
@@ -135,6 +121,27 @@ extern __printf(1, 2) void __warn_printk
})
#endif
+#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)
+#endif
+
+#ifndef __WARN
+#define __WARN() __WARN_printf(TAINT_WARN, NULL)
+#endif
+
/* used internally by panic.c */
#ifndef WARN_ON
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 08/12] x86/bug: Add BUG_FORMAT basics
2025-11-10 11:46 [PATCH v2 00/12] x86: WARN() hackery Peter Zijlstra
` (6 preceding siblings ...)
2025-11-10 11:46 ` [PATCH v2 07/12] bug: Allow architectures to provide __WARN_printf() Peter Zijlstra
@ 2025-11-10 11:46 ` Peter Zijlstra
2025-11-25 11:17 ` Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 09/12] x86/bug: Use BUG_FORMAT for DEBUG_BUGVERBOSE_DETAILED Peter Zijlstra
` (4 subsequent siblings)
12 siblings, 1 reply; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-10 11:46 UTC (permalink / raw)
To: x86
Cc: linux-kernel, peterz, kees, acarmina, jpoimboe, mark.rutland,
torvalds, maciej.wieczor-retman
Opt-in to BUG_FORMAT for x86_64, adjust the BUGTABLE helper and for
now, just store NULL pointers.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
arch/x86/include/asm/bug.h | 31 +++++++++++++++++++++----------
1 file changed, 21 insertions(+), 10 deletions(-)
--- a/arch/x86/include/asm/bug.h
+++ b/arch/x86/include/asm/bug.h
@@ -50,33 +50,44 @@
#define __BUG_ENTRY_VERBOSE(file, line)
#endif
-#define __BUG_ENTRY(file, line, flags) \
+#if defined(CONFIG_X86_64) || defined(CONFIG_DEBUG_BUGVERBOSE_DETAILED)
+#define HAVE_ARCH_BUG_FORMAT
+#define __BUG_ENTRY_FORMAT(format) \
+ "\t" __BUG_REL(format) "\t# bug_entry::format\n"
+#else
+#define __BUG_ENTRY_FORMAT(format)
+#endif
+
+#define __BUG_ENTRY(format, file, line, flags) \
__BUG_REL("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(ins, file, line, flags, size, extra) \
+#define _BUG_FLAGS_ASM(ins, format, file, line, flags, size, extra) \
"1:\t" ins "\n" \
".pushsection __bug_table,\"aw\"\n\t" \
ANNOTATE_DATA_SPECIAL \
"2:\n\t" \
- __BUG_ENTRY(file, line, flags) \
+ __BUG_ENTRY(format, file, line, flags) \
"\t.org 2b + " size "\n" \
".popsection\n" \
extra
#define _BUG_FLAGS(cond_str, ins, flags, extra) \
do { \
- asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c0", \
- "%c1", "%c2", "%c3", extra) \
- : : "i" (WARN_CONDITION_STR(cond_str) __FILE__), \
- "i" (__LINE__), \
- "i" (flags), \
- "i" (sizeof(struct bug_entry))); \
+ asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c[fmt]", "%c[file]", \
+ "%c[line]", "%c[fl]", \
+ "%c[size]", extra) \
+ : : [fmt] "i" (NULL), \
+ [file] "i" (WARN_CONDITION_STR(cond_str) __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, "")
+ _BUG_FLAGS_ASM(ASM_UD2, "NULL", file, line, flags, size, "")
#else
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 09/12] x86/bug: Use BUG_FORMAT for DEBUG_BUGVERBOSE_DETAILED
2025-11-10 11:46 [PATCH v2 00/12] x86: WARN() hackery Peter Zijlstra
` (7 preceding siblings ...)
2025-11-10 11:46 ` [PATCH v2 08/12] x86/bug: Add BUG_FORMAT basics Peter Zijlstra
@ 2025-11-10 11:46 ` Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 10/12] x86_64/bug: Implement __WARN_printf() Peter Zijlstra
` (3 subsequent siblings)
12 siblings, 0 replies; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-10 11:46 UTC (permalink / raw)
To: x86
Cc: linux-kernel, peterz, kees, acarmina, jpoimboe, mark.rutland,
torvalds, maciej.wieczor-retman
Since we have an explicit format string, use it for the condition string
instead of frobbing it in the file string.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
arch/x86/include/asm/bug.h | 10 ++++++++--
include/asm-generic/bug.h | 8 +++++---
2 files changed, 13 insertions(+), 5 deletions(-)
--- a/arch/x86/include/asm/bug.h
+++ b/arch/x86/include/asm/bug.h
@@ -74,13 +74,19 @@
".popsection\n" \
extra
+#ifdef CONFIG_DEBUG_BUGVERBOSE_DETAILED
+#define WARN_CONDITION_STR(cond_str) cond_str
+#else
+#define WARN_CONDITION_STR(cond_str) NULL
+#endif
+
#define _BUG_FLAGS(cond_str, ins, flags, extra) \
do { \
asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c[fmt]", "%c[file]", \
"%c[line]", "%c[fl]", \
"%c[size]", extra) \
- : : [fmt] "i" (NULL), \
- [file] "i" (WARN_CONDITION_STR(cond_str) __FILE__), \
+ : : [fmt] "i" (WARN_CONDITION_STR(cond_str)), \
+ [file] "i" (__FILE__), \
[line] "i" (__LINE__), \
[fl] "i" (flags), \
[size] "i" (sizeof(struct bug_entry))); \
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -18,11 +18,13 @@
#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
+# 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>
@@ -106,7 +108,7 @@ extern __printf(1, 2) void __warn_printk
#define WARN_ON(condition) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
- __WARN_FLAGS("["#condition"] ", \
+ __WARN_FLAGS(#condition, \
BUGFLAG_TAINT(TAINT_WARN)); \
unlikely(__ret_warn_on); \
})
@@ -114,7 +116,7 @@ extern __printf(1, 2) void __warn_printk
#define WARN_ON_ONCE(condition) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
- __WARN_FLAGS("["#condition"] ", \
+ __WARN_FLAGS(#condition, \
BUGFLAG_ONCE | \
BUGFLAG_TAINT(TAINT_WARN)); \
unlikely(__ret_warn_on); \
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 10/12] x86_64/bug: Implement __WARN_printf()
2025-11-10 11:46 [PATCH v2 00/12] x86: WARN() hackery Peter Zijlstra
` (8 preceding siblings ...)
2025-11-10 11:46 ` [PATCH v2 09/12] x86/bug: Use BUG_FORMAT for DEBUG_BUGVERBOSE_DETAILED Peter Zijlstra
@ 2025-11-10 11:46 ` Peter Zijlstra
2025-11-11 9:54 ` Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 11/12] x86/bug: Implement WARN_ONCE() Peter Zijlstra
` (2 subsequent siblings)
12 siblings, 1 reply; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-10 11:46 UTC (permalink / raw)
To: x86
Cc: linux-kernel, peterz, kees, acarmina, jpoimboe, mark.rutland,
torvalds, maciej.wieczor-retman
The basic idea is to have __WARN_printf() be a vararg function such
that the compiler can do the optimal calling convention for us. This
function body will be a #UD and then set up a va_list in the exception
from pt_regs.
But because the trap will be in a called function, the bug_entry must
be passed in. Have that be the first argument, with the format tucked
away inside the bug_entry.
The comments should clarify the real fun details.
The big downside is that all WARNs will now show:
RIP: 0010:__WARN_trap:+0
One possible solution is to simply discard the top frame when
unwinding. A follow up patch takes care of this slightly differently
by abusing the x86 static_call implementation.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
arch/x86/entry/entry.S | 8 +++
arch/x86/include/asm/bug.h | 60 +++++++++++++++++++++--
arch/x86/kernel/traps.c | 115 ++++++++++++++++++++++++++++++++++++++++-----
3 files changed, 168 insertions(+), 15 deletions(-)
--- a/arch/x86/entry/entry.S
+++ b/arch/x86/entry/entry.S
@@ -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 (%ecx), %_ASM_ARG1
+ RET
+SYM_FUNC_END(__WARN_trap)
+EXPORT_SYMBOL(__WARN_trap)
+
.popsection
/*
--- a/arch/x86/include/asm/bug.h
+++ b/arch/x86/include/asm/bug.h
@@ -31,6 +31,7 @@
#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
@@ -58,14 +59,17 @@
#define __BUG_ENTRY_FORMAT(format)
#endif
+#ifdef CONFIG_X86_64
+#define HAVE_ARCH_BUG_FORMAT_ARGS
+#endif
+
#define __BUG_ENTRY(format, file, line, flags) \
__BUG_REL("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(ins, format, file, line, flags, size, extra) \
- "1:\t" ins "\n" \
+#define _BUG_FLAGS_ASM(format, file, line, flags, size, extra) \
".pushsection __bug_table,\"aw\"\n\t" \
ANNOTATE_DATA_SPECIAL \
"2:\n\t" \
@@ -82,7 +86,8 @@
#define _BUG_FLAGS(cond_str, ins, flags, extra) \
do { \
- asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c[fmt]", "%c[file]", \
+ 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)), \
@@ -93,7 +98,8 @@ do { \
} while (0)
#define ARCH_WARN_ASM(file, line, flags, size) \
- _BUG_FLAGS_ASM(ASM_UD2, "NULL", file, line, flags, size, "")
+ "1:\t " ASM_UD2 "\n" \
+ _BUG_FLAGS_ASM("NULL", file, line, flags, size, "")
#else
@@ -126,6 +132,52 @@ do { \
instrumentation_end(); \
} while (0)
+#ifdef HAVE_ARCH_BUG_FORMAT_ARGS
+
+#ifndef __ASSEMBLY__
+struct bug_entry;
+extern void __WARN_trap(struct bug_entry *bug, ...);
+
+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 ; \
+ __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)
+
+#endif /* HAVE_ARCH_BUG_FORMAT_ARGS */
+
#include <asm-generic/bug.h>
#endif /* _ASM_X86_BUG_H */
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -102,25 +102,37 @@ __always_inline int is_valid_bugaddr(uns
* 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 39 ud1 (%ecx),%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 +168,33 @@ __always_inline int decode_bug(unsigned
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 == 1) { /* (%ecx) */
+ *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 +203,73 @@ __always_inline int decode_bug(unsigned
/* record instruction length */
*len = addr - start;
- if (X86_MODRM_REG(v) == 0) /* EAX */
- return BUG_UD1_UBSAN;
+ return type;
+}
- 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
+/*
+ * 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 +422,11 @@ static noinstr bool handle_bug(struct pt
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;
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 11/12] x86/bug: Implement WARN_ONCE()
2025-11-10 11:46 [PATCH v2 00/12] x86: WARN() hackery Peter Zijlstra
` (9 preceding siblings ...)
2025-11-10 11:46 ` [PATCH v2 10/12] x86_64/bug: Implement __WARN_printf() Peter Zijlstra
@ 2025-11-10 11:46 ` Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 12/12] x86_64/bug: Inline the UD1 Peter Zijlstra
2025-11-10 16:18 ` [PATCH v2 00/12] x86: WARN() hackery Linus Torvalds
12 siblings, 0 replies; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-10 11:46 UTC (permalink / raw)
To: x86
Cc: linux-kernel, peterz, kees, acarmina, jpoimboe, mark.rutland,
torvalds, maciej.wieczor-retman
Implement WARN_ONCE like WARN using BUGFLAG_ONCE.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
arch/x86/include/asm/bug.h | 9 +++++++++
include/asm-generic/bug.h | 2 ++
2 files changed, 11 insertions(+)
--- a/arch/x86/include/asm/bug.h
+++ b/arch/x86/include/asm/bug.h
@@ -176,6 +176,15 @@ do { \
#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>
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -176,8 +176,10 @@ extern __printf(1, 2) void __warn_printk
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)
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 12/12] x86_64/bug: Inline the UD1
2025-11-10 11:46 [PATCH v2 00/12] x86: WARN() hackery Peter Zijlstra
` (10 preceding siblings ...)
2025-11-10 11:46 ` [PATCH v2 11/12] x86/bug: Implement WARN_ONCE() Peter Zijlstra
@ 2025-11-10 11:46 ` Peter Zijlstra
2025-11-10 16:18 ` [PATCH v2 00/12] x86: WARN() hackery Linus Torvalds
12 siblings, 0 replies; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-10 11:46 UTC (permalink / raw)
To: x86
Cc: linux-kernel, peterz, kees, acarmina, jpoimboe, mark.rutland,
torvalds, maciej.wieczor-retman
(Ab)use the static_call infrastructure to convert all:
call __WARN_trap
instances into the desired:
ud1 (%ecx), %rdi
eliminating the CALL/RET, but more importantly, fixing the
fact that all WARNs will have:
RIP: 0010:__WARN_trap+0
Basically, by making it a static_call trampoline call, objtool will
collect the callsites, and then the inline rewrite will hit the
special case and replace the code with the magic instruction.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
arch/x86/include/asm/bug.h | 5 ++++-
arch/x86/kernel/static_call.c | 13 +++++++++++--
arch/x86/kernel/traps.c | 4 ++++
3 files changed, 19 insertions(+), 3 deletions(-)
--- a/arch/x86/include/asm/bug.h
+++ b/arch/x86/include/asm/bug.h
@@ -137,9 +137,12 @@ do { \
#ifdef HAVE_ARCH_BUG_FORMAT_ARGS
#ifndef __ASSEMBLY__
+#include <linux/static_call_types.h>
struct bug_entry;
extern void __WARN_trap(struct bug_entry *bug, ...);
+DECLARE_STATIC_CALL(WARN_trap, __WARN_trap);
+
struct pt_regs;
struct sysv_va_list { /* from AMD64 System V ABI */
unsigned int gp_offset;
@@ -171,7 +174,7 @@ extern void *__warn_args(struct arch_va_
#define __WARN_print_arg(flags, format, arg...) \
do { \
int __flags = (flags) | BUGFLAG_WARNING | BUGFLAG_ARGS ; \
- __WARN_trap(__WARN_bug_entry(__flags, format), ## arg); \
+ static_call_mod(WARN_trap)(__WARN_bug_entry(__flags, format), ## arg); \
asm (""); /* inhibit tail-call optimization */ \
} while (0)
--- a/arch/x86/kernel/static_call.c
+++ b/arch/x86/kernel/static_call.c
@@ -26,6 +26,11 @@ static const u8 xor5rax[] = { 0x2e, 0x2e
static const u8 retinsn[] = { RET_INSN_OPCODE, 0xcc, 0xcc, 0xcc, 0xcc };
+/*
+ * ud1 (%ecx),%rdi -- see __WARN_trap() / decode_bug()
+ */
+static const u8 warninsn[] = { 0x67, 0x48, 0x0f, 0xb9, 0x39 };
+
static u8 __is_Jcc(u8 *insn) /* Jcc.d32 */
{
u8 ret = 0;
@@ -69,7 +74,10 @@ static void __ref __static_call_transfor
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 *i
} 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;
}
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -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>
@@ -215,6 +216,9 @@ static inline unsigned long pt_regs_val(
}
#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.
*/
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 00/12] x86: WARN() hackery
2025-11-10 11:46 [PATCH v2 00/12] x86: WARN() hackery Peter Zijlstra
` (11 preceding siblings ...)
2025-11-10 11:46 ` [PATCH v2 12/12] x86_64/bug: Inline the UD1 Peter Zijlstra
@ 2025-11-10 16:18 ` Linus Torvalds
2025-11-10 22:20 ` Peter Zijlstra
12 siblings, 1 reply; 23+ messages in thread
From: Linus Torvalds @ 2025-11-10 16:18 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar
Cc: x86, linux-kernel, kees, acarmina, jpoimboe, mark.rutland,
maciej.wieczor-retman
On Mon, 10 Nov 2025 at 03:58, Peter Zijlstra <peterz@infradead.org> wrote:
>
> We should probably just merge these, as they improve the code-gen for WARN()
> significantly on x86
Yes.. And I'd actually like to see some example code generation in
the commit messages just to make that "improve the code-gen" more
explicit.
I know I saw some originally (or at least we discussed it), but it's
been long enough that my bird-brain has long since forgotten..
It looks like this series is on top of the BUGVERBOSE_DETAILED updates
(that I like but don't have in my tree yet - I think it's all
scheduled for 6.19). So I just scanned the patches visually and they
all look fine, I was just left wanting a more concrete "look, we used
to do this, now we generate _this_ instead".
Linus
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 00/12] x86: WARN() hackery
2025-11-10 16:18 ` [PATCH v2 00/12] x86: WARN() hackery Linus Torvalds
@ 2025-11-10 22:20 ` Peter Zijlstra
0 siblings, 0 replies; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-10 22:20 UTC (permalink / raw)
To: Linus Torvalds
Cc: Ingo Molnar, x86, linux-kernel, kees, acarmina, jpoimboe,
mark.rutland, maciej.wieczor-retman
On Mon, Nov 10, 2025 at 08:18:02AM -0800, Linus Torvalds wrote:
> On Mon, 10 Nov 2025 at 03:58, Peter Zijlstra <peterz@infradead.org> wrote:
> >
> > We should probably just merge these, as they improve the code-gen for WARN()
> > significantly on x86
>
> Yes.. And I'd actually like to see some example code generation in
> the commit messages just to make that "improve the code-gen" more
> explicit.
Damn, I actually thought to include some and then I totally forgot.
So lets see:
if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
"corrupted preempt_count: %s/%d/0x%x\n",
current->comm, current->pid, preempt_count()))
#define WARN_ONCE(condition, format...) \
DO_ONCE_LITE_IF(condition, WARN, 1, format)
#define WARN(condition, format...) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN_printf(TAINT_WARN, format); \
unlikely(__ret_warn_on); \
})
#define __WARN_printf(taint, arg...) do { \
instrumentation_begin(); \
__warn_printk(arg); \
__WARN_FLAGS("", BUGFLAG_NO_CUT_HERE | BUGFLAG_TAINT(taint));\
instrumentation_end(); \
} while (0)
Turns into this majestic pile:
# ../kernel/sched/core.c:5093: if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
cmpl $2, %ecx #, _7
jne .L1472 #,
...
.L1472:
# ../kernel/sched/core.c:5093: if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
cmpb $0, __already_done.11(%rip) #, __already_done
je .L1513 #,
...
.L1513:
# ../kernel/sched/core.c:5093: if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
movb $1, __already_done.11(%rip) #, __already_done
# ../kernel/sched/core.c:5093: if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
# 0 "" 2
# ../kernel/sched/core.c:5093: if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
#NO_APP
movl 1424(%r14), %edx # _15->pid, _15->pid
leaq 1912(%r14), %rsi #, _17
movq $.LC43, %rdi #,
call __warn_printk #
# ../kernel/sched/core.c:5093: if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
# 0 "" 2
# 5093 "../kernel/sched/core.c" 1
1: .byte 0x0f, 0x0b ;
.pushsection __bug_table,"aw"
912:
.pushsection .discard.annotate_data,"M", @progbits, 8
.long 912b - .
.long 1
.popsection
2: .long 1b - . # bug_entry::bug_addr
.long .LC1 - . # bug_entry::file #
.word 5093 # bug_entry::line #
.word 2313 # bug_entry::flags #
.org 2b + 12 #
.popsection
.pushsection .discard.annotate_insn,"M", @progbits, 8
.long 1b - .
.long 8 # ANNOTYPE_REACHABLE
.popsection
While afterwards it looks like so:
# ../kernel/sched/core.c:5093: if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
cmpl $2, %ecx #, _7
jne .L1442 #,
...
.L1442:
# ../kernel/sched/core.c:5093: if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
#APP
# 5093 "../kernel/sched/core.c" 1
lea (2f)(%rip), %rdi # bug
1:
.pushsection __bug_table,"aw"
912:
.pushsection .discard.annotate_data,"M", @progbits, 8
.long 912b - .
.long 1
.popsection
2:
.long 1b - . # bug_entry::bug_addr
.long .LC43 - . # bug_entry::format #
.long .LC1 - . # bug_entry::file #
.word 5093 # bug_entry::line #
.word 2323 # bug_entry::flags #
.org 2b + 16 #
.popsection
# 0 "" 2
#NO_APP
movl 1424(%r14), %edx # pretmp_19->pid, pretmp_19->pid
# ../kernel/sched/core.c:5093: if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
leaq 1912(%r14), %rsi #, _13
# ../kernel/sched/core.c:5093: if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
call __SCT__WARN_trap #
Where at runtime we patch that "call __SCT__WARN_trap" to read like:
ud1 (%ecx), %rdi
The whole DO_ONCE_LITE_IF/__already_done thing is gone (replaced with
BUGFLAG_ONCE) and the external __warn_prink() call is also gone, now
inside the exception handler through the pt_regs->va_list magic.
I'll see if I can clean that up somewhat and stick in the changelog.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 10/12] x86_64/bug: Implement __WARN_printf()
2025-11-10 11:46 ` [PATCH v2 10/12] x86_64/bug: Implement __WARN_printf() Peter Zijlstra
@ 2025-11-11 9:54 ` Peter Zijlstra
0 siblings, 0 replies; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-11 9:54 UTC (permalink / raw)
To: x86
Cc: linux-kernel, kees, acarmina, jpoimboe, mark.rutland, torvalds,
maciej.wieczor-retman
On Mon, Nov 10, 2025 at 12:46:43PM +0100, Peter Zijlstra wrote:
> #endif /* _ASM_X86_BUG_H */
> --- a/arch/x86/kernel/traps.c
> +++ b/arch/x86/kernel/traps.c
> @@ -102,25 +102,37 @@ __always_inline int is_valid_bugaddr(uns
> * 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 39 ud1 (%ecx),%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.
> */
Maciej; you were working on making KASAN emit UD1 instructions, right?
Where are you with those patches and are we conflicting on the encoding?
/me goes find the emails.. Yeah, I suggested UD1 /1, specifically:
ud1 xx(%ecx), %ecx
and that does conflict. How about I switch to using:
ud1 (%edx), %reg
for this?
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 10/12] x86_64/bug: Implement __WARN_printf()
@ 2025-11-17 9:22 Maciej Wieczór-Retman
0 siblings, 0 replies; 23+ messages in thread
From: Maciej Wieczór-Retman @ 2025-11-17 9:22 UTC (permalink / raw)
To: Peter Zijlstra
Cc: x86, linux-kernel, kees, acarmina, jpoimboe, mark.rutland,
torvalds, maciej.wieczor-retman
On 2025-11-11 at 10:54:42 +0100, Peter Zijlstra wrote:
>On Mon, Nov 10, 2025 at 12:46:43PM +0100, Peter Zijlstra wrote:
>
>> #endif /* _ASM_X86_BUG_H */
>> --- a/arch/x86/kernel/traps.c
>> +++ b/arch/x86/kernel/traps.c
>> @@ -102,25 +102,37 @@ __always_inline int is_valid_bugaddr(uns
>> * 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 39 ud1 (%ecx),%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.
>> */
>
>Maciej; you were working on making KASAN emit UD1 instructions, right?
>Where are you with those patches and are we conflicting on the encoding?
I noticed your comment on my KASAN series so I'll just reply there.
>
>/me goes find the emails.. Yeah, I suggested UD1 /1, specifically:
>
> ud1 xx(%ecx), %ecx
>
>and that does conflict. How about I switch to using:
>
> ud1 (%edx), %reg
>
>for this?
Thanks for asking, that sounds good :)
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 08/12] x86/bug: Add BUG_FORMAT basics
2025-11-10 11:46 ` [PATCH v2 08/12] x86/bug: Add BUG_FORMAT basics Peter Zijlstra
@ 2025-11-25 11:17 ` Peter Zijlstra
2025-11-25 12:33 ` Peter Zijlstra
0 siblings, 1 reply; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-25 11:17 UTC (permalink / raw)
To: x86
Cc: linux-kernel, kees, acarmina, jpoimboe, mark.rutland, torvalds,
maciej.wieczor-retman
On Mon, Nov 10, 2025 at 12:46:41PM +0100, Peter Zijlstra wrote:
> Opt-in to BUG_FORMAT for x86_64, adjust the BUGTABLE helper and for
> now, just store NULL pointers.
>
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> ---
> arch/x86/include/asm/bug.h | 31 +++++++++++++++++++++----------
> 1 file changed, 21 insertions(+), 10 deletions(-)
>
> --- a/arch/x86/include/asm/bug.h
> +++ b/arch/x86/include/asm/bug.h
> @@ -50,33 +50,44 @@
> #define __BUG_ENTRY_VERBOSE(file, line)
> #endif
>
> -#define __BUG_ENTRY(file, line, flags) \
> +#if defined(CONFIG_X86_64) || defined(CONFIG_DEBUG_BUGVERBOSE_DETAILED)
> +#define HAVE_ARCH_BUG_FORMAT
> +#define __BUG_ENTRY_FORMAT(format) \
> + "\t" __BUG_REL(format) "\t# bug_entry::format\n"
> +#else
> +#define __BUG_ENTRY_FORMAT(format)
> +#endif
> +
> +#define __BUG_ENTRY(format, file, line, flags) \
> __BUG_REL("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(ins, file, line, flags, size, extra) \
> +#define _BUG_FLAGS_ASM(ins, format, file, line, flags, size, extra) \
> "1:\t" ins "\n" \
> ".pushsection __bug_table,\"aw\"\n\t" \
> ANNOTATE_DATA_SPECIAL \
> "2:\n\t" \
> - __BUG_ENTRY(file, line, flags) \
> + __BUG_ENTRY(format, file, line, flags) \
> "\t.org 2b + " size "\n" \
> ".popsection\n" \
> extra
>
> #define _BUG_FLAGS(cond_str, ins, flags, extra) \
> do { \
> - asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c0", \
> - "%c1", "%c2", "%c3", extra) \
> - : : "i" (WARN_CONDITION_STR(cond_str) __FILE__), \
> - "i" (__LINE__), \
> - "i" (flags), \
> - "i" (sizeof(struct bug_entry))); \
> + asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c[fmt]", "%c[file]", \
> + "%c[line]", "%c[fl]", \
> + "%c[size]", extra) \
> + : : [fmt] "i" (NULL), \
This doesn't work right with KASLR on -- and I hadn't noticed because
most of my machines have nokaslr because of debugability :/
When we relocate the kernel, everything shifts by kaslr_offset(), and
that works just fine when both the __bug_table and the target string is
shifted, because then the relative position is the same and so the
relocation keeps working.
However, when the target is the absolute value 0, this breaks, because 0
isn't shifted by kaslr_offset() but the __bug_table itself is.
So the relative entry:
.long 0 - .
and its inverse:
format = (const char *)&bug->format_disp + bug->format_disp;
then end up at kaslr_offset() and things are sad.
The relative entry has a SHN_UNDEF relocation, which is ignored by the
relocs tool.
How is this supposed to be fixed?
> + [file] "i" (WARN_CONDITION_STR(cond_str) __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, "")
> + _BUG_FLAGS_ASM(ASM_UD2, "NULL", file, line, flags, size, "")
>
> #else
>
>
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 08/12] x86/bug: Add BUG_FORMAT basics
2025-11-25 11:17 ` Peter Zijlstra
@ 2025-11-25 12:33 ` Peter Zijlstra
2025-11-25 15:17 ` Peter Zijlstra
0 siblings, 1 reply; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-25 12:33 UTC (permalink / raw)
To: x86
Cc: linux-kernel, kees, acarmina, jpoimboe, mark.rutland, torvalds,
maciej.wieczor-retman
On Tue, Nov 25, 2025 at 12:17:50PM +0100, Peter Zijlstra wrote:
> On Mon, Nov 10, 2025 at 12:46:41PM +0100, Peter Zijlstra wrote:
> > Opt-in to BUG_FORMAT for x86_64, adjust the BUGTABLE helper and for
> > now, just store NULL pointers.
> >
> > Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> > ---
> > arch/x86/include/asm/bug.h | 31 +++++++++++++++++++++----------
> > 1 file changed, 21 insertions(+), 10 deletions(-)
> >
> > --- a/arch/x86/include/asm/bug.h
> > +++ b/arch/x86/include/asm/bug.h
> > @@ -50,33 +50,44 @@
> > #define __BUG_ENTRY_VERBOSE(file, line)
> > #endif
> >
> > -#define __BUG_ENTRY(file, line, flags) \
> > +#if defined(CONFIG_X86_64) || defined(CONFIG_DEBUG_BUGVERBOSE_DETAILED)
> > +#define HAVE_ARCH_BUG_FORMAT
> > +#define __BUG_ENTRY_FORMAT(format) \
> > + "\t" __BUG_REL(format) "\t# bug_entry::format\n"
> > +#else
> > +#define __BUG_ENTRY_FORMAT(format)
> > +#endif
> > +
> > +#define __BUG_ENTRY(format, file, line, flags) \
> > __BUG_REL("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(ins, file, line, flags, size, extra) \
> > +#define _BUG_FLAGS_ASM(ins, format, file, line, flags, size, extra) \
> > "1:\t" ins "\n" \
> > ".pushsection __bug_table,\"aw\"\n\t" \
> > ANNOTATE_DATA_SPECIAL \
> > "2:\n\t" \
> > - __BUG_ENTRY(file, line, flags) \
> > + __BUG_ENTRY(format, file, line, flags) \
> > "\t.org 2b + " size "\n" \
> > ".popsection\n" \
> > extra
> >
> > #define _BUG_FLAGS(cond_str, ins, flags, extra) \
> > do { \
> > - asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c0", \
> > - "%c1", "%c2", "%c3", extra) \
> > - : : "i" (WARN_CONDITION_STR(cond_str) __FILE__), \
> > - "i" (__LINE__), \
> > - "i" (flags), \
> > - "i" (sizeof(struct bug_entry))); \
> > + asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c[fmt]", "%c[file]", \
> > + "%c[line]", "%c[fl]", \
> > + "%c[size]", extra) \
> > + : : [fmt] "i" (NULL), \
>
> This doesn't work right with KASLR on -- and I hadn't noticed because
> most of my machines have nokaslr because of debugability :/
>
> When we relocate the kernel, everything shifts by kaslr_offset(), and
> that works just fine when both the __bug_table and the target string is
> shifted, because then the relative position is the same and so the
> relocation keeps working.
>
> However, when the target is the absolute value 0, this breaks, because 0
> isn't shifted by kaslr_offset() but the __bug_table itself is.
>
> So the relative entry:
>
> .long 0 - .
>
> and its inverse:
>
> format = (const char *)&bug->format_disp + bug->format_disp;
>
> then end up at kaslr_offset() and things are sad.
>
> The relative entry has a SHN_UNDEF relocation, which is ignored by the
> relocs tool.
>
> How is this supposed to be fixed?
This seems to work. Is this something we can all live with? It feels a
bit like a hack, but there doesn't appear to be anything better at hand.
---
diff --git a/lib/bug.c b/lib/bug.c
index 581a66b88c5c..8b470cc70afc 100644
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -48,6 +48,7 @@
#include <linux/rculist.h>
#include <linux/ftrace.h>
#include <linux/context_tracking.h>
+#include <asm/setup.h>
extern struct bug_entry __start___bug_table[], __stop___bug_table[];
@@ -145,6 +146,10 @@ static const char *bug_get_format(struct bug_entry *bug)
#ifdef HAVE_ARCH_BUG_FORMAT
#ifdef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
format = (const char *)&bug->format_disp + bug->format_disp;
+#ifdef CONFIG_RANDOMIZE_BASE
+ if ((unsigned long)format == kaslr_offset())
+ format = NULL;
+#endif
#else
format = bug->format;
#endif
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v2 08/12] x86/bug: Add BUG_FORMAT basics
2025-11-25 12:33 ` Peter Zijlstra
@ 2025-11-25 15:17 ` Peter Zijlstra
2025-11-25 16:27 ` Linus Torvalds
0 siblings, 1 reply; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-25 15:17 UTC (permalink / raw)
To: x86, ardb
Cc: linux-kernel, kees, acarmina, jpoimboe, mark.rutland, torvalds,
maciej.wieczor-retman
On Tue, Nov 25, 2025 at 01:33:01PM +0100, Peter Zijlstra wrote:
> > > + asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c[fmt]", "%c[file]", \
> > > + "%c[line]", "%c[fl]", \
> > > + "%c[size]", extra) \
> > > + : : [fmt] "i" (NULL), \
> >
> > This doesn't work right with KASLR on -- and I hadn't noticed because
> > most of my machines have nokaslr because of debugability :/
> >
> > When we relocate the kernel, everything shifts by kaslr_offset(), and
> > that works just fine when both the __bug_table and the target string is
> > shifted, because then the relative position is the same and so the
> > relocation keeps working.
> >
> > However, when the target is the absolute value 0, this breaks, because 0
> > isn't shifted by kaslr_offset() but the __bug_table itself is.
> >
> > So the relative entry:
> >
> > .long 0 - .
> >
> > and its inverse:
> >
> > format = (const char *)&bug->format_disp + bug->format_disp;
> >
> > then end up at kaslr_offset() and things are sad.
> >
> > The relative entry has a SHN_UNDEF relocation, which is ignored by the
> > relocs tool.
> >
> > How is this supposed to be fixed?
>
> This seems to work. Is this something we can all live with? It feels a
> bit like a hack, but there doesn't appear to be anything better at hand.
Ard came up with this glorious hack :-)
---
diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h
index d0a96ff5c02c..812ec8932734 100644
--- a/arch/x86/include/asm/bug.h
+++ b/arch/x86/include/asm/bug.h
@@ -5,6 +5,7 @@
#include <linux/stringify.h>
#include <linux/instrumentation.h>
#include <linux/objtool.h>
+#include <linux/compiler.h>
#include <asm/asm.h>
#ifndef __ASSEMBLY__
@@ -59,7 +60,7 @@ extern void __WARN_trap(struct bug_entry *bug, ...);
#if defined(CONFIG_X86_64) || defined(CONFIG_DEBUG_BUGVERBOSE_DETAILED)
#define HAVE_ARCH_BUG_FORMAT
#define __BUG_ENTRY_FORMAT(format) \
- "\t" __BUG_REL(format) "\t# bug_entry::format\n"
+ "3:\t" __BUG_REL(format) "\t# bug_entry::format\n"
#else
#define __BUG_ENTRY_FORMAT(format)
#endif
@@ -84,18 +85,20 @@ extern void __WARN_trap(struct bug_entry *bug, ...);
extra
#ifdef CONFIG_DEBUG_BUGVERBOSE_DETAILED
-#define WARN_CONDITION_STR(cond_str) cond_str
+#define WARN_CONDITION_STR(cond_str, null_str) cond_str
#else
-#define WARN_CONDITION_STR(cond_str) NULL
+#define WARN_CONDITION_STR(cond_str, null_str) null_str
#endif
-#define _BUG_FLAGS(cond_str, ins, flags, extra) \
+#define _BUG_FLAGS(cond_str, ins, flags, extra, id) \
do { \
+ extern typeof(cond_str) id; \
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)), \
+ ".set " __stringify(id) ", 3b\n" \
+ : : [fmt] "i" (WARN_CONDITION_STR(cond_str, id)), \
[file] "i" (__FILE__), \
[line] "i" (__LINE__), \
[fl] "i" (flags), \
@@ -104,11 +107,11 @@ do { \
#define ARCH_WARN_ASM(file, line, flags, size) \
"1:\t " ASM_UD2 "\n" \
- _BUG_FLAGS_ASM("0", file, line, flags, size, "")
+ _BUG_FLAGS_ASM("3b", file, line, flags, size, "")
#else
-#define _BUG_FLAGS(cond_str, ins, flags, extra) asm volatile(ins)
+#define _BUG_FLAGS(cond_str, ins, flags, extra, id) asm volatile(ins)
#endif /* CONFIG_GENERIC_BUG */
@@ -116,7 +119,7 @@ do { \
#define BUG() \
do { \
instrumentation_begin(); \
- _BUG_FLAGS("", ASM_UD2, 0, ""); \
+ _BUG_FLAGS("", ASM_UD2, 0, "", __UNIQUE_ID(bug)); \
__builtin_unreachable(); \
} while (0)
@@ -133,7 +136,8 @@ do { \
do { \
__auto_type __flags = BUGFLAG_WARNING|(flags); \
instrumentation_begin(); \
- _BUG_FLAGS(cond_str, ASM_UD2, __flags, ARCH_WARN_REACHABLE); \
+ _BUG_FLAGS(cond_str, ASM_UD2, __flags, ARCH_WARN_REACHABLE, \
+ __UNIQUE_ID(warn)); \
instrumentation_end(); \
} while (0)
diff --git a/lib/bug.c b/lib/bug.c
index 581a66b88c5c..9a598b7dfc11 100644
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -144,7 +145,8 @@ 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
- format = (const char *)&bug->format_disp + bug->format_disp;
+ if (bug->format_disp)
+ format = (const char *)&bug->format_disp + bug->format_disp;
#else
format = bug->format;
#endif
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v2 08/12] x86/bug: Add BUG_FORMAT basics
2025-11-25 15:17 ` Peter Zijlstra
@ 2025-11-25 16:27 ` Linus Torvalds
2025-11-26 9:54 ` Peter Zijlstra
2025-11-26 10:56 ` Peter Zijlstra
0 siblings, 2 replies; 23+ messages in thread
From: Linus Torvalds @ 2025-11-25 16:27 UTC (permalink / raw)
To: Peter Zijlstra
Cc: x86, ardb, linux-kernel, kees, acarmina, jpoimboe, mark.rutland,
maciej.wieczor-retman
On Tue, 25 Nov 2025 at 07:17, Peter Zijlstra <peterz@infradead.org> wrote:
>
> Ard came up with this glorious hack :-)
"glorious".
Hmm. I _really_ don't love how it now uses that odd label across
macros, it's not screaming "obvious" to me.
How bad would it be to just not use a NULL pointer at all, and instead
make the "no format" be "empty format"?
(Slightly related thing: could we perhaps just make x86-32 also use
the relative format, and get rid of some of the horrid #ifdef's in the
x86 <asm/bug.h> implementation? Because that "__BUG_REL" thing is not
a thing of beauty either).
Linus
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 08/12] x86/bug: Add BUG_FORMAT basics
2025-11-25 16:27 ` Linus Torvalds
@ 2025-11-26 9:54 ` Peter Zijlstra
2025-11-26 10:56 ` Peter Zijlstra
1 sibling, 0 replies; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-26 9:54 UTC (permalink / raw)
To: Linus Torvalds
Cc: x86, ardb, linux-kernel, kees, acarmina, jpoimboe, mark.rutland,
maciej.wieczor-retman
On Tue, Nov 25, 2025 at 08:27:50AM -0800, Linus Torvalds wrote:
> On Tue, 25 Nov 2025 at 07:17, Peter Zijlstra <peterz@infradead.org> wrote:
> >
> > Ard came up with this glorious hack :-)
>
> "glorious".
>
> Hmm. I _really_ don't love how it now uses that odd label across
> macros, it's not screaming "obvious" to me.
True.
> How bad would it be to just not use a NULL pointer at all, and instead
> make the "no format" be "empty format"?
Certainly, how does this look?
I verified it works on normal C code and that it compiles and the object
data looks right for Rust code.
---
diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h
index d0a96ff5c02c..4b5ab56903bb 100644
--- a/arch/x86/include/asm/bug.h
+++ b/arch/x86/include/asm/bug.h
@@ -86,7 +86,7 @@ extern void __WARN_trap(struct bug_entry *bug, ...);
#ifdef CONFIG_DEBUG_BUGVERBOSE_DETAILED
#define WARN_CONDITION_STR(cond_str) cond_str
#else
-#define WARN_CONDITION_STR(cond_str) NULL
+#define WARN_CONDITION_STR(cond_str) ""
#endif
#define _BUG_FLAGS(cond_str, ins, flags, extra) \
@@ -103,8 +103,12 @@ do { \
} while (0)
#define ARCH_WARN_ASM(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("0", file, line, flags, size, "")
+ _BUG_FLAGS_ASM("99b", file, line, flags, size, "")
#else
diff --git a/lib/bug.c b/lib/bug.c
index 581a66b88c5c..edd9041f89f3 100644
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -144,7 +144,17 @@ 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
- format = (const char *)&bug->format_disp + bug->format_disp;
+ /*
+ * 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
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v2 08/12] x86/bug: Add BUG_FORMAT basics
2025-11-25 16:27 ` Linus Torvalds
2025-11-26 9:54 ` Peter Zijlstra
@ 2025-11-26 10:56 ` Peter Zijlstra
1 sibling, 0 replies; 23+ messages in thread
From: Peter Zijlstra @ 2025-11-26 10:56 UTC (permalink / raw)
To: Linus Torvalds
Cc: x86, ardb, linux-kernel, kees, acarmina, jpoimboe, mark.rutland,
maciej.wieczor-retman
On Tue, Nov 25, 2025 at 08:27:50AM -0800, Linus Torvalds wrote:
> (Slightly related thing: could we perhaps just make x86-32 also use
> the relative format, and get rid of some of the horrid #ifdef's in the
> x86 <asm/bug.h> implementation? Because that "__BUG_REL" thing is not
> a thing of beauty either).
This builds and appears to work. I'll slap a changelog on and stick on
top of those other patches.
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -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
--- a/arch/x86/include/asm/bug.h
+++ b/arch/x86/include/asm/bug.h
@@ -42,16 +42,10 @@ extern void __WARN_trap(struct bug_entry
#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_VERBOSE(file, line) \
- "\t" __BUG_REL(file) "\t# bug_entry::file\n" \
- "\t.word " line "\t# bug_entry::line\n"
+ "\t.long " file " - .\t# bug_entry::file\n" \
+ "\t.word " line "\t# bug_entry::line\n"
#else
#define __BUG_ENTRY_VERBOSE(file, line)
#endif
@@ -59,7 +53,7 @@ extern void __WARN_trap(struct bug_entry
#if defined(CONFIG_X86_64) || defined(CONFIG_DEBUG_BUGVERBOSE_DETAILED)
#define HAVE_ARCH_BUG_FORMAT
#define __BUG_ENTRY_FORMAT(format) \
- "\t" __BUG_REL(format) "\t# bug_entry::format\n"
+ "\t.long " format " - .\t# bug_entry::format\n"
#else
#define __BUG_ENTRY_FORMAT(format)
#endif
@@ -69,7 +63,7 @@ extern void __WARN_trap(struct bug_entry
#endif
#define __BUG_ENTRY(format, file, line, flags) \
- __BUG_REL("1b") "\t# bug_entry::bug_addr\n" \
+ "\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"
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2025-11-26 10:56 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-10 11:46 [PATCH v2 00/12] x86: WARN() hackery Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 01/12] x86: Rework __bug_table helpers Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 02/12] bug: Add BUG_FORMAT infrastructure Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 03/12] bug: Clean up CONFIG_GENERIC_BUG_RELATIVE_POINTERS Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 04/12] bug: Add BUG_FORMAT_ARGS infrastructure Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 05/12] bug: Add report_bug_entry() Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 06/12] bug: Implement WARN_ON() using __WARN_FLAGS() Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 07/12] bug: Allow architectures to provide __WARN_printf() Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 08/12] x86/bug: Add BUG_FORMAT basics Peter Zijlstra
2025-11-25 11:17 ` Peter Zijlstra
2025-11-25 12:33 ` Peter Zijlstra
2025-11-25 15:17 ` Peter Zijlstra
2025-11-25 16:27 ` Linus Torvalds
2025-11-26 9:54 ` Peter Zijlstra
2025-11-26 10:56 ` Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 09/12] x86/bug: Use BUG_FORMAT for DEBUG_BUGVERBOSE_DETAILED Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 10/12] x86_64/bug: Implement __WARN_printf() Peter Zijlstra
2025-11-11 9:54 ` Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 11/12] x86/bug: Implement WARN_ONCE() Peter Zijlstra
2025-11-10 11:46 ` [PATCH v2 12/12] x86_64/bug: Inline the UD1 Peter Zijlstra
2025-11-10 16:18 ` [PATCH v2 00/12] x86: WARN() hackery Linus Torvalds
2025-11-10 22:20 ` Peter Zijlstra
-- strict thread matches above, loose matches on Subject: below --
2025-11-17 9:22 [PATCH v2 10/12] x86_64/bug: Implement __WARN_printf() Maciej Wieczór-Retman
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.