Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ALSA: usb-audio: Add sanity check for OOB writes at silencing

At silencing the playback URB packets in the implicit fb mode before
the actual playback, we blindly assume that the received packets fit
with the buffer size. But when the setup in the capture stream
differs from the playback stream (e.g. due to the USB core limitation
of max packet size), such an inconsistency may lead to OOB writes to
the buffer, resulting in a crash.

For addressing it, add a sanity check of the transfer buffer size at
prepare_silent_urb(), and stop the data copy if the received data
overflows. Also, report back the transfer error properly from there,
too.

Note that this doesn't fix the root cause of the playback error
itself, but this merely covers the kernel Oops.

Link: https://bugzilla.kernel.org/show_bug.cgi?id=221076
Link: https://patch.msgid.link/20260216141209.1849200-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>

+21 -16
+21 -16
sound/usb/endpoint.c
··· 275 275 return chip->quirk_flags & QUIRK_FLAG_TX_LENGTH; 276 276 } 277 277 278 - static void prepare_silent_urb(struct snd_usb_endpoint *ep, 279 - struct snd_urb_ctx *ctx) 278 + static int prepare_silent_urb(struct snd_usb_endpoint *ep, 279 + struct snd_urb_ctx *ctx) 280 280 { 281 281 struct urb *urb = ctx->urb; 282 282 unsigned int offs = 0; ··· 289 289 extra = sizeof(packet_length); 290 290 291 291 for (i = 0; i < ctx->packets; ++i) { 292 - unsigned int offset; 293 - unsigned int length; 294 - int counts; 292 + int length; 295 293 296 - counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, 0); 297 - length = counts * ep->stride; /* number of silent bytes */ 298 - offset = offs * ep->stride + extra * i; 299 - urb->iso_frame_desc[i].offset = offset; 294 + length = snd_usb_endpoint_next_packet_size(ep, ctx, i, 0); 295 + if (length < 0) 296 + return length; 297 + length *= ep->stride; /* number of silent bytes */ 298 + if (offs + length + extra > ctx->buffer_size) 299 + break; 300 + urb->iso_frame_desc[i].offset = offs; 300 301 urb->iso_frame_desc[i].length = length + extra; 301 302 if (extra) { 302 303 packet_length = cpu_to_le32(length); 303 - memcpy(urb->transfer_buffer + offset, 304 + memcpy(urb->transfer_buffer + offs, 304 305 &packet_length, sizeof(packet_length)); 306 + offs += extra; 305 307 } 306 - memset(urb->transfer_buffer + offset + extra, 308 + memset(urb->transfer_buffer + offs, 307 309 ep->silence_value, length); 308 - offs += counts; 310 + offs += length; 309 311 } 310 312 311 - urb->number_of_packets = ctx->packets; 312 - urb->transfer_buffer_length = offs * ep->stride + ctx->packets * extra; 313 + if (!offs) 314 + return -EPIPE; 315 + 316 + urb->number_of_packets = i; 317 + urb->transfer_buffer_length = offs; 313 318 ctx->queued = 0; 319 + return 0; 314 320 } 315 321 316 322 /* ··· 338 332 if (data_subs && ep->prepare_data_urb) 339 333 return ep->prepare_data_urb(data_subs, urb, in_stream_lock); 340 334 /* no data provider, so send silence */ 341 - prepare_silent_urb(ep, ctx); 342 - break; 335 + return prepare_silent_urb(ep, ctx); 343 336 344 337 case SND_USB_ENDPOINT_TYPE_SYNC: 345 338 if (snd_usb_get_speed(ep->chip->dev) >= USB_SPEED_HIGH) {