mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC] rust: kernel: Add KUnit tests for ARCH_WARN_ASM bug table emission
@ 2026-09-22  5:50 Mukesh Kumar Chaurasiya (IBM)
  2026-09-22  6:47 ` Peter Zijlstra
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Mukesh Kumar Chaurasiya (IBM) @ 2026-09-22  5:50 UTC (permalink / raw)
  To: catalin.marinas, will, mark.rutland, maddy, mpe, npiggin,
	chleroy, ritesh.list, sshegde, pjw, palmer, aou, alex, hca, gor,
	agordeev, borntraeger, svens, tglx, mingo, bp, dave.hansen, x86,
	hpa, ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg,
	aliceryhl, tmgross, dakr, daniel.almeida, tamird, acourbot, work,
	nathan, ndesaulniers, morbo, justinstitt, mkchauras, jszhang,
	japo, peterz, jpoimboe, seanjc, pmladek, thuth, ynorov,
	joelagnelf, david, fujita.tomonori, linux-arm-kernel,
	linux-kernel, linuxppc-dev, linux-riscv, linux-s390,
	rust-for-linux, llvm

Verify that the __bug_table entry emitted by ARCH_WARN_ASM has a correct
bug_addr displacement — i.e. the arch's trap label reference resolves to
the trap instruction — by calling find_bug() with the exact virtual address
of the trap, mirroring what the real trap handler does.

To support all architectures, each arch that implements ARCH_WARN_ASM now
defines ARCH_WARN_ASM_TRAP_LABEL, a string constant naming the local label
at which the trap instruction is placed:

  x86       "1"     (ud2 at label 1:)
  powerpc   "1"     (twi at label 1:)
  riscv     "1"     (ebreak at label 1:)
  arm64     "14471" (brk placed at 14471: by __BUG_ENTRY_END)
  s390      "0"     (mc at label 0:)

The label is used consistently: in ARCH_WARN_ASM itself, in the
bug_addr back-reference inside __BUG_ENTRY / _EMIT_BUG_ENTRY, and in
the new generated_arch_warn_asm_trap_label.rs template, which the
C preprocessor expands at build time into a .rs file included by the
test's global_asm! block.

global_asm! is used instead of asm! because LLVM eliminates asm! blocks
in dead branches; global_asm! is file-scope and always emitted.
BUG_KUNIT_TRAP_ADDR is defined as a .global symbol in .data on top of a
.dc.a {trap_label}b relocation so the linker resolves the trap address
into it at link time. A Rust static initialized to zero would land in
BSS where relocations are not applied.

The global_asm! string mirrors warn_flags!() exactly — same "/* {size} */"
prefix, same generated_arch_warn_asm.rs and generated_arch_reachable_asm.rs
includes, same operands — plus the .pushsection/.dc.a/.popsection tail.
This keeps the test structurally in sync with the macro it exercises.

The CONFIG_RUST_BUG_WARN_ASM_KUNIT_TEST option depends on GENERIC_BUG
and excludes ARM, LOONGARCH, and UML (which use a different warn_flags!
path without ARCH_WARN_ASM).

Five tests are included in the rust_kernel_bug_warn_asm suite:

  bug_entry_found           - find_bug() returns non-NULL for the trap
                              address, proving bug_addr is correct
  bug_entry_is_warning      - the emitted entry has BUGFLAG_WARNING set
  bug_entry_file            - bug_get_file_line() returns the correct
                              source file (CONFIG_DEBUG_BUGVERBOSE only)
  bug_entry_line            - recorded line number is non-zero, confirming
                              the {line} operand was substituted correctly
                              (CONFIG_DEBUG_BUGVERBOSE only)
  bug_entry_addr_is_in_text - kernel_text_address() confirms the trap
                              address lies in kernel text

Tested (pass:5 fail:0 skip:0) on:
  x86_64  - QEMU x86_64
  powerpc - QEMU pseries ppc64le (pseries_le_defconfig)
  riscv   - QEMU riscv64
  arm64   - QEMU aarch64
  s390    - QEMU s390x

Signed-off-by: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
---
Note: I sent this out as a powerpc specific tests earlier. During review
process some people wanted these to be arch independent. Hence sending
this out as an RFC.

Earlier version: https://lore.kernel.org/all/20260915090453.1227034-3-mkchauras@gmail.com/

 arch/arm64/include/asm/asm-bug.h              |   3 +
 arch/powerpc/include/asm/bug.h                |  22 +-
 arch/riscv/include/asm/bug.h                  |   5 +-
 arch/s390/include/asm/bug.h                   |   4 +-
 arch/x86/include/asm/bug.h                    |   5 +-
 rust/Makefile                                 |   2 +-
 rust/kernel/.gitignore                        |   1 +
 rust/kernel/Kconfig.test                      |  15 ++
 rust/kernel/bug.rs                            | 191 ++++++++++++++++++
 .../generated_arch_warn_asm_trap_label.rs.S   |   7 +
 10 files changed, 239 insertions(+), 16 deletions(-)
 create mode 100644 rust/kernel/generated_arch_warn_asm_trap_label.rs.S

diff --git a/arch/arm64/include/asm/asm-bug.h b/arch/arm64/include/asm/asm-bug.h
index a5f13801b784..e0694543115e 100644
--- a/arch/arm64/include/asm/asm-bug.h
+++ b/arch/arm64/include/asm/asm-bug.h
@@ -60,6 +60,9 @@ _BUGVERBOSE_LOCATION(__FILE__, __LINE__)		\
 		".short " flags ";"			\
 		__stringify(__BUG_ENTRY_END)
 
+/* Trap instruction is placed at label 14471: by __BUG_ENTRY_END */
+#define ARCH_WARN_ASM_TRAP_LABEL "14471"
+
 #define ARCH_WARN_ASM(file, line, flags, size)		\
 	__BUG_ENTRY_STRING(file, line, flags)		\
 	__stringify(brk BUG_BRK_IMM)
diff --git a/arch/powerpc/include/asm/bug.h b/arch/powerpc/include/asm/bug.h
index bf31ee1e902a..f0333c564d4f 100644
--- a/arch/powerpc/include/asm/bug.h
+++ b/arch/powerpc/include/asm/bug.h
@@ -32,6 +32,8 @@
 #endif /* verbose */
 
 #else /* !__ASSEMBLER__ */
+#define ARCH_WARN_ASM_TRAP_LABEL "1"
+
 #ifdef CONFIG_DEBUG_BUGVERBOSE
 #define _EMIT_BUG_ENTRY(bug_entry, trap, file, line, flags)	\
 	".section __bug_table,\"aw\"\n"				\
@@ -45,21 +47,21 @@
 	"		.short " flags "\n"
 #endif
 
-#define BUG_ENTRY(cond_str, insn, flags, ...)			\
-	__asm__ __volatile__(					\
-		"1:	" insn "\n"				\
-		_EMIT_BUG_ENTRY(2, 1b, "%0", "%1", "%2")	\
-		".org 2b+%3\n"					\
-		".previous\n"					\
+#define BUG_ENTRY(cond_str, insn, flags, ...)				\
+	__asm__ __volatile__(						\
+		ARCH_WARN_ASM_TRAP_LABEL ":	" insn "\n"		\
+		_EMIT_BUG_ENTRY(2, 1b, "%0", "%1", "%2")		\
+		".org 2b+%3\n"						\
+		".previous\n"						\
 		: : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__),	\
 		  "i" (flags),					\
 		  "i" (sizeof(struct bug_entry)),		\
 		  ##__VA_ARGS__)
 
-#define ARCH_WARN_ASM(file, line, flags, size)			\
-		"1:	twi 31, 0, 0\n"				\
-		_EMIT_BUG_ENTRY(2, 1b, file, line, flags)	\
-		".org 2b+" size "\n"				\
+#define ARCH_WARN_ASM(file, line, flags, size)					\
+		ARCH_WARN_ASM_TRAP_LABEL ":	twi 31, 0, 0\n"			\
+		_EMIT_BUG_ENTRY(2, 1b, file, line, flags)			\
+		".org 2b+" size "\n"						\
 		".previous\n"
 
 #define ARCH_WARN_REACHABLE
diff --git a/arch/riscv/include/asm/bug.h b/arch/riscv/include/asm/bug.h
index 699c0cf3e4ef..def6b0d153f2 100644
--- a/arch/riscv/include/asm/bug.h
+++ b/arch/riscv/include/asm/bug.h
@@ -29,7 +29,8 @@
 
 typedef u32 bug_insn_t;
 
-#define __BUG_ENTRY_ADDR	RISCV_INT " 1b - ."
+#define ARCH_WARN_ASM_TRAP_LABEL	"1"
+#define __BUG_ENTRY_ADDR	RISCV_INT " " ARCH_WARN_ASM_TRAP_LABEL "b - ."
 #define __BUG_ENTRY_FILE(file)	RISCV_INT " " file " - ."
 
 #ifdef CONFIG_DEBUG_BUGVERBOSE
@@ -47,7 +48,7 @@ typedef u32 bug_insn_t;
 #ifdef CONFIG_GENERIC_BUG
 
 #define ARCH_WARN_ASM(file, line, flags, size)			\
-		"1:\n\t"					\
+		ARCH_WARN_ASM_TRAP_LABEL ":\n\t"		\
 			"ebreak\n"				\
 			".pushsection __bug_table,\"aw\"\n\t"	\
 		"2:\n\t"					\
diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h
index 2010342c97b1..3c0fa1acc92a 100644
--- a/arch/s390/include/asm/bug.h
+++ b/arch/s390/include/asm/bug.h
@@ -26,9 +26,11 @@
 #define WARN_CONDITION_STR(cond_str) ""
 #endif
 
+#define ARCH_WARN_ASM_TRAP_LABEL "0"
+
 #define __BUG_ENTRY(format, file, line, flags, size)			\
 		"	.section __bug_table,\"aw\"\n"			\
-		"1:	.long	0b - .		# bug_entry::bug_addr\n"\
+		"1:	.long	" ARCH_WARN_ASM_TRAP_LABEL "b - .	# bug_entry::bug_addr\n" \
 		"	.long	" format " - .	# bug_entry::format\n"	\
 		__BUG_ENTRY_VERBOSE(file, line)				\
 		"	.short	"flags"		# bug_entry::flags\n"	\
diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h
index 23ab05438269..6cca8880660e 100644
--- a/arch/x86/include/asm/bug.h
+++ b/arch/x86/include/asm/bug.h
@@ -62,8 +62,9 @@ extern void __WARN_trap(struct bug_entry *bug, ...);
 #define HAVE_ARCH_BUG_FORMAT_ARGS
 #endif
 
+#define ARCH_WARN_ASM_TRAP_LABEL "1"
 #define __BUG_ENTRY(format, file, line, flags)				\
-	"\t.long 1b - ."	"\t# bug_entry::bug_addr\n"		\
+	"\t.long " ARCH_WARN_ASM_TRAP_LABEL "b - ." "\t# bug_entry::bug_addr\n" \
 	__BUG_ENTRY_FORMAT(format)					\
 	__BUG_ENTRY_VERBOSE(file, line)					\
 	"\t.word " flags	"\t# bug_entry::flags\n"
@@ -101,7 +102,7 @@ do {									\
 	"99:\n"								\
 	"\t.string \"\"\n"						\
 	".popsection\n"							\
-	"1:\t " ASM_UD2 "\n"						\
+	ARCH_WARN_ASM_TRAP_LABEL ":\t " ASM_UD2 "\n"			\
 	_BUG_FLAGS_ASM("99b", file, line, flags, size, "")
 
 #else
diff --git a/rust/Makefile b/rust/Makefile
index da1a7409d984..8f253d516495 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -46,7 +46,7 @@ obj-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.o
 
 always-$(subst y,$(CONFIG_RUST),$(CONFIG_JUMP_LABEL)) += kernel/generated_arch_static_branch_asm.rs
 ifndef CONFIG_UML
-always-$(subst y,$(CONFIG_RUST),$(CONFIG_BUG)) += kernel/generated_arch_warn_asm.rs kernel/generated_arch_reachable_asm.rs
+always-$(subst y,$(CONFIG_RUST),$(CONFIG_BUG)) += kernel/generated_arch_warn_asm.rs kernel/generated_arch_reachable_asm.rs kernel/generated_arch_warn_asm_trap_label.rs
 endif
 
 # Avoids running `$(RUSTC)` when it may not be available.
diff --git a/rust/kernel/.gitignore b/rust/kernel/.gitignore
index f636ad95aaf3..05bb00bbb83c 100644
--- a/rust/kernel/.gitignore
+++ b/rust/kernel/.gitignore
@@ -3,3 +3,4 @@
 /generated_arch_static_branch_asm.rs
 /generated_arch_warn_asm.rs
 /generated_arch_reachable_asm.rs
+/generated_arch_warn_asm_trap_label.rs
diff --git a/rust/kernel/Kconfig.test b/rust/kernel/Kconfig.test
index e6a5c7a795f0..8cd7c8f91b07 100644
--- a/rust/kernel/Kconfig.test
+++ b/rust/kernel/Kconfig.test
@@ -83,4 +83,19 @@ config RUST_BITFIELD_KUNIT_TEST
 
 	  If unsure, say N.
 
+config RUST_BUG_WARN_ASM_KUNIT_TEST
+	bool "KUnit tests for ARCH_WARN_ASM bug table emission" if !KUNIT_ALL_TESTS
+	depends on GENERIC_BUG && !ARM && !LOONGARCH && !UML
+	default KUNIT_ALL_TESTS
+	help
+	  This option enables KUnit tests that verify ARCH_WARN_ASM emits a
+	  correct __bug_table entry: the bug_addr displacement must resolve
+	  back to the trap instruction so that find_bug() can locate the
+	  entry — exactly as the real trap handler does.
+
+	  Supported on all architectures that implement ARCH_WARN_ASM
+	  (x86, powerpc, riscv, arm64, s390).
+
+	  If unsure, say N.
+
 endif
diff --git a/rust/kernel/bug.rs b/rust/kernel/bug.rs
index 3566f0234ca4..9e9b6d3a68b7 100644
--- a/rust/kernel/bug.rs
+++ b/rust/kernel/bug.rs
@@ -152,3 +152,194 @@ macro_rules! warn_on {
         cond
     }};
 }
+
+// Test-only constants and file static referenced by the global_asm block below.
+//
+// global_asm! is file-scope and always emitted — LLVM cannot eliminate it,
+// unlike asm! inside a function which is subject to dead-code removal.
+//
+// BUG_KUNIT_TRAP_ADDR is declared as a .global symbol entirely inside the
+// global_asm! block so the .dc.a {trap_label}b relocation lands directly on it.
+// A Rust static initialized to zero would end up in BSS; the linker does
+// not apply relocations to BSS, so the address would stay zero at runtime.
+#[cfg(CONFIG_RUST_BUG_WARN_ASM_KUNIT_TEST)]
+mod test_statics {
+    use crate::bindings::{bug_entry, BUGFLAG_WARNING, TAINT_WARN};
+
+    pub(super) const FLAGS: u32 = BUGFLAG_WARNING | (TAINT_WARN << 8);
+    pub(super) const SIZE: usize = core::mem::size_of::<bug_entry>();
+
+    // LINE and BUG_KUNIT_FILE are only referenced by the {file}/{line}
+    // operands in global_asm!, which are only present when
+    // CONFIG_DEBUG_BUGVERBOSE is set (the non-verbose _EMIT_BUG_ENTRY
+    // drops the file/line fields from the bug table entry entirely).
+    #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
+    pub(super) const LINE: u32 = line!();
+
+    // Null-terminated source file name — the assembler references this symbol
+    // for the verbose file pointer in __bug_table, same as warn_flags!.
+    #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
+    const _FILE: &[u8] = file!().as_bytes();
+    #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
+    #[no_mangle]
+    pub(super) static BUG_KUNIT_FILE: [u8; _FILE.len() + 1] = {
+        let mut bytes = [0u8; _FILE.len() + 1];
+        let mut i = 0;
+        while i < _FILE.len() {
+            bytes[i] = _FILE[i];
+            i += 1;
+        }
+        bytes
+    };
+}
+
+// Emit ARCH_WARN_ASM at file scope and capture the trap address.
+//
+// The asm string mirrors warn_flags!() exactly — same "/* {size} */" prefix,
+// same generated_arch_warn_asm.rs and generated_arch_reachable_asm.rs includes,
+// same operands — so the two stay in sync as the macro evolves.  The only
+// addition is the .pushsection/.dc.a/.popsection tail that records the trap
+// address into BUG_KUNIT_TRAP_ADDR at link time.
+//
+// BUG_KUNIT_TRAP_ADDR is a .global symbol in .data whose value is set by a
+// .dc.a relocation to the arch's trap label (from
+// generated_arch_warn_asm_trap_label.rs); .dc.a self-aligns to pointer width
+// so no .balign is needed.
+#[cfg(all(CONFIG_RUST_BUG_WARN_ASM_KUNIT_TEST, CONFIG_DEBUG_BUGVERBOSE))]
+::core::arch::global_asm!(
+    concat!(
+        "/* {size} */",
+        include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")),
+        include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_reachable_asm.rs")),
+        "\n\t.pushsection .data\n\t",
+        ".global BUG_KUNIT_TRAP_ADDR\n\t",
+        "BUG_KUNIT_TRAP_ADDR:\n\t",
+        ".dc.a ",
+        include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm_trap_label.rs")),
+        "b\n\t",
+        ".popsection\n",
+    ),
+    file  = sym test_statics::BUG_KUNIT_FILE,
+    line  = const test_statics::LINE,
+    flags = const test_statics::FLAGS,
+    size  = const test_statics::SIZE,
+);
+
+#[cfg(all(CONFIG_RUST_BUG_WARN_ASM_KUNIT_TEST, not(CONFIG_DEBUG_BUGVERBOSE)))]
+::core::arch::global_asm!(
+    concat!(
+        "/* {size} */",
+        include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")),
+        include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_reachable_asm.rs")),
+        "\n\t.pushsection .data\n\t",
+        ".global BUG_KUNIT_TRAP_ADDR\n\t",
+        "BUG_KUNIT_TRAP_ADDR:\n\t",
+        ".dc.a ",
+        include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm_trap_label.rs")),
+        "b\n\t",
+        ".popsection\n",
+    ),
+    flags = const test_statics::FLAGS,
+    size  = const test_statics::SIZE,
+);
+
+#[cfg(CONFIG_RUST_BUG_WARN_ASM_KUNIT_TEST)]
+#[::kernel::macros::kunit_tests(rust_kernel_bug_warn_asm)]
+mod tests {
+    use crate::bindings;
+
+    fn trap_addr() -> usize {
+        // BUG_KUNIT_TRAP_ADDR is a .global symbol defined in the global_asm!
+        // block above, placed in .data at the exact .dc.a 1b relocation word.
+        // The linker resolves it to the virtual address of the twi instruction
+        // before any Rust code runs, so reading it here is always safe.
+        extern "C" {
+            // .dc.a emits a pointer-width word: 4 bytes on ppc32, 8 on ppc64.
+            // usize matches the native pointer width on both.
+            static BUG_KUNIT_TRAP_ADDR: usize;
+        }
+        // SAFETY: read-only after link time, no concurrent mutation possible.
+        unsafe { BUG_KUNIT_TRAP_ADDR }
+    }
+
+    /// The `__bug_table` entry emitted by `ARCH_WARN_ASM` must be locatable
+    /// via `find_bug()` using the trap instruction's address.  A NULL result
+    /// means the `1b` label reference in `_EMIT_BUG_ENTRY` resolved to the
+    /// wrong address and the real trap handler would not recognise the site.
+    #[test]
+    fn bug_entry_found() {
+        // Non-zero proves the .dc.a relocation was resolved by the linker.
+        assert!(trap_addr() != 0);
+
+        // SAFETY: find_bug() is always safe to call with any address; it
+        // simply walks __bug_table and returns NULL if nothing matches.
+        let entry = unsafe { bindings::find_bug(trap_addr()) };
+        // Non-NULL proves the bug_addr displacement in _EMIT_BUG_ENTRY is correct.
+        assert!(!entry.is_null());
+    }
+
+    /// The emitted entry must be flagged as a warning (not a hard BUG).
+    #[test]
+    fn bug_entry_is_warning() {
+        assert!(trap_addr() != 0);
+        let entry = unsafe { bindings::find_bug(trap_addr()) };
+        assert!(!entry.is_null());
+        // SAFETY: entry is non-null and points to a valid bug_entry.
+        let flags = unsafe { (*entry).flags } as u32;
+        assert!(flags & bindings::BUGFLAG_WARNING != 0);
+    }
+
+    /// With `CONFIG_DEBUG_BUGVERBOSE` the entry must record a non-null file
+    /// pointer pointing back into this source file.
+    #[test]
+    #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
+    fn bug_entry_file() {
+        use kernel::str::CStrExt;
+
+        assert!(trap_addr() != 0);
+        let entry = unsafe { bindings::find_bug(trap_addr()) };
+        assert!(!entry.is_null());
+
+        let mut file_ptr: *const kernel::ffi::c_char = core::ptr::null();
+        let mut line: u32 = 0;
+        // SAFETY: entry is non-null and valid; file_ptr and line are local
+        // variables passed as out-parameters.
+        unsafe { bindings::bug_get_file_line(entry, &mut file_ptr, &mut line) };
+
+        assert!(!file_ptr.is_null());
+        // SAFETY: file_ptr is a non-null, null-terminated C string from BUG_KUNIT_FILE,
+        // valid for the lifetime of this function.
+        let file_str = unsafe { kernel::prelude::CStr::from_char_ptr(file_ptr) }
+            .to_str()
+            .unwrap_or("");
+        assert!(file_str.contains("bug"));
+    }
+
+    /// With `CONFIG_DEBUG_BUGVERBOSE` the recorded line number must be
+    /// non-zero (a zero line would mean the asm operand was not substituted).
+    #[test]
+    #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
+    fn bug_entry_line() {
+        assert!(trap_addr() != 0);
+        let entry = unsafe { bindings::find_bug(trap_addr()) };
+        assert!(!entry.is_null());
+
+        let mut file_ptr: *const kernel::ffi::c_char = core::ptr::null();
+        let mut line: u32 = 0;
+        // SAFETY: entry is non-null and valid.
+        unsafe { bindings::bug_get_file_line(entry, &mut file_ptr, &mut line) };
+
+        assert!(line != 0);
+    }
+
+    /// The trap address stored in `__bug_table` must lie within the kernel
+    /// text segment.  If the label reference in `_EMIT_BUG_ENTRY` resolved
+    /// to data or zero, `kernel_text_address()` would return false.
+    #[test]
+    fn bug_entry_addr_is_in_text() {
+        assert!(trap_addr() != 0);
+        // SAFETY: kernel_text_address() is always safe to call with any addr.
+        let in_text = unsafe { bindings::kernel_text_address(trap_addr()) };
+        assert!(in_text != 0);
+    }
+}
diff --git a/rust/kernel/generated_arch_warn_asm_trap_label.rs.S b/rust/kernel/generated_arch_warn_asm_trap_label.rs.S
new file mode 100644
index 000000000000..d7bed8751f10
--- /dev/null
+++ b/rust/kernel/generated_arch_warn_asm_trap_label.rs.S
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <linux/bug.h>
+
+// Cut here.
+
+::kernel::concat_literals!(ARCH_WARN_ASM_TRAP_LABEL)
-- 
2.55.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] rust: kernel: Add KUnit tests for ARCH_WARN_ASM bug table emission
  2026-09-22  5:50 [RFC] rust: kernel: Add KUnit tests for ARCH_WARN_ASM bug table emission Mukesh Kumar Chaurasiya (IBM)
@ 2026-09-22  6:47 ` Peter Zijlstra
  2026-09-23  6:28   ` Mukesh Kumar Chaurasiya
  2026-09-24 12:31 ` Mark Rutland
  2026-09-24 13:00 ` Christophe Leroy (CS GROUP)
  2 siblings, 1 reply; 10+ messages in thread
From: Peter Zijlstra @ 2026-09-22  6:47 UTC (permalink / raw)
  To: Mukesh Kumar Chaurasiya (IBM)
  Cc: catalin.marinas, will, mark.rutland, maddy, mpe, npiggin,
	chleroy, ritesh.list, sshegde, pjw, palmer, aou, alex, hca, gor,
	agordeev, borntraeger, svens, tglx, mingo, bp, dave.hansen, x86,
	hpa, ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg,
	aliceryhl, tmgross, dakr, daniel.almeida, tamird, acourbot, work,
	nathan, ndesaulniers, morbo, justinstitt, jszhang, japo,
	jpoimboe, seanjc, pmladek, thuth, ynorov, joelagnelf, david,
	fujita.tomonori, linux-arm-kernel, linux-kernel, linuxppc-dev,
	linux-riscv, linux-s390, rust-for-linux, llvm

On Tue, Sep 22, 2026 at 11:20:00AM +0530, Mukesh Kumar Chaurasiya (IBM) wrote:
> Verify that the __bug_table entry emitted by ARCH_WARN_ASM has a correct
> bug_addr displacement — i.e. the arch's trap label reference resolves to
> the trap instruction — by calling find_bug() with the exact virtual address
> of the trap, mirroring what the real trap handler does.
> 
> To support all architectures, each arch that implements ARCH_WARN_ASM now
> defines ARCH_WARN_ASM_TRAP_LABEL, a string constant naming the local label
> at which the trap instruction is placed:
> 
>   x86       "1"     (ud2 at label 1:)
>   powerpc   "1"     (twi at label 1:)
>   riscv     "1"     (ebreak at label 1:)
>   arm64     "14471" (brk placed at 14471: by __BUG_ENTRY_END)
>   s390      "0"     (mc at label 0:)
> 
> The label is used consistently: in ARCH_WARN_ASM itself, in the
> bug_addr back-reference inside __BUG_ENTRY / _EMIT_BUG_ENTRY, 


> diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h
> index 23ab05438269..6cca8880660e 100644
> --- a/arch/x86/include/asm/bug.h
> +++ b/arch/x86/include/asm/bug.h
> @@ -62,8 +62,9 @@ extern void __WARN_trap(struct bug_entry *bug, ...);
>  #define HAVE_ARCH_BUG_FORMAT_ARGS
>  #endif
>  
> +#define ARCH_WARN_ASM_TRAP_LABEL "1"
>  #define __BUG_ENTRY(format, file, line, flags)				\
> -	"\t.long 1b - ."	"\t# bug_entry::bug_addr\n"		\
> +	"\t.long " ARCH_WARN_ASM_TRAP_LABEL "b - ." "\t# bug_entry::bug_addr\n" \
>  	__BUG_ENTRY_FORMAT(format)					\
>  	__BUG_ENTRY_VERBOSE(file, line)					\
>  	"\t.word " flags	"\t# bug_entry::flags\n"
> @@ -101,7 +102,7 @@ do {									\
>  	"99:\n"								\
>  	"\t.string \"\"\n"						\
>  	".popsection\n"							\
> -	"1:\t " ASM_UD2 "\n"						\
> +	ARCH_WARN_ASM_TRAP_LABEL ":\t " ASM_UD2 "\n"			\
>  	_BUG_FLAGS_ASM("99b", file, line, flags, size, "")
>  
>  #else

Not really a fan of that. And I can't really tell what you're doing with
it either. The kunit is in Rust and thus unreadable :-(

I would rather you fix up is_valid_bugaddr(), some architectures seem to
have an always true stub because of the callchains always being from the
break instruction.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] rust: kernel: Add KUnit tests for ARCH_WARN_ASM bug table emission
  2026-09-22  6:47 ` Peter Zijlstra
