[RFC PATCH] net/tap-win32.c: fix segmentation faults when multiple -netdev tap are used

Matheus Ferst via qemu development posted 1 patch 3 weeks, 3 days ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260313194849.859579-1-matheus.ferst@eldorado.org.br
Maintainers: Jason Wang <jasowang@redhat.com>, Stefan Weil <sw@weilnetz.de>
net/tap-win32.c | 47 +++++++++++++++++++++--------------------------
1 file changed, 21 insertions(+), 26 deletions(-)
[RFC PATCH] net/tap-win32.c: fix segmentation faults when multiple -netdev tap are used
Posted by Matheus Ferst via qemu development 3 weeks, 3 days ago
The static declaration of tap_overlapped does not work when multiple
"-netdev tap" are used, tap_win32_overlapped_init will be called with
the same struct but a different handler, and the already running thread
with tap_win32_thread_entry will have the semaphores and free lists
changed under its feet.

Make tap_win32_overlapped_t a member of TAPState to be allocated with
qemu_new_net_client, and move out the tap_win32_overlapped_init and
CreateThread calls from tap_win32_open to tap_win32_init.

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3314
Signed-off-by: Matheus Ferst <matheus.ferst@eldorado.org.br>
---
I'm no longer seeing the segfaults with this patch, but I only have
connectivity when using the e1000 or virtio-net-pci devices. With
virtio-net-device (as shown in the issue with aarch64) I cannot ping
anything when two interfaces are used. I'm not sure if it's a problem
with this patch or a different problem, since it works with the other
devices.
---
 net/tap-win32.c | 47 +++++++++++++++++++++--------------------------
 1 file changed, 21 insertions(+), 26 deletions(-)

diff --git a/net/tap-win32.c b/net/tap-win32.c
index 38baf90e0b..af20c62868 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -114,8 +114,6 @@ typedef struct tap_win32_overlapped {
     tun_buffer_t* output_queue_back;
 } tap_win32_overlapped_t;
 
