From nobody Sun Jun 14 00:16:56 2026 Received: from www262.sakura.ne.jp (www262.sakura.ne.jp [202.181.97.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3EAC633F5BF for ; Tue, 5 May 2026 08:15:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.181.97.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777968950; cv=none; b=ae5MSfx2UGfuaXxQx+sIRcrAewnP/vLcvvdNEaT4kX+oEZ50+ic1/FLXjBt9EPaAs/GH2cZ6dmixR5RLyrMSvBgqGfPoeoGdqEculKfGRYGo5ebth4Zq+NtyrZK5Ketq9TnWPPAwGzcY1FKNFbz8uInUjHBuLPwQC270UaoujU4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777968950; c=relaxed/simple; bh=43y7Y8c/qNuMrK7ZECpmX5Z96l0wK6TxPFnw6NyOL4A=; h=Message-ID:Date:MIME-Version:Subject:From:To:Cc:References: In-Reply-To:Content-Type; b=B3UysJgsekCw/FpIEL4m7gWh7ilQC/zef4DJ2JvofGJ7lPjqEDDbiWDyS55FJAV9wowXF8tdeVsaUavzEGNdAaY5iLjV53c8Chg1e45HLW5DuIj6K/vlDelZfRIkHdXNHatRr9bd9mZlH5pocNgWjJk0a2QcQb4GQdORLvrZARo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=I-love.SAKURA.ne.jp; spf=pass smtp.mailfrom=I-love.SAKURA.ne.jp; arc=none smtp.client-ip=202.181.97.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=I-love.SAKURA.ne.jp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=I-love.SAKURA.ne.jp Received: from www262.sakura.ne.jp (localhost [127.0.0.1]) by www262.sakura.ne.jp (8.15.2/8.15.2) with ESMTP id 6458FfVu076465; Tue, 5 May 2026 17:15:41 +0900 (JST) (envelope-from penguin-kernel@I-love.SAKURA.ne.jp) Received: from [192.168.1.5] (M106072072000.v4.enabler.ne.jp [106.72.72.0]) (authenticated bits=0) by www262.sakura.ne.jp (8.15.2/8.15.2) with ESMTPSA id 6458Ffu4076462 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=NO); Tue, 5 May 2026 17:15:41 +0900 (JST) (envelope-from penguin-kernel@I-love.SAKURA.ne.jp) Message-ID: Date: Tue, 5 May 2026 17:15:41 +0900 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: [PATCH] kcov: fix potential kcov_mode corruption under CONFIG_PREEMPT_RT From: Tetsuo Handa To: kasan-dev , LKML Cc: Dmitry Vyukov , Alexander Potapenko References: Content-Language: en-US In-Reply-To: Content-Transfer-Encoding: quoted-printable X-Anti-Virus-Server: fsav401.rs.sakura.ne.jp X-Virus-Status: clean Content-Type: text/plain; charset="utf-8" Problem Description: syzbot has reported intermittent kcov_mode corruption when running with CONFIG_PREEMPT_RT=3Dy. Specifically, struct task_struct->kcov_mode is fou= nd to be unexpectedly modified (e.g., changed from 0 to 2) during workqueue execution, even when the work handler itself does not use KCOV. [ 224.426704][ T5698] BUG: workqueue function defense_work_handler cha= nged kcov_mode from 0 to 2 [ 224.441977][ C1] BUG: workqueue function usb_giveback_urb_bh chan= ged kcov_mode from 2 to 0 Root Cause Analysis: The issue stems from the ambiguity of in_task() under CONFIG_PREEMPT_RT. Since commit 5ff3b30ab57d ("kcov: collect coverage from interrupts"), kcov_remote_start() uses in_task() to decide whether to use a per-CPU preallocated buffer (irq_area) or a task-specific buffer (allocated via vmalloc). In a CONFIG_PREEMPT_RT kernel, in_task() returns true even for threaded softirqs (e.g., ksoftirqd or threaded IRQ handlers), unless they are explicitly within a local_bh_disable() region at the time of the check. This leads to several critical issues: 1. Context Mismatch: A threaded interrupt handler (like usb_giveback_urb_bh()) might be identified as a "task" context. If it starts a remote KCOV session, it incorrectly modifies the kworker's task_struct->kcov_mode using the logic intended for system calls. 2. State Leakage: If the result of in_task() differs between kcov_remote_start() and kcov_remote_stop() due to the subtle state changes in RT scheduling, the cleanup code (kcov_stop()) might be skipped or applied to the wrong context. This leaves kcov_mode set to a non-zero value (e.g., KCOV_MODE_TRACE_CMP) when the worker returns to process_one_work(). 3. Evidence: The message quoted in Problem Description showed the pr_err() from process_one_work() carrying a [ C1] (CPU-based) prefix instead of a [Txxx] (Task-based) prefix. This confirms that at the moment of the error, in_serving_softirq() was true, causing in_task() to be false, which contradicts the state when the work started. Proposed Fix: Introduce a more strict context check, in_task_really(), which explicitly excludes any softirq context (including threaded ones) by checking in_serving_softirq() regardless of the CONFIG_PREEMPT_RT configuration. This ensures that threaded interrupts always use the irq_area path and do not corrupt the task_struct state of the worker threads they happen to preempt. Also, replace !in_task() && !in_serving_softirq() test in kcov_remote_{start,stop}() with in_hardirq() || in_nmi() test, for relying on unstable in_serving_softirq() test might break symmetry of kcov_remote_{start,stop}() calls. Link: https://syzkaller.appspot.com/bug?extid=3De6686317bd9fe911591a Analyzed-by: Google AI mode (no mail address) Signed-off-by: Tetsuo Handa Fixes: 5ff3b30ab57d ("kcov: collect coverage from interrupts") --- kernel/kcov.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/kernel/kcov.c b/kernel/kcov.c index 0b369e88c7c9..f77bcd507701 100644 --- a/kernel/kcov.c +++ b/kernel/kcov.c @@ -171,6 +171,16 @@ static __always_inline bool in_softirq_really(void) return in_serving_softirq() && !in_hardirq() && !in_nmi(); } =20 +static __always_inline bool in_task_really(void) +{ + /* Caller won't call this function if in_hardirq() || in_nmi(). */ +#ifdef CONFIG_PREEMPT_RT + return !in_serving_softirq(); +#else + return in_task(); +#endif +} + static notrace bool check_kcov_mode(enum kcov_mode needed_mode, struct tas= k_struct *t) { unsigned int mode; @@ -869,9 +879,10 @@ void kcov_remote_start(u64 handle) int sequence; unsigned long flags; =20 - if (WARN_ON(!kcov_check_handle(handle, true, true, true))) + /* Don't use in_task() in order to allow consistent checks in RT kernels.= */ + if (in_hardirq() || in_nmi()) return; - if (!in_task() && !in_softirq_really()) + if (WARN_ON(!kcov_check_handle(handle, true, true, true))) return; =20 local_lock_irqsave(&kcov_percpu_data.lock, flags); @@ -903,7 +914,7 @@ void kcov_remote_start(u64 handle) return; } kcov_debug("handle =3D %llx, context: %s\n", handle, - in_task() ? "task" : "softirq"); + in_task_really() ? "task" : "softirq"); kcov =3D remote->kcov; /* Put in kcov_remote_stop(). */ kcov_get(kcov); @@ -915,7 +926,7 @@ void kcov_remote_start(u64 handle) */ mode =3D context_unsafe(kcov->mode); sequence =3D kcov->sequence; - if (in_task()) { + if (in_task_really()) { size =3D kcov->remote_size; area =3D kcov_remote_area_get(size); } else { @@ -924,7 +935,7 @@ void kcov_remote_start(u64 handle) } spin_unlock(&kcov_remote_lock); =20 - /* Can only happen when in_task(). */ + /* Can only happen when in_task_really(). */ if (!area) { local_unlock_irqrestore(&kcov_percpu_data.lock, flags); area =3D vmalloc(size * sizeof(unsigned long)); @@ -1024,7 +1035,8 @@ void kcov_remote_stop(void) int sequence; unsigned long flags; =20 - if (!in_task() && !in_softirq_really()) + /* Don't use in_task() in order to allow consistent checks in RT kernels.= */ + if (in_hardirq() || in_nmi()) return; =20 local_lock_irqsave(&kcov_percpu_data.lock, flags); @@ -1069,7 +1081,7 @@ void kcov_remote_stop(void) kcov_move_area(kcov->mode, kcov->area, kcov->size, area); spin_unlock(&kcov->lock); =20 - if (in_task()) { + if (in_task_really()) { spin_lock(&kcov_remote_lock); kcov_remote_area_put(area, size); spin_unlock(&kcov_remote_lock); --=20 2.47.3