hw/usb/hcd-ohci.c: Memory leak in `ohci_service_iso_td`
## Host environment - Operating system: Linux - OS/kernel version: Linux 6.8.0-106-generic x86_64 - Architecture: x86_64 - QEMU flavor: qemu-system-i386 - QEMU version: QEMU emulator version 10.2.50 (v10.2.0-1816-g3fb456e9a0) - QEMU command line: ``` LSAN_OPTIONS=fast_unwind_on_malloc=0 qemu-system-i386 \ -display none -machine accel=qtest, -m 512M -machine q35 -nodefaults \ -device pci-ohci,id=fdev_ohci -device usb-kbd,id=fdev_usb_kbd \ -qtest stdio --trace "*hci*" --trace "*usb*" ``` ## Emulated/Virtualized environment - Operating system: N/A - OS/kernel version: N/A - Architecture: N/A ## Description of problem A memory leak occurs in `ohci_service_iso_td` at `hw/usb/hcd-ohci.c:743` when a guest submits a malformed isochronous transfer descriptor to the OHCI controller. The function allocates an `IOVector` via `qemu_iovec_init()` but exits early on error without calling `qemu_iovec_destroy()`, leaking 16 bytes per malformed TD. Confirmed that the bug manifests with `QEMU emulator version 11.0.50 (v11.0.0-427-g282771e1f9)`. ## Steps to reproduce 1. Run the below qtest reproducer (LeakSanitizer build required): ```sh cat <<'EOF' | LSAN_OPTIONS=fast_unwind_on_malloc=0 $TARGET \ -display none -machine accel=qtest, -m 512M -device pci-ohci,id=fdev_ohci \ -device usb-kbd,id=fdev_usb_kbd -machine q35 -nodefaults \ -qtest stdio --trace "*hci*" --trace "*usb*" outl 0xcf8 0x80000800 inw 0xcfc outl 0xcf8 0x80000810 outl 0xcfc 0xffffffff outl 0xcf8 0x80000810 inl 0xcfc outl 0xcf8 0x80000810 outl 0xcfc 0xe0000000 outl 0xcf8 0x80000804 inw 0xcfc outl 0xcf8 0x80000804 outw 0xcfc 0x7 outl 0xcf8 0x80000804 inw 0xcfc outl 0xcf8 0x80000834 inb 0xcfc outl 0xcf8 0x80000800 inb 0xcfc outl 0xcf8 0x80000801 inb 0xcfc outl 0xcf8 0x80000810 inb 0xcfc outl 0xcf8 0x80000811 inb 0xcfc writeq 0xe0000062 0x3def5ad7 writew 0xe000001f 0xc59b writeb 0xe0000054 0x36 readl 0xe0000013 readl 0xe00000ee readl 0xe0000013 readl 0xe0000019 write 0x0 0x1 0xa0 writeq 0xa0 0x8f00 writeq 0xa4 0xd0 writeq 0xa8 0xc0 writeq 0xac 0x0 writeq 0xc4 0x1 writeq 0xcc 0x1 writel 0xd0 0xe000 writeq 0xe0000002 0x60951da7 clock_step EOF ``` Execution output: ```bash $ qemu-system-i386 --version ==129124==WARNING: ASan doesn't fully support makecontext/swapcontext functions and may produce false positives in some cases! QEMU emulator version 10.2.50 (v10.2.0-1816-g3fb456e9a0) Copyright (c) 2003-2026 Fabrice Bellard and the QEMU Project developers $ cat <<'EOF' | LSAN_OPTIONS=fast_unwind_on_malloc=0 qemu-system-i386 ... ==129128==WARNING: ASan doesn't fully support makecontext/swapcontext functions and may produce false positives in some cases! [I 0.000000] OPENED usb_ohci_init_time usb_bit_time=1000000 usb_frame_time=83 usb_port_claim bus 0, port 1 usb_port_attach bus 0, port 1, devspeed full+high, portspeed full usb_ohci_port_attach port #0 usb_ohci_reset pci-ohci usb_ohci_stop pci-ohci: USB Suspended usb_ohci_stop pci-ohci: USB Suspended usb_ohci_port_detach port #0 usb_ohci_port_attach port #0 ahci_reset ahci(0x51f000001940): HBA reset ahci_reset_port ahci(0x51f000001940)[0]: reset port ahci_reset_port ahci(0x51f000001940)[1]: reset port ahci_reset_port ahci(0x51f000001940)[2]: reset port ahci_reset_port ahci(0x51f000001940)[3]: reset port ahci_reset_port ahci(0x51f000001940)[4]: reset port ahci_reset_port ahci(0x51f000001940)[5]: reset port [R +0.298415] outl 0xcf8 0x80000800 [S +0.298442] OK OK [R +0.298499] inw 0xcfc [S +0.298519] OK 0x106b OK 0x106b [R +0.298581] outl 0xcf8 0x80000810 [S +0.298593] OK OK [R +0.298659] outl 0xcfc 0xffffffff [S +0.298671] OK OK [R +0.298727] outl 0xcf8 0x80000810 [S +0.298735] OK OK [R +0.298786] inl 0xcfc [S +0.298799] OK 0xffffff00 OK 0xffffff00 [R +0.298854] outl 0xcf8 0x80000810 [S +0.298861] OK OK [R +0.298916] outl 0xcfc 0xe0000000 [S +0.298924] OK OK [R +0.298980] outl 0xcf8 0x80000804 [S +0.298987] OK OK [R +0.299039] inw 0xcfc [S +0.299051] OK 0x0000 OK 0x0000 [R +0.299106] outl 0xcf8 0x80000804 [S +0.299116] OK OK [R +0.299171] outw 0xcfc 0x7 [S +0.301267] OK OK [R +0.301341] outl 0xcf8 0x80000804 [S +0.301351] OK OK [R +0.301403] inw 0xcfc [S +0.301419] OK 0x0007 OK 0x0007 [R +0.301474] outl 0xcf8 0x80000834 [S +0.301482] OK OK [R +0.301533] inb 0xcfc [S +0.301545] OK 0x0000 OK 0x0000 [R +0.301616] outl 0xcf8 0x80000800 [S +0.301625] OK OK [R +0.301677] inb 0xcfc [S +0.301692] OK 0x006b OK 0x006b [R +0.301747] outl 0xcf8 0x80000801 [S +0.301755] OK OK [R +0.301828] inb 0xcfc [S +0.301841] OK 0x0010 OK 0x0010 [R +0.301902] outl 0xcf8 0x80000810 [S +0.301911] OK OK [R +0.301967] inb 0xcfc [S +0.301980] OK 0x0000 OK 0x0000 [R +0.302041] outl 0xcf8 0x80000811 [S +0.302049] OK OK [R +0.302105] inb 0xcfc [S +0.302118] OK 0x0000 OK 0x0000 [R +0.302192] writeq 0xe0000062 0x3def5ad7 usb_ohci_mem_write_unaligned at 0x62 usb_ohci_mem_write 4 <unknown> 0x64 25 <- 0x3def usb_ohci_reset pci-ohci usb_ohci_stop pci-ohci: USB Suspended usb_ohci_stop pci-ohci: USB Suspended usb_ohci_port_detach port #0 usb_ohci_port_attach port #0 usb_ohci_mem_write 2 <unknown> 0x68 26 <- 0x0 [S +0.302291] OK OK [R +0.302354] writew 0xe000001f 0xc59b usb_ohci_mem_write_unaligned at 0x1f usb_ohci_mem_write 1 HcControlHeadED 0x20 8 <- 0xc5 [S +0.302379] OK OK [R +0.302441] writeb 0xe0000054 0x36 usb_ohci_mem_port_write 1 HcRhPortStatus[1] 0x54 21 <- 0x36 usb_ohci_port_suspend port #0 usb_ohci_port_reset port #0 [S +0.302474] OK OK [R +0.302531] readl 0xe0000013 usb_ohci_mem_read_unaligned at 0x13 usb_ohci_mem_read 2 HcInterruptDisable 0x14 5 -> 0x80000000 usb_ohci_mem_read_unaligned at 0x16 [S +0.302572] OK 0x00000000ff0000ff OK 0x00000000ff0000ff [R +0.302648] readl 0xe00000ee usb_ohci_mem_read_unaligned at 0xee usb_ohci_mem_read_bad_offset 0xf0 usb_ohci_mem_read 2 <unknown> 0xf0 60 -> 0xffffffff [S +0.302687] OK 0x00000000ffffffff OK 0x00000000ffffffff [R +0.302744] readl 0xe0000013 usb_ohci_mem_read_unaligned at 0x13 usb_ohci_mem_read 2 HcInterruptDisable 0x14 5 -> 0x80000000 usb_ohci_mem_read_unaligned at 0x16 [S +0.302781] OK 0x00000000ff0000ff OK 0x00000000ff0000ff [R +0.302838] readl 0xe0000019 usb_ohci_mem_read_unaligned at 0x19 usb_ohci_mem_read_unaligned at 0x1a usb_ohci_mem_read 1 HcPeriodCurrentED 0x1c 7 -> 0x0 [S +0.302877] OK 0x0000000000ffffff OK 0x0000000000ffffff [R +0.302948] write 0x0 0x1 0xa0 [S +0.303231] OK OK [R +0.303304] writeq 0xa0 0x8f00 [S +0.303314] OK OK [R +0.303379] writeq 0xa4 0xd0 [S +0.303387] OK OK [R +0.303458] writeq 0xa8 0xc0 [S +0.303465] OK OK [R +0.303520] writeq 0xac 0x0 [S +0.303528] OK OK [R +0.303606] writeq 0xc4 0x1 [S +0.303621] OK OK [R +0.303681] writeq 0xcc 0x1 [S +0.303689] OK OK [R +0.303744] writel 0xd0 0xe000 [S +0.303753] OK OK [R +0.303807] writeq 0xe0000002 0x60951da7 usb_ohci_mem_write_unaligned at 0x2 usb_ohci_mem_write 4 HcControl 0x4 1 <- 0x6095 usb_ohci_set_ctl pci-ohci: new state 0x80 usb_ohci_start pci-ohci: USB Operational usb_ohci_mem_write 2 HcCommandStatus 0x8 2 <- 0x0 [S +0.303859] OK OK [R +0.303902] clock_step usb_ohci_ed_pkt ED @ 0x000000a0 h=0 c=0 head=0x000000c0 tailp=0x000000d0 next=0x00000000 usb_ohci_ed_pkt_flags fa=0 en=14 d=1 s=0 k=0 f=1 mps=0 usb_ohci_iso_td_head ISO_TD ED head 0x000000c0 tailp 0x000000d0, flags 0x00000000 bp 0x00000001 next 0x00000000 be 0x00000001, frame_number 0x00000000 starting_frame 0x00000000, frame_count 0x00000000 relative 0 usb_ohci_iso_td_head_offset 0x0000e000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 usb_packet_state_change bus 0, port 1, ep 14, packet 0x50d000026750, state undef -> setup usb_packet_state_change bus 0, port 1, ep 14, packet 0x50d000026750, state setup -> complete usb_ohci_iso_td_so 0x0000e000 eo 0x00000000 sa 0x00000000 ea 0x00000001 dir out len 2 ret -3 usb_ohci_iso_td_nak got NAK/STALL -3 [S +0.304036] OK 1000000 OK 1000000 [I +0.304132] CLOSED ================================================================= ==129128==ERROR: LeakSanitizer: detected memory leaks Direct leak of 16 byte(s) in 1 object(s) allocated from: #0 0x60d451ac1973 in malloc (/media/sdb2/won26/dpfuzz-test.code/qemu-upstream/install/bin/qemu-system-i386+0x2cef973) (BuildId: e78039a123ad00f7bb6b1dfa71d401d31ac77053) #1 0x7ac28b949ac9 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62ac9) (BuildId: 116e142b9b52c8a4dfd403e759e71ab8f95d8bb3) #2 0x60d453ffc3c7 in qemu_iovec_init /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../util/iov.c:284:17 #3 0x60d452777230 in ohci_service_iso_td /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../hw/usb/hcd-ohci.c:743:5 #4 0x60d452777230 in ohci_service_ed_list /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../hw/usb/hcd-ohci.c:1160:21 #5 0x60d45276e55c in ohci_frame_boundary /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../hw/usb/hcd-ohci.c:1226:9 #6 0x60d453fd5d7e in timerlist_run_timers /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../util/qemu-timer.c:593:9 #7 0x60d453fd7554 in qemu_clock_run_timers /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../util/qemu-timer.c:607:12 #8 0x60d453fd7554 in qemu_clock_advance_virtual_time /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../util/qemu-timer.c:713:9 #9 0x60d452b5a14b in qtest_process_command /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../system/qtest.c:726:18 #10 0x60d452b54ffb in qtest_process_inbuf /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../system/qtest.c:777:9 #11 0x60d453c2fd48 in fd_chr_read /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../chardev/char-fd.c:72:9 #12 0x7ac28b94445d (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x5d45d) (BuildId: 116e142b9b52c8a4dfd403e759e71ab8f95d8bb3) #13 0x7ac28b9446cf in g_main_context_dispatch (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x5d6cf) (BuildId: 116e142b9b52c8a4dfd403e759e71ab8f95d8bb3) #14 0x60d453fbab58 in glib_pollfds_poll /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../util/main-loop.c:290:9 #15 0x60d453fbab58 in os_host_main_loop_wait /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../util/main-loop.c:313:5 #16 0x60d453fbab58 in main_loop_wait /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../util/main-loop.c:592:11 #17 0x60d452b66e94 in qemu_main_loop /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../system/runstate.c:943:9 #18 0x60d453cdd84b in qemu_default_main /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../system/main.c:50:14 #19 0x60d453cdda08 in main /media/sdb2/won26/dpfuzz-test.code/qemu-upstream/build/../system/main.c:93:9 #20 0x7ac28ae2a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 #21 0x7ac28ae2a28a in __libc_start_main csu/../csu/libc-start.c:360:3 #22 0x60d451a26b24 in _start (/media/sdb2/won26/dpfuzz-test.code/qemu-upstream/install/bin/qemu-system-i386+0x2c54b24) (BuildId: e78039a123ad00f7bb6b1dfa71d401d31ac77053) SUMMARY: AddressSanitizer: 16 byte(s) leaked in 1 allocation(s). ``` ## Additional information OSS-fuzz report has been verified while the bug remains: https://issues.oss-fuzz.com/issues/42521338 Let me know if additional information is needed or if there are any other issues I missed. Thank you.
issue