-static tap_win32_overlapped_t tap_overlapped;
-
 static tun_buffer_t* get_buffer_from_free_list(tap_win32_overlapped_t* const overlapped)
 {
     tun_buffer_t* buffer = NULL;
@@ -589,8 +587,7 @@ static void tap_win32_free_buffer(tap_win32_overlapped_t *overlapped,
     put_buffer_on_free_list(overlapped, buffer);
 }
 
-static int tap_win32_open(tap_win32_overlapped_t **phandle,
-                          const char *preferred_name)
+static HANDLE tap_win32_open(const char *preferred_name)
 {
     g_autofree char *device_path = NULL;
     char device_guid[0x100];
@@ -604,7 +601,6 @@ static int tap_win32_open(tap_win32_overlapped_t **phandle,
         unsigned long debug;
     } version;
     DWORD version_len;
-    DWORD idThread;
 
     if (preferred_name != NULL) {
         snprintf(name_buffer, sizeof(name_buffer), "%s", preferred_name);
@@ -612,7 +608,7 @@ static int tap_win32_open(tap_win32_overlapped_t **phandle,
 
     rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer));
     if (rc)
-        return -1;
+        return INVALID_HANDLE_VALUE;
 
     device_path = g_strdup_printf("%s%s%s",
               USERMODEDEVICEDIR,
@@ -629,7 +625,7 @@ static int tap_win32_open(tap_win32_overlapped_t **phandle,
         0 );
 
     if (handle == INVALID_HANDLE_VALUE) {
-        return -1;
+        return INVALID_HANDLE_VALUE;
     }
 
     bret = DeviceIoControl(handle, TAP_IOCTL_GET_VERSION,
@@ -638,34 +634,28 @@ static int tap_win32_open(tap_win32_overlapped_t **phandle,
 
     if (bret == FALSE) {
         CloseHandle(handle);
-        return -1;
+        return INVALID_HANDLE_VALUE;
     }
 
     if (!tap_win32_set_status(handle, TRUE)) {
-        return -1;
+        return INVALID_HANDLE_VALUE;
     }
 
-    tap_win32_overlapped_init(&tap_overlapped, handle);
-
-    *phandle = &tap_overlapped;
-
-    CreateThread(NULL, 0, tap_win32_thread_entry,
-                 (LPVOID)&tap_overlapped, 0, &idThread);
-    return 0;
+    return handle;
 }
 
 /********************************************/
 
  typedef struct TAPState {
      NetClientState nc;
-     tap_win32_overlapped_t *handle;
+     tap_win32_overlapped_t tap_overlapped;
  } TAPState;
 
 static void tap_cleanup(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    qemu_del_wait_object(s->handle->tap_semaphore, NULL, NULL);
+    qemu_del_wait_object(s->tap_overlapped.tap_semaphore, NULL, NULL);
 
     /* FIXME: need to kill thread and close file handle:
        tap_win32_close(s);
@@ -676,7 +666,7 @@ static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    return tap_win32_write(s->handle, buf, size);
+    return tap_win32_write(&s->tap_overlapped, buf, size);
 }
 
 static void tap_win32_send(void *opaque)
@@ -688,7 +678,7 @@ static void tap_win32_send(void *opaque)
     uint8_t min_pkt[ETH_ZLEN];
     size_t min_pktsz = sizeof(min_pkt);
 
-    size = tap_win32_read(s->handle, &buf, max_size);
+    size = tap_win32_read(&s->tap_overlapped, &buf, max_size);
     if (size > 0) {
         orig_buf = buf;
 
@@ -700,7 +690,7 @@ static void tap_win32_send(void *opaque)
         }
 
         qemu_send_packet(&s->nc, buf, size);
-        tap_win32_free_buffer(s->handle, orig_buf);
+        tap_win32_free_buffer(&s->tap_overlapped, orig_buf);
     }
 }
 
@@ -716,9 +706,11 @@ static int tap_win32_init(NetClientState *peer, const char *model,
 {
     NetClientState *nc;
     TAPState *s;
-    tap_win32_overlapped_t *handle;
+    HANDLE handle;
+    DWORD idThread;
 
-    if (tap_win32_open(&handle, ifname) < 0) {
+    handle = tap_win32_open(ifname);
+    if (handle == INVALID_HANDLE_VALUE) {
         printf("tap: Could not open '%s'\n", ifname);
         return -1;
     }
@@ -727,11 +719,14 @@ static int tap_win32_init(NetClientState *peer, const char *model,
 
     s = DO_UPCAST(TAPState, nc, nc);
 
+    tap_win32_overlapped_init(&s->tap_overlapped, handle);
+
+    CreateThread(NULL, 0, tap_win32_thread_entry,
+                 (LPVOID)&s->tap_overlapped, 0, &idThread);
+
     qemu_set_info_str(&s->nc, "tap: ifname=%s", ifname);
 
-    s->handle = handle;
-
-    qemu_add_wait_object(s->handle->tap_semaphore, tap_win32_send, s);
+    qemu_add_wait_object(s->tap_overlapped.tap_semaphore, tap_win32_send, s);
 
     return 0;
 }
-- 
2.43.0
Re: [RFC PATCH] net/tap-win32.c: fix segmentation faults when multiple -netdev tap are used
Posted by Matheus K. Ferst 1 week, 3 days ago
ping

Em sex., 13 de mar. de 2026 às 17:19, Matheus Ferst via qemu development <
qemu-devel@nongnu.org> escreveu:

> The static declaration of tap_overlapped does not work when multiple
> "-netdev tap" are used, tap_win32_overlapped_init will be called with
> the same struct but a different handler, and the already running thread
> with tap_win32_thread_entry will have the semaphores and free lists
> changed under its feet.
>
> Make tap_win32_overlapped_t a member of TAPState to be allocated with
> qemu_new_net_client, and move out the tap_win32_overlapped_init and
> CreateThread calls from tap_win32_open to tap_win32_init.
>
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3314
> Signed-off-by: Matheus Ferst <matheus.ferst@eldorado.org.br>
> ---
> I'm no longer seeing the segfaults with this patch, but I only have
> connectivity when using the e1000 or virtio-net-pci devices. With
> virtio-net-device (as shown in the issue with aarch64) I cannot ping
> anything when two interfaces are used. I'm not sure if it's a problem
> with this patch or a different problem, since it works with the other
> devices.
> ---
>  net/tap-win32.c | 47 +++++++++++++++++++++--------------------------
>  1 file changed, 21 insertions(+), 26 deletions(-)
>
> diff --git a/net/tap-win32.c b/net/tap-win32.c
> index 38baf90e0b..af20c62868 100644
> --- a/net/tap-win32.c
> +++ b/net/tap-win32.c
> @@ -114,8 +114,6 @@ typedef struct tap_win32_overlapped {
>      tun_buffer_t* output_queue_back;
>  } tap_win32_overlapped_t;
>
> -static tap_win32_overlapped_t tap_overlapped;
> -
>  static tun_buffer_t* get_buffer_from_free_list(tap_win32_overlapped_t*
> const overlapped)
>  {
>      tun_buffer_t* buffer = NULL;
> @@ -589,8 +587,7 @@ static void
> tap_win32_free_buffer(tap_win32_overlapped_t *overlapped,
>      put_buffer_on_free_list(overlapped, buffer);
>  }
>
> -static int tap_win32_open(tap_win32_overlapped_t **phandle,
> -                          const char *preferred_name)
> +static HANDLE tap_win32_open(const char *preferred_name)
>  {
>      g_autofree char *device_path = NULL;
>      char device_guid[0x100];
> @@ -604,7 +601,6 @@ static int tap_win32_open(tap_win32_overlapped_t
> **phandle,
>          unsigned long debug;
>      } version;
>      DWORD version_len;
> -    DWORD idThread;
>
>      if (preferred_name != NULL) {
>          snprintf(name_buffer, sizeof(name_buffer), "%s", preferred_name);
> @@ -612,7 +608,7 @@ static int tap_win32_open(tap_win32_overlapped_t
> **phandle,
>
>      rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer,
> sizeof(name_buffer));
>      if (rc)
> -        return -1;
> +        return INVALID_HANDLE_VALUE;
>
>      device_path = g_strdup_printf("%s%s%s",
>                USERMODEDEVICEDIR,
> @@ -629,7 +625,7 @@ static int tap_win32_open(tap_win32_overlapped_t
> **phandle,
>          0 );
>
>      if (handle == INVALID_HANDLE_VALUE) {
> -        return -1;
> +        return INVALID_HANDLE_VALUE;
>      }
>
>      bret = DeviceIoControl(handle, TAP_IOCTL_GET_VERSION,
> @@ -638,34 +634,28 @@ static int tap_win32_open(tap_win32_overlapped_t
> **phandle,
>
>      if (bret == FALSE) {
>          CloseHandle(handle);
> -        return -1;
> +        return INVALID_HANDLE_VALUE;
>      }
>
>      if (!tap_win32_set_status(handle, TRUE)) {
> -        return -1;
> +        return INVALID_HANDLE_VALUE;
>      }
>
> -    tap_win32_overlapped_init(&tap_overlapped, handle);
> -
> -    *phandle = &tap_overlapped;
> -
> -    CreateThread(NULL, 0, tap_win32_thread_entry,
> -                 (LPVOID)&tap_overlapped, 0, &idThread);
> -    return 0;
> +    return handle;
>  }
>
>  /********************************************/
>
>   typedef struct TAPState {
>       NetClientState nc;
> -     tap_win32_overlapped_t *handle;
> +     tap_win32_overlapped_t tap_overlapped;
>   } TAPState;
>
>  static void tap_cleanup(NetClientState *nc)
>  {
>      TAPState *s = DO_UPCAST(TAPState, nc, nc);
>
> -    qemu_del_wait_object(s->handle->tap_semaphore, NULL, NULL);
> +    qemu_del_wait_object(s->tap_overlapped.tap_semaphore, NULL, NULL);
>
>      /* FIXME: need to kill thread and close file handle:
>         tap_win32_close(s);
> @@ -676,7 +666,7 @@ static ssize_t tap_receive(NetClientState *nc, const
> uint8_t *buf, size_t size)
>  {
>      TAPState *s = DO_UPCAST(TAPState, nc, nc);
>
> -    return tap_win32_write(s->handle, buf, size);
> +    return tap_win32_write(&s->tap_overlapped, buf, size);
>  }
>
>  static void tap_win32_send(void *opaque)
> @@ -688,7 +678,7 @@ static void tap_win32_send(void *opaque)
>      uint8_t min_pkt[ETH_ZLEN];
>      size_t min_pktsz = sizeof(min_pkt);
>
> -    size = tap_win32_read(s->handle, &buf, max_size);
> +    size = tap_win32_read(&s->tap_overlapped, &buf, max_size);
>      if (size > 0) {
>          orig_buf = buf;
>
> @@ -700,7 +690,7 @@ static void tap_win32_send(void *opaque)
>          }
>
>          qemu_send_packet(&s->nc, buf, size);
> -        tap_win32_free_buffer(s->handle, orig_buf);
> +        tap_win32_free_buffer(&s->tap_overlapped, orig_buf);
>      }
>  }
>
> @@ -716,9 +706,11 @@ static int tap_win32_init(NetClientState *peer, const
> char *model,
>  {
>      NetClientState *nc;
>      TAPState *s;
> -    tap_win32_overlapped_t *handle;
> +    HANDLE handle;
> +    DWORD idThread;
>
> -    if (tap_win32_open(&handle, ifname) < 0) {
> +    handle = tap_win32_open(ifname);
> +    if (handle == INVALID_HANDLE_VALUE) {
>          printf("tap: Could not open '%s'\n", ifname);
>          return -1;
>      }
> @@ -727,11 +719,14 @@ static int tap_win32_init(NetClientState *peer,
> const char *model,
>
>      s = DO_UPCAST(TAPState, nc, nc);
>
> +    tap_win32_overlapped_init(&s->tap_overlapped, handle);
> +
> +    CreateThread(NULL, 0, tap_win32_thread_entry,
> +                 (LPVOID)&s->tap_overlapped, 0, &idThread);
> +
>      qemu_set_info_str(&s->nc, "tap: ifname=%s", ifname);
>
> -    s->handle = handle;
> -
> -    qemu_add_wait_object(s->handle->tap_semaphore, tap_win32_send, s);
> +    qemu_add_wait_object(s->tap_overlapped.tap_semaphore, tap_win32_send,
> s);
>
>      return 0;
>  }
> --
> 2.43.0
>
>
>