Linux USB
 help / color / mirror / Atom feed
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