From nobody Sun Jun 14 02:37:15 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 91FE13C2782 for ; Mon, 4 May 2026 15:31:08 +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=1777908671; cv=none; b=J3DD/MklK6ACm//3LOwf5ZVZ5zZAdbr7SkyF/fGrlKLLeMQyK6PswXwX2VBdXIbEGyN11Z/WPGgJoZx37sJibudVeyUK75vbHyJDtR1gmIkPT4SAn33j3nV5QSSIJPh32o29bRVms6JrS0DUGXD1A/R6GavUPdJEnJOH9oiRH40= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777908671; c=relaxed/simple; bh=sypYVmn4l/nfABoC4ULEE2pvHe1QGehjC67fb2gOhjQ=; h=Message-ID:Date:MIME-Version:To:From:Subject:Cc:Content-Type; b=X5uxwyKA63Io9EGEA8XhdyCIPmuCR9atE/XB83Eh4n1Pg+NMX/OdvkToyi3bt2Kkc3dAG0TrwB8U0GSzmuirHcWfIJLLpEh66sk2pQxQxIQbI+9zRL6Gvx17+wLk6Y6UUHZHWy/e8spu7uowoJ9dcKWuW0chbQ7FqKlyArJ6Alw= 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 644FUtAI095021; Tue, 5 May 2026 00:30:55 +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 644FUGCA094845 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=NO); Tue, 5 May 2026 00:30:55 +0900 (JST) (envelope-from penguin-kernel@I-love.SAKURA.ne.jp) Message-ID: Date: Tue, 5 May 2026 00:30:14 +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 Content-Language: en-US To: kasan-dev , LKML From: Tetsuo Handa Subject: [PATCH (draft)] kcov: fix potential kcov_mode corruption under CONFIG_PREEMPT_RT Cc: Dmitry Vyukov , Alexander Potapenko Content-Transfer-Encoding: quoted-printable X-Virus-Status: clean X-Anti-Virus-Server: fsav404.rs.sakura.ne.jp 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 [1]. 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_ur= b_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 skipp= ed 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: Debug logs show 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. I wrote this patch's body, but patch description above was generated using Google AI mode. I can't tell whether above explanation is correct. Therefore, please review but don't accept yet. For now: Link: https://syzkaller.appspot.com/bug?extid=3Da21650c1666eae7b2aae [1] Analyzed-by: https://share.google/aimode/5aL6wtYxiS3koWnAx (expires in 7 da= ys) Not-yet-signed-off-by: Tetsuo Handa Fixes: 5ff3b30ab57d ("kcov: collect coverage from interrupts") Reported-by: syzbot+3f51ad7ac3ae57a6fdcc@syzkaller.appspotmail.com Maybe-closes: https://syzkaller.appspot.com/bug?extid=3D3f51ad7ac3ae57a6fdcc --- kernel/kcov.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/kernel/kcov.c b/kernel/kcov.c index 0b369e88c7c9..c20d3c2712d7 100644 --- a/kernel/kcov.c +++ b/kernel/kcov.c @@ -171,6 +171,15 @@ 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) +{ +#ifdef CONFIG_PREEMPT_RT + return !in_serving_softirq() && !in_hardirq() && !in_nmi(); +#else + return in_task(); +#endif +} + static notrace bool check_kcov_mode(enum kcov_mode needed_mode, struct tas= k_struct *t) { unsigned int mode; @@ -903,7 +912,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 +924,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 +933,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)); @@ -1069,7 +1078,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