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