Re: [PATCH 0/2] ALSA: usb-audio, ua101: oversized packet copies overrun PCM ring buffers

From: Takashi Iwai

Date: Mon Oct 26 2026 - 04:08:54 EST


On Mon, 26 Oct 2026 01:10:37 +0100,
x8svc wrote:
>
> Hello,
>
> I found ring-buffer overruns in snd-usb-audio and snd-ua101 when the
> amount of data copied from or to an isochronous transfer exceeds the
> ALSA PCM buffer size. The copy paths use a two-part memcpy() and a
> position update that handle only one wrap, which is not enough when a
> single packet is larger than the whole buffer.
>
> I reproduced the overruns under KASAN on 7.3-rc7 (2f2f7eeef8b0) in
> snd-usb-audio and snd-ua101, for capture and playback. The two patches
> that follow apply to and build on current mainline (c03c1a14507d). I
> found similar code in snd-usb-line6, snd-usb-usx2y and cx231xx-audio,
> and reproduced them on 7.3-rc7. em28xx-audio has the same shape. I
> booted it, and KASAN reported nothing. By reading, the
> same shape is in 5.15.y, 6.1.y, 6.6.y, 6.12.y, 6.18.y and 7.2.y. I did
> not boot those branches. 5.10.y has older code, which I have not
> checked.
>
> The reproducer is available to maintainers on request.
>
> snd-usb-audio (sound/usb/pcm.c)
> -------------------------------
>
> retire_capture_urb() and copy_to_urb(), called from
> prepare_playback_urb(), copy data across the ALSA ring buffer with a
> two-part memcpy() and update the position with a single wrap:
>
> subs->hwptr_done += bytes;
> if (subs->hwptr_done >= subs->buffer_bytes)
> subs->hwptr_done -= subs->buffer_bytes;
>
> if (oldptr + bytes > subs->buffer_bytes) {
> unsigned int bytes1 = subs->buffer_bytes - oldptr;
> memcpy(runtime->dma_area + oldptr, cp, bytes1);
> memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
> }
>
> Nothing in the path guarantees bytes <= buffer_bytes. The device sets
> the packet size and the PCM client sets the buffer size.
> setup_hw_info() permits 8 kHz mono U8 with a 128-byte buffer (64-byte
> periods, two periods), and a full-speed endpoint can send 1023-byte
> packets.
>
> The first oversized capture packet already overruns the logical PCM
> buffer: with oldptr = 0, the two-part copy writes 128 bytes and then
> another 895 bytes at the start of the 128-byte buffer. It also leaves
> hwptr_done at 895 (1023 - 128), beyond buffer_bytes. On the next
> packet, bytes1 = 128 - 895 wraps to 4294966529, and memcpy() runs past
> the URB buffer and the vmalloc'd DMA buffer.
>
> For playback, copy_to_urb() has the same single-wrap assumption, and
> the reproducer reaches an out-of-bounds read there as well.
>
> KASAN reports (7.3-rc7, x86_64):
>
> Capture:
>
> snd-usb-audio 2-1:1.0: invalid position: , pos = 895, buffer size = 128, period size = 64
> BUG: KASAN: slab-out-of-bounds in retire_capture_urb+0x251/0x870
> Read of size 4294966529 at addr ffff88800c1e2800 by task vhci_rx/82
> ...
> retire_capture_urb+0x251/0x870
> snd_complete_urb
> __usb_hcd_giveback_urb
> vhci_rx_loop
>
> Playback:
>
> BUG: KASAN: vmalloc-out-of-bounds in copy_to_urb.isra.0+0x188/0x430
> Read of size 4294966529 at addr ffffc9000004537f by task vhci_rx/79
> ...
> prepare_playback_urb+0x98a/0x1fc0
> The buggy address belongs to a 1-page vmalloc region starting at
> 0xffffc90000045000 allocated at __snd_dma_alloc_pages
>
> On 6.19-rc3 the same reproducer faults in memcpy() from
> retire_capture_urb() without KASAN (RDX = 0xfffffd01, the wrapped
> bytes1).
>
> The USB/IP server never reports an actual_length larger than the
> requested packet: actual_length equals the URB's iso_frame_desc length
> and curpacksize. I reproduced this through USB/IP only. I have not
> tested a physical device or a physical host controller.
>
> The local PCM client only needs permission to open the PCM device. My
> reproducer runs as an unprivileged user in the audio group. I also
> tested Fedora 42's PipeWire 1.4.11 with WirePlumber 0.5.14 and
> PulseAudio 17.0 against the same USB/IP device:
>
> - In the tested PipeWire configuration, the driver selects its maximum
> buffer, capped at two seconds (16000 bytes for this stream).
> Full-speed 1023-byte and high-speed 3072-byte packets fit. With a
> super-speed device advertising wBytesPerInterval 49152, pw-record
> hits retire_capture_urb() with a 33152-byte write (49152 - 16000)
> and then faults at the vmalloc guard page. pw-play hits copy_to_urb()
> with a 33152-byte read on PipeWire's data-loop.0 thread, and the
> machine soft-locks.
> - PulseAudio 17.0 inside this VM turns timer-based scheduling off by
> itself. The measured buffer is four 25 ms fragments, 799 bytes, and
> a full-speed 1023-byte capture packet reproduces the
> retire_capture_urb() report. I did not measure timer-based scheduling
> left on. The PulseAudio source uses a 2 second buffer in that case.
>
> A client with permission to open the PCM device directly can trigger
> the full- and high-speed cases by selecting a sufficiently small
> buffer. The super-speed case reproduced in the tested PipeWire
> configuration with an ordinary recording or playback application when
> the emulated device was selected as the default device.
>
> Reproducer
> ----------
>
> I used USB/IP because I could not exercise this isochronous transfer
> path with dummy_hcd and raw-gadget. The reproducer is available to
> maintainers on request:
>
> - usbip-server.py emulates a UAC1 microphone or, with --mode playback,
> a speaker. It advertises a one-channel, 8-bit, 8000 Hz Type I stream
> and a full-speed isochronous endpoint with wMaxPacketSize 1023 and
> bmAttributes 0x80 (MaxPacketsOnly). Each packet is 1023 bytes.
> - attach.c imports the device and attaches the socket to vhci-hcd
> (CONFIG_USBIP_VHCI_HCD).
> - client.c opens the PCM device with U8, one channel, 8000 Hz,
> period_size 64 and two periods (buffer_bytes 128), sets
> stop_threshold to LONG_MAX, and starts the stream. For playback it
> first writes 128 bytes.
>
> The fault occurs within a second of starting the stream in my tests.
>
> Patch 1
> -------
>
> The snd-usb-audio patch bounds the capture packet copy by the PCM
> buffer size and stops prepare_playback_urb() from scheduling more data
> than the buffer can hold.
>
> I tested it on 7.3-rc7 with the same reproducer for 30 seconds per
> mode. KASAN reported nothing. Capture kept streaming and logged the
> ratelimited truncation warning. Playback entered XRUN at once because
> the buffer cannot hold a single packet, which is the safe failure for
> such a device. With PipeWire and a super-speed device, capture
> truncates the packet with the same warning and the stream keeps
> running. I did not repeat the patched playback run with PipeWire.
>
> Patch 2
> -------
>
> In snd-ua101 (sound/usb/misc/ua101.c), copy_capture_data() takes the
> frame count from actual_length, and copy_playback_data() takes it from
> the capture packets through rate_feedback. Both split the copy at the
> end of the ring buffer and wrap the position once.
>
> set_stream_hw() permits an 18-frame buffer: periods_min is 2 and the
> period can be 9 frames. A 672-byte packet of one-channel, 24-bit audio
> holds 224 frames. After the first packet the position is 206, which is
> 188 frames past the 18-frame buffer (224 - 18). On the next packet the
> single-wrap calculation underflows.
>
> KASAN reports in my tests:
>
> snd-ua101 2-1:1.0: invalid position: , pos = 206, buffer size = 18, period size = 9
> BUG: KASAN: slab-out-of-bounds in capture_urb_complete+0x9eb/0xcb0
> Read of size 4294966732 at addr ffff88800d73e7e0 by task vhci_rx/81
>
> BUG: KASAN: vmalloc-out-of-bounds in playback_work+0x789/0xac0
> Read of size 4294966732 at addr ffffc9000002626a by task kworker/0:1H/80
>
> The reproducer uses the same USB/IP server with --device ua101 to
> present a UA-101 interface layout, and a client using S24_3LE, 44100
> Hz, period_size 9 and one channel.
>
> The patch clamps the capture copy to the buffer size. On playback it
> copies at most one buffer and fills the rest of the packet with
> silence, so the URB length, the delay accounting and the data sent stay
> consistent. I first tried shortening the playback packet instead, but
> that makes the URB's declared length disagree with its buffer and
> USB/IP drops the device, so I kept the length. With the patch, capture
> and playback each ran for 30 seconds without a KASAN report and the
> streams kept advancing.
>
> The period accounting still subtracts one period at a time, so a
> packet spanning several periods of a tiny buffer leaves period_pos
> above one period until later calls drain it. I did not see this cause
> a problem in the runs, but I have not analysed it further and would
> welcome your view.
>
> Other drivers
> --------------
>
> snd-usb-line6, capture, on 7.3-rc7. A GuitarPort device sent 1024-byte
> packets into a 128-byte buffer (S16_LE, 44100 Hz, 16 frames, 2 periods,
> 2 channels). ALSA logged:
>
> snd_usb_toneport 2-1:1.0: invalid position: , pos = 224, buffer size = 32, period size = 16
>
> KASAN reported nothing. The ring is a whole page, so the extra bytes of
> the first copy stay inside that page. The driver then stopped copying
> and logged "driver bug: len".
>
> snd-usb-usx2y, capture, on the same tree. After the hwdep firmware
> load, a US-122 device sent 1024-byte packets into a 512-byte buffer
> (S16_LE, 44100 Hz, 64 frames, 2 periods, 2 channels). KASAN reported:
>
> BUG: KASAN: out-of-bounds in i_usx2y_urb_complete+0x1285/0x1ac0
> Read of size 18446744073709551104 at addr ffff88800e448800 by task vhci_rx/83
>
> That read size is the single-wrap length once the position has passed
> the buffer. These two patches do not change line6, usx2y, cx231xx
> or em28xx.
>
> cx231xx-audio, capture, on the same tree. A Pixelview Xcapture device
> sent 1024-byte packets into a 128-byte buffer (S16_LE, 48000 Hz, 16
> frames, 2 periods, 2 channels). KASAN reported:
>
> BUG: KASAN: use-after-free in cx231xx_audio_isocirq+0x2c7/0x780
> Read of size 4294966528 at addr ffff88800e8e0400 by task vhci_rx/82
>
> ALSA logged pos = 224, buffer size = 32, period size = 16. That
> position is one 256-frame packet minus the 32-frame buffer. The read
> size is the single-wrap length once the position has passed the buffer.
>
> em28xx-audio, capture, on the same tree. An em2860 device sent
> 3072-byte packets into a 24576-byte buffer (S16_LE, 48000 Hz, 3072
> frames, 2 periods, 2 channels). The endpoint was 0x83, with 64 packets
> per URB. Capture ran for 30 seconds. KASAN reported nothing.
> period_bytes_min is 11059 and periods_min is 2, so the smallest buffer
> is 22118 bytes. em28xx_audio_ep_packet_size() caps a high-speed
> endpoint at 8188 bytes, so one packet cannot pass that buffer.
>
> Process
> -------
>
> I understand this to fall under the regular-bug handling in
> Documentation/process/threat-model.rst, because reproduction uses an
> emulated USB device, so I am sending it to the subsystem list with the
> proposed fixes. An AI tool helped with the analysis and the patches.
> I reviewed and tested them myself.

Through a quick glance, the code changes look OK. But, could you give
a proper Signed-off-by tag (and the corresponding From tag) with a
real name (or a known identity) and resubmit? Signed-off-by is a
legal requirement for upstreaming.


thanks,

Takashi