@ 2026-09-23  6:28   ` Mukesh Kumar Chaurasiya
  2026-09-24 11:15     ` Peter Zijlstra
  0 siblings, 1 reply; 10+ messages in thread
From: Mukesh Kumar Chaurasiya @ 2026-09-23  6:28 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: catalin.marinas, will, mark.rutland, maddy, mpe, npiggin,
	chleroy, ritesh.list, sshegde, pjw, palmer, aou, alex, hca, gor,
	agordeev, borntraeger, svens, tglx, mingo, bp, dave.hansen, x86,
	hpa, ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg,
	aliceryhl, tmgross, dakr, daniel.almeida, tamird, acourbot, work,
	nathan, ndesaulniers, morbo, justinstitt, jszhang, japo,
	jpoimboe, seanjc, pmladek, thuth, ynorov, joelagnelf, david,
	fujita.tomonori, linux-arm-kernel, linux-kernel, linuxppc-dev,
	linux-riscv, linux-s390, rust-for-linux, llvm

On Tue, Sep 22, 2026 at 08:47:35AM +0200, Peter Zijlstra wrote:
> On Tue, Sep 22, 2026 at 11:20:00AM +0530, Mukesh Kumar Chaurasiya (IBM) wrote:
> > Verify that the __bug_table entry emitted by ARCH_WARN_ASM has a correct
> > bug_addr displacement — i.e. the arch's trap label reference resolves to
> > the trap instruction — by calling find_bug() with the exact virtual address
> > of the trap, mirroring what the real trap handler does.
> > 
> > To support all architectures, each arch that implements ARCH_WARN_ASM now
> > defines ARCH_WARN_ASM_TRAP_LABEL, a string constant naming the local label
> > at which the trap instruction is placed:
> > 
> >   x86       "1"     (ud2 at label 1:)
> >   powerpc   "1"     (twi at label 1:)
> >   riscv     "1"     (ebreak at label 1:)
> >   arm64     "14471" (brk placed at 14471: by __BUG_ENTRY_END)
> >   s390      "0"     (mc at label 0:)
> > 
> > The label is used consistently: in ARCH_WARN_ASM itself, in the
> > bug_addr back-reference inside __BUG_ENTRY / _EMIT_BUG_ENTRY, 
> 
> 
> > diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h
> > index 23ab05438269..6cca8880660e 100644
> > --- a/arch/x86/include/asm/bug.h
> > +++ b/arch/x86/include/asm/bug.h
> > @@ -62,8 +62,9 @@ extern void __WARN_trap(struct bug_entry *bug, ...);
> >  #define HAVE_ARCH_BUG_FORMAT_ARGS
> >  #endif
> >  
> > +#define ARCH_WARN_ASM_TRAP_LABEL "1"
> >  #define __BUG_ENTRY(format, file, line, flags)				\
> > -	"\t.long 1b - ."	"\t# bug_entry::bug_addr\n"		\
> > +	"\t.long " ARCH_WARN_ASM_TRAP_LABEL "b - ." "\t# bug_entry::bug_addr\n" \
> >  	__BUG_ENTRY_FORMAT(format)					\
> >  	__BUG_ENTRY_VERBOSE(file, line)					\
> >  	"\t.word " flags	"\t# bug_entry::flags\n"
> > @@ -101,7 +102,7 @@ do {									\
> >  	"99:\n"								\
> >  	"\t.string \"\"\n"						\
> >  	".popsection\n"							\
> > -	"1:\t " ASM_UD2 "\n"						\
> > +	ARCH_WARN_ASM_TRAP_LABEL ":\t " ASM_UD2 "\n"			\
> >  	_BUG_FLAGS_ASM("99b", file, line, flags, size, "")
> >  
> >  #else
> 
> Not really a fan of that. And I can't really tell what you're doing with
> it either. The kunit is in Rust and thus unreadable :-(
> 
> I would rather you fix up is_valid_bugaddr(), some architectures seem to
> have an always true stub because of the callchains always being from the
> break instruction.

Hey Peter,

thanks for the suggestion. I will fix up the is_valid_bugaddr for
powerpc. I am not very good with other archs asm but i can do it for
ppc32 and ppc64.

Regards,
Mukesh

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] rust: kernel: Add KUnit tests for ARCH_WARN_ASM bug table emission
  2026-09-23  6:28   ` Mukesh Kumar Chaurasiya
@ 2026-09-24 11:15     ` Peter Zijlstra
  2026-09-24 12:46       ` Mark Rutland
  2026-09-24 15:13       ` Heiko Carstens
  0 siblings, 2 replies; 10+ messages in thread
From: Peter Zijlstra @ 2026-09-24 11:15 UTC (permalink / raw)
  To: Mukesh Kumar Chaurasiya
  Cc: catalin.marinas, will, mark.rutland, maddy, mpe, npiggin,
	chleroy, ritesh.list, sshegde, pjw, palmer, aou, alex, hca, gor,
	agordeev, borntraeger, svens, tglx, mingo, bp, dave.hansen, x86,
	hpa, ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg,
	aliceryhl, tmgross, dakr, daniel.almeida, tamird, acourbot, work,
	nathan, ndesaulniers, morbo, justinstitt, jszhang, japo,
	jpoimboe, seanjc, pmladek, thuth, ynorov, joelagnelf, david,
	fujita.tomonori, linux-arm-kernel, linux-kernel, linuxppc-dev,
	linux-riscv, linux-s390, rust-for-linux, llvm

On Wed, Sep 23, 2026 at 11:58:13AM +0530, Mukesh Kumar Chaurasiya wrote:
> On Tue, Sep 22, 2026 at 08:47:35AM +0200, Peter Zijlstra wrote:
> > On Tue, Sep 22, 2026 at 11:20:00AM +0530, Mukesh Kumar Chaurasiya (IBM) wrote:
> > > Verify that the __bug_table entry emitted by ARCH_WARN_ASM has a correct
> > > bug_addr displacement — i.e. the arch's trap label reference resolves to
> > > the trap instruction — by calling find_bug() with the exact virtual address
> > > of the trap, mirroring what the real trap handler does.
> > > 
> > > To support all architectures, each arch that implements ARCH_WARN_ASM now
> > > defines ARCH_WARN_ASM_TRAP_LABEL, a string constant naming the local label
> > > at which the trap instruction is placed:
> > > 
> > >   x86       "1"     (ud2 at label 1:)
> > >   powerpc   "1"     (twi at label 1:)
> > >   riscv     "1"     (ebreak at label 1:)
> > >   arm64     "14471" (brk placed at 14471: by __BUG_ENTRY_END)
> > >   s390      "0"     (mc at label 0:)
> > > 
> > > The label is used consistently: in ARCH_WARN_ASM itself, in the
> > > bug_addr back-reference inside __BUG_ENTRY / _EMIT_BUG_ENTRY, 
> > 
> > 
> > > diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h
> > > index 23ab05438269..6cca8880660e 100644
> > > --- a/arch/x86/include/asm/bug.h
> > > +++ b/arch/x86/include/asm/bug.h
> > > @@ -62,8 +62,9 @@ extern void __WARN_trap(struct bug_entry *bug, ...);
> > >  #define HAVE_ARCH_BUG_FORMAT_ARGS
> > >  #endif
> > >  
> > > +#define ARCH_WARN_ASM_TRAP_LABEL "1"
> > >  #define __BUG_ENTRY(format, file, line, flags)				\
> > > -	"\t.long 1b - ."	"\t# bug_entry::bug_addr\n"		\
> > > +	"\t.long " ARCH_WARN_ASM_TRAP_LABEL "b - ." "\t# bug_entry::bug_addr\n" \
> > >  	__BUG_ENTRY_FORMAT(format)					\
> > >  	__BUG_ENTRY_VERBOSE(file, line)					\
> > >  	"\t.word " flags	"\t# bug_entry::flags\n"
> > > @@ -101,7 +102,7 @@ do {									\
> > >  	"99:\n"								\
> > >  	"\t.string \"\"\n"						\
> > >  	".popsection\n"							\
> > > -	"1:\t " ASM_UD2 "\n"						\
> > > +	ARCH_WARN_ASM_TRAP_LABEL ":\t " ASM_UD2 "\n"			\
> > >  	_BUG_FLAGS_ASM("99b", file, line, flags, size, "")
> > >  
> > >  #else
> > 
> > Not really a fan of that. And I can't really tell what you're doing with
> > it either. The kunit is in Rust and thus unreadable :-(
> > 
> > I would rather you fix up is_valid_bugaddr(), some architectures seem to
> > have an always true stub because of the callchains always being from the
> > break instruction.
> 
> Hey Peter,
> 
> thanks for the suggestion. I will fix up the is_valid_bugaddr for
> powerpc. I am not very good with other archs asm but i can do it for
> ppc32 and ppc64.

Untested thingies for arm64 and s390x.

diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index 914282016069..3d8a969eab5f 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -987,14 +987,13 @@ void do_serror(struct pt_regs *regs, unsigned long esr)
 #ifdef CONFIG_GENERIC_BUG
 int is_valid_bugaddr(unsigned long addr)
 {
-	/*
-	 * bug_brk_handler() only called for BRK #BUG_BRK_IMM.
-	 * So the answer is trivial -- any spurious instances with no
-	 * bug table entry will be rejected by report_bug() and passed
-	 * back to the debug-monitors code and handled as a fatal
-	 * unexpected debug exception.
-	 */
-	return 1;
+	u32 insn;
+
+	if (aarch64_insn_read((u32 *)addr, &insn))
+		return 0;
+
+	/* Match ASM_BUG_FLAGS() / BUG() instructions. */
+	return insn == (0xd4200000 | (BUG_BRK_IMM << 5));
 }
 #endif
 
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index b6ba4465f59d..8bbb082fd5e3 100644
--- a/arch/s390/kernel/traps.c
+++ b/arch/s390/kernel/traps.c
@@ -26,6 +26,7 @@
 #include <linux/entry-common.h>
 #include <linux/kmsan.h>
 #include <linux/bug.h>
+#include <linux/uaccess.h>
 #include <asm/entry-percpu.h>
 #include <asm/asm-extable.h>
 #include <asm/irqflags.h>
@@ -55,7 +56,12 @@ static inline void __user *get_trap_ip(struct pt_regs *regs)
 #ifdef CONFIG_GENERIC_BUG
 int is_valid_bugaddr(unsigned long addr)
 {
-	return 1;
+	u16 insn;
+
+	if (get_kernel_nofault(insn, (u16 *)addr))
+		return 0;
+
+	return insn == 0x0001 || insn == 0x000e;
 }
 #endif
 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] rust: kernel: Add KUnit tests for ARCH_WARN_ASM bug table emission
  2026-09-22  5:50 [RFC] rust: kernel: Add KUnit tests for ARCH_WARN_ASM bug table emission Mukesh Kumar Chaurasiya (IBM)
  2026-09-22  6:47 ` Peter Zijlstra
@ 2026-09-24 12:31 ` Mark Rutland
  2026-09-24 13:00 ` Christophe Leroy (CS GROUP)
  2 siblings, 0 replies; 10+ messages in thread
From: Mark Rutland @ 2026-09-24 12:31 UTC (permalink / raw)
  To: Mukesh Kumar Chaurasiya (IBM)
  Cc: catalin.marinas, will, maddy, mpe, npiggin, chleroy, ritesh.list,
	sshegde, pjw, palmer, aou, alex, hca, gor, agordeev, borntraeger,
	svens, tglx, mingo, bp, dave.hansen, x86, hpa, ojeda, boqun,
	gary, bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, dakr,
	daniel.almeida, tamird, acourbot, work, nathan, ndesaulniers,
	morbo, justinstitt, jszhang, japo, peterz, jpoimboe, seanjc,
	pmladek, thuth, ynorov, joelagnelf, david, fujita.tomonori,
	linux-arm-kernel, linux-kernel, linuxppc-dev, linux-riscv,
	linux-s390, rust-for-linux, llvm

On Tue, Sep 22, 2026 at 11:20:00AM +0530, Mukesh Kumar Chaurasiya (IBM) wrote:
> Verify that the __bug_table entry emitted by ARCH_WARN_ASM has a correct
> bug_addr displacement — i.e. the arch's trap label reference resolves to
> the trap instruction — by calling find_bug() with the exact virtual address
> of the trap, mirroring what the real trap handler does.
> 
> To support all architectures, each arch that implements ARCH_WARN_ASM now
> defines ARCH_WARN_ASM_TRAP_LABEL, a string constant naming the local label
> at which the trap instruction is placed:
> 
>   x86       "1"     (ud2 at label 1:)
>   powerpc   "1"     (twi at label 1:)
>   riscv     "1"     (ebreak at label 1:)
>   arm64     "14471" (brk placed at 14471: by __BUG_ENTRY_END)
>   s390      "0"     (mc at label 0:)

I'm not a fan of exposing such a low-level implementation detail out of
the architecture code, and I don't think we should expose this.

What exactly are you trying to verify here, and why? Has there been
some _specific_ issue on powerpc that caused you to look at this?

Mark.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] rust: kernel: Add KUnit tests for ARCH_WARN_ASM bug table emission
  2026-09-24 11:15     ` Peter Zijlstra
@ 2026-09-24 12:46       ` Mark Rutland
  2026-09-24 12:52         ` Peter Zijlstra
  2026-09-24 15:13       ` Heiko Carstens
  1 sibling, 1 reply; 10+ messages in thread
From: Mark Rutland @ 2026-09-24 12:46 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Mukesh Kumar Chaurasiya, catalin.marinas, will, maddy, mpe,
	npiggin, chleroy, ritesh.list, sshegde, pjw, palmer, aou, alex,
	hca, gor, agordeev, borntraeger, svens, tglx, mingo, bp,
	dave.hansen, x86, hpa, ojeda, boqun, gary, bjorn3_gh, lossin,
	a.hindborg, aliceryhl, tmgross, dakr, daniel.almeida, tamird,
	acourbot, work, nathan, ndesaulniers, morbo, justinstitt,
	jszhang, japo, jpoimboe, seanjc, pmladek, thuth, ynorov,
	joelagnelf, david, fujita.tomonori, linux-arm-kernel,
	linux-kernel, linuxppc-dev, linux-riscv, linux-s390,
	rust-for-linux, llvm

On Thu, Sep 24, 2026 at 01:15:57PM +0200, Peter Zijlstra wrote:
> On Wed, Sep 23, 2026 at 11:58:13AM +0530, Mukesh Kumar Chaurasiya wrote:
> > On Tue, Sep 22, 2026 at 08:47:35AM +0200, Peter Zijlstra wrote:
> > > On Tue, Sep 22, 2026 at 11:20:00AM +0530, Mukesh Kumar Chaurasiya (IBM) wrote:
> > > > Verify that the __bug_table entry emitted by ARCH_WARN_ASM has a correct
> > > > bug_addr displacement — i.e. the arch's trap label reference resolves to
> > > > the trap instruction — by calling find_bug() with the exact virtual address
> > > > of the trap, mirroring what the real trap handler does.
> > > > 
> > > > To support all architectures, each arch that implements ARCH_WARN_ASM now
> > > > defines ARCH_WARN_ASM_TRAP_LABEL, a string constant naming the local label
> > > > at which the trap instruction is placed:
> > > > 
> > > >   x86       "1"     (ud2 at label 1:)
> > > >   powerpc   "1"     (twi at label 1:)
> > > >   riscv     "1"     (ebreak at label 1:)
> > > >   arm64     "14471" (brk placed at 14471: by __BUG_ENTRY_END)
> > > >   s390      "0"     (mc at label 0:)
> > > > 
> > > > The label is used consistently: in ARCH_WARN_ASM itself, in the
> > > > bug_addr back-reference inside __BUG_ENTRY / _EMIT_BUG_ENTRY, 

> > > Not really a fan of that. And I can't really tell what you're doing with
> > > it either. The kunit is in Rust and thus unreadable :-(
> > > 
> > > I would rather you fix up is_valid_bugaddr(), some architectures seem to
> > > have an always true stub because of the callchains always being from the
> > > break instruction.
> > 
> > Hey Peter,
> > 
> > thanks for the suggestion. I will fix up the is_valid_bugaddr for
> > powerpc. I am not very good with other archs asm but i can do it for
> > ppc32 and ppc64.
> 
> Untested thingies for arm64 and s390x.
> 
> diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
> index 914282016069..3d8a969eab5f 100644
> --- a/arch/arm64/kernel/traps.c
> +++ b/arch/arm64/kernel/traps.c
> @@ -987,14 +987,13 @@ void do_serror(struct pt_regs *regs, unsigned long esr)
>  #ifdef CONFIG_GENERIC_BUG
>  int is_valid_bugaddr(unsigned long addr)
>  {
> -	/*
> -	 * bug_brk_handler() only called for BRK #BUG_BRK_IMM.
> -	 * So the answer is trivial -- any spurious instances with no
> -	 * bug table entry will be rejected by report_bug() and passed
> -	 * back to the debug-monitors code and handled as a fatal
> -	 * unexpected debug exception.
> -	 */
> -	return 1;
> +	u32 insn;
> +
> +	if (aarch64_insn_read((u32 *)addr, &insn))
> +		return 0;
> +
> +	/* Match ASM_BUG_FLAGS() / BUG() instructions. */
> +	return insn == (0xd4200000 | (BUG_BRK_IMM << 5));
>  }
>  #endif

What problem is this trying to solve?

Mukesh's original patch seems to be trying to check whether we compiled
things correctly (such that the bug_table entry points at the expected
insturction), and I don't think that warrants a runtime check, but (as
per my reply to him) I don't understand why that's a thing to check in
the first place.

On arm64 we only call is_valid_bugaddr() under do_el1_brk64() ->
call_el1_break_hook() -> bug_brk_handler(). We only call
bug_brk_handler() when HW has taken a BRK exception with the BUG_BRK_IMM
immediate, and that can only be triggered by executing the right
instruction (BRK #BUG_BRK_IMM), which we ONLY use for BUG(). and WARN().

So I don't see why any change is necessary here.

If the HW has somehow taken a BRK exception from any other instruction
(which would presumably be a HW bug), there won't be a bug_table entry,
and so __report_bug() will fail to find an entry, and will return
BUG_TRAP_TYPE_NONE, same as for the !is_valid_bugaddr() case.

Mark.  

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] rust: kernel: Add KUnit tests for ARCH_WARN_ASM bug table emission
  2026-09-24 12:46       ` Mark Rutland
@ 2026-09-24 12:52         ` Peter Zijlstra
  2026-09-24 13:05           ` Mark Rutland
  0 siblings, 1 reply; 10+ messages in thread
From: Peter Zijlstra @ 2026-09-24 12:52 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Mukesh Kumar Chaurasiya, catalin.marinas, will, maddy, mpe,
	npiggin, chleroy, ritesh.list, sshegde, pjw, palmer, aou, alex,
	hca, gor, agordeev, borntraeger, svens, tglx, mingo, bp,
	dave.hansen, x86, hpa, ojeda, boqun, gary, bjorn3_gh, lossin,
	a.hindborg, aliceryhl, tmgross, dakr, daniel.almeida, tamird,
	acourbot, work, nathan, ndesaulniers, morbo, justinstitt,
	jszhang, japo, jpoimboe, seanjc, pmladek, thuth, ynorov,
	joelagnelf, david, fujita.tomonori, linux-arm-kernel,
	linux-kernel, linuxppc-dev, linux-riscv, linux-s390,
	rust-for-linux, llvm

On Thu, Sep 24, 2026 at 01:46:59PM +0100, Mark Rutland wrote:

> > diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
> > index 914282016069..3d8a969eab5f 100644
> > --- a/arch/arm64/kernel/traps.c
> > +++ b/arch/arm64/kernel/traps.c
> > @@ -987,14 +987,13 @@ void do_serror(struct pt_regs *regs, unsigned long esr)
> >  #ifdef CONFIG_GENERIC_BUG
> >  int is_valid_bugaddr(unsigned long addr)
> >  {
> > -	/*
> > -	 * bug_brk_handler() only called for BRK #BUG_BRK_IMM.
> > -	 * So the answer is trivial -- any spurious instances with no
> > -	 * bug table entry will be rejected by report_bug() and passed
> > -	 * back to the debug-monitors code and handled as a fatal
> > -	 * unexpected debug exception.
> > -	 */
> > -	return 1;
> > +	u32 insn;
> > +
> > +	if (aarch64_insn_read((u32 *)addr, &insn))
> > +		return 0;
> > +
> > +	/* Match ASM_BUG_FLAGS() / BUG() instructions. */
> > +	return insn == (0xd4200000 | (BUG_BRK_IMM << 5));
> >  }
> >  #endif
> 
> What problem is this trying to solve?
> 
> Mukesh's original patch seems to be trying to check whether we compiled
> things correctly (such that the bug_table entry points at the expected
> insturction), and I don't think that warrants a runtime check, but (as
> per my reply to him) I don't understand why that's a thing to check in
> the first place.
> 
> On arm64 we only call is_valid_bugaddr() under do_el1_brk64() ->
> call_el1_break_hook() -> bug_brk_handler(). We only call
> bug_brk_handler() when HW has taken a BRK exception with the BUG_BRK_IMM
> immediate, and that can only be triggered by executing the right
> instruction (BRK #BUG_BRK_IMM), which we ONLY use for BUG(). and WARN().
> 
> So I don't see why any change is necessary here.
> 
> If the HW has somehow taken a BRK exception from any other instruction
> (which would presumably be a HW bug), there won't be a bug_table entry,
> and so __report_bug() will fail to find an entry, and will return
> BUG_TRAP_TYPE_NONE, same as for the !is_valid_bugaddr() case.

Right, so I wasn't at all sure what he was doing, Rust is still mostly
unreadable garbage to me.

But I figured I'd much rather see him use is_valid_bugaddr() to do
runtime checks than whatever dodgy games he was playing with that label.

So *if* doing runtime checks is what is wanted, then is_valid_bugaddr()
needs to actually work, because then it grows a user outside of the BRK
handler.

Lots of IFs etc, but since I am having a head-ache and am mostly
useless, I figured I'd distract myself doing whatever :-)

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] rust: kernel: Add KUnit tests for ARCH_WARN_ASM bug table emission
  2026-09-22  5:50 [RFC] rust: kernel: Add KUnit tests for ARCH_WARN_ASM bug table emission Mukesh Kumar Chaurasiya (IBM)
  2026-09-22  6:47 ` Peter Zijlstra
  2026-09-24 12:31 ` Mark Rutland
@ 2026-09-24 13:00 ` Christophe Leroy (CS GROUP)
  2 siblings, 0 replies; 10+ messages in thread
From: Christophe Leroy (CS GROUP) @ 2026-09-24 13:00 UTC (permalink / raw)
  To: Mukesh Kumar Chaurasiya (IBM),
	catalin.marinas, will, mark.rutland, maddy, mpe, npiggin,
	ritesh.list, sshegde, pjw, palmer, aou, alex, hca, gor, agordeev,
	borntraeger, svens, tglx, mingo, bp, dave.hansen, x86, hpa,
	ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
	tmgross, dakr, daniel.almeida, tamird, acourbot, work, nathan,
	ndesaulniers, morbo, justinstitt, jszhang, japo, peterz,
	jpoimboe, seanjc, pmladek, thuth, ynorov, joelagnelf, david,
	fujita.tomonori, linux-arm-kernel, linux-kernel, linuxppc-dev,
	linux-riscv, linux-s390, rust-for-linux, llvm



Le 22/09/2026 à 07:50, Mukesh Kumar Chaurasiya (IBM) a écrit :
> Verify that the __bug_table entry emitted by ARCH_WARN_ASM has a correct
> bug_addr displacement — i.e. the arch's trap label reference resolves to
> the trap instruction — by calling find_bug() with the exact virtual address
> of the trap, mirroring what the real trap handler does.
> 
> To support all architectures, each arch that implements ARCH_WARN_ASM now
> defines ARCH_WARN_ASM_TRAP_LABEL, a string constant naming the local label
> at which the trap instruction is placed:
> 
>    x86       "1"     (ud2 at label 1:)
>    powerpc   "1"     (twi at label 1:)

Shouldn't it also handle twnei and tdnei used for WARN_ON() and BUG_ON() ?

Christophe


>    riscv     "1"     (ebreak at label 1:)
>    arm64     "14471" (brk placed at 14471: by __BUG_ENTRY_END)
>    s390      "0"     (mc at label 0:)
> 
> The label is used consistently: in ARCH_WARN_ASM itself, in the
> bug_addr back-reference inside __BUG_ENTRY / _EMIT_BUG_ENTRY, and in
> the new generated_arch_warn_asm_trap_label.rs template, which the
> C preprocessor expands at build time into a .rs file included by the
> test's global_asm! block.
> 
> global_asm! is used instead of asm! because LLVM eliminates asm! blocks
> in dead branches; global_asm! is file-scope and always emitted.
> BUG_KUNIT_TRAP_ADDR is defined as a .global symbol in .data on top of a
> .dc.a {trap_label}b relocation so the linker resolves the trap address
> into it at link time. A Rust static initialized to zero would land in
> BSS where relocations are not applied.
> 
> The global_asm! string mirrors warn_flags!() exactly — same "/* {size} */"
> prefix, same generated_arch_warn_asm.rs and generated_arch_reachable_asm.rs
> includes, same operands — plus the .pushsection/.dc.a/.popsection tail.
> This keeps the test structurally in sync with the macro it exercises.
> 
> The CONFIG_RUST_BUG_WARN_ASM_KUNIT_TEST option depends on GENERIC_BUG
> and excludes ARM, LOONGARCH, and UML (which use a different warn_flags!
> path without ARCH_WARN_ASM).
> 
> Five tests are included in the rust_kernel_bug_warn_asm suite:
> 
>    bug_entry_found           - find_bug() returns non-NULL for the trap
>                                address, proving bug_addr is correct
>    bug_entry_is_warning      - the emitted entry has BUGFLAG_WARNING set
>    bug_entry_file            - bug_get_file_line() returns the correct
>                                source file (CONFIG_DEBUG_BUGVERBOSE only)
>    bug_entry_line            - recorded line number is non-zero, confirming
>                                the {line} operand was substituted correctly
>                                (CONFIG_DEBUG_BUGVERBOSE only)
>    bug_entry_addr_is_in_text - kernel_text_address() confirms the trap
>                                address lies in kernel text
> 
> Tested (pass:5 fail:0 skip:0) on:
>    x86_64  - QEMU x86_64
>    powerpc - QEMU pseries ppc64le (pseries_le_defconfig)
>    riscv   - QEMU riscv64
>    arm64   - QEMU aarch64
>    s390    - QEMU s390x
> 
> Signed-off-by: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
> ---
> Note: I sent this out as a powerpc specific tests earlier. During review
> process some people wanted these to be arch independent. Hence sending
> this out as an RFC.
> 
> Earlier version: https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Flore.kernel.org%2Fall%2F20260915090453.1227034-3-mkchauras%40gmail.com%2F&data=05%7C02%7Cchristophe.leroy%40csgroup.eu%7C2fb25d39088f4c4e7ba308df186d76f5%7C8b87af7d86474dc78df45f69a2011bb5%7C0%7C0%7C639256530592353420%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=lzso63z76C74rUe7h%2FG%2FEkDi1yXK0oPXBVrlG427Z%2BM%3D&reserved=0
> 
>   arch/arm64/include/asm/asm-bug.h              |   3 +
>   arch/powerpc/include/asm/bug.h                |  22 +-
>   arch/riscv/include/asm/bug.h                  |   5 +-
>   arch/s390/include/asm/bug.h                   |   4 +-
>   arch/x86/include/asm/bug.h                    |   5 +-
>   rust/Makefile                                 |   2 +-
>   rust/kernel/.gitignore                        |   1 +
>   rust/kernel/Kconfig.test                      |  15 ++
>   rust/kernel/bug.rs                            | 191 ++++++++++++++++++
>   .../generated_arch_warn_asm_trap_label.rs.S   |   7 +
>   10 files changed, 239 insertions(+), 16 deletions(-)
>   create mode 100644 rust/kernel/generated_arch_warn_asm_trap_label.rs.S
> 
> diff --git a/arch/arm64/include/asm/asm-bug.h b/arch/arm64/include/asm/asm-bug.h
> index a5f13801b784..e0694543115e 100644
> --- a/arch/arm64/include/asm/asm-bug.h
> +++ b/arch/arm64/include/asm/asm-bug.h
> @@ -60,6 +60,9 @@ _BUGVERBOSE_LOCATION(__FILE__, __LINE__)		\
>   		".short " flags ";"			\
>   		__stringify(__BUG_ENTRY_END)
>   
> +/* Trap instruction is placed at label 14471: by __BUG_ENTRY_END */
> +#define ARCH_WARN_ASM_TRAP_LABEL "14471"
> +
>   #define ARCH_WARN_ASM(file, line, flags, size)		\
>   	__BUG_ENTRY_STRING(file, line, flags)		\
>   	__stringify(brk BUG_BRK_IMM)
> diff --git a/arch/powerpc/include/asm/bug.h b/arch/powerpc/include/asm/bug.h
> index bf31ee1e902a..f0333c564d4f 100644
> --- a/arch/powerpc/include/asm/bug.h
> +++ b/arch/powerpc/include/asm/bug.h
> @@ -32,6 +32,8 @@
>   #endif /* verbose */
>   
>   #else /* !__ASSEMBLER__ */
> +#define ARCH_WARN_ASM_TRAP_LABEL "1"
> +
>   #ifdef CONFIG_DEBUG_BUGVERBOSE
>   #define _EMIT_BUG_ENTRY(bug_entry, trap, file, line, flags)	\
>   	".section __bug_table,\"aw\"\n"				\
> @@ -45,21 +47,21 @@
>   	"		.short " flags "\n"
>   #endif
>   
> -#define BUG_ENTRY(cond_str, insn, flags, ...)			\
> -	__asm__ __volatile__(					\
> -		"1:	" insn "\n"				\
> -		_EMIT_BUG_ENTRY(2, 1b, "%0", "%1", "%2")	\
> -		".org 2b+%3\n"					\
> -		".previous\n"					\
> +#define BUG_ENTRY(cond_str, insn, flags, ...)				\
> +	__asm__ __volatile__(						\
> +		ARCH_WARN_ASM_TRAP_LABEL ":	" insn "\n"		\
> +		_EMIT_BUG_ENTRY(2, 1b, "%0", "%1", "%2")		\
> +		".org 2b+%3\n"						\
> +		".previous\n"						\
>   		: : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__),	\
>   		  "i" (flags),					\
>   		  "i" (sizeof(struct bug_entry)),		\
>   		  ##__VA_ARGS__)
>   
> -#define ARCH_WARN_ASM(file, line, flags, size)			\
> -		"1:	twi 31, 0, 0\n"				\
> -		_EMIT_BUG_ENTRY(2, 1b, file, line, flags)	\
> -		".org 2b+" size "\n"				\
> +#define ARCH_WARN_ASM(file, line, flags, size)					\
> +		ARCH_WARN_ASM_TRAP_LABEL ":	twi 31, 0, 0\n"			\
> +		_EMIT_BUG_ENTRY(2, 1b, file, line, flags)			\
> +		".org 2b+" size "\n"						\
>   		".previous\n"
>   
>   #define ARCH_WARN_REACHABLE
> diff --git a/arch/riscv/include/asm/bug.h b/arch/riscv/include/asm/bug.h
> index 699c0cf3e4ef..def6b0d153f2 100644
> --- a/arch/riscv/include/asm/bug.h
> +++ b/arch/riscv/include/asm/bug.h
> @@ -29,7 +29,8 @@
>   
>   typedef u32 bug_insn_t;
>   
> -#define __BUG_ENTRY_ADDR	RISCV_INT " 1b - ."
> +#define ARCH_WARN_ASM_TRAP_LABEL	"1"
> +#define __BUG_ENTRY_ADDR	RISCV_INT " " ARCH_WARN_ASM_TRAP_LABEL "b - ."
>   #define __BUG_ENTRY_FILE(file)	RISCV_INT " " file " - ."
>   
>   #ifdef CONFIG_DEBUG_BUGVERBOSE
> @@ -47,7 +48,7 @@ typedef u32 bug_insn_t;
>   #ifdef CONFIG_GENERIC_BUG
>   
>   #define ARCH_WARN_ASM(file, line, flags, size)			\
> -		"1:\n\t"					\
> +		ARCH_WARN_ASM_TRAP_LABEL ":\n\t"		\
>   			"ebreak\n"				\
>   			".pushsection __bug_table,\"aw\"\n\t"	\
>   		"2:\n\t"					\
> diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h
> index 2010342c97b1..3c0fa1acc92a 100644
> --- a/arch/s390/include/asm/bug.h
> +++ b/arch/s390/include/asm/bug.h
> @@ -26,9 +26,11 @@
>   #define WARN_CONDITION_STR(cond_str) ""
>   #endif
>   
> +#define ARCH_WARN_ASM_TRAP_LABEL "0"
> +
>   #define __BUG_ENTRY(format, file, line, flags, size)			\
>   		"	.section __bug_table,\"aw\"\n"			\
> -		"1:	.long	0b - .		# bug_entry::bug_addr\n"\
> +		"1:	.long	" ARCH_WARN_ASM_TRAP_LABEL "b - .	# bug_entry::bug_addr\n" \
>   		"	.long	" format " - .	# bug_entry::format\n"	\
>   		__BUG_ENTRY_VERBOSE(file, line)				\
>   		"	.short	"flags"		# bug_entry::flags\n"	\
> diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h
> index 23ab05438269..6cca8880660e 100644
> --- a/arch/x86/include/asm/bug.h
> +++ b/arch/x86/include/asm/bug.h
> @@ -62,8 +62,9 @@ extern void __WARN_trap(struct bug_entry *bug, ...);
>   #define HAVE_ARCH_BUG_FORMAT_ARGS
>   #endif
>   
> +#define ARCH_WARN_ASM_TRAP_LABEL "1"
>   #define __BUG_ENTRY(format, file, line, flags)				\
> -	"\t.long 1b - ."	"\t# bug_entry::bug_addr\n"		\
> +	"\t.long " ARCH_WARN_ASM_TRAP_LABEL "b - ." "\t# bug_entry::bug_addr\n" \
>   	__BUG_ENTRY_FORMAT(format)					\
>   	__BUG_ENTRY_VERBOSE(file, line)					\
>   	"\t.word " flags	"\t# bug_entry::flags\n"
> @@ -101,7 +102,7 @@ do {									\
>   	"99:\n"								\
>   	"\t.string \"\"\n"						\
>   	".popsection\n"							\
> -	"1:\t " ASM_UD2 "\n"						\
> +	ARCH_WARN_ASM_TRAP_LABEL ":\t " ASM_UD2 "\n"			\
>   	_BUG_FLAGS_ASM("99b", file, line, flags, size, "")
>   
>   #else
> diff --git a/rust/Makefile b/rust/Makefile
> index da1a7409d984..8f253d516495 100644
> --- a/rust/Makefile
> +++ b/rust/Makefile
> @@ -46,7 +46,7 @@ obj-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.o
>   
>   always-$(subst y,$(CONFIG_RUST),$(CONFIG_JUMP_LABEL)) += kernel/generated_arch_static_branch_asm.rs
>   ifndef CONFIG_UML
> -always-$(subst y,$(CONFIG_RUST),$(CONFIG_BUG)) += kernel/generated_arch_warn_asm.rs kernel/generated_arch_reachable_asm.rs
> +always-$(subst y,$(CONFIG_RUST),$(CONFIG_BUG)) += kernel/generated_arch_warn_asm.rs kernel/generated_arch_reachable_asm.rs kernel/generated_arch_warn_asm_trap_label.rs
>   endif
>   
>   # Avoids running `$(RUSTC)` when it may not be available.
> diff --git a/rust/kernel/.gitignore b/rust/kernel/.gitignore
> index f636ad95aaf3..05bb00bbb83c 100644
> --- a/rust/kernel/.gitignore
> +++ b/rust/kernel/.gitignore
> @@ -3,3 +3,4 @@
>   /generated_arch_static_branch_asm.rs
>   /generated_arch_warn_asm.rs
>   /generated_arch_reachable_asm.rs
> +/generated_arch_warn_asm_trap_label.rs
> diff --git a/rust/kernel/Kconfig.test b/rust/kernel/Kconfig.test
> index e6a5c7a795f0..8cd7c8f91b07 100644
> --- a/rust/kernel/Kconfig.test
> +++ b/rust/kernel/Kconfig.test
> @@ -83,4 +83,19 @@ config RUST_BITFIELD_KUNIT_TEST
>   
>   	  If unsure, say N.
>   
> +config RUST_BUG_WARN_ASM_KUNIT_TEST
> +	bool "KUnit tests for ARCH_WARN_ASM bug table emission" if !KUNIT_ALL_TESTS
> +	depends on GENERIC_BUG && !ARM && !LOONGARCH && !UML
> +	default KUNIT_ALL_TESTS
> +	help
> +	  This option enables KUnit tests that verify ARCH_WARN_ASM emits a
> +	  correct __bug_table entry: the bug_addr displacement must resolve
> +	  back to the trap instruction so that find_bug() can locate the
> +	  entry — exactly as the real trap handler does.
> +
> +	  Supported on all architectures that implement ARCH_WARN_ASM
> +	  (x86, powerpc, riscv, arm64, s390).
> +
> +	  If unsure, say N.
> +
>   endif
> diff --git a/rust/kernel/bug.rs b/rust/kernel/bug.rs
> index 3566f0234ca4..9e9b6d3a68b7 100644
> --- a/rust/kernel/bug.rs
> +++ b/rust/kernel/bug.rs
> @@ -152,3 +152,194 @@ macro_rules! warn_on {
>           cond
>       }};
>   }
> +
> +// Test-only constants and file static referenced by the global_asm block below.
> +//
> +// global_asm! is file-scope and always emitted — LLVM cannot eliminate it,
> +// unlike asm! inside a function which is subject to dead-code removal.
> +//
> +// BUG_KUNIT_TRAP_ADDR is declared as a .global symbol entirely inside the
> +// global_asm! block so the .dc.a {trap_label}b relocation lands directly on it.
> +// A Rust static initialized to zero would end up in BSS; the linker does
> +// not apply relocations to BSS, so the address would stay zero at runtime.
> +#[cfg(CONFIG_RUST_BUG_WARN_ASM_KUNIT_TEST)]
> +mod test_statics {
> +    use crate::bindings::{bug_entry, BUGFLAG_WARNING, TAINT_WARN};
> +
> +    pub(super) const FLAGS: u32 = BUGFLAG_WARNING | (TAINT_WARN << 8);
> +    pub(super) const SIZE: usize = core::mem::size_of::<bug_entry>();
> +
> +    // LINE and BUG_KUNIT_FILE are only referenced by the {file}/{line}
> +    // operands in global_asm!, which are only present when
> +    // CONFIG_DEBUG_BUGVERBOSE is set (the non-verbose _EMIT_BUG_ENTRY
> +    // drops the file/line fields from the bug table entry entirely).
> +    #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
> +    pub(super) const LINE: u32 = line!();
> +
> +    // Null-terminated source file name — the assembler references this symbol
> +    // for the verbose file pointer in __bug_table, same as warn_flags!.
> +    #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
> +    const _FILE: &[u8] = file!().as_bytes();
> +    #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
> +    #[no_mangle]
> +    pub(super) static BUG_KUNIT_FILE: [u8; _FILE.len() + 1] = {
> +        let mut bytes = [0u8; _FILE.len() + 1];
> +        let mut i = 0;
> +        while i < _FILE.len() {
> +            bytes[i] = _FILE[i];
> +            i += 1;
> +        }
> +        bytes
> +    };
> +}
> +
> +// Emit ARCH_WARN_ASM at file scope and capture the trap address.
> +//
> +// The asm string mirrors warn_flags!() exactly — same "/* {size} */" prefix,
> +// same generated_arch_warn_asm.rs and generated_arch_reachable_asm.rs includes,
> +// same operands — so the two stay in sync as the macro evolves.  The only
> +// addition is the .pushsection/.dc.a/.popsection tail that records the trap
> +// address into BUG_KUNIT_TRAP_ADDR at link time.
> +//
> +// BUG_KUNIT_TRAP_ADDR is a .global symbol in .data whose value is set by a
> +// .dc.a relocation to the arch's trap label (from
> +// generated_arch_warn_asm_trap_label.rs); .dc.a self-aligns to pointer width
> +// so no .balign is needed.
> +#[cfg(all(CONFIG_RUST_BUG_WARN_ASM_KUNIT_TEST, CONFIG_DEBUG_BUGVERBOSE))]
> +::core::arch::global_asm!(
> +    concat!(
> +        "/* {size} */",
> +        include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")),
> +        include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_reachable_asm.rs")),
> +        "\n\t.pushsection .data\n\t",
> +        ".global BUG_KUNIT_TRAP_ADDR\n\t",
> +        "BUG_KUNIT_TRAP_ADDR:\n\t",
> +        ".dc.a ",
> +        include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm_trap_label.rs")),
> +        "b\n\t",
> +        ".popsection\n",
> +    ),
> +    file  = sym test_statics::BUG_KUNIT_FILE,
> +    line  = const test_statics::LINE,
> +    flags = const test_statics::FLAGS,
> +    size  = const test_statics::SIZE,
> +);
> +
> +#[cfg(all(CONFIG_RUST_BUG_WARN_ASM_KUNIT_TEST, not(CONFIG_DEBUG_BUGVERBOSE)))]
> +::core::arch::global_asm!(
> +    concat!(
> +        "/* {size} */",
> +        include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")),
> +        include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_reachable_asm.rs")),
> +        "\n\t.pushsection .data\n\t",
> +        ".global BUG_KUNIT_TRAP_ADDR\n\t",
> +        "BUG_KUNIT_TRAP_ADDR:\n\t",
> +        ".dc.a ",
> +        include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm_trap_label.rs")),
> +        "b\n\t",
> +        ".popsection\n",
> +    ),
> +    flags = const test_statics::FLAGS,
> +    size  = const test_statics::SIZE,
> +);
> +
> +#[cfg(CONFIG_RUST_BUG_WARN_ASM_KUNIT_TEST)]
> +#[::kernel::macros::kunit_tests(rust_kernel_bug_warn_asm)]
> +mod tests {
> +    use crate::bindings;
> +
> +    fn trap_addr() -> usize {
> +        // BUG_KUNIT_TRAP_ADDR is a .global symbol defined in the global_asm!
> +        // block above, placed in .data at the exact .dc.a 1b relocation word.
> +        // The linker resolves it to the virtual address of the twi instruction
> +        // before any Rust code runs, so reading it here is always safe.
> +        extern "C" {
> +            // .dc.a emits a pointer-width word: 4 bytes on ppc32, 8 on ppc64.
> +            // usize matches the native pointer width on both.
> +            static BUG_KUNIT_TRAP_ADDR: usize;
> +        }
> +        // SAFETY: read-only after link time, no concurrent mutation possible.
> +        unsafe { BUG_KUNIT_TRAP_ADDR }
> +    }
> +
> +    /// The `__bug_table` entry emitted by `ARCH_WARN_ASM` must be locatable
> +    /// via `find_bug()` using the trap instruction's address.  A NULL result
> +    /// means the `1b` label reference in `_EMIT_BUG_ENTRY` resolved to the
> +    /// wrong address and the real trap handler would not recognise the site.
> +    #[test]
> +    fn bug_entry_found() {
> +        // Non-zero proves the .dc.a relocation was resolved by the linker.
> +        assert!(trap_addr() != 0);
> +
> +        // SAFETY: find_bug() is always safe to call with any address; it
> +        // simply walks __bug_table and returns NULL if nothing matches.
> +        let entry = unsafe { bindings::find_bug(trap_addr()) };
> +        // Non-NULL proves the bug_addr displacement in _EMIT_BUG_ENTRY is correct.
> +        assert!(!entry.is_null());
> +    }
> +
> +    /// The emitted entry must be flagged as a warning (not a hard BUG).
> +    #[test]
> +    fn bug_entry_is_warning() {
> +        assert!(trap_addr() != 0);
> +        let entry = unsafe { bindings::find_bug(trap_addr()) };
> +        assert!(!entry.is_null());
> +        // SAFETY: entry is non-null and points to a valid bug_entry.
> +        let flags = unsafe { (*entry).flags } as u32;
> +        assert!(flags & bindings::BUGFLAG_WARNING != 0);
> +    }
> +
> +    /// With `CONFIG_DEBUG_BUGVERBOSE` the entry must record a non-null file
> +    /// pointer pointing back into this source file.
> +    #[test]
> +    #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
> +    fn bug_entry_file() {
> +        use kernel::str::CStrExt;
> +
> +        assert!(trap_addr() != 0);
> +        let entry = unsafe { bindings::find_bug(trap_addr()) };
> +        assert!(!entry.is_null());
> +
> +        let mut file_ptr: *const kernel::ffi::c_char = core::ptr::null();
> +        let mut line: u32 = 0;
> +        // SAFETY: entry is non-null and valid; file_ptr and line are local
> +        // variables passed as out-parameters.
> +        unsafe { bindings::bug_get_file_line(entry, &mut file_ptr, &mut line) };
> +
> +        assert!(!file_ptr.is_null());
> +        // SAFETY: file_ptr is a non-null, null-terminated C string from BUG_KUNIT_FILE,
> +        // valid for the lifetime of this function.
> +        let file_str = unsafe { kernel::prelude::CStr::from_char_ptr(file_ptr) }
> +            .to_str()
> +            .unwrap_or("");
> +        assert!(file_str.contains("bug"));
> +    }
> +
> +    /// With `CONFIG_DEBUG_BUGVERBOSE` the recorded line number must be
> +    /// non-zero (a zero line would mean the asm operand was not substituted).
> +    #[test]
> +    #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
> +    fn bug_entry_line() {
> +        assert!(trap_addr() != 0);
> +        let entry = unsafe { bindings::find_bug(trap_addr()) };
> +        assert!(!entry.is_null());
> +
> +        let mut file_ptr: *const kernel::ffi::c_char = core::ptr::null();
> +        let mut line: u32 = 0;
> +        // SAFETY: entry is non-null and valid.
> +        unsafe { bindings::bug_get_file_line(entry, &mut file_ptr, &mut line) };
> +
> +        assert!(line != 0);
> +    }
> +
> +    /// The trap address stored in `__bug_table` must lie within the kernel
> +    /// text segment.  If the label reference in `_EMIT_BUG_ENTRY` resolved
> +    /// to data or zero, `kernel_text_address()` would return false.
> +    #[test]
> +    fn bug_entry_addr_is_in_text() {
> +        assert!(trap_addr() != 0);
> +        // SAFETY: kernel_text_address() is always safe to call with any addr.
> +        let in_text = unsafe { bindings::kernel_text_address(trap_addr()) };
> +        assert!(in_text != 0);
> +    }
> +}
> diff --git a/rust/kernel/generated_arch_warn_asm_trap_label.rs.S b/rust/kernel/generated_arch_warn_asm_trap_label.rs.S
> new file mode 100644
> index 000000000000..d7bed8751f10
> --- /dev/null
> +++ b/rust/kernel/generated_arch_warn_asm_trap_label.rs.S
> @@ -0,0 +1,7 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#include <linux/bug.h>
> +
> +// Cut here.
> +
> +::kernel::concat_literals!(ARCH_WARN_ASM_TRAP_LABEL)


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] rust: kernel: Add KUnit tests for ARCH_WARN_ASM bug table emission
  2026-09-24 12:52         ` Peter Zijlstra
@ 2026-09-24 13:05           ` Mark Rutland
  0 siblings, 0 replies; 10+ messages in thread
From: Mark Rutland @ 2026-09-24 13:05 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Mukesh Kumar Chaurasiya, catalin.marinas, will, maddy, mpe,
	npiggin, chleroy, ritesh.list, sshegde, pjw, palmer, aou, alex,
	hca, gor, agordeev, borntraeger, svens, tglx, mingo, bp,
	dave.hansen, x86, hpa, ojeda, boqun, gary, bjorn3_gh, lossin,
	a.hindborg, aliceryhl, tmgross, dakr, daniel.almeida, tamird,
	acourbot, work, nathan, ndesaulniers, morbo, justinstitt,
	jszhang, japo, jpoimboe, seanjc, pmladek, thuth, ynorov,
	joelagnelf, david, fujita.tomonori, linux-arm-kernel,
	linux-kernel, linuxppc-dev, linux-riscv, linux-s390,
	rust-for-linux, llvm

On Thu, Sep 24, 2026 at 02:52:53PM +0200, Peter Zijlstra wrote:
> On Thu, Sep 24, 2026 at 01:46:59PM +0100, Mark Rutland wrote:
> > What problem is this trying to solve?
> > 
> > Mukesh's original patch seems to be trying to check whether we compiled
> > things correctly (such that the bug_table entry points at the expected
> > insturction), and I don't think that warrants a runtime check, but (as
> > per my reply to him) I don't understand why that's a thing to check in
> > the first place.
> > 
> > On arm64 we only call is_valid_bugaddr() under do_el1_brk64() ->
> > call_el1_break_hook() -> bug_brk_handler(). We only call
> > bug_brk_handler() when HW has taken a BRK exception with the BUG_BRK_IMM
> > immediate, and that can only be triggered by executing the right
> > instruction (BRK #BUG_BRK_IMM), which we ONLY use for BUG(). and WARN().
> > 
> > So I don't see why any change is necessary here.
> > 
> > If the HW has somehow taken a BRK exception from any other instruction
> > (which would presumably be a HW bug), there won't be a bug_table entry,
> > and so __report_bug() will fail to find an entry, and will return
> > BUG_TRAP_TYPE_NONE, same as for the !is_valid_bugaddr() case.
> 
> Right, so I wasn't at all sure what he was doing, Rust is still mostly
> unreadable garbage to me.
> 
> But I figured I'd much rather see him use is_valid_bugaddr() to do
> runtime checks than whatever dodgy games he was playing with that label.
> 
> So *if* doing runtime checks is what is wanted, then is_valid_bugaddr()
> needs to actually work, because then it grows a user outside of the BRK
> handler.

Thanks; I think we're aligned. :)

Before we delve into implementation details, I'd like to understand the
problem Mukesh is trying to solve.

Mark.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] rust: kernel: Add KUnit tests for ARCH_WARN_ASM bug table emission
  2026-09-24 11:15     ` Peter Zijlstra
  2026-09-24 12:46       ` Mark Rutland
@ 2026-09-24 15:13       ` Heiko Carstens
  1 sibling, 0 replies; 10+ messages in thread
From: Heiko Carstens @ 2026-09-24 15:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Mukesh Kumar Chaurasiya, catalin.marinas, will, mark.rutland,
	maddy, mpe, npiggin, chleroy, ritesh.list, sshegde, pjw, palmer,
	aou, alex, gor, agordeev, borntraeger, svens, tglx, mingo, bp,
	dave.hansen, x86, hpa, ojeda, boqun, gary, bjorn3_gh, lossin,
	a.hindborg, aliceryhl, tmgross, dakr, daniel.almeida, tamird,
	acourbot, work, nathan, ndesaulniers, morbo, justinstitt,
	jszhang, japo, jpoimboe, seanjc, pmladek, thuth, ynorov,
	joelagnelf, david, fujita.tomonori, linux-arm-kernel,
	linux-kernel, linuxppc-dev, linux-riscv, linux-s390,
	rust-for-linux, llvm

On Thu, Sep 24, 2026 at 01:15:57PM +0200, Peter Zijlstra wrote:
> Untested thingies for arm64 and s390x.
...
> diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
> index b6ba4465f59d..8bbb082fd5e3 100644
> --- a/arch/s390/kernel/traps.c
> +++ b/arch/s390/kernel/traps.c
> @@ -26,6 +26,7 @@
>  #include <linux/entry-common.h>
>  #include <linux/kmsan.h>
>  #include <linux/bug.h>
> +#include <linux/uaccess.h>
>  #include <asm/entry-percpu.h>
>  #include <asm/asm-extable.h>
>  #include <asm/irqflags.h>
> @@ -55,7 +56,12 @@ static inline void __user *get_trap_ip(struct pt_regs *regs)
>  #ifdef CONFIG_GENERIC_BUG
>  int is_valid_bugaddr(unsigned long addr)
>  {
> -	return 1;
> +	u16 insn;
> +
> +	if (get_kernel_nofault(insn, (u16 *)addr))
> +		return 0;
> +
> +	return insn == 0x0001 || insn == 0x000e;

I have no idea why anybody wants to do this, but it looks like I'm not
alone :) In any case: the check is wrong.
s390 uses only the monitor call instruction and this should be

	return insn == 0xaf00;

At least it is not possible to set a kprobe on bug instructions, which
would make this "interesting".

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-09-24 15:14 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-22  5:50 [RFC] rust: kernel: Add KUnit tests for ARCH_WARN_ASM bug table emission Mukesh Kumar Chaurasiya (IBM)
2026-09-22  6:47 ` Peter Zijlstra
2026-09-23  6:28   ` Mukesh Kumar Chaurasiya
2026-09-24 11:15     ` Peter Zijlstra
2026-09-24 12:46       ` Mark Rutland
2026-09-24 12:52         ` Peter Zijlstra
2026-09-24 13:05           ` Mark Rutland
2026-09-24 15:13       ` Heiko Carstens
2026-09-24 12:31 ` Mark Rutland
2026-09-24 13:00 ` Christophe Leroy (CS GROUP)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®