From: Cen Zhang <zzzccc427@gmail.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
linux-usb@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, baijiaju1990@gmail.com
Subject: [BUG] usb: gadget: u_serial: ttyGS open races with configfs function removal
Date: Tue, 23 Jun 2026 10:43:14 +0800 [thread overview]
Message-ID: <20260623024314.51948-1-zzzccc427@gmail.com> (raw)
Hi,
I hit a KASAN use-after-free in the USB gadget serial function when a
ttyGS device is opened while the corresponding configfs gser function
instance is being removed.
The race is between a userspace open of /dev/ttyGS0 and configfs removal
of functions/gser.usb0. gserial_free_line() can free struct gs_port
while the tty device/cdev path can still route a first open to the
embedded tty_port.
A simplified ordering is:
configfs removal racing opener
rmdir functions/gser.usb0 open("/dev/ttyGS0")
gser_free_inst()
gserial_free_line()
gserial_free_port()
kfree(struct gs_port) tty_open()
tty_init_dev()
write through freed tty_port
Observed report:
BUG: KASAN: slab-use-after-free in tty_init_dev.part.0+0xd8/0x2a0
The buggy address belongs to the object at ffff888101011000 which
belongs to the cache kmalloc-2k of size 2048.
The buggy address is located 296 bytes inside of freed 2048-byte region
[ffff888101011000, ffff888101011800).
Write of size 8
Call Trace:
<TASK>
dump_stack_lvl+0x66/0xa0
print_report+0xce/0x630
? fixup_red_left+0x9/0x30
? tty_init_dev.part.0+0xd8/0x2a0
kasan_report+0xe0/0x110
? tty_init_dev.part.0+0xd8/0x2a0
tty_init_dev.part.0+0xd8/0x2a0
tty_open+0x702/0xa40
? 0xffffffffc0000095
? __pfx_tty_open+0x10/0x10
? __pfx_tty_open+0x10/0x10
? chrdev_open+0x143/0x360
chrdev_open+0x157/0x360
? __pfx_chrdev_open+0x10/0x10
? do_dentry_open+0x610/0x7f0
? __pfx_chrdev_open+0x10/0x10
do_dentry_open+0x233/0x7f0
vfs_open+0x5a/0x1b0
path_openat+0x66d/0x1540
? srso_alias_return_thunk+0x5/0xfbef5
? 0xffffffffc0000095
? __pfx_path_openat+0x10/0x10
? srso_alias_return_thunk+0x5/0xfbef5
? do_file_open+0x165/0x2b0
do_file_open+0x186/0x2b0
? __pfx_do_file_open+0x10/0x10
? do_raw_spin_unlock+0x9a/0x100
? srso_alias_return_thunk+0x5/0xfbef5
? _raw_spin_unlock+0x23/0x40
? do_raw_spin_unlock+0x9a/0x100
do_sys_openat2+0xce/0x150
? __pfx_do_sys_openat2+0x10/0x10
? __x64_sys_openat+0x99/0x140
__x64_sys_openat+0xd0/0x140
? __pfx___x64_sys_openat+0x10/0x10
do_syscall_64+0x115/0x6a0
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Allocated by task 475:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
__kasan_kmalloc+0x8f/0xa0
gserial_alloc_line_no_console+0x7e/0x4d0
gser_alloc_inst+0x58/0xa0
try_get_usb_function_instance+0xae/0xf0
usb_get_function_instance+0x12/0x50
function_make+0x155/0x290
configfs_mkdir+0x2aa/0x680
vfs_mkdir+0x150/0x390
filename_mkdirat+0x2ef/0x350
__x64_sys_mkdir+0x47/0x60
do_syscall_64+0x115/0x6a0
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 549:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
kasan_save_free_info+0x3b/0x60
__kasan_slab_free+0x43/0x70
kfree+0x2f9/0x530
gserial_free_port+0xe1/0x1f0
gserial_free_line+0xa0/0x150
gser_free_inst+0x25/0x30
usb_put_function_instance+0x4e/0x60
config_item_cleanup+0x8f/0xf0
configfs_rmdir+0x39c/0x4f0
vfs_rmdir+0x18d/0x380
filename_rmdir+0x2d2/0x360
__x64_sys_rmdir+0x34/0x50
do_syscall_64+0x115/0x6a0
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Reproducer, run as root on a KASAN kernel:
modprobe libcomposite || true
modprobe usb_f_serial || true
mount -t configfs none /sys/kernel/config 2>/dev/null || true
G=/sys/kernel/config/usb_gadget/gser-race
F=$G/functions/gser.usb0
rmdir "$F" 2>/dev/null || true
rmdir "$G" 2>/dev/null || true
mkdir "$G"
mkdir "$F"
port=$(cat "$F/port_num")
dev=/dev/ttyGS$port
cat > /tmp/ttygs_open_worker.c <<'EOF'
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
int main(int argc, char **argv)
{
const char *path = argv[1];
time_t end = time(NULL) + 12;
while (time(NULL) < end) {
int fd = open(path, O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd >= 0)
close(fd);
usleep(500);
}
return 0;
}
EOF
gcc -O2 -o /tmp/ttygs_open_worker /tmp/ttygs_open_worker.c
/tmp/ttygs_open_worker "$dev" &
sleep 1
rmdir "$F"
wait
Thanks,
Cen
reply other threads:[~2026-06-23 2:43 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260623024314.51948-1-zzzccc427@gmail.com \
--to=zzzccc427@gmail.com \
--cc=baijiaju1990@gmail.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox