SEGV on exit: net_cleanup() frees devices it doesn't own.
Host environment
- Operating system: Linux
- OS/kernel version: Linux 5.12+ (for Xen support)
- Architecture: x86
- QEMU flavor: qemu-system-x86_64
- QEMU version: ad6ef0a4 (HEAD as of 2023-11-09)
- QEMU command line:
./qemu-system-x86_64 -accel kvm,xen-version=0x40010,kernel-irqchip=split
Emulated/Virtualized environment
- Operating system: N/A
- OS/kernel version: N/A
- Architecture: N/A
Description of problem
On exiting QEMU, the net_cleanup() function iterates over all existing net_clients, both netdevs and nics, and deletes them all. Freeing the netdevs is fine, and they are correctly detached from their peer nic as appropriate. But the nics belong to an actual device and this can cause a use-after-free or double-free.
Mostly this doesn't happen because emulated devices don't bother to clean up after themselves on exit; none of their state is going to outlast the QEMU process so there's no point. But XenBus devices interact with the external XenStore and do need to perform a cleanup. The xen_netdev_unrealize() function calls qemu_del_nic() on the nic which net_cleanup() already stole from it, and crashes...
QEMU: Terminated
Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault.
qemu_del_nic (nic=0x55555846ab00) at ../net/net.c:451
451 int i, queues = MAX(nic->conf->peers.queues, 1);
(gdb) bt
#0 qemu_del_nic (nic=0x55555846ab00) at ../net/net.c:451
#1 0x0000555555a89ce3 in xen_device_unrealize (dev=<optimized out>) at ../hw/xen/xen-bus.c:973
#2 0x0000555555e5c847 in notifier_list_notify (list=<optimized out>, data=0x0) at ../util/notify.c:39
#3 0x00007ffff5fe51e6 in __run_exit_handlers (status=0, listp=<optimized out>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at exit.c:111
#4 0x00007ffff5fe532e in __GI_exit (status=<optimized out>) at exit.c:141
#5 0x00007ffff5fccb91 in __libc_start_call_main (main=main@entry=0x5555558837a0 <main>, argc=argc@entry=23, argv=argv@entry=0x7fffffffd7a8) at ../sysdeps/nptl/libc_start_call_main.h:74
#6 0x00007ffff5fccc4b in __libc_start_main_impl
(main=0x5555558837a0 <main>, argc=23, argv=0x7fffffffd7a8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffd798) at ../csu/libc-start.c:360
#7 0x0000555555885345 in _start ()
Steps to reproduce
- Launch a Xen guest as described at https://qemu-project.gitlab.io/qemu/system/i386/xen.html (which will get a Xen NIC by default).
- Terminate QEMU.
It doesn't need to boot, doesn't need to do anything. Just launch a completely non-functional guest and then hit Ctrl-a x on the default monitor:
$ ./qemu-system-x86_64 -accel kvm,xen-version=0x40010,kernel-irqchip=split -display none
QEMU: Terminated
Segmentation fault
For net_cleanup() to clean up the netdevs makes sense, because those might have state which persists in the system after QEMU exits, and need to be cleaned up. But deleting the nics doesn't seem to be necessary.
Fix at https://lore.kernel.org/qemu-devel/61ea91785772a8138ad12b305cbd5aac4aa1e86a.camel@infradead.org