From nobody Sat Feb 7 15:35:09 2026 Received: from mail-dy1-f169.google.com (mail-dy1-f169.google.com [74.125.82.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3E6B423E33D for ; Thu, 22 Jan 2026 04:27:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769056050; cv=none; b=TT5S7YfgXBT65dIIMSXl77ABjM+HqpchT+aQGk2UaqE1i+wu1vlPYPJfp4+aH3/oQfBFyGcbXz+S7Eh18FjLnTgFRHsaMqqNZVRWXAmGTE/M0E7B7cEBKMmrh7iAfDQuqUWoMiKw/VSHwzDiOlPJRHYCAv4hC6fv1oWc8PtFXrA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769056050; c=relaxed/simple; bh=tMCG7MEFgmx74XCQvDM8IA7fv+ccRPY8I80ezU21rA8=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=AEegG6znAaAETrHl1MYd6zTBMJYSmsiKVlbnBagiRFRxMFtFg9VoF2jCB2i5FfQ0GRXijca2asm78BlzDpEbw5Sr30C7C0nCVu1M4BKxtIAfjNpfliLudKlxwFzXcN6JLpZI9nyz3OT6dcN+mRnyd+chb3fdxhMA7W192KV41Ek= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=JFJqVfTl; arc=none smtp.client-ip=74.125.82.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="JFJqVfTl" Received: by mail-dy1-f169.google.com with SMTP id 5a478bee46e88-2b6fd5bec41so1071556eec.1 for ; Wed, 21 Jan 2026 20:27:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769056046; x=1769660846; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=P3Z866xf7eZGY/ifDHN1Pz0RwvrrkjdVsd+yaSnpiyc=; b=JFJqVfTl5sg3gI7wwe9AHYNguSb2bQKDRztMSKBv2xrrgXf/OyyRS3XleEi51sDC+r 1v7Bpc5EgeNY5rhwFl1M9PaLM70OSju5nWMup4fpECzOj9HDTL9Xoo1WgnD1V0EiGyRO V/OM7HH1O52XLIkJJShJR77NfAl30AVkl0qGToiPDzEVpch2q9ymFbfQE7+ls0Alj0ve ygp/MyfkK0sDPbB94kJ+kxuDin3y8Jsj3nnAoandyfIpBXtM2zmluKsFpYRhk6NVl+h7 RzFZohogPBOTFM1qdvwcZuKawAg5pIxona+Uxf8AcYxSZV9d86zW+sVpavBtm9z/Gqim 46Xw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769056046; x=1769660846; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=P3Z866xf7eZGY/ifDHN1Pz0RwvrrkjdVsd+yaSnpiyc=; b=XRASH3kBXJWZOCVxI93kAWfJfthzE7s+7ITTwRJWsfnC35J31qCj/3vOEWUSu9x8Nz D74Fv4rD73d3XM9/bHOuLTQBgW6/fiighqrhQvb0tf+Q/L535PslNHUQ36JZmnFRKj0q U2E60/9MTvvDes0RXQBjVqR3XZZLzUYao3no1kr/p4Woafbc7tEXTCXusEZafVQBRU+o KEkXdpySe8DUYAKwZby9eZauRXiqGdzJJNBqMrC697qipcxip4EvUKYTUYOzd1nM1e+r tz66Ema3c+b945DcJSt/DjmMla3CuRsOsrojuZVCC4lHw6oCKdozcocOBsNeJ2FUeIo+ S52A== X-Forwarded-Encrypted: i=1; AJvYcCVCMw/aHhgewFNciCCtbTwl0Tm4nQdLQT6zHwQxijoJNz2QxX0/uKMkdlmxrdEC7ky8mqPj2wh5DLD0qdo=@vger.kernel.org X-Gm-Message-State: AOJu0YwIgjCcge6XneQzo6RBUmCIdfyJu0VxmhOycimTxADbkEOgfy8N zyhoCA7WGsCZSfQTmi6LXpvwqGGTolkzxAsZOT5Kc/2zjimCALD0zGTy X-Gm-Gg: AZuq6aLM/wB5nUjYLQD7WE4cfZiP7JlGyaeHu0nlhiGANETLXOI9+ZA5C/jb/AeaGiV 58RQPscPrqdseEuiA8FiyiQqXyuptG3XuBlUbjndG3BcBFPhXUFQrhwMcCfvqdEATKdtHM4dcUB dREVabZyYPj3PlsalI9YrbTQbboVbB5paGrQXwMyvNrAPhP9tUrkiRYL5K9Ghog3j4XqA/1JxIB PTkvDBN++pv06BMafSZF4UvUki4eeQ1m264LlgKj0twkJcgzDtxrMc4HELnrRFmNFf/CvWTGuV8 FM3NuowAgbbC9q8iIn1HQ0Vwjys59N3OFqxqZpHoLarnI1hmXoBkEbj7ez7sHpr89QjavscLys7 f+N12VD0j1TQHPhdAXcKGsnWL3tS81SvZLYUkWu/ZIlqmSLvRQYpdlJYqkpivyCePUuwmqEAaQG NPpP0= X-Received: by 2002:a05:7300:ef83:b0:2b7:2bf3:ce05 with SMTP id 5a478bee46e88-2b72bf3d112mr176786eec.28.1769056045959; Wed, 21 Jan 2026 20:27:25 -0800 (PST) Received: from debian ([74.48.213.230]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-1244af10c3csm26637657c88.15.2026.01.21.20.27.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Jan 2026 20:27:25 -0800 (PST) From: Qiliang Yuan To: Li Huafei , Ingo Molnar , Andrew Morton Cc: Thorsten Blum , Yicong Yang , Jinchao Wang , linux-kernel@vger.kernel.org, Qiliang Yuan , Shouxin Sun , Junnan Xhang , Qiliang Yuan Subject: [PATCH] watchdog/hardlockup: Fix UAF in perf event cleanup due to migration race Date: Wed, 21 Jan 2026 23:27:17 -0500 Message-ID: <20260122042717.657231-1-realwujing@gmail.com> X-Mailer: git-send-email 2.51.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" During the early initialization of the hardlockup detector, the hardlockup_detector_perf_init() function probes for PMU hardware availabili= ty. It originally used hardlockup_detector_event_create(), which interacts with the per-cpu 'watchdog_ev' variable. If the initializing task migrates to another CPU during this probe phase, two issues arise: 1. The 'watchdog_ev' pointer on the original CPU is set but not cleared, leaving a stale pointer to a freed perf event. 2. The 'watchdog_ev' pointer on the new CPU might be incorrectly cleared. This race condition was observed in console logs (captured by adding debug = printks): [23.038376] hardlockup_detector_perf_init 313 cur_cpu=3D2 ... [23.076385] hardlockup_detector_event_create 203 cpu(cur)=3D2 set watchdog_= ev ... [23.095788] perf_event_release_kernel 4623 cur_cpu=3D2 ... [23.116963] lockup_detector_reconfigure 577 cur_cpu=3D3 The log shows the task started on CPU 2, set watchdog_ev on CPU 2, released the event on CPU 2, but then migrated to CPU 3 before the cleanup logic (which would clear watchdog_ev) could run. This left watchdog_ev on CPU 2 pointing to a freed event. Later, when the watchdog is enabled/disabled on CPU 2, this stale pointer leads to a Use-After-Free (UAF) in perf_event_disable(), as detected by KAS= AN: [26.539140] =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D [26.540732] BUG: KASAN: use-after-free in perf_event_ctx_lock_nested.isra.7= 2+0x6b/0x140 [26.542442] Read of size 8 at addr ff110006b360d718 by task kworker/2:1/94 [26.543954] [26.544744] CPU: 2 PID: 94 Comm: kworker/2:1 Not tainted 4.19.90-debugkasan= #11 [26.546505] Hardware name: GoStack Foundation OpenStack Nova, BIOS 1.16.3-3= .ctl3 04/01/2014 [26.548256] Workqueue: events smp_call_on_cpu_callback [26.549267] Call Trace: [26.549936] dump_stack+0x8b/0xbb [26.550731] print_address_description+0x6a/0x270 [26.551688] kasan_report+0x179/0x2c0 [26.552519] ? perf_event_ctx_lock_nested.isra.72+0x6b/0x140 [26.553654] ? watchdog_disable+0x80/0x80 [26.553657] perf_event_ctx_lock_nested.isra.72+0x6b/0x140 [26.556951] ? dump_stack+0xa0/0xbb [26.564006] ? watchdog_disable+0x80/0x80 [26.564886] perf_event_disable+0xa/0x30 [26.565746] hardlockup_detector_perf_disable+0x1b/0x60 [26.566776] watchdog_disable+0x51/0x80 [26.567624] softlockup_stop_fn+0x11/0x20 [26.568499] smp_call_on_cpu_callback+0x5b/0xb0 [26.569443] process_one_work+0x389/0x770 [26.570311] worker_thread+0x57/0x5a0 [26.571124] ? process_one_work+0x770/0x770 [26.572031] kthread+0x1ae/0x1d0 [26.572810] ? kthread_create_worker_on_cpu+0xc0/0xc0 [26.573821] ret_from_fork+0x1f/0x40 [26.574638] [26.575178] Allocated by task 1: [26.575990] kasan_kmalloc+0xa0/0xd0 [26.576814] kmem_cache_alloc_trace+0xf3/0x1e0 [26.577732] perf_event_alloc.part.89+0xb5/0x12b0 [26.578700] perf_event_create_kernel_counter+0x1e/0x1d0 [26.579728] hardlockup_detector_event_create+0x4e/0xc0 [26.580744] hardlockup_detector_perf_init+0x2f/0x60 [26.581746] lockup_detector_init+0x85/0xdc [26.582645] kernel_init_freeable+0x34d/0x40e [26.583568] kernel_init+0xf/0x130 [26.584428] ret_from_fork+0x1f/0x40 [26.584429] [26.584430] Freed by task 0: [26.584433] __kasan_slab_free+0x130/0x180 [26.584436] kfree+0x90/0x1a0 [26.589641] rcu_process_callbacks+0x2cb/0x6e0 [26.590935] __do_softirq+0x119/0x3a2 [26.591965] [26.592630] The buggy address belongs to the object at ff110006b360d500 [26.592630] which belongs to the cache kmalloc-2048 of size 2048 [26.592633] The buggy address is located 536 bytes inside of [26.592633] 2048-byte region [ff110006b360d500, ff110006b360dd00) [26.592634] The buggy address belongs to the page: [26.592637] page:ffd400001acd8200 count:1 mapcount:0 mapping:ff11000107c0e8= 00 index:0x0 compound_mapcount: 0 [26.600959] flags: 0x17ffffc0010200(slab|head) [26.601891] raw: 0017ffffc0010200 dead000000000100 dead000000000200 ff11000= 107c0e800 [26.603541] raw: 0000000000000000 00000000800f000f 00000001ffffffff 0000000= 000000000 [26.605546] page dumped because: kasan: bad access detected [26.606788] [26.607351] Memory state around the buggy address: [26.608556] ff110006b360d600: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb= fb [26.610565] ff110006b360d680: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb= fb [26.610567] >ff110006b360d700: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb= fb [26.610568] ^ [26.610570] ff110006b360d780: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb= fb [26.610573] ff110006b360d800: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb= fb [26.618955] =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Fix this by making the probe logic stateless. Use a local variable for the perf event and avoid accessing the per-cpu 'watchdog_ev' during initializat= ion. This ensures that the probe event is always properly released regardless of task migration, and no stale global state is left behind. Signed-off-by: Shouxin Sun Signed-off-by: Junnan Xhang Signed-off-by: Qiliang Yuan Signed-off-by: Qiliang Yuan --- kernel/watchdog_perf.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/kernel/watchdog_perf.c b/kernel/watchdog_perf.c index d3ca70e3c256..5066be7bba03 100644 --- a/kernel/watchdog_perf.c +++ b/kernel/watchdog_perf.c @@ -264,18 +264,38 @@ bool __weak __init arch_perf_nmi_is_available(void) int __init watchdog_hardlockup_probe(void) { int ret; + struct perf_event_attr *wd_attr =3D &wd_hw_attr; + struct perf_event *evt; + unsigned int cpu; =20 if (!arch_perf_nmi_is_available()) return -ENODEV; =20 - ret =3D hardlockup_detector_event_create(); + /* + * Test hardware PMU availability. Avoid using + * hardlockup_detector_event_create() to prevent migration-related + * stale pointers in the per-cpu watchdog_ev during early probe. + */ + wd_attr->sample_period =3D hw_nmi_get_sample_period(watchdog_thresh); + if (!wd_attr->sample_period) + return -EINVAL; =20 - if (ret) { + /* + * Use raw_smp_processor_id() for probing in preemptible init code. + * Migration after reading ID is acceptable as counter creation on + * the old CPU is sufficient for the probe. + */ + cpu =3D raw_smp_processor_id(); + evt =3D perf_event_create_kernel_counter(wd_attr, cpu, NULL, + watchdog_overflow_callback, NULL); + if (IS_ERR(evt)) { pr_info("Perf NMI watchdog permanently disabled\n"); + ret =3D PTR_ERR(evt); } else { - perf_event_release_kernel(this_cpu_read(watchdog_ev)); - this_cpu_write(watchdog_ev, NULL); + perf_event_release_kernel(evt); + ret =3D 0; } + return ret; } =20 --=20 2.51.0