From nobody Sat Feb 7 22:55:23 2026 Received: from out-181.mta1.migadu.com (out-181.mta1.migadu.com [95.215.58.181]) (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 ADBD038E129 for ; Wed, 4 Feb 2026 08:20:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770193214; cv=none; b=tkDlJ4ZaKf/DrLvwjpbwIiPjtOsNIGWfu79/R6IdEYEjuT4g6iE90ODnI0fzy71SsnnRejtZWcnyXSSebnkxZCpceL40lpKiA6X1KeSwsxRUV48rJpyORsJWA7cvZA/sC07Ss9/mXYrBNW67VNeYLg4q0vEJkbxygYE3srhtzos= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770193214; c=relaxed/simple; bh=cZhTHKpcC9h4Z/NZrd/KhdMDwD/RT0GMH1AxlWEZhj4=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=jCoaCj9lzvrdc7HvbWYtfgPrmK5kjymtbIXDIyiS9WLrJ2SnmHzt2Sq7MBLhoEoJoCHlIQMLiUT2I6ovgSCcCUKNOe8ymvReAkjA+MAQdOVVPy28/jQ+mIRWLtNTbAoqb+5Qlj+tfNPNjdQJZkhhXK0XRDvzUsCyvj1uG7UQYtY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=nF070XVQ; arc=none smtp.client-ip=95.215.58.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="nF070XVQ" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1770193201; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=8vjXUNA9bTyi4x4bf9Kr+34dm9fLJzmNRQce1RSSVw8=; b=nF070XVQeSsRudZuPRRheROVMhsfTyCUN2kWZigJjzrt8ogkgsfe3QqIpq8pwXqNlBZvPY 8a5YySdqBrsMwQ3u62C9Mw33ShuSkTTeK2egwmS0RlrTsH1z4femtpM6Zt/i41kD9DtwAV dDCP4QJpyZgMfuFvZxQOvJW+vSwdqBg= From: Jiayuan Chen To: netdev@vger.kernel.org Cc: Jiayuan Chen , syzbot+827272712bd6d12c79a4@syzkaller.appspotmail.com, Jiayuan Chen , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Greg Kroah-Hartman , "Jiri Slaby (SUSE)" , Sjur Braendeland , linux-kernel@vger.kernel.org Subject: [PATCH net v1] serial: caif: fix use-after-free in caif_serial ldisc_close() Date: Wed, 4 Feb 2026 16:19:33 +0800 Message-ID: <20260204081939.237738-1-jiayuan.chen@linux.dev> 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 X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" From: Jiayuan Chen There is a use-after-free bug in caif_serial where handle_tx() may access ser->tty after the tty has been freed. The race condition occurs between ldisc_close() and packet transmission: CPU 0 (close) CPU 1 (xmit) ------------- ------------ ldisc_close() tty_kref_put(ser->tty) [tty may be freed here] <-- race window --> caif_xmit() handle_tx() tty =3D ser->tty // dangling ptr tty->ops->write() // UAF! schedule_work() ser_release() unregister_netdevice() The root cause is that tty_kref_put() is called in ldisc_close() while the network device is still active and can receive packets. Since ser and tty have a 1:1 binding relationship with consistent lifecycles (ser is created/destroyed in ldisc_open/close, and each ser binds exactly one tty), we can safely defer the tty reference release to ser_release() where the network device is unregistered. Fix this by moving tty_kref_put() from ldisc_close() to ser_release(), after unregister_netdevice(). This ensures the tty reference is held as long as the network device exists, preventing the UAF. Note: We save ser->tty before unregister_netdevice() because ser is embedded in netdev's private data and will be freed along with netdev (needs_free_netdev =3D true). How to reproduce: Add mdelay(500) at the beginning of ldisc_close() to widen the race window, then run the reproducer program [1]. Note: There is a separate deadloop issue in handle_tx() when using PORT_UNKNOWN serial ports (e.g., /dev/ttyS3 in QEMU without proper serial backend). This deadloop exists even without this patch, and is caused by an independent bug: inconsistency between uart_write_room() and uart_write() in serial core. It has been addressed in a separate patch [2]. KASAN report: =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 BUG: KASAN: slab-use-after-free in handle_tx+0x5d1/0x620 Read of size 1 at addr ffff8881131e1490 by task caif_uaf_trigge/9929 Call Trace: dump_stack_lvl+0x10e/0x1f0 print_report+0xd0/0x630 kasan_report+0xe4/0x120 handle_tx+0x5d1/0x620 dev_hard_start_xmit+0x9d/0x6c0 __dev_queue_xmit+0x6e2/0x4410 packet_xmit+0x243/0x360 packet_sendmsg+0x26cf/0x5500 __sys_sendto+0x4a3/0x520 __x64_sys_sendto+0xe0/0x1c0 do_syscall_64+0xc9/0xf80 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f615df2c0d7 Allocated by task 9930: Freed by task 64: Last potentially related work creation: The buggy address belongs to the object at ffff8881131e1000 which belongs to the cache kmalloc-cg-2k of size 2048 The buggy address is located 1168 bytes inside of freed 2048-byte region [ffff8881131e1000, ffff8881131e1800) The buggy address belongs to the physical page: page_owner tracks the page as allocated page last free pid 9778 tgid 9778 stack trace: Memory state around the buggy address: ffff8881131e1380: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff8881131e1400: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb >ffff8881131e1480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff8881131e1500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff8881131e1580: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb =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 [1]: https://gist.github.com/mrpre/f683f244544f7b11e7fa87df9e6c2eeb [2]: https://lore.kernel.org/linux-serial/20260204074327.226165-1-jiayuan.c= hen@linux.dev/T/#u Reported-by: syzbot+827272712bd6d12c79a4@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/000000000000a4a7550611e234f5@google.com= /T/ Fixes: 9b27105b4a44 ("net-caif-driver: add CAIF serial driver (ldisc)") Signed-off-by: Jiayuan Chen Signed-off-by: Jiayuan Chen Reviewed-by: Greg Kroah-Hartman --- drivers/net/caif/caif_serial.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index c398ac42eae9..b90890030751 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -284,6 +284,7 @@ static void ser_release(struct work_struct *work) { struct list_head list; struct ser_device *ser, *tmp; + struct tty_struct *tty; =20 spin_lock(&ser_lock); list_replace_init(&ser_release_list, &list); @@ -292,9 +293,11 @@ static void ser_release(struct work_struct *work) if (!list_empty(&list)) { rtnl_lock(); list_for_each_entry_safe(ser, tmp, &list, node) { + tty =3D ser->tty; dev_close(ser->dev); unregister_netdevice(ser->dev); debugfs_deinit(ser); + tty_kref_put(tty); } rtnl_unlock(); } @@ -355,8 +358,6 @@ static void ldisc_close(struct tty_struct *tty) { struct ser_device *ser =3D tty->disc_data; =20 - tty_kref_put(ser->tty); - spin_lock(&ser_lock); list_move(&ser->node, &ser_release_list); spin_unlock(&ser_lock); --=20 2.43.0