CVE-2026-46048
Description
In the Linux kernel, the following vulnerability has been resolved:
ALSA: caiaq: fix usb_dev refcount leak on probe failure
create_card() takes a reference on the USB device with usb_get_dev() and stores the matching usb_put_dev() in card_free(), which is installed as the snd_card's ->private_free destructor.
However, ->private_free is only assigned near the end of init_card(), after several failure points (usb_set_interface(), EP type checks, usb_submit_urb(), the EP1_CMD_GET_DEVICE_INFO exchange, and its timeout). When any of those fail, init_card() returns an error to snd_probe(), which calls snd_card_free(card). Because ->private_free is still NULL, card_free() never runs, the usb_get_dev() reference is not dropped, and the struct usb_device leaks along with its descriptor allocations and device_private.
syzbot reproduces this with a malformed UAC3 device whose only valid altsetting is 0; init_card()'s usb_set_interface(usb_dev, 0, 1) call fails with -EIO and triggers the leak.
Move the ->private_free assignment into create_card(), immediately after usb_get_dev(), so that every error path reaching snd_card_free() balances the reference. card_free()'s callees (snd_usb_caiaq_input_free, free_urbs, kfree) already tolerate the partially-initialized state because the chip private area is zero-initialized by snd_card_new().
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In the Linux kernel's ALSA caiaq driver, a usb_dev refcount leak on probe failure can be triggered by a malicious USB device, leading to memory exhaustion.
Vulnerability
In the Linux kernel's ALSA caiaq driver (sound/usb/caiaq/device.c), the usb_dev reference count is not properly released on probe failure. The function create_card() takes a reference with usb_get_dev() and stores the matching usb_put_dev() in card_free(), which is installed as the snd_card's private_free destructor. However, private_free is only assigned near the end of init_card(), after several failure points (usb_set_interface(), endpoint type checks, usb_submit_urb(), the EP1_CMD_GET_DEVICE_INFO exchange, and its timeout). When any of those fail, init_card() returns an error to snd_probe(), which calls snd_card_free(card). Because private_free is still NULL, card_free() never runs, and the usb_get_dev() reference is not dropped, causing a leak of the struct usb_device along with its descriptor allocations and device_private. The bug affects Linux kernel versions before the fix commit da3b8fd6a202d94fef11a443abc9171c52426a1c [1].
Exploitation
An attacker with physical access or the ability to connect a malicious USB device can trigger the vulnerability. The attacker crafts a USB device (e.g., a malformed UAC3 device whose only valid altsetting is 0) that causes init_card() to fail at one of the early failure points, such as usb_set_interface(usb_dev, 0, 1) returning -EIO. No authentication or special privileges are required; the attack is performed by inserting the malicious device into a system with the vulnerable driver loaded. The leak occurs each time the probe fails, allowing the attacker to repeatedly trigger the refcount leak.
Impact
Successful exploitation results in a memory leak of the struct usb_device and associated kernel allocations. Over time, repeated leaks can exhaust system memory, leading to denial of service (system instability, resource exhaustion, or crash). The vulnerability does not allow privilege escalation, code execution, or information disclosure; the impact is limited to availability.
Mitigation
The fix is included in Linux kernel commit da3b8fd6a202d94fef11a443abc9171c52426a1c, which moves the private_free assignment into create_card(), immediately after usb_get_dev(), ensuring that every error path reaching snd_card_free() balances the reference [1]. Users should update to a kernel version containing this commit (e.g., stable releases that backport the fix). No workaround is available; the only mitigation is to apply the kernel patch.
AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
1050c6a1f05973ALSA: caiaq: fix usb_dev refcount leak on probe failure
1 file changed · +1 −2
sound/usb/caiaq/device.c+1 −2 modifieddiff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index e78980ab17567d..b20aae0caf60a4 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -423,6 +423,7 @@ static int create_card(struct usb_device *usb_dev, cdev = caiaqdev(card); cdev->chip.dev = usb_get_dev(usb_dev); + card->private_free = card_free; cdev->chip.card = card; cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct)); @@ -511,7 +512,6 @@ static int init_card(struct snd_usb_caiaqdev *cdev) scnprintf(card->longname, sizeof(card->longname), "%s %s (%s)", cdev->vendor_name, cdev->product_name, usbpath); - card->private_free = card_free; err = setup_card(cdev); if (err < 0) goto err_kill_urb; -- cgit 1.3-korg
da3b8fd6a202ALSA: caiaq: fix usb_dev refcount leak on probe failure
1 file changed · +1 −2
sound/usb/caiaq/device.c+1 −2 modifieddiff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index e78980ab17567d..b20aae0caf60a4 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -423,6 +423,7 @@ static int create_card(struct usb_device *usb_dev, cdev = caiaqdev(card); cdev->chip.dev = usb_get_dev(usb_dev); + card->private_free = card_free; cdev->chip.card = card; cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct)); @@ -511,7 +512,6 @@ static int init_card(struct snd_usb_caiaqdev *cdev) scnprintf(card->longname, sizeof(card->longname), "%s %s (%s)", cdev->vendor_name, cdev->product_name, usbpath); - card->private_free = card_free; err = setup_card(cdev); if (err < 0) goto err_kill_urb; -- cgit 1.3-korg
6153878c5255ALSA: caiaq: fix usb_dev refcount leak on probe failure
1 file changed · +1 −2
sound/usb/caiaq/device.c+1 −2 modifieddiff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index e78980ab17567d..b20aae0caf60a4 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -423,6 +423,7 @@ static int create_card(struct usb_device *usb_dev, cdev = caiaqdev(card); cdev->chip.dev = usb_get_dev(usb_dev); + card->private_free = card_free; cdev->chip.card = card; cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct)); @@ -511,7 +512,6 @@ static int init_card(struct snd_usb_caiaqdev *cdev) scnprintf(card->longname, sizeof(card->longname), "%s %s (%s)", cdev->vendor_name, cdev->product_name, usbpath); - card->private_free = card_free; err = setup_card(cdev); if (err < 0) goto err_kill_urb; -- cgit 1.3-korg
21ca595aafa4ALSA: caiaq: fix usb_dev refcount leak on probe failure
1 file changed · +1 −2
sound/usb/caiaq/device.c+1 −2 modifieddiff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index e78980ab17567d..b20aae0caf60a4 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -423,6 +423,7 @@ static int create_card(struct usb_device *usb_dev, cdev = caiaqdev(card); cdev->chip.dev = usb_get_dev(usb_dev); + card->private_free = card_free; cdev->chip.card = card; cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct)); @@ -511,7 +512,6 @@ static int init_card(struct snd_usb_caiaqdev *cdev) scnprintf(card->longname, sizeof(card->longname), "%s %s (%s)", cdev->vendor_name, cdev->product_name, usbpath); - card->private_free = card_free; err = setup_card(cdev); if (err < 0) goto err_kill_urb; -- cgit 1.3-korg
7a5f1cd22d47ALSA: caiaq: fix usb_dev refcount leak on probe failure
1 file changed · +1 −2
sound/usb/caiaq/device.c+1 −2 modifieddiff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index 8af0c04041ee3e..ad9f744b496bfb 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -423,6 +423,7 @@ static int create_card(struct usb_device *usb_dev, cdev = caiaqdev(card); cdev->chip.dev = usb_get_dev(usb_dev); + card->private_free = card_free; cdev->chip.card = card; cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct)); @@ -511,7 +512,6 @@ static int init_card(struct snd_usb_caiaqdev *cdev) scnprintf(card->longname, sizeof(card->longname), "%s %s (%s)", cdev->vendor_name, cdev->product_name, usbpath); - card->private_free = card_free; err = setup_card(cdev); if (err < 0) return err; -- cgit 1.3-korg
50c6a1f05973ALSA: caiaq: fix usb_dev refcount leak on probe failure
1 file changed · +1 −2
sound/usb/caiaq/device.c+1 −2 modifieddiff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index e78980ab17567d..b20aae0caf60a4 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -423,6 +423,7 @@ static int create_card(struct usb_device *usb_dev, cdev = caiaqdev(card); cdev->chip.dev = usb_get_dev(usb_dev); + card->private_free = card_free; cdev->chip.card = card; cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct)); @@ -511,7 +512,6 @@ static int init_card(struct snd_usb_caiaqdev *cdev) scnprintf(card->longname, sizeof(card->longname), "%s %s (%s)", cdev->vendor_name, cdev->product_name, usbpath); - card->private_free = card_free; err = setup_card(cdev); if (err < 0) goto err_kill_urb; -- cgit 1.3-korg
da3b8fd6a202ALSA: caiaq: fix usb_dev refcount leak on probe failure
1 file changed · +1 −2
sound/usb/caiaq/device.c+1 −2 modifieddiff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index e78980ab17567d..b20aae0caf60a4 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -423,6 +423,7 @@ static int create_card(struct usb_device *usb_dev, cdev = caiaqdev(card); cdev->chip.dev = usb_get_dev(usb_dev); + card->private_free = card_free; cdev->chip.card = card; cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct)); @@ -511,7 +512,6 @@ static int init_card(struct snd_usb_caiaqdev *cdev) scnprintf(card->longname, sizeof(card->longname), "%s %s (%s)", cdev->vendor_name, cdev->product_name, usbpath); - card->private_free = card_free; err = setup_card(cdev); if (err < 0) goto err_kill_urb; -- cgit 1.3-korg
21ca595aafa4ALSA: caiaq: fix usb_dev refcount leak on probe failure
1 file changed · +1 −2
sound/usb/caiaq/device.c+1 −2 modifieddiff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index e78980ab17567d..b20aae0caf60a4 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -423,6 +423,7 @@ static int create_card(struct usb_device *usb_dev, cdev = caiaqdev(card); cdev->chip.dev = usb_get_dev(usb_dev); + card->private_free = card_free; cdev->chip.card = card; cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct)); @@ -511,7 +512,6 @@ static int init_card(struct snd_usb_caiaqdev *cdev) scnprintf(card->longname, sizeof(card->longname), "%s %s (%s)", cdev->vendor_name, cdev->product_name, usbpath); - card->private_free = card_free; err = setup_card(cdev); if (err < 0) goto err_kill_urb; -- cgit 1.3-korg
7a5f1cd22d47ALSA: caiaq: fix usb_dev refcount leak on probe failure
1 file changed · +1 −2
sound/usb/caiaq/device.c+1 −2 modifieddiff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index 8af0c04041ee3e..ad9f744b496bfb 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -423,6 +423,7 @@ static int create_card(struct usb_device *usb_dev, cdev = caiaqdev(card); cdev->chip.dev = usb_get_dev(usb_dev); + card->private_free = card_free; cdev->chip.card = card; cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct)); @@ -511,7 +512,6 @@ static int init_card(struct snd_usb_caiaqdev *cdev) scnprintf(card->longname, sizeof(card->longname), "%s %s (%s)", cdev->vendor_name, cdev->product_name, usbpath); - card->private_free = card_free; err = setup_card(cdev); if (err < 0) return err; -- cgit 1.3-korg
6153878c5255ALSA: caiaq: fix usb_dev refcount leak on probe failure
1 file changed · +1 −2
sound/usb/caiaq/device.c+1 −2 modifieddiff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index e78980ab17567d..b20aae0caf60a4 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -423,6 +423,7 @@ static int create_card(struct usb_device *usb_dev, cdev = caiaqdev(card); cdev->chip.dev = usb_get_dev(usb_dev); + card->private_free = card_free; cdev->chip.card = card; cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct)); @@ -511,7 +512,6 @@ static int init_card(struct snd_usb_caiaqdev *cdev) scnprintf(card->longname, sizeof(card->longname), "%s %s (%s)", cdev->vendor_name, cdev->product_name, usbpath); - card->private_free = card_free; err = setup_card(cdev); if (err < 0) goto err_kill_urb; -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"The snd_card's ->private_free destructor (card_free), which calls usb_put_dev(), was assigned too late in init_card(), after several failure points, so any probe failure before that assignment caused the usb_get_dev() reference taken in create_card() to never be released."
Attack vector
An attacker with physical access to a USB port can plug a malformed UAC3 device whose only valid altsetting is 0. When the caiaq driver probes this device, init_card() calls usb_set_interface(usb_dev, 0, 1), which fails with -EIO. Because the ->private_free destructor had not yet been assigned at that point in the code path, snd_card_free() does not invoke card_free(), and the usb_get_dev() reference taken in create_card() is never dropped. This leaks the struct usb_device along with its descriptor allocations and device_private [patch_id=2660149].
Affected code
The bug is in sound/usb/caiaq/device.c, in the functions create_card() and init_card(). The assignment `card->private_free = card_free` was originally placed at line 512 inside init_card(), after several failure points (usb_set_interface, endpoint type checks, usb_submit_urb, EP1_CMD_GET_DEVICE_INFO exchange). The patch moves it to line 423 in create_card(), right after the usb_get_dev() call [patch_id=2660149].
What the fix does
The patch moves the `card->private_free = card_free` assignment from near the end of init_card() (sound/usb/caiaq/device.c line 512) to immediately after `usb_get_dev()` in create_card() (line 423). This ensures that snd_card_free() on any error path will call card_free(), which drops the USB device reference via usb_put_dev(). The callees of card_free() (snd_usb_caiaq_input_free, free_urbs, kfree) are safe to call on a partially-initialized chip because the private area is zero-initialized by snd_card_new() [patch_id=2660149].
Preconditions
- physical_accessAttacker must have physical access to a USB port to connect a malicious or malformed USB device.
- inputThe malformed device must present itself as a UAC3 device whose only valid altsetting is 0, causing usb_set_interface() to fail with -EIO.
- configThe caiaq driver must be loaded and probe the device.
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/21ca595aafa40d3ac70eab1f4cb62cc00ca21657nvd
- git.kernel.org/stable/c/50c6a1f05973f56d23280c9d7645a7a5734e0907nvd
- git.kernel.org/stable/c/6153878c5255bb69b7d0868105ca078ef13cbcf8nvd
- git.kernel.org/stable/c/7a5f1cd22d47f8ca4b760b6334378ae42c1bd24bnvd
- git.kernel.org/stable/c/da3b8fd6a202d94fef11a443abc9171c52426a1cnvd
News mentions
0No linked articles in our index yet.