Linux Kernel Selftest development
 help / color / mirror / Atom feed
From: Kees Cook <kees@kernel.org>
To: Peter Zijlstra <peterz@infradead.org>
Cc: Alessandro Carminati <acarmina@redhat.com>,
	linux-kselftest@vger.kernel.org,
	Dan Carpenter <dan.carpenter@linaro.org>,
	Daniel Diaz <daniel.diaz@linaro.org>,
	David Gow <davidgow@google.com>,
	Arthur Grillo <arthurgrillo@riseup.net>,
	Brendan Higgins <brendan.higgins@linux.dev>,
	Naresh Kamboju <naresh.kamboju@linaro.org>,
	Andrew Morton <akpm@linux-foundation.org>,
	Maxime Ripard <mripard@kernel.org>,
	Ville Syrjala <ville.syrjala@linux.intel.com>,
	Daniel Vetter <daniel@ffwll.ch>,
	Guenter Roeck <linux@roeck-us.net>,
	Alessandro Carminati <alessandro.carminati@gmail.com>,
	Jani Nikula <jani.nikula@intel.com>,
	Jeff Johnson <jeff.johnson@oss.qualcomm.com>,
	Josh Poimboeuf <jpoimboe@kernel.org>,
	Shuah Khan <skhan@linuxfoundation.org>,
	Linux Kernel Functional Testing <lkft@linaro.org>,
	dri-devel@lists.freedesktop.org, kunit-dev@googlegroups.com,
	linux-kernel@vger.kernel.org, Mark Rutland <mark.rutland@arm.com>
Subject: Re: [PATCH v5 1/5] bug/kunit: Core support for suppressing warning backtraces
Date: Fri, 30 May 2025 10:48:47 -0700	[thread overview]
Message-ID: <202505301037.D816A49@keescook> (raw)
In-Reply-To: <20250530140140.GE21197@noisy.programming.kicks-ass.net>

On Fri, May 30, 2025 at 04:01:40PM +0200, Peter Zijlstra wrote:
> I'm not really concerned with performance here, but more with the size
> of the code emitted by WARN_ONCE(). There are a *ton* of WARN sites,
> while only one report_bug() and printk().
> 
> The really offensive thing is that this is for a feature most nobody
> will ever need :/

Well, it won't be enabled often -- this reminds me of ftrace: it needs
to work, but it'll be off most of the time.

> The below results in:
> 
> 03dc  7ac:      48 c7 c0 00 00 00 00    mov    $0x0,%rax        7af: R_X86_64_32S       .rodata.str1.1+0x223
> 03e3  7b3:      ba 2a 00 00 00          mov    $0x2a,%edx
> 03e8  7b8:      48 0f b9 d0             ud1    %rax,%rdx
> 
> And it even works :-)
> 
> Hmm... I should try and stick the format string into the __bug_table,
> its const after all. Then I can get 2 arguments covered.

I like the patch! Can you add a _little_ documentation, though? e.g.
explaining that BUG_WARN ... BUG_WARN_END is for format string args,
etc.

But yes, I *love* that this moves the printk into the handler! Like you,
I have been bothered by this for a long time and could not find a good
way to do it. This is nice.

