From a75b4c405faa6d0b97cbf464ee051441c2b07f66 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 6 Oct 2026 06:27:35 +0200 Subject: [PATCH] hostapd: continue a pending interface setup from the event loop The bss_remove hook of hostapd.uc sends a service event with a synchronous ubus call. While libubus waits for that reply, it runs the completion callbacks of other pending requests, among them the wpa_supplicant phy_status or phy_set_state request of an interface setup in progress. The callback advanced the setup to hostapd.add_iface() nested inside the teardown that called the hook. With an AP MLD, a config_set for radio 0 followed by one for radio 1 added the radio 0 link while the radio 1 link was half removed and still held its driver BSS and management frame registrations. The add failed with "Match already configured", and its cleanup crashed hostapd in hostapd_wpa_event(). This happened on the first such reconfiguration after a bring-up. Run the next step of the pending setup from a uloop timer instead, and cancel the timer where the pending request is aborted. Signed-off-by: Felix Fietkau --- package/network/services/hostapd/files/hostapd.uc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc index 417ea6f7d16..830d685b47c 100644 --- a/package/network/services/hostapd/files/hostapd.uc +++ b/package/network/services/hostapd/files/hostapd.uc @@ -275,6 +275,9 @@ function __iface_pending_next(pending, state, ret, data) if (pending.defer) pending.defer.abort(); delete pending.defer; + if (pending.timer) + pending.timer.cancel(); + delete pending.timer; switch (state) { case "init": return "create_bss"; @@ -357,7 +360,16 @@ function iface_pending_ubus_call(obj, method, arg) { let ubus = hostapd.data.ubus; let pending = this; - this.defer = ubus.defer(obj, method, arg, (ret, data) => { delete pending.defer; pending.next(ret, data) }); + + /* libubus can run this from inside a synchronous ubus call, so continue + * from the event loop */ + this.defer = ubus.defer(obj, method, arg, (ret, data) => { + delete pending.defer; + pending.timer = uloop.timer(0, () => { + delete pending.timer; + pending.next(ret, data); + }); + }); } const iface_pending_proto = {