Package: wpasupplicant
Version: 2:2.10-24
Severity: important
Tags: patch
X-Debbugs-Cc: [email protected]

wpa_supplicant process gets blocked when trying to send ping frame to
the AP that was powered down or power cycled without proper deassociation.
The event loop of wpa_supplicant blocks completely rendering NetworkManager
unusable for Wi-Fi connections.
I observe the bug when using open access point (no security) connecting
through the NetworkManager when putting wifi interface inside VRF.

(gdb) bt
  #0  __internal_syscall_cancel (a1=24, a2=a2@entry=367381808464, 
a3=a3@entry=0, a4=a4@entry=0, a5=549500786560, a6=a6@entry=20, nr=nr@entry=206)
      at ./nptl/cancellation.c:40
  #1  0x0000007faeb5265c in __syscall_cancel (a1=<optimized out>, 
a2=a2@entry=367381808464, a3=a3@entry=0, a4=a4@entry=0, a5=<optimized out>,
      a6=a6@entry=20, nr=nr@entry=206) at ./nptl/cancellation.c:75
  #2  0x0000007faebc0358 in __libc_sendto (fd=<optimized out>, 
buf=buf@entry=0x5589a98550, len=len@entry=0, flags=flags@entry=0, addr=...,
      addr@entry=..., addrlen=addrlen@entry=20) at 
../sysdeps/unix/sysv/linux/sendto.c:27
  #3  0x0000005589a476f0 in l2_packet_send (l2=<optimized out>, 
dst_addr=dst_addr@entry=0x559e9f7130 "\020VʢWh", proto=proto@entry=2048,
      buf=buf@entry=0x5589a98550 "", len=len@entry=0) at 
../src/l2_packet/l2_packet_linux.c:137
  #4  0x0000005589a099d0 in wnm_bss_keep_alive (sock_ctx=0xffff00ff00000000, 
eloop_ctx=0x559e9f6fe0) at ./wpa_supplicant/events.c:2479
  #5  wnm_bss_keep_alive (eloop_ctx=eloop_ctx@entry=0x559e9f6fe0, 
sock_ctx=sock_ctx@entry=0x0) at ./wpa_supplicant/events.c:2465
  #6  0x0000005589869a60 in eloop_run () at ../src/utils/eloop.c:1206
  #7  0x0000005589a012a4 in wpa_supplicant_run 
(global=global@entry=0x559e9326d0) at ./wpa_supplicant/wpa_supplicant.c:7477
  #8  0x000000558984f30c in main (argc=<optimized out>, argv=<optimized out>) 
at ./wpa_supplicant/main.c:392

dmesg:

   [290611.134170] wlan1: Connection to AP 10:56:ca:a2:57:68 lost .

The fix should consist of using sendto() with MSG_DONTWAIT flag.
Attaching patch candidate developed by Claude Code as separated file for your 
review.


-- System Information:
Debian Release: 13.4
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 
'stable-debug'), (500, 'proposed-updates-debug'), (500, 'stable')
Architecture: arm64 (aarch64)
Foreign Architectures: armhf

Kernel: Linux 6.12.75+rpt-rpi-v8 (SMP w/4 CPU threads; PREEMPT)
Kernel taint flags: TAINT_CRAP
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)

Versions of packages wpasupplicant depends on:
ii  adduser            3.152
ii  libc6              2.41-12+rpt1+deb13u2
ii  libdbus-1-3        1.16.2-2
ii  libnl-3-200        3.7.0-2
ii  libnl-genl-3-200   3.7.0-2
ii  libnl-route-3-200  3.7.0-2
ii  libpcsclite1       2.3.3-1
ii  libreadline8t64    8.2-6
ii  libssl3t64         3.5.5-1~deb13u2+rpt1

wpasupplicant recommends no packages.

Versions of packages wpasupplicant suggests:
pn  libengine-pkcs11-openssl  <none>
pn  wpagui                    <none>

-- no debconf information
diff --git a/src/l2_packet/l2_packet.h b/src/l2_packet/l2_packet.h
index 6a862806f..34cbbb3f8 100644
--- a/src/l2_packet/l2_packet.h
+++ b/src/l2_packet/l2_packet.h
@@ -114,6 +114,34 @@ int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 
*addr);
 int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
                   const u8 *buf, size_t len);
 
