Overview
About vulnerability
In the Linux kernel, the following vulnerability has been resolved:
serial: caif: fix use-after-free in caif_serial ldisc_close()
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 = 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 allocated in ldisc_open and freed in ser_release via unregister_netdevice, 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 = 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 likely caused by inconsistency between uart_write_room() and uart_write() in serial core. It has been addressed in a separate patch [2].
KASAN report:
================================================================== 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:
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 ================================================================== [1]: https://gist.github.com/mrpre/f683f244544f7b11e7fa87df9e6c2eeb [2]: https://lore.kernel.org/linux-serial/[email protected]/T/#u
Details
- Affected product:
- AlmaLinux 9.2 ESU , Amazon Linux 2 ELS , CentOS 8.4 ELS , CentOS 8.5 ELS , CentOS Stream 8 ELS , Debian 10 ELS , Oracle Linux 7 ELS , TuxCare 9.6 ESU , Ubuntu 16.04 ELS , Ubuntu 18.04 ELS , Ubuntu 20.04 ELS
- Affected packages:
- kernel @ 5.14.0 (+12 more)
Fixes
KernelCare state
Live-patch status from KernelCare for each operating system.
| Operating system | Status | Covered kernels |
|---|---|---|
| Debian 11 | Planned | — |
| Debian 11 cloud | Planned | — |
| Debian 13 | Planned | — |
| Ubuntu 22.04 | Planned | — |
| Ubuntu 24.04 | Planned | — |
| Ubuntu 24.04 AWS | Planned | — |