mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] ALSA: caiaq: Kill EP1 URB before unregistering the input device
@ 2026-11-12  9:48 Sergei Nizovtsev
  2026-11-12 12:36 ` Takashi Iwai
  0 siblings, 1 reply; 2+ messages in thread
From: Sergei Nizovtsev @ 2026-11-12  9:48 UTC (permalink / raw)
  To: Takashi Iwai, Jaroslav Kysela, Daniel Mack
  Cc: linux-sound, linux-kernel, Sergei Nizovtsev, stable

snd_disconnect() unregisters the input device via
snd_usb_caiaq_input_disconnect() and only afterwards kills the EP1
command URB (cdev->ep1_in_urb). The EP1 completion handler
usb_ep1_command_reply_dispatch() feeds snd_usb_caiaq_input_dispatch()
for EP1_CMD_READ_{ERP,ANALOG,IO} replies, which report into
cdev->input_dev. Since commit b04dcbb7f7b1 ("ALSA: caiaq: Use
snd_card_free_when_closed() at disconnection") the input device is
unregistered from snd_usb_caiaq_input_disconnect() but cdev->input_dev
is cleared only later, in snd_usb_caiaq_input_free() from the card
destructor. As the dispatch guard only tests cdev->input_dev for NULL,
an EP1 reply that completes after input_unregister_device() but before
cdev->ep1_in_urb is killed reports into an input device that has
already been unregistered and, with no evdev client holding it open,
freed.

Normally the window is closed by the USB core: usb_unbind_interface()
calls usb_disable_interface() before ->disconnect(), which flushes the
EP1 URB as long as endpoint 0x81 belongs to the interface being
unbound. But caiaq addresses EP1 through the device-wide pipe
usb_rcvbulkpipe(usb_dev, 0x1) without checking which interface owns
the endpoint. If a device declares it in another interface, the URB
stays live across snd_disconnect() of the interface that owns the card.

This needs a crafted device descriptor; a device that has EP1 in the
interface caiaq is bound to is not affected.

Kill cdev->ep1_in_urb (and the paired midi_out_urb) before the input
and audio disconnect helpers, so no EP1 completion can run once the
input device has been unregistered.

Reproduced under KASAN with raw-gadget + dummy_hcd emulating an Audio
Kontrol 1 (17cc:0815) with the EP1 bulk pair in interface 1: only
interface 0 was bound to snd-usb-caiaq and then unbound through sysfs
while the gadget flooded EP1 with EP1_CMD_READ_ERP replies. With no PCM
stream running the window is very short and was only hit by stalling
the unbinding CPU with a kgdb breakpoint at
snd_usb_caiaq_audio_disconnect(). With the fix applied, the same test
runs clean.

  BUG: KASAN: slab-use-after-free in input_event+0x3a/0xc0
  Read of size 8 at addr ffff8880151a4028 by task sh/118

  Call Trace:
   <IRQ>
   input_event+0x3a/0xc0
   snd_usb_caiaq_input_dispatch+0xab2/0xf00
   usb_ep1_command_reply_dispatch+0x1f3/0x580
   __usb_hcd_giveback_urb+0x246/0x400
   dummy_timer+0x1415/0x3380
   __hrtimer_run_queues+0x39d/0x800
   hrtimer_run_softirq+0x187/0x2d0
   handle_softirqs+0x1ff/0x930
   __irq_exit_rcu+0x15b/0x210
   irq_exit_rcu+0xe/0x20
   sysvec_apic_timer_interrupt+0x81/0xb0
   </IRQ>
   <TASK>
   asm_sysvec_apic_timer_interrupt+0x1a/0x20
  RIP: 0010:snd_usb_caiaq_audio_disconnect+0x0/0x70
   snd_disconnect+0x81/0x110
   usb_unbind_interface+0x1a0/0x860
   device_release_driver_internal+0x2c5/0x470
   unbind_store+0xde/0x100
   </TASK>

  Allocated by task 80:
   input_allocate_device+0x47/0x340
   snd_usb_caiaq_input_init+0x4c/0x1ac0
   snd_probe+0xab3/0x1160
   usb_probe_interface+0x2ea/0x810

  Freed by task 118:
   kfree+0x1ea/0x5c0
   input_dev_release+0xab/0xd0
   device_release+0xcf/0x250
   kobject_put+0x169/0x320
   snd_disconnect+0x79/0x110
   usb_unbind_interface+0x1a0/0x860

Fixes: b04dcbb7f7b1 ("ALSA: caiaq: Use snd_card_free_when_closed() at disconnection")
Cc: stable@vger.kernel.org
Signed-off-by: Sergei Nizovtsev <snizovtsev@cloudlinux.com>
Assisted-by: Claude:claude-opus-4-8
---
 sound/usb/caiaq/device.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c
index 634dc36f2eade..d28465e916df2 100644
--- a/sound/usb/caiaq/device.c
+++ b/sound/usb/caiaq/device.c
@@ -600,14 +600,19 @@ static void snd_disconnect(struct usb_interface *intf)
 
 	snd_card_disconnect(card);
 
+	/*
+	 * Kill the EP1 URB before disconnecting the input device: its
+	 * completion reports into cdev->input_dev, which input_disconnect()
+	 * frees without clearing the pointer.
+	 */
+	usb_kill_urb(cdev->ep1_in_urb);
+	usb_kill_urb(cdev->midi_out_urb);
+
 #ifdef CONFIG_SND_USB_CAIAQ_INPUT
 	snd_usb_caiaq_input_disconnect(cdev);
 #endif
 	snd_usb_caiaq_audio_disconnect(cdev);
 
-	usb_kill_urb(cdev->ep1_in_urb);
-	usb_kill_urb(cdev->midi_out_urb);
-
 	snd_card_free_when_closed(card);
 }
 
-- 
2.56.1


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-11-12 12:36 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-12  9:48 [PATCH] ALSA: caiaq: Kill EP1 URB before unregistering the input device Sergei Nizovtsev
2026-11-12 12:36 ` Takashi Iwai

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®