> 
> ---
> diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h
> index f0e9acf72547..88b305d49f35 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/args.h>
>  
>  /*
>   * Despite that some emulators terminate on UD2, we use it for WARN().
> @@ -28,50 +29,44 @@
>  #define BUG_UD1_UBSAN		0xfffc
>  #define BUG_EA			0xffea
>  #define BUG_LOCK		0xfff0
> +#define BUG_WARN		0xfe00
> +#define BUG_WARN_END		0xfeff
>  
>  #ifdef CONFIG_GENERIC_BUG
>  
>  #ifdef CONFIG_X86_32
> -# define __BUG_REL(val)	".long " __stringify(val)
> +#define ASM_BUG_REL(val)	.long val
>  #else
> -# define __BUG_REL(val)	".long " __stringify(val) " - ."
> +#define ASM_BUG_REL(val)	.long val - .
>  #endif
>  
>  #ifdef CONFIG_DEBUG_BUGVERBOSE
> +#define ASM_BUGTABLE_VERBOSE(file, line)				\
> +	ASM_BUG_REL(file) ;						\
> +	.word line
> +#define ASM_BUGTABLE_VERBOSE_SIZE	6
> +#else
> +#define ASM_BUGTABLE_VERBOSE(file, line)
> +#define ASM_BUGTABLE_VERBOSE_SIZE	0
> +#endif
>  
> -#define _BUG_FLAGS(ins, flags, extra)					\
> -do {									\
> -	asm_inline volatile("1:\t" ins "\n"				\
> -		     ".pushsection __bug_table,\"aw\"\n"		\
> -		     "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n"	\
> -		     "\t"  __BUG_REL(%c0) "\t# bug_entry::file\n"	\
> -		     "\t.word %c1"        "\t# bug_entry::line\n"	\
> -		     "\t.word %c2"        "\t# bug_entry::flags\n"	\
> -		     "\t.org 2b+%c3\n"					\
> -		     ".popsection\n"					\
> -		     extra						\
> -		     : : "i" (__FILE__), "i" (__LINE__),		\
> -			 "i" (flags),					\
> -			 "i" (sizeof(struct bug_entry)));		\
> -} while (0)
> -
> -#else /* !CONFIG_DEBUG_BUGVERBOSE */
> +#define ASM_BUGTABLE_FLAGS(at, file, line, flags)			\
> +	.pushsection __bug_table, "aw" ;				\
> +	123:	ASM_BUG_REL(at) ;					\
> +	ASM_BUGTABLE_VERBOSE(file, line) ;				\
> +	.word	flags ;							\
> +	.org 123b + 6 + ASM_BUGTABLE_VERBOSE_SIZE ;			\
> +	.popsection
>  
> -#define _BUG_FLAGS(ins, flags, extra)					\
> +#define _BUG_FLAGS(ins, flags, extra, extra_args...)			\
>  do {									\
>  	asm_inline volatile("1:\t" ins "\n"				\
> -		     ".pushsection __bug_table,\"aw\"\n"		\
> -		     "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n"	\
> -		     "\t.word %c0"        "\t# bug_entry::flags\n"	\
> -		     "\t.org 2b+%c1\n"					\
> -		     ".popsection\n"					\
> -		     extra						\
> -		     : : "i" (flags),					\
> -			 "i" (sizeof(struct bug_entry)));		\
> +	    __stringify(ASM_BUGTABLE_FLAGS(1b, %c0, %c1, %c2)) "\n"	\
> +			    extra					\
> +		     : : "i" (__FILE__), "i" (__LINE__),		\
> +			 "i" (flags), ## extra_args);			\
>  } while (0)
>  
> -#endif /* CONFIG_DEBUG_BUGVERBOSE */
> -
>  #else
>  
>  #define _BUG_FLAGS(ins, flags, extra)  asm volatile(ins)
> @@ -100,6 +95,40 @@ do {								\
>  	instrumentation_end();					\
>  } while (0)
>  
> +#define __WARN_printf_1(taint, format)				\
> +do { \
> +	__auto_type __flags = BUGFLAG_WARNING | BUGFLAG_NO_CUT_HERE | BUGFLAG_TAINT(taint); \
> +	unsigned long dummy = 0; \
> +	instrumentation_begin(); \
> +	asm_inline volatile("1: ud1 %[fmt], %[arg]\n"			\
> +	    __stringify(ASM_BUGTABLE_FLAGS(1b, %c0, %c1, %c2)) "\n"	\
> +		     : : "i" (__FILE__), "i" (__LINE__),		\
> +			 "i" (__flags), [fmt] "r" (format), [arg] "r" (dummy));		\
> +	instrumentation_end(); \
> +} while (0)
> +
> +#define __WARN_printf_2(taint, format, _arg)				\
> +do { \
> +	__auto_type __flags = BUGFLAG_WARNING | BUGFLAG_NO_CUT_HERE | BUGFLAG_TAINT(taint); \
> +	instrumentation_begin(); \
> +	asm_inline volatile("1: ud1 %[fmt], %[arg]\n"			\
> +	    __stringify(ASM_BUGTABLE_FLAGS(1b, %c0, %c1, %c2)) "\n"	\
> +		     : : "i" (__FILE__), "i" (__LINE__),		\
> +			 "i" (__flags), [fmt] "r" (format), [arg] "r" ((unsigned long)(_arg)));		\
> +	instrumentation_end(); \
> +} while (0)
> +
> +#define __WARN_printf_n(taint, fmt, arg...) do {			\
> +		instrumentation_begin();				\
> +		__warn_printk(fmt, arg);				\
> +		__WARN_FLAGS(BUGFLAG_NO_CUT_HERE | BUGFLAG_TAINT(taint));\
> +		instrumentation_end();					\
> +	} while (0)
> +
> +#define WARN_ARGS(X...) __COUNT_ARGS(, ##X, n, n, n, n, n, n, n, n, n, n, n, n, n, 2, 1, 0)
> +
> +#define __WARN_printf(taint, arg...) CONCATENATE(__WARN_printf_, WARN_ARGS(arg))(taint, arg)

This needs docs too. I think this is collapsing 1 and 2 argument WARNs
into the ud1, and anything larger is explicitly calling __warn_printk +
__WARN_FLAGS? If only 1 and 2 args can be collapsed, why reserve 0xfe00
through 0xfeff? I feel like I'm missing something here...

