On Wed, Sep 24, 2025 at 2:28 PM Houqi (Nick) Zuo <[email protected]> wrote:
>
> This patch addresses a scenario where QEMU would abort with a core dump
> when a tap device created by QEMU is manually deleted from the host while
> the guest is running.
>
> The specific negative test case is:
> 1. Start QEMU with a tap device (created by QEMU)
> 2. Manually delete the tap device on the host
> 3. Execute shutdown in the guest
> 4. QEMU attempts to clean up the tap device but finds the file descriptor
>    in a bad state, leading to abort and core dump
>
> The patch introduces a tap device file descriptor validity check using
> the TUNGETIFF ioctl to detect when the underlying tap device has been
> removed. When detected, the operations are skipped gracefully instead
> of proceeding with invalid file descriptors that cause ioctl failures.
>
> The validity check is integrated into:
> - qemu_set_vnet_hdr_len() in net/net.c
> - qemu_set_offload() in net/net.c
>
> This ensures that when the tap device is no longer valid, these functions
> return early without attempting operations that would fail and trigger
> aborts, thus achieving the expected behavior of error reporting without
> crashing.
>
> (gdb) bt full
> #0  __pthread_kill_implementation (threadid=<optimized out>, 
> signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
>         tid = <optimized out>
>         ret = 0
>         pd = <optimized out>
>         old_mask = {__val = {10}}
>         ret = <optimized out>
> #1  0x00007f1710b6bff3 in __pthread_kill_internal (threadid=<optimized out>, 
> signo=6) at pthread_kill.c:78
> #2  0x00007f1710b15f56 in __GI_raise (sig=sig@entry=6) at 
> ../sysdeps/posix/raise.c:26
>         ret = <optimized out>
> #3  0x00007f1710afd8fa in __GI_abort () at abort.c:79
>         save_stage = 1
>         act = {__sigaction_handler = {sa_handler = 0x20, sa_sigaction = 
> 0x20}, sa_mask = {__val = {16929458408262392576, 18446744073709550848, 
> 139737042419943, 139737042419943, 0, 94049703655600, 139737042419943, 
> 139737042670528, 18446744073709550328, 77, 139705603579344, 
> 18446744073709551615, 139737041472378, 139705595179568, 16929458408262392576, 
> 94049679794864}}, sa_flags = 281695456, sa_restorer = 0xa}
> #4  0x000055899a71de58 in tap_fd_set_vnet_hdr_len (fd=<optimized out>, 
> len=10) at ../net/tap-linux.c:204
> #5  tap_set_vnet_hdr_len (nc=<optimized out>, len=10) at ../net/tap.c:269
>         s = <optimized out>
> #6  0x000055899a8be67f in qemu_set_vnet_hdr_len (nc=0x2956, len=10588) at 
> ../net/net.c:573
> #7  virtio_net_set_mrg_rx_bufs (n=0x5589a72cfa10, 
> mergeable_rx_bufs=<optimized out>, version_1=<error reading variable: 
> Incompatible types on DWARF stack>, hash_report=<optimized out>) at 
> ../hw/net/virtio-net.c:664
>         i = 0
>         nc = 0x5589a730ab28
> #8  virtio_net_set_features (vdev=0x5589a72cfa10, features=0) at 
> ../hw/net/virtio-net.c:897
>         n = 0x5589a72cfa10
>         err = 0x0
>         i = 0
> #9  0x000055899a8e4eaa in virtio_set_features_nocheck (vdev=0x5589a72cfa10, 
> val=0) at ../hw/virtio/virtio.c:3079
>         k = <optimized out>
>         bad = <optimized out>
> #10 virtio_reset (opaque=0x5589a72cfa10) at ../hw/virtio/virtio.c:3184
>         vdev = 0x5589a72cfa10
>         k = 0x5589a5c162b0
>         i = 0
> #11 0x000055899a630d2b in virtio_bus_reset (bus=0x5589a72cf990) at 
> ../hw/virtio/virtio-bus.c:109
>         vdev = <optimized out>
> #12 virtio_pci_reset (qdev=0x5589a72c7470) at ../hw/virtio/virtio-pci.c:2311
>         proxy = 0x5589a72c7470
>         i = 0
>         bus = 0x5589a72cf990
> #13 0x000055899a686ded in memory_region_write_accessor (mr=<optimized out>, 
> addr=<optimized out>, value=<optimized out>, size=<optimized out>, 
> shift=<optimized out>, mask=<optimized out>, attrs=...) at 
> ../system/memory.c:490
>         tmp = <optimized out>
> #14 0x000055899a686cbc in access_with_adjusted_size (addr=20, 
> value=0x7f0fbedfde00, size=1, access_size_min=<optimized out>, 
> access_size_max=<optimized out>, access_fn=0x55899a686d30 
> <memory_region_write_accessor>, mr=0x5589a72c8040, attrs=...) at 
> ../system/memory.c:566
>         print_once_ = false
>         access_mask = 255
>         access_size = 1
>         i = 0
>         r = 0
>         reentrancy_guard_applied = <optimized out>
> #15 0x000055899a686ac5 in memory_region_dispatch_write (mr=<optimized out>, 
> addr=20, data=<optimized out>, op=<optimized out>, attrs=...) at 
> ../system/memory.c:1545
>         size = <optimized out>
> #16 0x000055899a69f7da in flatview_write_continue_step (attrs=..., 
> buf=0x7f1711da6028 <error: Cannot access memory at address 0x7f1711da6028>, 
> len=<optimized out>, mr_addr=20, l=0x7f0fbedfde28, mr=0x5589a72c8040) at 
> ../system/physmem.c:2972
>         val = 6
>         result = 0
>         release_lock = <optimized out>
> #17 0x000055899a697c15 in flatview_write_continue (fv=0x7f0f6c124d90, 
> addr=61675730370580, attrs=..., ptr=0x7f1711da6028, len=1, mr_addr=6, l=1, 
> mr=0x0) at ../system/physmem.c:3002
>         result = 0
>         buf = 0x7f1711da6028 <error: Cannot access memory at address 
> 0x7f1711da6028>
> #18 flatview_write (fv=0x7f0f6c124d90, addr=61675730370580, attrs=..., 
> buf=0x7f1711da6028, len=1) at ../system/physmem.c:3033
> --Type <RET> for more, q to quit, c to continue without paging--
>         l = <optimized out>
>         mr_addr = 6
>         mr = 0x0
> #19 0x000055899a697a91 in address_space_write (as=0x55899bceeba0 
> <address_space_memory>, addr=61675730370580, attrs=..., buf=0x7f1711da6028, 
> len=1) at ../system/physmem.c:3153
>         _rcu_read_auto = 0x1
>         result = 0
>         fv = 0x2956
> #20 0x000055899a91159b in address_space_rw (addr=10588, attrs=..., 
> buf=0x7f1711da6028, len=0, as=<optimized out>, is_write=<optimized out>) at 
> ../system/physmem.c:3163
> #21 kvm_cpu_exec (cpu=0x5589a5d68b40) at ../accel/kvm/kvm-all.c:3255
>         attrs = {secure = 0, space = 0, user = 0, memory = 0, debug = 0, 
> requester_id = 0, pid = 0, address_type = 0, unspecified = false, _reserved1 
> = 0 '\000', _reserved2 = 0}
>         run = 0x7f1711da6000
>         ret = <optimized out>
>         run_ret = <optimized out>
> #22 0x000055899a9189ca in kvm_vcpu_thread_fn (arg=0x5589a5d68b40) at 
> ../accel/kvm/kvm-accel-ops.c:51
>         r = <optimized out>
>         cpu = <optimized out>
> #23 0x000055899aba817a in qemu_thread_start (args=0x5589a5d72580) at 
> ../util/qemu-thread-posix.c:393
>         __clframe = {__cancel_routine = <optimized out>, __cancel_arg = 0x0, 
> __do_it = 1, __cancel_type = <optimized out>}
>         qemu_thread_args = 0x5589a5d72580
>         start_routine = 0x55899a918850 <kvm_vcpu_thread_fn>
>         arg = 0x5589a5d68b40
>         r = 0x0
> #24 0x00007f1710b6a128 in start_thread (arg=<optimized out>) at 
> pthread_create.c:448
>         ret = <optimized out>
>         pd = <optimized out>
>         out = <optimized out>
>         unwind_buf = {cancel_jmp_buf = {{jmp_buf = {32, 8894544057743421332, 
> -1288, 0, 140726164742416, 140726164742679, -8831356496486092908, 
> -8844535456800460908}, mask_was_saved = 0}}, priv = {pad = {0x0, 0x0, 0x0, 
> 0x0}, data = {prev = 0x0, cleanup = 0x0, canceltype = 0}}}
>         not_first_call = <optimized out>
> #25 0x00007f1710bda924 in clone () at 
> ../sysdeps/unix/sysv/linux/x86_64/clone.S:100
>
Please also add  Fixes: ***commit id ***** in the commit log


