[PATCH 0/1] ALSA: bcd2000: fix use-after-free of MIDI URBs on disconnect

Yuanzhe Liu posted 1 patch 1 day, 1 hour ago
sound/usb/bcd2000/bcd2000.c | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)
[PATCH 0/1] ALSA: bcd2000: fix use-after-free of MIDI URBs on disconnect
Posted by Yuanzhe Liu 1 day, 1 hour ago
Hi,

this single-patch series addresses a slab-use-after-free in the
snd-bcd2000 driver, observed on a Behringer BCD2000 USB DJ controller
under a KASAN-enabled kernel (7.1.2, x86_64, CONFIG_SND_BCD2000=y).

Root-cause walkthrough
----------------------

The bug is a lifetime mismatch between driver disconnect and ALSA rawmidi
file lifetime:

1. A userspace process (e.g. amidi) opens /dev/snd/midiC0D0.
2. The USB interface is unbound (via USBDEVFS_DISCONNECT ioctl or physical
   unplug).
3. bcd2000_disconnect() runs:
     snd_card_disconnect(bcd2k->card);
     bcd2000_free_usb_related_resources(bcd2k, interface);
   snd_card_disconnect() only marks the card disconnected and stops new
   opens, but returns immediately while open file descriptors still exist.
   bcd2000_free_usb_related_resources() proceeds to free both
   bcd2k->midi_out_urb and bcd2k->midi_in_urb via usb_free_urb().
4. Later, the process closes the rawmidi fd (or exits):
     snd_rawmidi_release -> close_substream -> bcd2000_midi_output_close()
   Because bcd2k->midi_out_active was set (and left set by completion
   re-submissions), bcd2000_midi_output_close() calls
   usb_kill_urb(bcd2k->midi_out_urb), reading the already-freed URB
   object (slab-use-after-free at urb->dev).

Patch summary
-------------

Patch 1 fixes the bug via two complementary changes:

 - Switch snd_card_disconnect() to snd_card_disconnect_sync(), which
   waits for all open card files to be closed before returning. The
   rawmidi close callbacks (and their usb_kill_urb()) run while the URBs
   are still valid. devices_mutex held during disconnect is not taken by
   rawmidi close, so the wait cannot deadlock.

 - Add an atomic shutdown flag set at the beginning of disconnect and
   checked in bcd2000_midi_send() and both URB completions, preventing
   URBs from being re-submitted after disconnect. Also NULL the URB
   pointers after usb_free_urb() so any stray access becomes an
   immediate NULL dereference rather than a silent UAF.

Crash report addressed
----------------------

  Report 1: slab-use-after-free in usb_kill_urb (read of size 8 at urb->dev,
            sound/usb/bcd2000/bcd2000.c:182 <- sound/core/rawmidi.c:560).
            Condensed splat appended below the diffstat.

Yuanzhe Liu (1):
  ALSA: bcd2000: fix use-after-free of MIDI URBs on file close after
    disconnect

 sound/usb/bcd2000/bcd2000.c | 25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)

--
Crash excerpt:

  BUG: KASAN: slab-use-after-free in usb_kill_urb+0x74/0x80 drivers/usb/core/urb.c:706
  Read of size 8 at addr ffff8880078e7d40 by task amidi/30287

  Call Trace:
   usb_kill_urb+0x74/0x80 drivers/usb/core/urb.c:706
   bcd2000_midi_output_close+0xd4/0x140 sound/usb/bcd2000/bcd2000.c:182
   close_substream.part.0+0x15f/0x8d0 sound/core/rawmidi.c:560
   close_substream sound/core/rawmidi.c:585 [inline]
   rawmidi_release_priv+0x21d/0x290 sound/core/rawmidi.c:580
   snd_rawmidi_release+0x4e/0xa0 sound/core/rawmidi.c:610
   __fput+0x3a6/0xac0 fs/file_table.c:510
   task_work_run+0x158/0x220 kernel/task_work.c:233
   do_exit+0x87b/0x22d0 kernel/exit.c:976

  Allocated by task 34:
   usb_alloc_urb+0x67/0x190 drivers/usb/core/urb.c:75
   bcd2000_init_midi sound/usb/bcd2000/bcd2000.c:319 [inline]
   bcd2000_probe+0x5a0/0x1350 sound/usb/bcd2000/bcd2000.c:401
   usb_probe_interface+0x395/0x850 drivers/usb/core/driver.c:396

  Freed by task 30294:
   usb_free_urb+0xbb/0x100 drivers/usb/core/urb.c:96
   bcd2000_free_usb_related_resources sound/usb/bcd2000/bcd2000.c:354 [inline]
   bcd2000_disconnect+0xfc/0x280 sound/usb/bcd2000/bcd2000.c:433
   usb_unbind_interface+0x188/0x770 drivers/usb/core/driver.c:458
   usb_driver_release_interface+0x105/0x180 drivers/usb/core/driver.c:640
   proc_ioctl drivers/usb/core/devio.c:2359 [inline]
   usbdev_ioctl+0x6f05/0x86d0 drivers/usb/core/devio.c:2826

  The buggy address belongs to the object at ffff8880078e7d00
   which belongs to the cache kmalloc-192 of size 192
  The buggy address is located 64 bytes inside of
   freed 192-byte region [ffff8880078e7d00, ffff8880078e7dc0)