> +
>  #include <asm-generic/bug.h>
>  
>  #endif /* _ASM_X86_BUG_H */
> diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
> index 94c0236963c6..b7f69f4addf4 100644
> --- a/arch/x86/kernel/traps.c
> +++ b/arch/x86/kernel/traps.c
> @@ -81,18 +81,6 @@
>  
>  DECLARE_BITMAP(system_vectors, NR_VECTORS);
>  
> -__always_inline int is_valid_bugaddr(unsigned long addr)
> -{
> -	if (addr < TASK_SIZE_MAX)
> -		return 0;
> -
> -	/*
> -	 * We got #UD, if the text isn't readable we'd have gotten
> -	 * a different exception.
> -	 */
> -	return *(unsigned short *)addr == INSN_UD2;
> -}
> -
>  /*
>   * Check for UD1 or UD2, accounting for Address Size Override Prefixes.
>   * If it's a UD1, further decode to determine its use:
> @@ -102,25 +90,37 @@ __always_inline int is_valid_bugaddr(unsigned long addr)
>   * UBSan{0}:     67 0f b9 00             ud1    (%eax),%eax
>   * UBSan{10}:    67 0f b9 40 10          ud1    0x10(%eax),%eax
>   * static_call:  0f b9 cc                ud1    %esp,%ecx
> + * WARN_printf:                          ud1    %reg,%reg
>   *
> - * Notably UBSAN uses EAX, static_call uses ECX.
> + * Notably UBSAN uses (%eax), static_call uses %esp,%ecx
>   */
>  __always_inline int decode_bug(unsigned long addr, s32 *imm, int *len)
>  {
>  	unsigned long start = addr;
> +	u8 v, rex = 0, reg, rm;
>  	bool lock = false;
> -	u8 v;
> +	int type = BUG_UD1;
>  
>  	if (addr < TASK_SIZE_MAX)
>  		return BUG_NONE;
>  
> -	v = *(u8 *)(addr++);
> -	if (v == INSN_ASOP)
> +	for (;;) {
>  		v = *(u8 *)(addr++);
>  
> -	if (v == INSN_LOCK) {
> -		lock = true;
> -		v = *(u8 *)(addr++);
> +		if (v == INSN_ASOP)
> +			continue;
> +
> +		if (v == INSN_LOCK) {
> +			lock = true;
> +			continue;
> +		}
> +
> +		if ((v & 0xf0) == 0x40) {
> +			rex = v;
> +			continue;
> +		}
> +
> +		break;
>  	}
>  
>  	switch (v) {
> @@ -156,9 +156,13 @@ __always_inline int decode_bug(unsigned long addr, s32 *imm, int *len)
>  	if (X86_MODRM_MOD(v) != 3 && X86_MODRM_RM(v) == 4)
>  		addr++;			/* SIB */
>  
> +	reg = X86_MODRM_REG(v) + 8*!!X86_REX_R(rex);
> +	rm  = X86_MODRM_RM(v)  + 8*!!X86_REX_B(rex);
> +
>  	/* Decode immediate, if present */
>  	switch (X86_MODRM_MOD(v)) {
> -	case 0: if (X86_MODRM_RM(v) == 5)
> +	case 0: *imm = 0;
> +		if (X86_MODRM_RM(v) == 5)
>  			addr += 4; /* RIP + disp32 */
>  		break;
>  
> @@ -170,18 +174,37 @@ __always_inline int decode_bug(unsigned long addr, s32 *imm, int *len)
>  		addr += 4;
>  		break;
>  
> -	case 3: break;
> +	case 3: if (rm != 4) /* %esp */
> +			type = BUG_WARN | (rm << 4) | reg;
> +		break;
>  	}
>  
>  	/* record instruction length */
>  	*len = addr - start;
>  
> -	if (X86_MODRM_REG(v) == 0)	/* EAX */
> +	if (!rm && X86_MODRM_MOD(v) != 3)	/* (%eax) */
>  		return BUG_UD1_UBSAN;
>  
> -	return BUG_UD1;
> +	return type;
>  }
>  
> +int is_valid_bugaddr(unsigned long addr)
> +{
> +	int ud_type, ud_len;
> +	u32 ud_imm;
> +
> +	if (addr < TASK_SIZE_MAX)
> +		return 0;
> +
> +	/*
> +	 * We got #UD, if the text isn't readable we'd have gotten
> +	 * a different exception.
> +	 */
> +	ud_type = decode_bug(addr, &ud_imm, &ud_len);
> +
> +	return ud_type == BUG_UD2 ||
> +		(ud_type >= BUG_WARN && ud_type <= BUG_WARN_END);
> +}
>  
>  static nokprobe_inline int
>  do_trap_no_signal(struct task_struct *tsk, int trapnr, const char *str,
> @@ -305,6 +328,14 @@ static inline void handle_invalid_op(struct pt_regs *regs)
>  		      ILL_ILLOPN, error_get_trap_addr(regs));
>  }
>  
> +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));
> +}
> +
>  static noinstr bool handle_bug(struct pt_regs *regs)
>  {
>  	unsigned long addr = regs->ip;
> @@ -334,6 +365,14 @@ static noinstr bool handle_bug(struct pt_regs *regs)
>  		raw_local_irq_enable();
>  
>  	switch (ud_type) {
> +	case BUG_WARN ... BUG_WARN_END:
> +		int ud_reg = ud_type & 0xf;
> +		int ud_rm  = (ud_type >> 4) & 0xf;
> +
> +		__warn_printk((const char *)(pt_regs_val(regs, ud_rm)),
> +			      pt_regs_val(regs, ud_reg));
> +		fallthrough;

Yay, internal printk. :):)

> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index 62b3416f5e43..564513f605ac 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -8703,6 +8703,8 @@ void __init sched_init(void)
>  	preempt_dynamic_init();
>  
>  	scheduler_running = 1;
> +
> +	WARN(true, "Ultimate answer: %d\n", 42);
>  }
>  
>  #ifdef CONFIG_DEBUG_ATOMIC_SLEEP

If any code would emit The Answer, it would be the scheduler. :)

-- 
Kees Cook

  reply	other threads:[~2025-05-30 17:48 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-05-26 13:27 [PATCH v5 0/5] kunit: Add support for suppressing warning backtraces Alessandro Carminati
2025-05-26 13:27 ` [PATCH v5 1/5] bug/kunit: Core " Alessandro Carminati
2025-05-28 22:47   ` Kees Cook
2025-05-29  9:02     ` Peter Zijlstra
2025-05-29 17:46       ` Kees Cook
2025-05-30  9:25         ` Peter Zijlstra
2025-05-29  9:01   ` Peter Zijlstra
2025-05-29 10:36     ` Alessandro Carminati
2025-05-30 14:01       ` Peter Zijlstra
2025-05-30 17:48         ` Kees Cook [this message]
2025-05-31 10:23           ` Peter Zijlstra
2025-05-31 13:51             ` Kees Cook
2025-06-02  7:57               ` Peter Zijlstra
2025-06-02 10:38                 ` Maxime Ripard
2025-06-03 11:40                   ` Simona Vetter
2025-06-02 11:13             ` Maxime Ripard
2025-06-03 12:26               ` Peter Zijlstra
2025-06-04  3:30                 ` Daniel Latypov
2025-06-06  8:05                 ` Maxime Ripard
2025-05-31 10:25           ` Peter Zijlstra
2025-05-31  7:46         ` Peter Zijlstra
2025-05-31  7:52         ` Alessandro Carminati
2025-05-30  9:26   ` Peter Zijlstra
2025-05-26 13:27 ` [PATCH v5 2/5] bug/kunit: Suppressing warning backtraces reduced impact on WARN*() sites Alessandro Carminati
2025-05-28 22:52   ` Kees Cook
2025-05-26 13:27 ` [PATCH v5 3/5] Add unit tests to verify that warning backtrace suppression works Alessandro Carminati
2025-05-26 13:27 ` [PATCH v5 4/5] drm: Suppress intentional warning backtraces in scaling unit tests Alessandro Carminati
2025-05-26 13:27 ` [PATCH v5 5/5] kunit: Add documentation for warning backtrace suppression API Alessandro Carminati
2025-06-02  7:24 ` [PATCH v5 0/5] kunit: Add support for suppressing warning backtraces Dan Carpenter
2025-06-02 10:47   ` Maxime Ripard

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=202505301037.D816A49@keescook \
    --to=kees@kernel.org \
    --cc=acarmina@redhat.com \
    --cc=akpm@linux-foundation.org \
    --cc=alessandro.carminati@gmail.com \
    --cc=arthurgrillo@riseup.net \
    --cc=brendan.higgins@linux.dev \
    --cc=dan.carpenter@linaro.org \
    --cc=daniel.diaz@linaro.org \
    --cc=daniel@ffwll.ch \
    --cc=davidgow@google.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=jani.nikula@intel.com \
    --cc=jeff.johnson@oss.qualcomm.com \
    --cc=jpoimboe@kernel.org \
    --cc=kunit-dev@googlegroups.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux@roeck-us.net \
    --cc=lkft@linaro.org \
    --cc=mark.rutland@arm.com \
    --cc=mripard@kernel.org \
    --cc=naresh.kamboju@linaro.org \
    --cc=peterz@infradead.org \
    --cc=skhan@linuxfoundation.org \
    --cc=ville.syrjala@linux.intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox