* [PATCH 0/3] Fix two station roaming crashes
@ 2026-07-16 2:34 Suchir Kavi
2026-07-16 2:34 ` [PATCH 1/3] station: fix use-after-free in preauthenticate_cb Suchir Kavi
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Suchir Kavi @ 2026-07-16 2:34 UTC (permalink / raw)
To: iwd
We run iwd on a large number of embedded aarch64 Linux devices
(WPA3-SAE, frequently multi-AP mesh networks) and root-caused two
recurring segfaults from coredumps and journals. Both are present in
current master by inspection, and together they account for the
majority of the iwd crashes we see on current builds. Heads up that
this cover letter and patchset are primarily AI-generated (Fable) but
reviewed by me.
Crash 1: use-after-free of handshake_state when a reassociation
overlaps a pending connection attempt.
netdev_connect() refuses to start while an attempt is pending
(netdev->connected || netdev->connect_cmd_id || netdev->work.id ->
-EISCONN). netdev_reassociate() has no equivalent guard, and the
station layer can invoke it mid-connect: station_cannot_roam() does
not cover the connecting states, connected_bss is already set while
connecting, and station_roam_scan_notify() does not re-check state.
Captured sequence (journal shows "state, old: connecting (auto), new:
roaming" in the same second as the crash):
1. A fresh SAE-H2E connect is queued behind a scan - the auth proto is
created holding its raw handshake pointer, netdev->work is queued
and never starts (sm->group_retry == -1 in the core).
2. A roam scan completes mid-connect -> netdev_reassociate() to a BSS
with a cached PMKSA. netdev_connect_common() takes the
PMKSA/CMD_CONNECT path, so netdev->ap is NOT replaced;
netdev->handshake is swapped; old_hs is unreffed here and the
caller swaps station->hs - both references gone, the handshake is
zeroed and freed under the stale auth proto. netdev_connect_common()
also re-inserts the already-queued embedded work item, linking the
same wiphy_radio_work_item into the queue twice.
3. The scan finishes -> the connect work runs ->
netdev_begin_connection() sends the CMD_CONNECT, then starts the
stale softmac auth proto -> sae_start() -> sae_choose_next_group()
reads sm->handshake->ecc_sae_pts from the freed (zeroed) handshake
-> NULL dereference.
Core anatomy: sm->handshake points at zeroed memory plus allocator
metadata; netdev->ap == sm while netdev->handshake is a different,
live handshake; station state == ROAMING.
Patch 2 adds the same guard netdev_connect() has. Both fields are
zero at any legitimate roam start since netdev_connect_ok() completes
the work item, and the station roam paths fail a rejected roam
cleanly - except station_preauthenticate_cb(), which is missing a
return in its error branch and falls through to handshake_state_ref()
on the new_hs it just unreffed. Patch 1 fixes that first so the new
error return cannot widen it.
Crash 2: station_ap_directed_roam() dereferences connected_bss before
its own state guard.
station_disconnect() clears connected_bss (via
station_reset_connection_state()) before entering DISCONNECTING, and
the WNM frame watch outlives the connection - so a BSS Transition
Management request arriving in that window (mesh APs send them around
disconnects; our journal shows connected -> disconnecting 2 s before
the crash) crashes on the ignore_candidates initializer that runs
before the guard meant to drop exactly that frame. The invariant
"state == CONNECTED => connected_bss != NULL" is otherwise sound -
the unguarded sanitize memcmp just below already relies on it.
Patch 3 assigns ignore_candidates after the state check.
Suchir Kavi (3):
station: fix use-after-free in preauthenticate_cb
netdev: reject reassociation while connect pending
station: fix NULL deref in ap_directed_roam
src/netdev.c | 3 +++
src/station.c | 7 +++++--
2 files changed, 8 insertions(+), 2 deletions(-)
--
2.54.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/3] station: fix use-after-free in preauthenticate_cb
2026-07-16 2:34 [PATCH 0/3] Fix two station roaming crashes Suchir Kavi
@ 2026-07-16 2:34 ` Suchir Kavi
2026-07-16 2:34 ` [PATCH 2/3] netdev: reject reassociation while connect pending Suchir Kavi
2026-07-16 2:34 ` [PATCH 3/3] station: fix NULL deref in ap_directed_roam Suchir Kavi
2 siblings, 0 replies; 4+ messages in thread
From: Suchir Kavi @ 2026-07-16 2:34 UTC (permalink / raw)
To: iwd
station_preauthenticate_cb() falls through its error branch: after
unreffing new_hs on a failed station_transition_reassociate(), it
proceeds to swap station->hs to a reference on the freed handshake.
Return instead.
Assisted-by: Claude:claude-fable-5
---
src/station.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/station.c b/src/station.c
index 8fcf8c70..0b2d7dc6 100644
--- a/src/station.c
+++ b/src/station.c
@@ -2598,6 +2598,7 @@ static void station_preauthenticate_cb(struct netdev *netdev,
if (station_transition_reassociate(station, bss, new_hs) < 0) {
handshake_state_unref(new_hs);
station_roam_failed(station);
+ return;
}
handshake_state_unref(station->hs);
--
2.54.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/3] netdev: reject reassociation while connect pending
2026-07-16 2:34 [PATCH 0/3] Fix two station roaming crashes Suchir Kavi
2026-07-16 2:34 ` [PATCH 1/3] station: fix use-after-free in preauthenticate_cb Suchir Kavi
@ 2026-07-16 2:34 ` Suchir Kavi
2026-07-16 2:34 ` [PATCH 3/3] station: fix NULL deref in ap_directed_roam Suchir Kavi
2 siblings, 0 replies; 4+ messages in thread
From: Suchir Kavi @ 2026-07-16 2:34 UTC (permalink / raw)
To: iwd
netdev_reassociate() replaces netdev->handshake and unrefs the old
handshake without freeing netdev->ap or dequeuing a pending connection
work item. If a reassociation begins while an earlier attempt's radio
work is still queued (reachable since station_cannot_roam() does not
cover the connecting states), the stale auth proto keeps a raw pointer
to the freed handshake. On the PMKSA-cache path netdev_connect_common()
does not replace netdev->ap, and it re-inserts the already-queued
embedded work item; when the work finally runs,
netdev_begin_connection() starts the stale auth proto and sae_start()
dereferences the freed handshake:
#0 sae_choose_next_group <- reads handshake->ecc_sae_pts == NULL
#1 sae_start
#2 netdev_begin_connection
#3 netdev_connection_work_ready
#4 wiphy_radio_work_next
#5 get_scan_done
Mirror netdev_connect(), which already refuses to start while
netdev->connect_cmd_id or netdev->work.id is set. Both are zero at any
legitimate roam start since netdev_connect_ok() completes the work item,
and the station roam paths handle a negative return by failing the roam
cleanly.
Assisted-by: Claude:claude-fable-5
---
src/netdev.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/netdev.c b/src/netdev.c
index e639a1f8..dadd90da 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -4394,6 +4394,9 @@ int netdev_reassociate(struct netdev *netdev, const struct scan_bss *target_bss,
struct handshake_state *old_hs;
struct eapol_sm *old_sm;
+ if (netdev->connect_cmd_id || netdev->work.id)
+ return -EBUSY;
+
old_sm = netdev->sm;
old_hs = netdev->handshake;
--
2.54.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 3/3] station: fix NULL deref in ap_directed_roam
2026-07-16 2:34 [PATCH 0/3] Fix two station roaming crashes Suchir Kavi
2026-07-16 2:34 ` [PATCH 1/3] station: fix use-after-free in preauthenticate_cb Suchir Kavi
2026-07-16 2:34 ` [PATCH 2/3] netdev: reject reassociation while connect pending Suchir Kavi
@ 2026-07-16 2:34 ` Suchir Kavi
2 siblings, 0 replies; 4+ messages in thread
From: Suchir Kavi @ 2026-07-16 2:34 UTC (permalink / raw)
To: iwd
station_ap_directed_roam() initialized ignore_candidates from
station->connected_bss->vendor_quirks before the state != CONNECTED
guard. station_disconnect() clears connected_bss before entering
DISCONNECTING and the WNM frame watch outlives the connection, so a BSS
Transition Management frame arriving in that window crashes on the
dereference the guard was meant to prevent.
Assign it after the state check, which guarantees connected_bss is set
(the frame-sanitize memcmp below already relies on the same invariant).
Assisted-by: Claude:claude-fable-5
---
src/station.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/station.c b/src/station.c
index 0b2d7dc6..e3aeb3fd 100644
--- a/src/station.c
+++ b/src/station.c
@@ -3273,8 +3273,7 @@ static void station_ap_directed_roam(struct station *station,
uint16_t dtimer;
uint8_t valid_interval;
bool can_roam = !station_cannot_roam(station);
- bool ignore_candidates =
- station->connected_bss->vendor_quirks.ignore_bss_tm_candidates;
+ bool ignore_candidates;
l_debug("ifindex: %u", netdev_get_ifindex(station->netdev));
@@ -3283,6 +3282,9 @@ static void station_ap_directed_roam(struct station *station,
return;
}
+ ignore_candidates =
+ station->connected_bss->vendor_quirks.ignore_bss_tm_candidates;
+
/*
* Sanitize the frame to check that it is from our current AP.
*
--
2.54.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-07-16 2:36 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-16 2:34 [PATCH 0/3] Fix two station roaming crashes Suchir Kavi
2026-07-16 2:34 ` [PATCH 1/3] station: fix use-after-free in preauthenticate_cb Suchir Kavi
2026-07-16 2:34 ` [PATCH 2/3] netdev: reject reassociation while connect pending Suchir Kavi
2026-07-16 2:34 ` [PATCH 3/3] station: fix NULL deref in ap_directed_roam Suchir Kavi
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.