+/**
+ * l2_packet_send_nonblock - Send a packet without blocking the caller
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @dst_addr: Destination address for the packet (only used if l2_hdr == 0)
+ * @proto: Protocol/ethertype for the packet in host byte order (only used if
+ * l2_hdr == 0)
+ * @buf: Packet contents to be sent
+ * @len: Length of the buffer
+ * Returns: >=0 on success, <0 on failure
+ *
+ * Same semantics as l2_packet_send(), but the send must not block the caller
+ * (e.g. if the underlying device queue is stalled). Intended for best-effort
+ * frames such as WNM keep-alives where dropping the frame is preferable to
+ * stalling the event loop. On platforms where a non-blocking send variant is
+ * not available, this falls back to l2_packet_send().
+ */
+#ifdef __linux__
+int l2_packet_send_nonblock(struct l2_packet_data *l2, const u8 *dst_addr,
+                           u16 proto, const u8 *buf, size_t len);
+#else /* __linux__ */
+static inline int
+l2_packet_send_nonblock(struct l2_packet_data *l2, const u8 *dst_addr,
+                       u16 proto, const u8 *buf, size_t len)
+{
+       return l2_packet_send(l2, dst_addr, proto, buf, len);
+}
+#endif /* __linux__ */
+
 /**
  * l2_packet_get_ip_addr - Get the current IP address from the interface
  * @l2: Pointer to internal l2_packet data from l2_packet_init()
diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
index 5da05056f..76e67e4f8 100644
--- a/src/l2_packet/l2_packet_linux.c
+++ b/src/l2_packet/l2_packet_linux.c
@@ -112,8 +112,8 @@ int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 
*addr)
 }
 
 
-int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
-                  const u8 *buf, size_t len)
+static int l2_packet_send_flags(struct l2_packet_data *l2, const u8 *dst_addr,
+                               u16 proto, const u8 *buf, size_t len, int flags)
 {
        int ret;
 
@@ -122,7 +122,7 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 
*dst_addr, u16 proto,
        if (l2 == NULL)
                return -1;
        if (l2->l2_hdr) {
-               ret = send(l2->fd, buf, len, 0);
+               ret = send(l2->fd, buf, len, flags);
                if (ret < 0)
                        wpa_printf(MSG_ERROR, "l2_packet_send - send: %s",
                                   strerror(errno));
@@ -134,7 +134,7 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 
*dst_addr, u16 proto,
                ll.sll_protocol = htons(proto);
                ll.sll_halen = ETH_ALEN;
                os_memcpy(ll.sll_addr, dst_addr, ETH_ALEN);
-               ret = sendto(l2->fd, buf, len, 0, (struct sockaddr *) &ll,
+               ret = sendto(l2->fd, buf, len, flags, (struct sockaddr *) &ll,
                             sizeof(ll));
                if (ret < 0) {
                        wpa_printf(MSG_ERROR, "l2_packet_send - sendto: %s",
@@ -145,6 +145,25 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 
*dst_addr, u16 proto,
 }
 
 
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+                  const u8 *buf, size_t len)
+{
+       return l2_packet_send_flags(l2, dst_addr, proto, buf, len, 0);
+}
+
+
+int l2_packet_send_nonblock(struct l2_packet_data *l2, const u8 *dst_addr,
+                           u16 proto, const u8 *buf, size_t len)
+{
+       /* Best-effort send: avoid blocking the eloop thread if the device
+        * tx queue is stalled (e.g. AP gone, qdisc full). EAGAIN/ENOBUFS
+        * mean the frame was dropped, which is acceptable for callers like
+        * the WNM keep-alive timer. */
+       return l2_packet_send_flags(l2, dst_addr, proto, buf, len,
+                                   MSG_DONTWAIT);
+}
+
+
 static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
 {
        struct l2_packet_data *l2 = eloop_ctx;
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 0684ada71..0054fa4a4 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -3017,8 +3017,8 @@ static void wnm_bss_keep_alive(void *eloop_ctx, void 
*sock_ctx)
                /* TODO: Consider using some more appropriate data frame for
                 * this */
                if (wpa_s->l2)
-                       l2_packet_send(wpa_s->l2, wpa_s->bssid, 0x0800,
-                                      (u8 *) "", 0);
+                       l2_packet_send_nonblock(wpa_s->l2, wpa_s->bssid,
+                                               0x0800, (u8 *) "", 0);
        }
 
 #ifdef CONFIG_SME

Reply via email to