| From 2d696c1ed3468dcb62578a3911e687110d463520 Mon Sep 17 00:00:00 2001 |
| From: Chengfeng Ye <nicoyip.dev@gmail.com> |
| Date: Sun, 27 Sep 2026 01:27:23 +0800 |
| Subject: Bluetooth: RFCOMM: Fix initial port reference race |
| |
| From: Chengfeng Ye <nicoyip.dev@gmail.com> |
| |
| commit 2d696c1ed3468dcb62578a3911e687110d463520 upstream. |
| |
| For RFCOMM_RELEASE_ONHUP devices, rfcomm_tty_install() and |
| __rfcomm_release_dev() both use RFCOMM_TTY_OWNED to decide who drops the |
| initial tty_port reference. The release path tests the bit separately |
| from the install path setting it, so both can drop that reference. |
| |
| The synchronous hangup does not prevent this race: port->tty is not |
| assigned until tty_port_open(), after installation. The following |
| interleaving is possible: |
| |
| 1. Install and release each obtain a reference with rfcomm_dev_get(). |
| 2. Release observes RFCOMM_TTY_OWNED clear. |
| 3. Install sets RFCOMM_TTY_OWNED and drops the initial reference. |
| 4. Release drops the same initial reference again. |
| 5. TTY cleanup drops its reference and frees the device. |
| 6. Release performs its final tty_port_put() on the freed port. |
| |
| KASAN reported: |
| |
| BUG: KASAN: slab-use-after-free in tty_port_put+0x22/0x190 |
| Write of size 4 at addr ffff8881001d3d5c by task poc/92 |
| |
| Call Trace: |
| tty_port_put+0x22/0x190 |
| rfcomm_dev_ioctl+0x1d4/0x1930 |
| sock_do_ioctl+0x110/0x260 |
| sock_ioctl+0x380/0x590 |
| __x64_sys_ioctl+0x134/0x1c0 |
| |
| Allocated by task 88: |
| rfcomm_dev_ioctl+0x8f6/0x1930 |
| sock_do_ioctl+0x110/0x260 |
| sock_ioctl+0x380/0x590 |
| __x64_sys_ioctl+0x134/0x1c0 |
| |
| Freed by task 65: |
| kfree+0x131/0x3c0 |
| rfcomm_dev_destruct+0x23b/0x2f0 |
| release_one_tty+0xc1/0x370 |
| process_one_work+0x661/0x1090 |
| |
| Use test_and_set_bit() in both paths so that only the caller that changes |
| RFCOMM_TTY_OWNED from clear to set drops the initial reference. Each path |
| keeps its own lookup reference until release or TTY cleanup, preserving |
| the existing callback ordering and error handling. |
| |
| Fixes: 80ea73378af4 ("Bluetooth: Fix unreleased rfcomm_dev reference") |
| Cc: stable@vger.kernel.org |
| Assisted-by: LLM |
| Signed-off-by: Chengfeng Ye <nicoyip.dev@gmail.com> |
| Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/bluetooth/rfcomm/tty.c | 7 +++---- |
| 1 file changed, 3 insertions(+), 4 deletions(-) |
| |
| --- a/net/bluetooth/rfcomm/tty.c |
| +++ b/net/bluetooth/rfcomm/tty.c |
| @@ -469,7 +469,7 @@ static int __rfcomm_release_dev(void __u |
| tty_kref_put(tty); |
| } |
| |
| - if (!test_bit(RFCOMM_TTY_OWNED, &dev->status)) |
| + if (!test_and_set_bit(RFCOMM_TTY_OWNED, &dev->status)) |
| tty_port_put(&dev->port); |
| |
| tty_port_put(&dev->port); |
| @@ -734,10 +734,9 @@ static int rfcomm_tty_install(struct tty |
| * when the last process closes the tty. The behaviour is expected by |
| * userspace. |
| */ |
| - if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { |
| - set_bit(RFCOMM_TTY_OWNED, &dev->status); |
| + if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) && |
| + !test_and_set_bit(RFCOMM_TTY_OWNED, &dev->status)) |
| tty_port_put(&dev->port); |
| - } |
| |
| return 0; |
| } |