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
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
>
>
>
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
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
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
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"
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
© 2016 - 2025 Red Hat, Inc.