The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v2 0/6] kcov: Suppress timer and scheduler coverage leaks
@ 2026-08-11 15:41 Karl Mehltretter
  2026-08-11 15:41 ` [PATCH v2 1/6] kcov: Use unsigned int for kcov_start() mode parameter Karl Mehltretter
                   ` (6 more replies)
  0 siblings, 7 replies; 13+ messages in thread
From: Karl Mehltretter @ 2026-08-11 15:41 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Karl Mehltretter, Andrey Konovalov, Alexander Potapenko,
	Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
	Frederic Weisbecker, Thomas Gleixner, Ingo Molnar, Peter Zijlstra,
	Juri Lelli, Vincent Guittot, Dietmar Eggemann, Steven Rostedt,
	Ben Segall, Mel Gorman, Valentin Schneider, K Prateek Nayak,
	Sebastian Andrzej Siewior, Clark Williams, linux-rt-devel,
	kasan-dev, linux-kernel

KCOV aims to exclude interrupt and scheduler coverage so syscall coverage
stays input-dependent. Instrumented callees can still record when
uninstrumented timer and scheduler paths run with in_task() true.

With the diagnostic patch in [1] applied, CONFIG_KCOV_SELFTEST exposes
three cases on x86-64: deferred hrtimer rearm, __schedule() callees and
PREEMPT_RT wakeups. Task-context wakeups and new-task enqueue also add
scheduler coverage to ordinary syscalls.

Add a nestable KCOV_PAUSED bit and a kcov_pause guard. Use the guard for
deferred hrtimer rearm, __schedule(), the try_to_wake_up() wakeup body
and wake_up_new_task(). This suppresses their instrumented callees
without excluding those callees from task-context coverage.

Changes in v2:
 - Add patch 1 to make kcov_start()'s mode parameter unsigned int. No
   functional change.
 - Rework patch 2 around a guard-only API with private current-only
   helpers (Bradley Morgan).
 - Use the guard in patches 3-6 and reword the pause comments.

v2 testing:
 - GCC builds on x86-64, arm32, arm64, MIPS32/64, PowerPC 32/64,
   s390, RISC-V 32/64, LoongArch, Xtensa and UML.
 - PREEMPT_RT builds on x86-64, arm32, arm64, RISC-V 32/64 and
   LoongArch.
 - x86-64 builds with GCC 8.1 and Clang 22.1. Both kernels passed a
   KCOV selftest boot.
 - x86-64 CONFIG_KCOV=n build, with no KCOV or pause references.
 - KCOV selftest, 10/10 x86-64 boots with and without PREEMPT_RT. A
   fresh non-RT boot passed after the helper-only rework.
 - 40 dummy_hcd/g_zero remote-KCOV cycles on x86-64 and arm64.
 - 400 repeated fork() calls on x86-64 PREEMPT_RT.

Three one-hour syzkaller A/B pairs were run. Each baseline and patched
run used four 2-vCPU PREEMPT_RT VMs. The patched kernel completed 22-51%
more executions than base. At matched execution counts, corpus size grew
42-54% and coverage 14-19%. No run produced a report.

With KCOV disabled, the pause sections compile away. With KCOV enabled
on x86-64, GCC 15.2 grows __schedule() by 117 bytes,
try_to_wake_up() by 94 bytes and wake_up_new_task() by 88 bytes relative
to the base commit.

[1] https://lore.kernel.org/r/20260724192122.73080-1-kmehltretter@gmail.com

v1: https://lore.kernel.org/r/20260807205027.31972-1-kmehltretter@gmail.com

Karl Mehltretter (6):
  kcov: Use unsigned int for kcov_start() mode parameter
  kcov: Add a kcov_pause guard
  hrtimer: Pause KCOV during deferred rearm
  sched/core: Pause KCOV in __schedule()
  sched/core: Pause KCOV in try_to_wake_up()
  sched/core: Pause KCOV in wake_up_new_task()

 include/linux/hrtimer_rearm.h | 17 +++++++++++++--
 include/linux/kcov.h          | 40 ++++++++++++++++++++++++++++++++++-
 kernel/kcov.c                 |  4 ++--
 kernel/sched/core.c           | 11 +++++++++-
 4 files changed, 66 insertions(+), 6 deletions(-)