> Signed-off-by: Houqi (Nick) Zuo <[email protected]>
> ---
>  include/net/net.h |  2 ++
>  net/net.c         |  7 +++++--
>  net/tap-linux.c   | 11 +++++++++++
>  net/tap.c         |  9 +++++++++
>  net/tap_int.h     |  1 +
>  5 files changed, 28 insertions(+), 2 deletions(-)
>
> diff --git a/include/net/net.h b/include/net/net.h
> index 84ee18e0f9..9e435f3275 100644
> --- a/include/net/net.h
> +++ b/include/net/net.h
> @@ -69,6 +69,7 @@ typedef void (NetAnnounce)(NetClientState *);
>  typedef bool (SetSteeringEBPF)(NetClientState *, int);
>  typedef bool (NetCheckPeerType)(NetClientState *, ObjectClass *, Error **);
>  typedef struct vhost_net *(GetVHostNet)(NetClientState *nc);
> +typedef int (QueryValidity)(NetClientState *nc);
>
>  typedef struct NetClientInfo {
>      NetClientDriver type;
> @@ -96,6 +97,7 @@ typedef struct NetClientInfo {
>      SetSteeringEBPF *set_steering_ebpf;
>      NetCheckPeerType *check_peer_type;
>      GetVHostNet *get_vhost_net;
> +    QueryValidity *query_validity;
>  } NetClientInfo;
>
>  struct NetClientState {
> diff --git a/net/net.c b/net/net.c
> index da275db86e..c0750fd0b9 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -57,6 +57,7 @@
>  #include "qapi/string-output-visitor.h"
>  #include "qapi/qobject-input-visitor.h"
>  #include "standard-headers/linux/virtio_net.h"
> +#include "qemu/log.h"
>
>  /* Net bridge is currently not supported for W32. */
>  #if !defined(_WIN32)
> @@ -543,7 +544,8 @@ bool qemu_has_vnet_hdr_len(NetClientState *nc, int len)
>  void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6,
>                            int ecn, int ufo, int uso4, int uso6)
>  {
> -    if (!nc || !nc->info->set_offload) {
> +    if (!nc || !nc->info->set_offload ||
> +        (nc->info->query_validity && nc->info->query_validity(nc) != 1)) {
>          return;
>      }
>
> @@ -561,7 +563,8 @@ int qemu_get_vnet_hdr_len(NetClientState *nc)
>
>  void qemu_set_vnet_hdr_len(NetClientState *nc, int len)
>  {
> -    if (!nc || !nc->info->set_vnet_hdr_len) {
> +    if (!nc || !nc->info->set_vnet_hdr_len ||
> +        (nc->info->query_validity && nc->info->query_validity(nc) != 1)) {
>          return;
>      }
>
> diff --git a/net/tap-linux.c b/net/tap-linux.c
> index e832810665..2911c66149 100644
> --- a/net/tap-linux.c
> +++ b/net/tap-linux.c
> @@ -346,3 +346,14 @@ int tap_fd_set_steering_ebpf(int fd, int prog_fd)
>
>      return 0;
>  }
> +
> +int tap_fd_query_validity(int fd)
> +{
> +    struct ifreq ifr;
> +
> +    if (ioctl(fd, TUNGETIFF, &ifr) != 0) {
> +        error_report("The tap device fd: %d is NOT valid.", fd);
> +        return -1;
> +    }
> +    return 1;
> +}
> diff --git a/net/tap.c b/net/tap.c
> index f37133e301..a9af3c7279 100644
> --- a/net/tap.c
> +++ b/net/tap.c
> @@ -364,6 +364,14 @@ static VHostNetState *tap_get_vhost_net(NetClientState 
> *nc)
>      return s->vhost_net;
>  }
>
> +static int tap_query_validity(NetClientState *nc)
> +{
> +    TAPState *s = DO_UPCAST(TAPState, nc, nc);
> +    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
> +
> +    return tap_fd_query_validity(s->fd);
> +}
> +
>  /* fd support */
>
>  static NetClientInfo net_tap_info = {
> @@ -383,6 +391,7 @@ static NetClientInfo net_tap_info = {
>      .set_vnet_be = tap_set_vnet_be,
>      .set_steering_ebpf = tap_set_steering_ebpf,
>      .get_vhost_net = tap_get_vhost_net,
> +    .query_validity = tap_query_validity,
>  };
>
>  static TAPState *net_tap_fd_init(NetClientState *peer,
> diff --git a/net/tap_int.h b/net/tap_int.h
> index 8857ff299d..5deb380201 100644
> --- a/net/tap_int.h
> +++ b/net/tap_int.h
> @@ -46,5 +46,6 @@ int tap_fd_enable(int fd);
>  int tap_fd_disable(int fd);
>  int tap_fd_get_ifname(int fd, char *ifname);
>  int tap_fd_set_steering_ebpf(int fd, int prog_fd);
> +int tap_fd_query_validity(int fd);
>
>  #endif /* NET_TAP_INT_H */
> --
> 2.47.3
>


Reply via email to