mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [BUG] usb: gadget: u_serial: ttyGS open races with configfs function removal
@ 2026-06-23  2:43 Cen Zhang
  0 siblings, 0 replies; only message in thread
From: Cen Zhang @ 2026-06-23  2:43 UTC (permalink / raw)
  To: Greg Kroah-Hartman, linux-usb; +Cc: linux-kernel, baijiaju1990

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

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-06-23  2:43 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-06-23  2:43 [BUG] usb: gadget: u_serial: ttyGS open races with configfs function removal Cen Zhang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®