Range-diff:
-:  ------------- > 1:  f60b858edad96 kcov: Use unsigned int for kcov_start() mode parameter
1:  a67095eb565de ! 2:  4415cac41ca43 kcov: add kcov_pause()/kcov_resume() helpers
    @@ Metadata
     Author: Karl Mehltretter <kmehltretter@gmail.com>
     
      ## Commit message ##
    -    kcov: add kcov_pause()/kcov_resume() helpers
    +    kcov: Add a kcov_pause guard
     
         Interrupt-return work can run after HARDIRQ_OFFSET is dropped, when
         in_task() is true. KCOV then attributes instrumented callees to the
         interrupted task.
     
         Add a KCOV_PAUSED bit next to KCOV_IN_CTXSW and mask both in
    -    kcov_mode_enabled(). The context switch suppression keeps its own bit:
    -    kcov_prepare_switch() runs on the previous task and kcov_finish_switch()
    -    on the one switched in, so its lifetime is not a pause section.
    +    kcov_mode_enabled(). The coverage callbacks need no new check because
    +    check_kcov_mode()'s exact comparison rejects modes with KCOV_PAUSED set.
     
    -    Sections nest by passing the state returned by kcov_pause() to
    -    kcov_resume(). Both operate on current. When task KCOV is active, remote
    -    softirq sections save and restore the complete mode, preserving the
    -    pause state.
    +    The context switch suppression keeps its own bit: kcov_prepare_switch()
    +    runs on the previous task and kcov_finish_switch() on the one switched
    +    in, so its lifetime is not a pause section.
     
    -    The helpers are __always_inline, and the caller must be uninstrumented:
    +    Provide a kcov_pause guard backed by internal helpers that operate on
    +    current. The guard saves the previous pause state and restores it at
    +    scope exit, so sections nest. When KCOV is enabled for current, remote
    +    softirq sections save and restore the complete mode, preserving the pause
    +    state.
    +
    +    The helpers are __always_inline, and guard users must be uninstrumented:
         inlining does not remove the caller's own coverage callbacks.
     
    -    Assisted-by: Claude:claude-opus-4-8
         Assisted-by: Claude:claude-fable-5
         Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
     
    @@ include/linux/kcov.h
      #define _LINUX_KCOV_H
      
     +#include <linux/bits.h>
    ++#include <linux/cleanup.h>
      #include <linux/sched.h>
      #include <uapi/linux/kcov.h>
      
    @@ include/linux/kcov.h: do {						\
      } while (0)
      
     +/*
    -+ * Pause coverage for current. Pass the returned state to kcov_resume().
    -+ * Callers must be uninstrumented.
    ++ * Pause coverage for current. Callers must be uninstrumented.
    ++ * Pass the returned state to __kcov_resume().
     + */
    -+static __always_inline unsigned int kcov_pause(struct task_struct *t)
    ++static __always_inline unsigned int __kcov_pause(void)
     +{
     +	unsigned int paused;
     +
    -+	paused = t->kcov_mode & KCOV_PAUSED;
    -+	t->kcov_mode |= KCOV_PAUSED;
    ++	paused = current->kcov_mode & KCOV_PAUSED;
    ++	current->kcov_mode |= KCOV_PAUSED;
     +	return paused;
     +}
     +
    -+static __always_inline void kcov_resume(struct task_struct *t, unsigned int paused)
    ++static __always_inline void __kcov_resume(unsigned int paused)
     +{
     +	if (!paused)
    -+		t->kcov_mode &= ~KCOV_PAUSED;
    ++		current->kcov_mode &= ~KCOV_PAUSED;
     +}
     +
      /* See Documentation/dev-tools/kcov.rst for usage details. */
    @@ include/linux/kcov.h: void __sanitizer_cov_trace_switch(kcov_u64 val, void *case
      
      static inline void kcov_task_init(struct task_struct *t) {}
      static inline void kcov_task_exit(struct task_struct *t) {}
    -+static inline unsigned int kcov_pause(struct task_struct *t) { return 0; }
    -+static inline void kcov_resume(struct task_struct *t, unsigned int paused) {}
    ++static inline unsigned int __kcov_pause(void) { return 0; }
    ++static inline void __kcov_resume(unsigned int paused) {}
      static inline void kcov_prepare_switch(struct task_struct *t) {}
      static inline void kcov_finish_switch(struct task_struct *t) {}
      static inline void kcov_remote_start(u64 handle) {}
    +@@ include/linux/kcov.h: static inline void kcov_remote_start_usb_softirq(u64 id) {}
    + static inline void kcov_remote_stop_softirq(void) {}
    + 
    + #endif /* CONFIG_KCOV */
    ++
    ++/*
    ++ * Scope-based KCOV pause:
    ++ *
    ++ *	guard(kcov_pause)();
    ++ *
    ++ * pauses coverage for current until the end of the scope. Callers must be
    ++ * uninstrumented.
    ++ */
    ++DEFINE_LOCK_GUARD_0(kcov_pause,
    ++		    _T->paused = __kcov_pause(),
    ++		    __kcov_resume(_T->paused),
    ++		    unsigned int paused)
    ++
    + #endif /* _LINUX_KCOV_H */
     
      ## kernel/kcov.c ##
     @@ kernel/kcov.c: static const struct file_operations kcov_fops = {
2:  2d64b45a316c8 ! 3:  c891839993a1e hrtimer: pause KCOV during deferred rearm
    @@ Metadata
     Author: Karl Mehltretter <kmehltretter@gmail.com>
     
      ## Commit message ##
    -    hrtimer: pause KCOV during deferred rearm
    +    hrtimer: Pause KCOV during deferred rearm
     
         Deferred hrtimer rearm can run after HARDIRQ_OFFSET is dropped. in_task()
         is then true, so KCOV attributes the instrumented timer-reprogramming
         subtree to current.
     
    -    With CONFIG_KCOV_SELFTEST, the interrupt selftest fails on x86_64
    -    defconfig under QEMU, detecting spurious coverage in
    -    __hrtimer_rearm_deferred(). The same happens on s390, RISC-V and
    -    LoongArch, which also enable HRTIMER_REARM_DEFERRED.
    +    With CONFIG_KCOV_SELFTEST added to x86_64 defconfig, the interrupt
    +    selftest fails under QEMU, detecting spurious coverage in
    +    __hrtimer_rearm_deferred(). The same happens on s390, RISC-V and LoongArch,
    +    which also enable HRTIMER_REARM_DEFERRED.
     
         Excluding the involved files instead would cost their coverage on real
         task-context paths, e.g. the hrtimer and timekeeping syscalls.
     
    -    Pause in the __always_inline wrappers, including hrtick_schedule_exit().
    -    Call sites where task KCOV can be active are KCOV-disabled or noinstr.
    -    HAVE_NOINSTR_HACK covers pre-GCC-12 x86. The other affected
    -    architectures restrict KCOV to GCC 12 or Clang through
    -    ARCH_WANTS_NO_INSTR. This avoids relying on __no_sanitize_coverage,
    -    which is empty before GCC 12. Tested with GCC 8.1 and 15 on x86_64.
    +    Take the kcov_pause guard in an __always_inline wrapper around
    +    __hrtimer_rearm_deferred(). Use it at all call sites, including
    +    hrtick_schedule_exit(). Callers that may run with KCOV enabled for current
    +    are built without KCOV instrumentation or marked noinstr. HAVE_NOINSTR_HACK
    +    covers pre-GCC-12 x86. The other affected architectures restrict KCOV to
    +    GCC 12 or Clang through ARCH_WANTS_NO_INSTR. This avoids relying on
    +    __no_sanitize_coverage, which is empty before GCC 12. Tested with GCC 8.1
    +    and 15 on x86_64.
     
         Fixes: 15dd3a948855 ("hrtimer: Push reprogramming timers into the interrupt return path")
    -    Assisted-by: Claude:claude-opus-4-8
         Assisted-by: Claude:claude-fable-5
         Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
     
    @@ include/linux/hrtimer_rearm.h
      void __hrtimer_rearm_deferred(void);
      
     +/*
    -+ * Pause outside __hrtimer_rearm_deferred() to suppress its entry coverage.
    -+ * Call sites where task KCOV can be active are uninstrumented.
    ++ * KCOV: Pause outside __hrtimer_rearm_deferred() to suppress entry coverage.
    ++ * Callers with KCOV enabled for current must be uninstrumented.
     + */
    -+static __always_inline void hrtimer_rearm_deferred_paused(void)
    ++static __always_inline void hrtimer_rearm_deferred_kcov_paused(void)
     +{
    -+	unsigned int kcov_paused = kcov_pause(current);
    ++	guard(kcov_pause)();
     +
     +	__hrtimer_rearm_deferred();
    -+	kcov_resume(current, kcov_paused);
     +}
     +
      /*
    @@ include/linux/hrtimer_rearm.h: hrtimer_rearm_deferred_user_irq(unsigned long *ti
      	if (unlikely((*tif_work & TIF_REARM_MASK) == _TIF_HRTIMER_REARM)) {
      		clear_thread_flag(TIF_HRTIMER_REARM);
     -		__hrtimer_rearm_deferred();
    -+		hrtimer_rearm_deferred_paused();
    ++		hrtimer_rearm_deferred_kcov_paused();
      		/* Don't go into the loop if HRTIMER_REARM was the only flag */
      		*tif_work &= ~TIF_HRTIMER_REARM;
      		return !*tif_work;
    @@ include/linux/hrtimer_rearm.h: hrtimer_rearm_deferred_user_irq(unsigned long *ti
      {
      	if (hrtimer_test_and_clear_rearm_deferred_tif(tif_work))
     -		__hrtimer_rearm_deferred();
    -+		hrtimer_rearm_deferred_paused();
    ++		hrtimer_rearm_deferred_kcov_paused();
      }
      
      /*
    @@ include/linux/hrtimer_rearm.h: static __always_inline bool hrtimer_test_and_clea
      
      #else  /* CONFIG_HRTIMER_REARM_DEFERRED */
      static __always_inline void __hrtimer_rearm_deferred(void) { }
    -+static __always_inline void hrtimer_rearm_deferred_paused(void) { }
    ++static __always_inline void hrtimer_rearm_deferred_kcov_paused(void) { }
      static __always_inline void hrtimer_rearm_deferred(void) { }
      static __always_inline void hrtimer_rearm_deferred_tif(unsigned long tif_work) { }
      static __always_inline bool
    @@ kernel/sched/core.c: static inline void hrtick_schedule_exit(struct rq *rq)
      
      	if (rq->hrtick_sched & HRTICK_SCHED_REARM_HRTIMER)
     -		__hrtimer_rearm_deferred();
    -+		hrtimer_rearm_deferred_paused();
    ++		hrtimer_rearm_deferred_kcov_paused();
      
      	rq->hrtick_sched = HRTICK_SCHED_NONE;
      }
3:  dc4bdcf8adacd ! 4:  63657f2c7ef08 sched: pause KCOV in __schedule()
    @@ Metadata
     Author: Karl Mehltretter <kmehltretter@gmail.com>
     
      ## Commit message ##
    -    sched: pause KCOV in __schedule()
    +    sched/core: Pause KCOV in __schedule()
     
         kernel/sched/ is not instrumented, but callees such as sched_clock(),
         architecture CPU-capacity helpers and profile_hits() are.
    @@ Commit message
         During preemption and schedule() calls, instrumented callees can add
         nondeterministic scheduler coverage to current.
     
    -    With CONFIG_KCOV_SELFTEST, the interrupt selftest fails on x86_64
    -    defconfig under QEMU, detecting spurious coverage in
    -    arch_scale_cpu_capacity(). On arm64 the same class of leak appears in
    -    sched_clock(), once the separate arm64 interrupt-accounting leak is
    -    suppressed.
    +    With CONFIG_KCOV_SELFTEST added to x86_64 defconfig, the interrupt
    +    selftest fails under QEMU, detecting spurious coverage in
    +    arch_scale_cpu_capacity().
     
         Annotating each callee would spread exclusions across architectures.
         Pause across __schedule() instead, extending the scheduler exclusion to
         its callees.
     
    -    KCOV_PAUSED remains set while a task is switched out. Its resumed
    -    __schedule() frame restores the prior state.
    +    KCOV_PAUSED remains set while a task is switched out. The guard in its
    +    resumed __schedule() frame restores the prior state.
     
         Fixes: 5c9a8750a640 ("kernel: add kcov code coverage")
    -    Assisted-by: Claude:claude-opus-4-8
         Assisted-by: Claude:claude-fable-5
         Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
     
      ## kernel/sched/core.c ##
     @@ kernel/sched/core.c: static void __sched notrace __schedule(int sched_mode)
    - 	bool is_switch = false;
    - 	unsigned long *switch_count;
    - 	unsigned long prev_state;
    -+	unsigned int kcov_paused;
    - 	struct rq_flags rf;
      	struct rq *rq;
      	int cpu;
      
    -+	/* KCOV: sched/ is uninstrumented but the __schedule() callees are not. */
    -+	kcov_paused = kcov_pause(current);
    ++	/* Instrumented callees would leak coverage into current. */
    ++	guard(kcov_pause)();
     +
      	/* Trace preemptions consistently with task switches */
      	trace_sched_entry_tp(sched_mode == SM_PREEMPT);
      
    -@@ kernel/sched/core.c: static void __sched notrace __schedule(int sched_mode)
    - 		raw_spin_rq_unlock_irq(rq);
    - 	}
    - 	trace_sched_exit_tp(is_switch);
    -+	kcov_resume(current, kcov_paused);
    - }
    - 
    - void __noreturn do_task_dead(void)
4:  b8e96cc1903de ! 5:  5cf8497b8a0ab sched: pause KCOV in try_to_wake_up()
    @@ Metadata
     Author: Karl Mehltretter <kmehltretter@gmail.com>
     
      ## Commit message ##
    -    sched: pause KCOV in try_to_wake_up()
    +    sched/core: Pause KCOV in try_to_wake_up()
     
         try_to_wake_up() is uninstrumented, but it calls instrumented helpers
         such as kthread_is_per_cpu(), CPU capacity helpers and SCHED_HRTICK
    @@ Commit message
         selftest's spin. The same helpers leak into non-RT syscall wakeups such
         as a pipe write waking a reader.
     
    -    Pause all of try_to_wake_up(). Wrapping only select_task_rq() would miss
    -    SCHED_HRTICK arming during enqueue.
    +    Pause the wakeup body with the kcov_pause guard. Wrapping only
    +    select_task_rq() would miss SCHED_HRTICK arming during enqueue.
     
         Fixes: 5c9a8750a640 ("kernel: add kcov code coverage")
    -    Assisted-by: Claude:claude-opus-4-8
    +    Assisted-by: Claude:claude-fable-5
         Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
     
      ## kernel/sched/core.c ##
     @@ kernel/sched/core.c: int try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
    - {
      	guard(preempt)();
      	int cpu, success = 0;
    -+	/* KCOV: sched/ is uninstrumented but the wakeup callees are not. */
    -+	unsigned int kcov_paused = kcov_pause(current);
      
    ++	/* Instrumented callees would leak coverage into current. */
    ++	guard(kcov_pause)();
    ++
      	wake_flags |= WF_TTWU;
      
    -@@ kernel/sched/core.c: int try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
    - 	if (success)
    - 		ttwu_stat(p, task_cpu(p), wake_flags);
    - 
    -+	kcov_resume(current, kcov_paused);
    - 	return success;
    - }
    - 
    + 	if (p == current) {
5:  f91a7644a15e8 ! 6:  a00870853f5a1 sched: pause KCOV in wake_up_new_task()
    @@ Metadata
     Author: Karl Mehltretter <kmehltretter@gmail.com>
     
      ## Commit message ##
    -    sched: pause KCOV in wake_up_new_task()
    +    sched/core: Pause KCOV in wake_up_new_task()
     
         wake_up_new_task() is uninstrumented, but CPU selection and enqueue call
         instrumented helpers. During a KCOV-enabled fork, they can record
    @@ Commit message
         scheduler exclusion to new-task wakeups.
     
         Fixes: 5c9a8750a640 ("kernel: add kcov code coverage")
    -    Assisted-by: Claude:claude-opus-4-8
    +    Assisted-by: Claude:claude-fable-5
         Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
     
      ## kernel/sched/core.c ##
     @@ kernel/sched/core.c: void wake_up_new_task(struct task_struct *p)
    - {
    - 	struct rq_flags rf;
      	struct rq *rq;
    -+	unsigned int kcov_paused;
      	int wake_flags = WF_FORK;
      
    -+	/* KCOV: sched/ is uninstrumented but the wakeup callees are not. */
    -+	kcov_paused = kcov_pause(current);
    ++	/* Instrumented callees would leak coverage into current. */
    ++	guard(kcov_pause)();
     +
      	raw_spin_lock_irqsave(&p->pi_lock, rf.flags);
      	WRITE_ONCE(p->__state, TASK_RUNNING);
      	/*
    -@@ kernel/sched/core.c: void wake_up_new_task(struct task_struct *p)
    - 		rq_repin_lock(rq, &rf);
    - 	}
    - 	task_rq_unlock(rq, p, &rf);
    -+	kcov_resume(current, kcov_paused);
    - }
    - 
    - #ifdef CONFIG_PREEMPT_NOTIFIERS

base-commit: 8ba098e6b6ff0db8edf28528d1552be261af30d4
-- 
2.53.0

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

* [PATCH v2 1/6] kcov: Use unsigned int for kcov_start() mode parameter
  2026-08-11 15:41 [PATCH v2 0/6] kcov: Suppress timer and scheduler coverage leaks Karl Mehltretter
@ 2026-08-11 15:41 ` Karl Mehltretter
  2026-08-11 15:41 ` [PATCH v2 2/6] kcov: Add a kcov_pause guard Karl Mehltretter
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Karl Mehltretter @ 2026-08-11 15:41 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Karl Mehltretter, Andrey Konovalov, Alexander Potapenko,
	Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
	Frederic Weisbecker, Thomas Gleixner, Ingo Molnar, Peter Zijlstra,
	Juri Lelli, Vincent Guittot, Dietmar Eggemann, Steven Rostedt,
	Ben Segall, Mel Gorman, Valentin Schneider, K Prateek Nayak,
	Sebastian Andrzej Siewior, Clark Williams, linux-rt-devel,
	kasan-dev, linux-kernel

task_struct::kcov_mode usually holds an enum kcov_mode value, but it can
also carry flag bits such as KCOV_IN_CTXSW, so the field is unsigned int.

kcov_remote_softirq_stop() passes the saved raw value through
kcov_start()'s enum kcov_mode parameter before kcov_start() stores it
back into the unsigned int field. The parameter type therefore does not
match the values it receives.

Type the parameter unsigned int, like the field and the saved copy. No
functional change.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
Notes:
    v2: new patch

 kernel/kcov.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/kernel/kcov.c b/kernel/kcov.c
index 1df373fb562bc..b5340369e6fe3 100644
--- a/kernel/kcov.c
+++ b/kernel/kcov.c
@@ -354,7 +354,7 @@ EXPORT_SYMBOL(__sanitizer_cov_trace_switch);
 #endif /* ifdef CONFIG_KCOV_ENABLE_COMPARISONS */
 
 static void kcov_start(struct task_struct *t, struct kcov *kcov,
-			unsigned int size, void *area, enum kcov_mode mode,
+			unsigned int size, void *area, unsigned int mode,
 			int sequence)
 {
 	kcov_debug("t = %px, size = %u, area = %px\n", t, size, area);
-- 
2.53.0

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

* [PATCH v2 2/6] kcov: Add a kcov_pause guard
  2026-08-11 15:41 [PATCH v2 0/6] kcov: Suppress timer and scheduler coverage leaks Karl Mehltretter
  2026-08-11 15:41 ` [PATCH v2 1/6] kcov: Use unsigned int for kcov_start() mode parameter Karl Mehltretter
@ 2026-08-11 15:41 ` Karl Mehltretter
  2026-08-11 15:41 ` [PATCH v2 3/6] hrtimer: Pause KCOV during deferred rearm Karl Mehltretter
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Karl Mehltretter @ 2026-08-11 15:41 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Karl Mehltretter, Andrey Konovalov, Alexander Potapenko,
	Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
	Frederic Weisbecker, Thomas Gleixner, Ingo Molnar, Peter Zijlstra,
	Juri Lelli, Vincent Guittot, Dietmar Eggemann, Steven Rostedt,
	Ben Segall, Mel Gorman, Valentin Schneider, K Prateek Nayak,
	Sebastian Andrzej Siewior, Clark Williams, linux-rt-devel,
	kasan-dev, linux-kernel

Interrupt-return work can run after HARDIRQ_OFFSET is dropped, when
in_task() is true. KCOV then attributes instrumented callees to the
interrupted task.

Add a KCOV_PAUSED bit next to KCOV_IN_CTXSW and mask both in
kcov_mode_enabled(). The coverage callbacks need no new check because
check_kcov_mode()'s exact comparison rejects modes with KCOV_PAUSED set.

The context switch suppression keeps its own bit: kcov_prepare_switch()
runs on the previous task and kcov_finish_switch() on the one switched
in, so its lifetime is not a pause section.

Provide a kcov_pause guard backed by internal helpers that operate on
current. The guard saves the previous pause state and restores it at
scope exit, so sections nest. When KCOV is enabled for current, remote
softirq sections save and restore the complete mode, preserving the pause
state.

The helpers are __always_inline, and guard users must be uninstrumented:
inlining does not remove the caller's own coverage callbacks.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
Notes:
    v2:
     - add guard(kcov_pause)() backed by private current-only helpers

 include/linux/kcov.h | 40 +++++++++++++++++++++++++++++++++++++++-
 kernel/kcov.c        |  2 +-
 2 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/include/linux/kcov.h b/include/linux/kcov.h
index 895b761b2db15..1b4806dab62fb 100644
--- a/include/linux/kcov.h
+++ b/include/linux/kcov.h
@@ -2,6 +2,8 @@
 #ifndef _LINUX_KCOV_H
 #define _LINUX_KCOV_H
 
+#include <linux/bits.h>
+#include <linux/cleanup.h>
 #include <linux/sched.h>
 #include <uapi/linux/kcov.h>
 
@@ -23,7 +25,8 @@ enum kcov_mode {
 	KCOV_MODE_TRACE_CMP = 3,
 };
 
-#define KCOV_IN_CTXSW	(1 << 30)
+#define KCOV_IN_CTXSW	BIT(30)
+#define KCOV_PAUSED	BIT(29)
 
 void kcov_task_init(struct task_struct *t);
 void kcov_task_exit(struct task_struct *t);
@@ -38,6 +41,25 @@ do {						\
 	(t)->kcov_mode &= ~KCOV_IN_CTXSW;	\
 } while (0)
 
+/*
+ * Pause coverage for current. Callers must be uninstrumented.
+ * Pass the returned state to __kcov_resume().
+ */
+static __always_inline unsigned int __kcov_pause(void)
+{
+	unsigned int paused;
+
+	paused = current->kcov_mode & KCOV_PAUSED;
+	current->kcov_mode |= KCOV_PAUSED;
+	return paused;
+}
+
+static __always_inline void __kcov_resume(unsigned int paused)
+{
+	if (!paused)
+		current->kcov_mode &= ~KCOV_PAUSED;
+}
+
 /* See Documentation/dev-tools/kcov.rst for usage details. */
 void kcov_remote_start(u64 handle);
 void kcov_remote_stop(void);
@@ -93,6 +115,8 @@ void __sanitizer_cov_trace_switch(kcov_u64 val, void *cases);
 
 static inline void kcov_task_init(struct task_struct *t) {}
 static inline void kcov_task_exit(struct task_struct *t) {}
+static inline unsigned int __kcov_pause(void) { return 0; }
+static inline void __kcov_resume(unsigned int paused) {}
 static inline void kcov_prepare_switch(struct task_struct *t) {}
 static inline void kcov_finish_switch(struct task_struct *t) {}
 static inline void kcov_remote_start(u64 handle) {}
@@ -107,4 +131,18 @@ static inline void kcov_remote_start_usb_softirq(u64 id) {}
 static inline void kcov_remote_stop_softirq(void) {}
 
 #endif /* CONFIG_KCOV */
+
+/*
+ * Scope-based KCOV pause:
+ *
+ *	guard(kcov_pause)();
+ *
+ * pauses coverage for current until the end of the scope. Callers must be
+ * uninstrumented.
+ */
+DEFINE_LOCK_GUARD_0(kcov_pause,
+		    _T->paused = __kcov_pause(),
+		    __kcov_resume(_T->paused),
+		    unsigned int paused)
+
 #endif /* _LINUX_KCOV_H */
diff --git a/kernel/kcov.c b/kernel/kcov.c
index b5340369e6fe3..a5c43764f21ac 100644
--- a/kernel/kcov.c
+++ b/kernel/kcov.c
@@ -830,7 +830,7 @@ static const struct file_operations kcov_fops = {
 
 static inline bool kcov_mode_enabled(unsigned int mode)
 {
-	return (mode & ~KCOV_IN_CTXSW) != KCOV_MODE_DISABLED;
+	return (mode & ~(KCOV_IN_CTXSW | KCOV_PAUSED)) != KCOV_MODE_DISABLED;
 }
 
 static void kcov_remote_softirq_start(struct task_struct *t)
-- 
2.53.0

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

* [PATCH v2 3/6] hrtimer: Pause KCOV during deferred rearm
  2026-08-11 15:41 [PATCH v2 0/6] kcov: Suppress timer and scheduler coverage leaks Karl Mehltretter
  2026-08-11 15:41 ` [PATCH v2 1/6] kcov: Use unsigned int for kcov_start() mode parameter Karl Mehltretter
  2026-08-11 15:41 ` [PATCH v2 2/6] kcov: Add a kcov_pause guard Karl Mehltretter
@ 2026-08-11 15:41 ` Karl Mehltretter
  2026-08-12 10:21   ` Peter Zijlstra
  2026-08-11 15:41 ` [PATCH v2 4/6] sched/core: Pause KCOV in __schedule() Karl Mehltretter
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 13+ messages in thread
From: Karl Mehltretter @ 2026-08-11 15:41 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Karl Mehltretter, Andrey Konovalov, Alexander Potapenko,
	Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
	Frederic Weisbecker, Thomas Gleixner, Ingo Molnar, Peter Zijlstra,
	Juri Lelli, Vincent Guittot, Dietmar Eggemann, Steven Rostedt,
	Ben Segall, Mel Gorman, Valentin Schneider, K Prateek Nayak,
	Sebastian Andrzej Siewior, Clark Williams, linux-rt-devel,
	kasan-dev, linux-kernel

Deferred hrtimer rearm can run after HARDIRQ_OFFSET is dropped. in_task()
is then true, so KCOV attributes the instrumented timer-reprogramming
subtree to current.

With CONFIG_KCOV_SELFTEST added to x86_64 defconfig, the interrupt
selftest fails under QEMU, detecting spurious coverage in
__hrtimer_rearm_deferred(). The same happens on s390, RISC-V and LoongArch,
which also enable HRTIMER_REARM_DEFERRED.

Excluding the involved files instead would cost their coverage on real
task-context paths, e.g. the hrtimer and timekeeping syscalls.

Take the kcov_pause guard in an __always_inline wrapper around
__hrtimer_rearm_deferred(). Use it at all call sites, including
hrtick_schedule_exit(). Callers that may run with KCOV enabled for current
are built without KCOV instrumentation or marked noinstr. HAVE_NOINSTR_HACK
covers pre-GCC-12 x86. The other affected architectures restrict KCOV to
GCC 12 or Clang through ARCH_WANTS_NO_INSTR. This avoids relying on
__no_sanitize_coverage, which is empty before GCC 12. Tested with GCC 8.1
and 15 on x86_64.

Fixes: 15dd3a948855 ("hrtimer: Push reprogramming timers into the interrupt return path")
Assisted-by: Claude:claude-fable-5
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
Notes:
    v2:
     - take guard(kcov_pause)() in an inline wrapper
     - clarify the test configuration and caller instrumentation requirements

 include/linux/hrtimer_rearm.h | 17 +++++++++++++++--
 kernel/sched/core.c           |  2 +-
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/include/linux/hrtimer_rearm.h b/include/linux/hrtimer_rearm.h
index a6f2e5d5e1c7d..45e47421fa0c7 100644
--- a/include/linux/hrtimer_rearm.h
+++ b/include/linux/hrtimer_rearm.h
@@ -3,10 +3,22 @@
 #define _LINUX_HRTIMER_REARM_H
 
 #ifdef CONFIG_HRTIMER_REARM_DEFERRED
+#include <linux/kcov.h>
 #include <linux/thread_info.h>
 
 void __hrtimer_rearm_deferred(void);
 
+/*
+ * KCOV: Pause outside __hrtimer_rearm_deferred() to suppress entry coverage.
+ * Callers with KCOV enabled for current must be uninstrumented.
+ */
+static __always_inline void hrtimer_rearm_deferred_kcov_paused(void)
+{
+	guard(kcov_pause)();
+
+	__hrtimer_rearm_deferred();
+}
+
 /*
  * This is purely CPU local, so check the TIF bit first to avoid the overhead of
  * the atomic test_and_clear_bit() operation for the common case where the bit
@@ -38,7 +50,7 @@ hrtimer_rearm_deferred_user_irq(unsigned long *tif_work, const unsigned long tif
 	 */
 	if (unlikely((*tif_work & TIF_REARM_MASK) == _TIF_HRTIMER_REARM)) {
 		clear_thread_flag(TIF_HRTIMER_REARM);
-		__hrtimer_rearm_deferred();
+		hrtimer_rearm_deferred_kcov_paused();
 		/* Don't go into the loop if HRTIMER_REARM was the only flag */
 		*tif_work &= ~TIF_HRTIMER_REARM;
 		return !*tif_work;
@@ -50,7 +62,7 @@ hrtimer_rearm_deferred_user_irq(unsigned long *tif_work, const unsigned long tif
 static __always_inline void hrtimer_rearm_deferred_tif(unsigned long tif_work)
 {
 	if (hrtimer_test_and_clear_rearm_deferred_tif(tif_work))
-		__hrtimer_rearm_deferred();
+		hrtimer_rearm_deferred_kcov_paused();
 }
 
 /*
@@ -73,6 +85,7 @@ static __always_inline bool hrtimer_test_and_clear_rearm_deferred(void)
 
 #else  /* CONFIG_HRTIMER_REARM_DEFERRED */
 static __always_inline void __hrtimer_rearm_deferred(void) { }
+static __always_inline void hrtimer_rearm_deferred_kcov_paused(void) { }
 static __always_inline void hrtimer_rearm_deferred(void) { }
 static __always_inline void hrtimer_rearm_deferred_tif(unsigned long tif_work) { }
 static __always_inline bool
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 96226707c2f61..3cbf817f52a4a 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1010,7 +1010,7 @@ static inline void hrtick_schedule_exit(struct rq *rq)
 	}
 
 	if (rq->hrtick_sched & HRTICK_SCHED_REARM_HRTIMER)
-		__hrtimer_rearm_deferred();
+		hrtimer_rearm_deferred_kcov_paused();
 
 	rq->hrtick_sched = HRTICK_SCHED_NONE;
 }
-- 
2.53.0

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

* [PATCH v2 4/6] sched/core: Pause KCOV in __schedule()
  2026-08-11 15:41 [PATCH v2 0/6] kcov: Suppress timer and scheduler coverage leaks Karl Mehltretter
                   ` (2 preceding siblings ...)
  2026-08-11 15:41 ` [PATCH v2 3/6] hrtimer: Pause KCOV during deferred rearm Karl Mehltretter
@ 2026-08-11 15:41 ` Karl Mehltretter
  2026-08-12 10:35   ` Peter Zijlstra
  2026-08-11 15:41 ` [PATCH v2 5/6] sched/core: Pause KCOV in try_to_wake_up() Karl Mehltretter
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 13+ messages in thread
From: Karl Mehltretter @ 2026-08-11 15:41 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Karl Mehltretter, Andrey Konovalov, Alexander Potapenko,
	Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
	Frederic Weisbecker, Thomas Gleixner, Ingo Molnar, Peter Zijlstra,
	Juri Lelli, Vincent Guittot, Dietmar Eggemann, Steven Rostedt,
	Ben Segall, Mel Gorman, Valentin Schneider, K Prateek Nayak,
	Sebastian Andrzej Siewior, Clark Williams, linux-rt-devel,
	kasan-dev, linux-kernel

kernel/sched/ is not instrumented, but callees such as sched_clock(),
architecture CPU-capacity helpers and profile_hits() are.

During preemption and schedule() calls, instrumented callees can add
nondeterministic scheduler coverage to current.

With CONFIG_KCOV_SELFTEST added to x86_64 defconfig, the interrupt
selftest fails under QEMU, detecting spurious coverage in
arch_scale_cpu_capacity().

Annotating each callee would spread exclusions across architectures.
Pause across __schedule() instead, extending the scheduler exclusion to
its callees.

KCOV_PAUSED remains set while a task is switched out. The guard in its
resumed __schedule() frame restores the prior state.

Fixes: 5c9a8750a640 ("kernel: add kcov code coverage")
Assisted-by: Claude:claude-fable-5
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
Notes:
    v2:
     - take guard(kcov_pause)()
     - clarify the test configuration and reword the pause comment

 kernel/sched/core.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 3cbf817f52a4a..e49f0bfa0ca73 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -7073,6 +7073,9 @@ static void __sched notrace __schedule(int sched_mode)
 	struct rq *rq;
 	int cpu;
 
+	/* Instrumented callees would leak coverage into current. */
+	guard(kcov_pause)();
+
 	/* Trace preemptions consistently with task switches */
 	trace_sched_entry_tp(sched_mode == SM_PREEMPT);
 
-- 
2.53.0

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

* [PATCH v2 5/6] sched/core: Pause KCOV in try_to_wake_up()
  2026-08-11 15:41 [PATCH v2 0/6] kcov: Suppress timer and scheduler coverage leaks Karl Mehltretter
                   ` (3 preceding siblings ...)
  2026-08-11 15:41 ` [PATCH v2 4/6] sched/core: Pause KCOV in __schedule() Karl Mehltretter
@ 2026-08-11 15:41 ` Karl Mehltretter
  2026-08-12 10:35   ` Peter Zijlstra
  2026-08-11 15:41 ` [PATCH v2 6/6] sched/core: Pause KCOV in wake_up_new_task() Karl Mehltretter
  2026-08-11 19:59 ` [PATCH v2 0/6] kcov: Suppress timer and scheduler coverage leaks Bradley Morgan
  6 siblings, 1 reply; 13+ messages in thread
From: Karl Mehltretter @ 2026-08-11 15:41 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Karl Mehltretter, Andrey Konovalov, Alexander Potapenko,
	Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
	Frederic Weisbecker, Thomas Gleixner, Ingo Molnar, Peter Zijlstra,
	Juri Lelli, Vincent Guittot, Dietmar Eggemann, Steven Rostedt,
	Ben Segall, Mel Gorman, Valentin Schneider, K Prateek Nayak,
	Sebastian Andrzej Siewior, Clark Williams, linux-rt-devel,
	kasan-dev, linux-kernel

try_to_wake_up() is uninstrumented, but it calls instrumented helpers
such as kthread_is_per_cpu(), CPU capacity helpers and SCHED_HRTICK
arming. They can record into current while in_task() is true.

CONFIG_KCOV_SELFTEST exposes this under PREEMPT_RT. The interrupt
selftest fails on x86_64 in kthread_is_per_cpu(): RT runs the timer
softirq in a thread, so the wakeup runs in task context during the
selftest's spin. The same helpers leak into non-RT syscall wakeups such
as a pipe write waking a reader.

Pause the wakeup body with the kcov_pause guard. Wrapping only
select_task_rq() would miss SCHED_HRTICK arming during enqueue.

Fixes: 5c9a8750a640 ("kernel: add kcov code coverage")
Assisted-by: Claude:claude-fable-5
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
Notes:
    v2:
     - take guard(kcov_pause)()
     - describe the paused wakeup body precisely

 kernel/sched/core.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index e49f0bfa0ca73..e863fac02e38f 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -4253,6 +4253,9 @@ int try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
 	guard(preempt)();
 	int cpu, success = 0;
 
+	/* Instrumented callees would leak coverage into current. */
+	guard(kcov_pause)();
+
 	wake_flags |= WF_TTWU;
 
 	if (p == current) {
-- 
2.53.0

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

* [PATCH v2 6/6] sched/core: Pause KCOV in wake_up_new_task()
  2026-08-11 15:41 [PATCH v2 0/6] kcov: Suppress timer and scheduler coverage leaks Karl Mehltretter
                   ` (4 preceding siblings ...)
  2026-08-11 15:41 ` [PATCH v2 5/6] sched/core: Pause KCOV in try_to_wake_up() Karl Mehltretter
@ 2026-08-11 15:41 ` Karl Mehltretter
  2026-08-12 10:36   ` Peter Zijlstra
  2026-08-11 19:59 ` [PATCH v2 0/6] kcov: Suppress timer and scheduler coverage leaks Bradley Morgan
  6 siblings, 1 reply; 13+ messages in thread
From: Karl Mehltretter @ 2026-08-11 15:41 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Karl Mehltretter, Andrey Konovalov, Alexander Potapenko,
	Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
	Frederic Weisbecker, Thomas Gleixner, Ingo Molnar, Peter Zijlstra,
	Juri Lelli, Vincent Guittot, Dietmar Eggemann, Steven Rostedt,
	Ben Segall, Mel Gorman, Valentin Schneider, K Prateek Nayak,
	Sebastian Andrzej Siewior, Clark Williams, linux-rt-devel,
	kasan-dev, linux-kernel

wake_up_new_task() is uninstrumented, but CPU selection and enqueue call
instrumented helpers. During a KCOV-enabled fork, they can record
scheduler, hrtimer and clockevent coverage into the parent.

The paths depend on runqueue and CPU state, so coverage varies between
identical forks. Pause KCOV for the whole function, extending the
scheduler exclusion to new-task wakeups.

Fixes: 5c9a8750a640 ("kernel: add kcov code coverage")
Assisted-by: Claude:claude-fable-5
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
Notes:
    v2:
     - take guard(kcov_pause)()
     - reword the pause comment

 kernel/sched/core.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index e863fac02e38f..f7ddfbbb5a494 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -4947,6 +4947,9 @@ void wake_up_new_task(struct task_struct *p)
 	struct rq *rq;
 	int wake_flags = WF_FORK;
 
+	/* Instrumented callees would leak coverage into current. */
+	guard(kcov_pause)();
+
 	raw_spin_lock_irqsave(&p->pi_lock, rf.flags);
 	WRITE_ONCE(p->__state, TASK_RUNNING);
 	/*
-- 
2.53.0

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

* Re: [PATCH v2 0/6] kcov: Suppress timer and scheduler coverage leaks
  2026-08-11 15:41 [PATCH v2 0/6] kcov: Suppress timer and scheduler coverage leaks Karl Mehltretter
                   ` (5 preceding siblings ...)
  2026-08-11 15:41 ` [PATCH v2 6/6] sched/core: Pause KCOV in wake_up_new_task() Karl Mehltretter
@ 2026-08-11 19:59 ` Bradley Morgan
  2026-08-12 10:36   ` Peter Zijlstra
  6 siblings, 1 reply; 13+ messages in thread
From: Bradley Morgan @ 2026-08-11 19:59 UTC (permalink / raw)
  To: Karl Mehltretter, Andrew Morton
  Cc: Andrey Konovalov, Alexander Potapenko, Dmitry Vyukov, Marco Elver,
	Anna-Maria Behnsen, Frederic Weisbecker, Thomas Gleixner,
	Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, K Prateek Nayak, Sebastian Andrzej Siewior,
	Clark Williams, linux-rt-devel, kasan-dev, linux-kernel

On 11 August 2026 16:41:05 BST, Karl Mehltretter <kmehltretter@gmail.com>
wrote:
>KCOV aims to exclude interrupt and scheduler coverage so syscall coverage
>stays input-dependent. Instrumented callees can still record when
>uninstrumented timer and scheduler paths run with in_task() true.
>
>With the diagnostic patch in [1] applied, CONFIG_KCOV_SELFTEST exposes
>three cases on x86-64: deferred hrtimer rearm, __schedule() callees and
>PREEMPT_RT wakeups. Task-context wakeups and new-task enqueue also add
>scheduler coverage to ordinary syscalls.
>
>Add a nestable KCOV_PAUSED bit and a kcov_pause guard. Use the guard for
>deferred hrtimer rearm, __schedule(), the try_to_wake_up() wakeup body
>and wake_up_new_task(). This suppresses their instrumented callees
>without excluding those callees from task-context coverage.
>
>Changes in v2:
> - Add patch 1 to make kcov_start()'s mode parameter unsigned int. No
>   functional change.
> - Rework patch 2 around a guard-only API with private current-only
>   helpers (Bradley Morgan).
> - Use the guard in patches 3-6 and reword the pause comments.
>
>v2 testing:
> - GCC builds on x86-64, arm32, arm64, MIPS32/64, PowerPC 32/64,
>   s390, RISC-V 32/64, LoongArch, Xtensa and UML.
> - PREEMPT_RT builds on x86-64, arm32, arm64, RISC-V 32/64 and
>   LoongArch.
> - x86-64 builds with GCC 8.1 and Clang 22.1. Both kernels passed a
>   KCOV selftest boot.
> - x86-64 CONFIG_KCOV=n build, with no KCOV or pause references.
> - KCOV selftest, 10/10 x86-64 boots with and without PREEMPT_RT. A
>   fresh non-RT boot passed after the helper-only rework.
> - 40 dummy_hcd/g_zero remote-KCOV cycles on x86-64 and arm64.
> - 400 repeated fork() calls on x86-64 PREEMPT_RT.
>
>Three one-hour syzkaller A/B pairs were run. Each baseline and patched
>run used four 2-vCPU PREEMPT_RT VMs. The patched kernel completed 22-51%
>more executions than base. At matched execution counts, corpus size grew
>42-54% and coverage 14-19%. No run produced a report.
>
>With KCOV disabled, the pause sections compile away. With KCOV enabled
>on x86-64, GCC 15.2 grows __schedule() by 117 bytes,
>try_to_wake_up() by 94 bytes and wake_up_new_task() by 88 bytes relative
>to the base commit.
>
>[1]
>https://lore.kernel.org/r/20260724192122.73080-1-kmehltretter@gmail.com
>


Reviewed-by: Bradley Morgan <include@grrlz.net>

On all 6 patches


>v1: https://lore.kernel.org/r/20260807205027.31972-1-kmehltretter@gmail.com
>
>Karl Mehltretter (6):
>  kcov: Use unsigned int for kcov_start() mode parameter
>  kcov: Add a kcov_pause guard
>  hrtimer: Pause KCOV during deferred rearm
>  sched/core: Pause KCOV in __schedule()
>  sched/core: Pause KCOV in try_to_wake_up()
>  sched/core: Pause KCOV in wake_up_new_task()
>
> include/linux/hrtimer_rearm.h | 17 +++++++++++++--
> include/linux/kcov.h          | 40 ++++++++++++++++++++++++++++++++++-
> kernel/kcov.c                 |  4 ++--
> kernel/sched/core.c           | 11 +++++++++-
> 4 files changed, 66 insertions(+), 6 deletions(-)
>
>Range-diff:
>-:  ------------- > 1:  f60b858edad96 kcov: Use unsigned int for kcov_start() mode parameter
>1:  a67095eb565de ! 2:  4415cac41ca43 kcov: add kcov_pause()/kcov_resume() helpers
>    @@ Metadata
>     Author: Karl Mehltretter <kmehltretter@gmail.com>
>     
>      ## Commit message ##
>    -    kcov: add kcov_pause()/kcov_resume() helpers
>    +    kcov: Add a kcov_pause guard
>     
>         Interrupt-return work can run after HARDIRQ_OFFSET is dropped, when
>         in_task() is true. KCOV then attributes instrumented callees to the
>         interrupted task.
>     
>         Add a KCOV_PAUSED bit next to KCOV_IN_CTXSW and mask both in
>    -    kcov_mode_enabled(). The context switch suppression keeps its own bit:
>    -    kcov_prepare_switch() runs on the previous task and kcov_finish_switch()
>    -    on the one switched in, so its lifetime is not a pause section.
>    +    kcov_mode_enabled(). The coverage callbacks need no new check because
>    +    check_kcov_mode()'s exact comparison rejects modes with KCOV_PAUSED set.
>     
>    -    Sections nest by passing the state returned by kcov_pause() to
>    -    kcov_resume(). Both operate on current. When task KCOV is active, remote
>    -    softirq sections save and restore the complete mode, preserving the
>    -    pause state.
>    +    The context switch suppression keeps its own bit: kcov_prepare_switch()
>    +    runs on the previous task and kcov_finish_switch() on the one switched
>    +    in, so its lifetime is not a pause section.
>     
>    -    The helpers are __always_inline, and the caller must be uninstrumented:
>    +    Provide a kcov_pause guard backed by internal helpers that operate on
>    +    current. The guard saves the previous pause state and restores it at
>    +    scope exit, so sections nest. When KCOV is enabled for current, remote
>    +    softirq sections save and restore the complete mode, preserving the pause
>    +    state.
>    +
>    +    The helpers are __always_inline, and guard users must be uninstrumented:
>         inlining does not remove the caller's own coverage callbacks.
>     
>    -    Assisted-by: Claude:claude-opus-4-8
>         Assisted-by: Claude:claude-fable-5
>         Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
>     
>    @@ include/linux/kcov.h
>      #define _LINUX_KCOV_H
>      
>     +#include <linux/bits.h>
>    ++#include <linux/cleanup.h>
>      #include <linux/sched.h>
>      #include <uapi/linux/kcov.h>
>      
>    @@ include/linux/kcov.h: do {						\
>      } while (0)
>      
>     +/*
>    -+ * Pause coverage for current. Pass the returned state to kcov_resume().
>    -+ * Callers must be uninstrumented.
>    ++ * Pause coverage for current. Callers must be uninstrumented.
>    ++ * Pass the returned state to __kcov_resume().
>     + */
>    -+static __always_inline unsigned int kcov_pause(struct task_struct *t)
>    ++static __always_inline unsigned int __kcov_pause(void)
>     +{
>     +	unsigned int paused;
>     +
>    -+	paused = t->kcov_mode & KCOV_PAUSED;
>    -+	t->kcov_mode |= KCOV_PAUSED;
>    ++	paused = current->kcov_mode & KCOV_PAUSED;
>    ++	current->kcov_mode |= KCOV_PAUSED;
>     +	return paused;
>     +}
>     +
>    -+static __always_inline void kcov_resume(struct task_struct *t, unsigned int paused)
>    ++static __always_inline void __kcov_resume(unsigned int paused)
>     +{
>     +	if (!paused)
>    -+		t->kcov_mode &= ~KCOV_PAUSED;
>    ++		current->kcov_mode &= ~KCOV_PAUSED;
>     +}
>     +
>      /* See Documentation/dev-tools/kcov.rst for usage details. */
>    @@ include/linux/kcov.h: void __sanitizer_cov_trace_switch(kcov_u64 val, void *case
>      
>      static inline void kcov_task_init(struct task_struct *t) {}
>      static inline void kcov_task_exit(struct task_struct *t) {}
>    -+static inline unsigned int kcov_pause(struct task_struct *t) { return 0; }
>    -+static inline void kcov_resume(struct task_struct *t, unsigned int paused) {}
>    ++static inline unsigned int __kcov_pause(void) { return 0; }
>    ++static inline void __kcov_resume(unsigned int paused) {}
>      static inline void kcov_prepare_switch(struct task_struct *t) {}
>      static inline void kcov_finish_switch(struct task_struct *t) {}
>      static inline void kcov_remote_start(u64 handle) {}
>    +@@ include/linux/kcov.h: static inline void kcov_remote_start_usb_softirq(u64 id) {}
>    + static inline void kcov_remote_stop_softirq(void) {}
>    + 
>    + #endif /* CONFIG_KCOV */
>    ++
>    ++/*
>    ++ * Scope-based KCOV pause:
>    ++ *
>    ++ *	guard(kcov_pause)();
>    ++ *
>    ++ * pauses coverage for current until the end of the scope. Callers must be
>    ++ * uninstrumented.
>    ++ */
>    ++DEFINE_LOCK_GUARD_0(kcov_pause,
>    ++		    _T->paused = __kcov_pause(),
>    ++		    __kcov_resume(_T->paused),
>    ++		    unsigned int paused)
>    ++
>    + #endif /* _LINUX_KCOV_H */
>     
>      ## kernel/kcov.c ##
>     @@ kernel/kcov.c: static const struct file_operations kcov_fops = {
>2:  2d64b45a316c8 ! 3:  c891839993a1e hrtimer: pause KCOV during deferred rearm
>    @@ Metadata
>     Author: Karl Mehltretter <kmehltretter@gmail.com>
>     
>      ## Commit message ##
>    -    hrtimer: pause KCOV during deferred rearm
>    +    hrtimer: Pause KCOV during deferred rearm
>     
>         Deferred hrtimer rearm can run after HARDIRQ_OFFSET is dropped. in_task()
>         is then true, so KCOV attributes the instrumented timer-reprogramming
>         subtree to current.
>     
>    -    With CONFIG_KCOV_SELFTEST, the interrupt selftest fails on x86_64
>    -    defconfig under QEMU, detecting spurious coverage in
>    -    __hrtimer_rearm_deferred(). The same happens on s390, RISC-V and
>    -    LoongArch, which also enable HRTIMER_REARM_DEFERRED.
>    +    With CONFIG_KCOV_SELFTEST added to x86_64 defconfig, the interrupt
>    +    selftest fails under QEMU, detecting spurious coverage in
>    +    __hrtimer_rearm_deferred(). The same happens on s390, RISC-V and LoongArch,
>    +    which also enable HRTIMER_REARM_DEFERRED.
>     
>         Excluding the involved files instead would cost their coverage on real
>         task-context paths, e.g. the hrtimer and timekeeping syscalls.
>     
>    -    Pause in the __always_inline wrappers, including hrtick_schedule_exit().
>    -    Call sites where task KCOV can be active are KCOV-disabled or noinstr.
>    -    HAVE_NOINSTR_HACK covers pre-GCC-12 x86. The other affected
>    -    architectures restrict KCOV to GCC 12 or Clang through
>    -    ARCH_WANTS_NO_INSTR. This avoids relying on __no_sanitize_coverage,
>    -    which is empty before GCC 12. Tested with GCC 8.1 and 15 on x86_64.
>    +    Take the kcov_pause guard in an __always_inline wrapper around
>    +    __hrtimer_rearm_deferred(). Use it at all call sites, including
>    +    hrtick_schedule_exit(). Callers that may run with KCOV enabled for current
>    +    are built without KCOV instrumentation or marked noinstr. HAVE_NOINSTR_HACK
>    +    covers pre-GCC-12 x86. The other affected architectures restrict KCOV to
>    +    GCC 12 or Clang through ARCH_WANTS_NO_INSTR. This avoids relying on
>    +    __no_sanitize_coverage, which is empty before GCC 12. Tested with GCC 8.1
>    +    and 15 on x86_64.
>     
>         Fixes: 15dd3a948855 ("hrtimer: Push reprogramming timers into the interrupt return path")
>    -    Assisted-by: Claude:claude-opus-4-8
>         Assisted-by: Claude:claude-fable-5
>         Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
>     
>    @@ include/linux/hrtimer_rearm.h
>      void __hrtimer_rearm_deferred(void);
>      
>     +/*
>    -+ * Pause outside __hrtimer_rearm_deferred() to suppress its entry coverage.
>    -+ * Call sites where task KCOV can be active are uninstrumented.
>    ++ * KCOV: Pause outside __hrtimer_rearm_deferred() to suppress entry coverage.
>    ++ * Callers with KCOV enabled for current must be uninstrumented.
>     + */
>    -+static __always_inline void hrtimer_rearm_deferred_paused(void)
>    ++static __always_inline void hrtimer_rearm_deferred_kcov_paused(void)
>     +{
>    -+	unsigned int kcov_paused = kcov_pause(current);
>    ++	guard(kcov_pause)();
>     +
>     +	__hrtimer_rearm_deferred();
>    -+	kcov_resume(current, kcov_paused);
>     +}
>     +
>      /*
>    @@ include/linux/hrtimer_rearm.h: hrtimer_rearm_deferred_user_irq(unsigned long *ti
>      	if (unlikely((*tif_work & TIF_REARM_MASK) == _TIF_HRTIMER_REARM)) {
>      		clear_thread_flag(TIF_HRTIMER_REARM);
>     -		__hrtimer_rearm_deferred();
>    -+		hrtimer_rearm_deferred_paused();
>    ++		hrtimer_rearm_deferred_kcov_paused();
>      		/* Don't go into the loop if HRTIMER_REARM was the only flag */
>      		*tif_work &= ~TIF_HRTIMER_REARM;
>      		return !*tif_work;
>    @@ include/linux/hrtimer_rearm.h: hrtimer_rearm_deferred_user_irq(unsigned long *ti
>      {
>      	if (hrtimer_test_and_clear_rearm_deferred_tif(tif_work))
>     -		__hrtimer_rearm_deferred();
>    -+		hrtimer_rearm_deferred_paused();
>    ++		hrtimer_rearm_deferred_kcov_paused();
>      }
>      
>      /*
>    @@ include/linux/hrtimer_rearm.h: static __always_inline bool hrtimer_test_and_clea
>      
>      #else  /* CONFIG_HRTIMER_REARM_DEFERRED */
>      static __always_inline void __hrtimer_rearm_deferred(void) { }
>    -+static __always_inline void hrtimer_rearm_deferred_paused(void) { }
>    ++static __always_inline void hrtimer_rearm_deferred_kcov_paused(void) { }
>      static __always_inline void hrtimer_rearm_deferred(void) { }
>      static __always_inline void hrtimer_rearm_deferred_tif(unsigned long tif_work) { }
>      static __always_inline bool
>    @@ kernel/sched/core.c: static inline void hrtick_schedule_exit(struct rq *rq)
>      
>      	if (rq->hrtick_sched & HRTICK_SCHED_REARM_HRTIMER)
>     -		__hrtimer_rearm_deferred();
>    -+		hrtimer_rearm_deferred_paused();
>    ++		hrtimer_rearm_deferred_kcov_paused();
>      
>      	rq->hrtick_sched = HRTICK_SCHED_NONE;
>      }
>3:  dc4bdcf8adacd ! 4:  63657f2c7ef08 sched: pause KCOV in __schedule()
>    @@ Metadata
>     Author: Karl Mehltretter <kmehltretter@gmail.com>
>     
>      ## Commit message ##
>    -    sched: pause KCOV in __schedule()
>    +    sched/core: Pause KCOV in __schedule()
>     
>         kernel/sched/ is not instrumented, but callees such as sched_clock(),
>         architecture CPU-capacity helpers and profile_hits() are.
>    @@ Commit message
>         During preemption and schedule() calls, instrumented callees can add
>         nondeterministic scheduler coverage to current.
>     
>    -    With CONFIG_KCOV_SELFTEST, the interrupt selftest fails on x86_64
>    -    defconfig under QEMU, detecting spurious coverage in
>    -    arch_scale_cpu_capacity(). On arm64 the same class of leak appears in
>    -    sched_clock(), once the separate arm64 interrupt-accounting leak is
>    -    suppressed.
>    +    With CONFIG_KCOV_SELFTEST added to x86_64 defconfig, the interrupt
>    +    selftest fails under QEMU, detecting spurious coverage in
>    +    arch_scale_cpu_capacity().
>     
>         Annotating each callee would spread exclusions across architectures.
>         Pause across __schedule() instead, extending the scheduler exclusion to
>         its callees.
>     
>    -    KCOV_PAUSED remains set while a task is switched out. Its resumed
>    -    __schedule() frame restores the prior state.
>    +    KCOV_PAUSED remains set while a task is switched out. The guard in its
>    +    resumed __schedule() frame restores the prior state.
>     
>         Fixes: 5c9a8750a640 ("kernel: add kcov code coverage")
>    -    Assisted-by: Claude:claude-opus-4-8
>         Assisted-by: Claude:claude-fable-5
>         Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
>     
>      ## kernel/sched/core.c ##
>     @@ kernel/sched/core.c: static void __sched notrace __schedule(int sched_mode)
>    - 	bool is_switch = false;
>    - 	unsigned long *switch_count;
>    - 	unsigned long prev_state;
>    -+	unsigned int kcov_paused;
>    - 	struct rq_flags rf;
>      	struct rq *rq;
>      	int cpu;
>      
>    -+	/* KCOV: sched/ is uninstrumented but the __schedule() callees are not. */
>    -+	kcov_paused = kcov_pause(current);
>    ++	/* Instrumented callees would leak coverage into current. */
>    ++	guard(kcov_pause)();
>     +
>      	/* Trace preemptions consistently with task switches */
>      	trace_sched_entry_tp(sched_mode == SM_PREEMPT);
>      
>    -@@ kernel/sched/core.c: static void __sched notrace __schedule(int sched_mode)
>    - 		raw_spin_rq_unlock_irq(rq);
>    - 	}
>    - 	trace_sched_exit_tp(is_switch);
>    -+	kcov_resume(current, kcov_paused);
>    - }
>    - 
>    - void __noreturn do_task_dead(void)
>4:  b8e96cc1903de ! 5:  5cf8497b8a0ab sched: pause KCOV in try_to_wake_up()
>    @@ Metadata
>     Author: Karl Mehltretter <kmehltretter@gmail.com>
>     
>      ## Commit message ##
>    -    sched: pause KCOV in try_to_wake_up()
>    +    sched/core: Pause KCOV in try_to_wake_up()
>     
>         try_to_wake_up() is uninstrumented, but it calls instrumented helpers
>         such as kthread_is_per_cpu(), CPU capacity helpers and SCHED_HRTICK
>    @@ Commit message
>         selftest's spin. The same helpers leak into non-RT syscall wakeups such
>         as a pipe write waking a reader.
>     
>    -    Pause all of try_to_wake_up(). Wrapping only select_task_rq() would miss
>    -    SCHED_HRTICK arming during enqueue.
>    +    Pause the wakeup body with the kcov_pause guard. Wrapping only
>    +    select_task_rq() would miss SCHED_HRTICK arming during enqueue.
>     
>         Fixes: 5c9a8750a640 ("kernel: add kcov code coverage")
>    -    Assisted-by: Claude:claude-opus-4-8
>    +    Assisted-by: Claude:claude-fable-5
>         Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
>     
>      ## kernel/sched/core.c ##
>     @@ kernel/sched/core.c: int try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
>    - {
>      	guard(preempt)();
>      	int cpu, success = 0;
>    -+	/* KCOV: sched/ is uninstrumented but the wakeup callees are not. */
>    -+	unsigned int kcov_paused = kcov_pause(current);
>      
>    ++	/* Instrumented callees would leak coverage into current. */
>    ++	guard(kcov_pause)();
>    ++
>      	wake_flags |= WF_TTWU;
>      
>    -@@ kernel/sched/core.c: int try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
>    - 	if (success)
>    - 		ttwu_stat(p, task_cpu(p), wake_flags);
>    - 
>    -+	kcov_resume(current, kcov_paused);
>    - 	return success;
>    - }
>    - 
>    + 	if (p == current) {
>5:  f91a7644a15e8 ! 6:  a00870853f5a1 sched: pause KCOV in wake_up_new_task()
>    @@ Metadata
>     Author: Karl Mehltretter <kmehltretter@gmail.com>
>     
>      ## Commit message ##
>    -    sched: pause KCOV in wake_up_new_task()
>    +    sched/core: Pause KCOV in wake_up_new_task()
>     
>         wake_up_new_task() is uninstrumented, but CPU selection and enqueue call
>         instrumented helpers. During a KCOV-enabled fork, they can record
>    @@ Commit message
>         scheduler exclusion to new-task wakeups.
>     
>         Fixes: 5c9a8750a640 ("kernel: add kcov code coverage")
>    -    Assisted-by: Claude:claude-opus-4-8
>    +    Assisted-by: Claude:claude-fable-5
>         Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
>     
>      ## kernel/sched/core.c ##
>     @@ kernel/sched/core.c: void wake_up_new_task(struct task_struct *p)
>    - {
>    - 	struct rq_flags rf;
>      	struct rq *rq;
>    -+	unsigned int kcov_paused;
>      	int wake_flags = WF_FORK;
>      
>    -+	/* KCOV: sched/ is uninstrumented but the wakeup callees are not. */
>    -+	kcov_paused = kcov_pause(current);
>    ++	/* Instrumented callees would leak coverage into current. */
>    ++	guard(kcov_pause)();
>     +
>      	raw_spin_lock_irqsave(&p->pi_lock, rf.flags);
>      	WRITE_ONCE(p->__state, TASK_RUNNING);
>      	/*
>    -@@ kernel/sched/core.c: void wake_up_new_task(struct task_struct *p)
>    - 		rq_repin_lock(rq, &rf);
>    - 	}
>    - 	task_rq_unlock(rq, p, &rf);
>    -+	kcov_resume(current, kcov_paused);
>    - }
>    - 
>    - #ifdef CONFIG_PREEMPT_NOTIFIERS
>
>base-commit: 8ba098e6b6ff0db8edf28528d1552be261af30d4
>

Thanks!

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

* Re: [PATCH v2 3/6] hrtimer: Pause KCOV during deferred rearm
  2026-08-11 15:41 ` [PATCH v2 3/6] hrtimer: Pause KCOV during deferred rearm Karl Mehltretter
@ 2026-08-12 10:21   ` Peter Zijlstra
  0 siblings, 0 replies; 13+ messages in thread
From: Peter Zijlstra @ 2026-08-12 10:21 UTC (permalink / raw)
  To: Karl Mehltretter
  Cc: Andrew Morton, Andrey Konovalov, Alexander Potapenko,
	Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
	Frederic Weisbecker, Thomas Gleixner, Ingo Molnar, Juri Lelli,
	Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
	Mel Gorman, Valentin Schneider, K Prateek Nayak,
	Sebastian Andrzej Siewior, Clark Williams, linux-rt-devel,
	kasan-dev, linux-kernel

On Tue, Aug 11, 2026 at 05:41:08PM +0200, Karl Mehltretter wrote:
> Deferred hrtimer rearm can run after HARDIRQ_OFFSET is dropped. in_task()
> is then true, so KCOV attributes the instrumented timer-reprogramming
> subtree to current.

But that is clearly noinstr code; there should be no kcov calls in
there.

If kcov is emitted inside noinstr, then kcov is a broken piece of crap
and needs to die.

NAK

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

* Re: [PATCH v2 4/6] sched/core: Pause KCOV in __schedule()
  2026-08-11 15:41 ` [PATCH v2 4/6] sched/core: Pause KCOV in __schedule() Karl Mehltretter
@ 2026-08-12 10:35   ` Peter Zijlstra
  0 siblings, 0 replies; 13+ messages in thread
From: Peter Zijlstra @ 2026-08-12 10:35 UTC (permalink / raw)
  To: Karl Mehltretter
  Cc: Andrew Morton, Andrey Konovalov, Alexander Potapenko,
	Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
	Frederic Weisbecker, Thomas Gleixner, Ingo Molnar, Juri Lelli,
	Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
	Mel Gorman, Valentin Schneider, K Prateek Nayak,
	Sebastian Andrzej Siewior, Clark Williams, linux-rt-devel,
	kasan-dev, linux-kernel

On Tue, Aug 11, 2026 at 05:41:09PM +0200, Karl Mehltretter wrote:
> kernel/sched/ is not instrumented, 

It bloody well is; there is no noinstr here, just a notrace.

> but callees such as sched_clock(),
> architecture CPU-capacity helpers and profile_hits() are.
> 
> During preemption and schedule() calls, instrumented callees can add
> nondeterministic scheduler coverage to current.
> 
> With CONFIG_KCOV_SELFTEST added to x86_64 defconfig, the interrupt
> selftest fails under QEMU, detecting spurious coverage in
> arch_scale_cpu_capacity().
> 
> Annotating each callee would spread exclusions across architectures.
> Pause across __schedule() instead, extending the scheduler exclusion to
> its callees.
> 
> KCOV_PAUSED remains set while a task is switched out. The guard in its
> resumed __schedule() frame restores the prior state.
> 
> Fixes: 5c9a8750a640 ("kernel: add kcov code coverage")
> Assisted-by: Claude:claude-fable-5
> Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
> ---
> Notes:
>     v2:
>      - take guard(kcov_pause)()
>      - clarify the test configuration and reword the pause comment
> 
>  kernel/sched/core.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index 3cbf817f52a4a..e49f0bfa0ca73 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -7073,6 +7073,9 @@ static void __sched notrace __schedule(int sched_mode)
>  	struct rq *rq;
>  	int cpu;
>  
> +	/* Instrumented callees would leak coverage into current. */

This comment makes about as much sense as your Changelog.

> +	guard(kcov_pause)();
> +
>  	/* Trace preemptions consistently with task switches */
>  	trace_sched_entry_tp(sched_mode == SM_PREEMPT);
>  
> -- 
> 2.53.0

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

* Re: [PATCH v2 5/6] sched/core: Pause KCOV in try_to_wake_up()
  2026-08-11 15:41 ` [PATCH v2 5/6] sched/core: Pause KCOV in try_to_wake_up() Karl Mehltretter
@ 2026-08-12 10:35   ` Peter Zijlstra
  0 siblings, 0 replies; 13+ messages in thread
From: Peter Zijlstra @ 2026-08-12 10:35 UTC (permalink / raw)
  To: Karl Mehltretter
  Cc: Andrew Morton, Andrey Konovalov, Alexander Potapenko,
	Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
	Frederic Weisbecker, Thomas Gleixner, Ingo Molnar, Juri Lelli,
	Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
	Mel Gorman, Valentin Schneider, K Prateek Nayak,
	Sebastian Andrzej Siewior, Clark Williams, linux-rt-devel,
	kasan-dev, linux-kernel

On Tue, Aug 11, 2026 at 05:41:10PM +0200, Karl Mehltretter wrote:
> try_to_wake_up() is uninstrumented, but it calls instrumented helpers
> such as kthread_is_per_cpu(), CPU capacity helpers and SCHED_HRTICK
> arming. They can record into current while in_task() is true.
> 
> CONFIG_KCOV_SELFTEST exposes this under PREEMPT_RT. The interrupt
> selftest fails on x86_64 in kthread_is_per_cpu(): RT runs the timer
> softirq in a thread, so the wakeup runs in task context during the
> selftest's spin. The same helpers leak into non-RT syscall wakeups such
> as a pipe write waking a reader.
> 
> Pause the wakeup body with the kcov_pause guard. Wrapping only
> select_task_rq() would miss SCHED_HRTICK arming during enqueue.
> 
> Fixes: 5c9a8750a640 ("kernel: add kcov code coverage")
> Assisted-by: Claude:claude-fable-5
> Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
> ---
> Notes:
>     v2:
>      - take guard(kcov_pause)()
>      - describe the paused wakeup body precisely
> 
>  kernel/sched/core.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index e49f0bfa0ca73..e863fac02e38f 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -4253,6 +4253,9 @@ int try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
>  	guard(preempt)();
>  	int cpu, success = 0;
>  
> +	/* Instrumented callees would leak coverage into current. */
> +	guard(kcov_pause)();

Same problem again. All of this code is instrumented.

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

* Re: [PATCH v2 6/6] sched/core: Pause KCOV in wake_up_new_task()
  2026-08-11 15:41 ` [PATCH v2 6/6] sched/core: Pause KCOV in wake_up_new_task() Karl Mehltretter
@ 2026-08-12 10:36   ` Peter Zijlstra
  0 siblings, 0 replies; 13+ messages in thread
From: Peter Zijlstra @ 2026-08-12 10:36 UTC (permalink / raw)
  To: Karl Mehltretter
  Cc: Andrew Morton, Andrey Konovalov, Alexander Potapenko,
	Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
	Frederic Weisbecker, Thomas Gleixner, Ingo Molnar, Juri Lelli,
	Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
	Mel Gorman, Valentin Schneider, K Prateek Nayak,
	Sebastian Andrzej Siewior, Clark Williams, linux-rt-devel,
	kasan-dev, linux-kernel

On Tue, Aug 11, 2026 at 05:41:11PM +0200, Karl Mehltretter wrote:
> wake_up_new_task() is uninstrumented, but CPU selection and enqueue call
> instrumented helpers. During a KCOV-enabled fork, they can record
> scheduler, hrtimer and clockevent coverage into the parent.
> 
> The paths depend on runqueue and CPU state, so coverage varies between
> identical forks. Pause KCOV for the whole function, extending the
> scheduler exclusion to new-task wakeups.
> 
> Fixes: 5c9a8750a640 ("kernel: add kcov code coverage")
> Assisted-by: Claude:claude-fable-5
> Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
> ---
> Notes:
>     v2:
>      - take guard(kcov_pause)()
>      - reword the pause comment
> 
>  kernel/sched/core.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index e863fac02e38f..f7ddfbbb5a494 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -4947,6 +4947,9 @@ void wake_up_new_task(struct task_struct *p)
>  	struct rq *rq;
>  	int wake_flags = WF_FORK;
>  
> +	/* Instrumented callees would leak coverage into current. */
> +	guard(kcov_pause)();

I'm sure you know what I'm about to say now, right?

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

* Re: [PATCH v2 0/6] kcov: Suppress timer and scheduler coverage leaks
  2026-08-11 19:59 ` [PATCH v2 0/6] kcov: Suppress timer and scheduler coverage leaks Bradley Morgan
@ 2026-08-12 10:36   ` Peter Zijlstra
  0 siblings, 0 replies; 13+ messages in thread
From: Peter Zijlstra @ 2026-08-12 10:36 UTC (permalink / raw)
  To: Bradley Morgan
  Cc: Karl Mehltretter, Andrew Morton, Andrey Konovalov,
	Alexander Potapenko, Dmitry Vyukov, Marco Elver,
	Anna-Maria Behnsen, Frederic Weisbecker, Thomas Gleixner,
	Ingo Molnar, Juri Lelli, Vincent Guittot, Dietmar Eggemann,
	Steven Rostedt, Ben Segall, Mel Gorman, Valentin Schneider,
	K Prateek Nayak, Sebastian Andrzej Siewior, Clark Williams,
	linux-rt-devel, kasan-dev, linux-kernel

On Tue, Aug 11, 2026 at 08:59:41PM +0100, Bradley Morgan wrote:
> On 11 August 2026 16:41:05 BST, Karl Mehltretter <kmehltretter@gmail.com>
> wrote:
> >KCOV aims to exclude interrupt and scheduler coverage so syscall coverage
> >stays input-dependent. Instrumented callees can still record when
> >uninstrumented timer and scheduler paths run with in_task() true.
> >
> >With the diagnostic patch in [1] applied, CONFIG_KCOV_SELFTEST exposes
> >three cases on x86-64: deferred hrtimer rearm, __schedule() callees and
> >PREEMPT_RT wakeups. Task-context wakeups and new-task enqueue also add
> >scheduler coverage to ordinary syscalls.
> >
> >Add a nestable KCOV_PAUSED bit and a kcov_pause guard. Use the guard for
> >deferred hrtimer rearm, __schedule(), the try_to_wake_up() wakeup body
> >and wake_up_new_task(). This suppresses their instrumented callees
> >without excluding those callees from task-context coverage.
> >
> >Changes in v2:
> > - Add patch 1 to make kcov_start()'s mode parameter unsigned int. No
> >   functional change.
> > - Rework patch 2 around a guard-only API with private current-only
> >   helpers (Bradley Morgan).
> > - Use the guard in patches 3-6 and reword the pause comments.
> >
> >v2 testing:
> > - GCC builds on x86-64, arm32, arm64, MIPS32/64, PowerPC 32/64,
> >   s390, RISC-V 32/64, LoongArch, Xtensa and UML.
> > - PREEMPT_RT builds on x86-64, arm32, arm64, RISC-V 32/64 and
> >   LoongArch.
> > - x86-64 builds with GCC 8.1 and Clang 22.1. Both kernels passed a
> >   KCOV selftest boot.
> > - x86-64 CONFIG_KCOV=n build, with no KCOV or pause references.
> > - KCOV selftest, 10/10 x86-64 boots with and without PREEMPT_RT. A
> >   fresh non-RT boot passed after the helper-only rework.
> > - 40 dummy_hcd/g_zero remote-KCOV cycles on x86-64 and arm64.
> > - 400 repeated fork() calls on x86-64 PREEMPT_RT.
> >
> >Three one-hour syzkaller A/B pairs were run. Each baseline and patched
> >run used four 2-vCPU PREEMPT_RT VMs. The patched kernel completed 22-51%
> >more executions than base. At matched execution counts, corpus size grew
> >42-54% and coverage 14-19%. No run produced a report.
> >
> >With KCOV disabled, the pause sections compile away. With KCOV enabled
> >on x86-64, GCC 15.2 grows __schedule() by 117 bytes,
> >try_to_wake_up() by 94 bytes and wake_up_new_task() by 88 bytes relative
> >to the base commit.
> >
> >[1]
> >https://lore.kernel.org/r/20260724192122.73080-1-kmehltretter@gmail.com
> >
> 
> 
> Reviewed-by: Bradley Morgan <include@grrlz.net>

Plonk

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

end of thread, other threads:[~2026-08-12 10:36 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-11 15:41 [PATCH v2 0/6] kcov: Suppress timer and scheduler coverage leaks Karl Mehltretter
2026-08-11 15:41 ` [PATCH v2 1/6] kcov: Use unsigned int for kcov_start() mode parameter Karl Mehltretter
2026-08-11 15:41 ` [PATCH v2 2/6] kcov: Add a kcov_pause guard Karl Mehltretter
2026-08-11 15:41 ` [PATCH v2 3/6] hrtimer: Pause KCOV during deferred rearm Karl Mehltretter
2026-08-12 10:21   ` Peter Zijlstra
2026-08-11 15:41 ` [PATCH v2 4/6] sched/core: Pause KCOV in __schedule() Karl Mehltretter
2026-08-12 10:35   ` Peter Zijlstra
2026-08-11 15:41 ` [PATCH v2 5/6] sched/core: Pause KCOV in try_to_wake_up() Karl Mehltretter
2026-08-12 10:35   ` Peter Zijlstra
2026-08-11 15:41 ` [PATCH v2 6/6] sched/core: Pause KCOV in wake_up_new_task() Karl Mehltretter
2026-08-12 10:36   ` Peter Zijlstra
2026-08-11 19:59 ` [PATCH v2 0/6] kcov: Suppress timer and scheduler coverage leaks Bradley Morgan
2026-08-12 10:36   ` Peter Zijlstra

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