Crash in network_info_get_roam_frequencies() when a neighbor report completes with -ENOTCONN during disconnect teardown
Hi,
iwd 3.12 crashes with SIGSEGV while roaming, when a pending 802.11k neighbor report request completes during connection teardown. The neighbor report callback only bails out on -ENODEV, so an -ENOTCONN completion falls through to a roam scan that dereferences the connection state netdev_connect_free() has just torn down.
I hit this twice within one minute on 2026-07-25 and captured both core dumps. The backtraces are identical, so this looks deterministic rather than memory corruption.
Backtrace (both dumps, identical):
#0 network_info_get_roam_frequencies (info=0x0, current_freq=2462,
max=max@entry=5 '\005') at src/knownnetworks.c:391
#1 station_roam_scan_known_freqs (station=station@entry=0x564903a82b50)
at src/station.c:3088
#2 station_neighbor_report_cb (netdev=<optimized out>, err=-107,
reports=<optimized out>, reports_len=0,
user_data=0x564903a82b50) at src/station.c:3129
#3 netdev_connect_free (netdev=netdev@entry=0x564903a77c40)
at src/netdev.c:867
#4 netdev_connect_failed (netdev=0x564903a77c40, result=<optimized out>,
status_or_reason=<optimized out>) at src/netdev.c:937
#5 netdev_disconnected (netdev=0x564903a77c40, result=<optimized out>,
event=NETDEV_EVENT_DISCONNECT_BY_SME,
status_or_reason=<optimized out>) at src/netdev.c:1004
#6 netdev_disconnect_by_sme_cb (msg=<optimized out>,
user_data=0x564903a77c40) at src/netdev.c:1019
#7 process_unicast (genl=<optimized out>, nlmsg=0x7ffffcf571d0)
at ell/genl.c:860
#8 received_data (io=<optimized out>, user_data=0x564903a69bb0)
at ell/genl.c:972
#9 io_callback (fd=<optimized out>, events=1, user_data=0x564903a69b00)
at ell/io.c:105
#10 l_main_iterate (timeout=<optimized out>) at ell/main.c:463
#11 l_main_run () at ell/main.c:511
#12 l_main_run () at ell/main.c:492
#13 l_main_run_with_signal (callback=0x5648f3df5bc0 <signal_handler>,
user_data=0x0) at ell/main.c:633
#14 main (argc=<optimized out>, argv=<optimized out>) at src/main.c:610The faulting line is knownnetworks.c:391, with info == NULL:
for (entry = l_queue_get_entries(info->known_frequencies); entry && max;
Sequence, as I read it:
1. The AP disconnects us, so netdev_disconnect_by_sme_cb() runs (NETDEV_EVENT_DISCONNECT_BY_SME).
2. netdev_connect_failed() -> netdev_connect_free() tears the connection down and completes the outstanding neighbor report request with an error.
3. station_neighbor_report_cb() is invoked with err = -107
(-ENOTCONN).
4. The guard at the top of that callback only returns early for
-ENODEV:
if (!station->preparing_roam || err == -ENODEV)
return;
so -ENOTCONN continues to:
if (!reports || err) {
r = station_roam_scan_known_freqs(station); 5. station_roam_scan_known_freqs() then reaches network_info_get_roam_frequencies() with the network info already gone, and dereferences NULL.
Commit 155c266 ("station: add checks to prevent multiple roam scans", Jan 2023) fixed a different path to the same crash signature and is of course already in 3.12. This one arrives through the disconnect teardown rather than the roam rearm timer, and the code in master still looks
affected: station_neighbor_report_cb() special-cases -ENODEV only, and station_roam_scan_known_freqs() has no NULL check on the connected network.
I have not written a patch because I do not know which fix you would prefer -- treating any err as terminal in the callback, checking station->connected_network before the fallback scan, or cancelling the neighbor report request earlier in netdev_connect_free(). Happy to test a patch on this hardware.
System:
iwd 3.12 (Arch Linux, iwd 3.12-1)
kernel 7.1.5-zen1-1-zen (x86_64)
device Intel Wi-Fi 6E AX210/AX1675 2x2 [Typhoon Peak]
[8086:2725] rev 1a, iwlwifi firmware 89.735b75a4.0 ty-a0-gf-a0-89.ucode, op_mode iwlmvm
band at crash 2462 MHz (2.4 GHz, channel 11), roaming on a weak link
Both core dumps are still on disk if anything else would help.
Thanks,
Muhammed Izzet Saglam
Hi Muhammed,
Quoted 91 lines · Muhammed Izzet Saglam
Hi,
iwd 3.12 crashes with SIGSEGV while roaming, when a pending 802.11k neighbor report request completes during connection teardown. The neighbor report callback only bails out on
-ENODEV, so an-ENOTCONNcompletion falls through to a roam scan that dereferences the connection statenetdev_connect_free()has just torn down.I hit this twice within one minute on 2026-07-25 and captured both core dumps. The backtraces are identical, so this looks deterministic rather than memory corruption.
Backtrace (both dumps, identical):
#0 network_info_get_roam_frequencies (info=0x0, current_freq=2462, max=max@entry=5 '\005') at src/knownnetworks.c:391 #1 station_roam_scan_known_freqs (station=station@entry=0x564903a82b50) at src/station.c:3088 #2 station_neighbor_report_cb (netdev=<optimized out>, err=-107, reports=<optimized out>, reports_len=0, user_data=0x564903a82b50) at src/station.c:3129 #3 netdev_connect_free (netdev=netdev@entry=0x564903a77c40) at src/netdev.c:867 #4 netdev_connect_failed (netdev=0x564903a77c40, result=<optimized out>, status_or_reason=<optimized out>) at src/netdev.c:937 #5 netdev_disconnected (netdev=0x564903a77c40, result=<optimized out>, event=NETDEV_EVENT_DISCONNECT_BY_SME, status_or_reason=<optimized out>) at src/netdev.c:1004 #6 netdev_disconnect_by_sme_cb (msg=<optimized out>, user_data=0x564903a77c40) at src/netdev.c:1019 #7 process_unicast (genl=<optimized out>, nlmsg=0x7ffffcf571d0) at ell/genl.c:860 #8 received_data (io=<optimized out>, user_data=0x564903a69bb0) at ell/genl.c:972 #9 io_callback (fd=<optimized out>, events=1, user_data=0x564903a69b00) at ell/io.c:105 #10 l_main_iterate (timeout=<optimized out>) at ell/main.c:463 #11 l_main_run () at ell/main.c:511 #12 l_main_run () at ell/main.c:492 #13 l_main_run_with_signal (callback=0x5648f3df5bc0 <signal_handler>, user_data=0x0) at ell/main.c:633 #14 main (argc=<optimized out>, argv=<optimized out>) at src/main.c:610The faulting line is knownnetworks.c:391, with info == NULL:
for (entry = l_queue_get_entries(info->known_frequencies); entry && max;Sequence, as I read it:
1. The AP disconnects us, so
netdev_disconnect_by_sme_cb()runs (NETDEV_EVENT_DISCONNECT_BY_SME).
2.netdev_connect_failed()->netdev_connect_free()tears the connection down and completes the outstanding neighbor report request with an error.
3.station_neighbor_report_cb()is invoked with err = -107(-ENOTCONN). 4. The guard at the top of that callback only returns early for -ENODEV: if (!station->preparing_roam || err == -ENODEV) return; so -ENOTCONN continues to: if (!reports || err) { r = station_roam_scan_known_freqs(station);5.
station_roam_scan_known_freqs()then reachesnetwork_info_get_roam_frequencies()with the network info already gone, and dereferences NULL.Commit 155c266 ("station: add checks to prevent multiple roam scans", Jan 2023) fixed a different path to the same crash signature and is of course already in 3.12. This one arrives through the disconnect teardown rather than the roam rearm timer, and the code in master still looks
affected:station_neighbor_report_cb()special-cases-ENODEVonly, andstation_roam_scan_known_freqs()has no NULL check on the connected network.I have not written a patch because I do not know which fix you would prefer -- treating any err as terminal in the callback, checking
station->connected_networkbefore the fallback scan, or cancelling the neighbor report request earlier innetdev_connect_free(). Happy to test a patch on this hardware.System:
iwd 3.12 (Arch Linux, iwd 3.12-1) kernel 7.1.5-zen1-1-zen (x86_64) device Intel Wi-Fi 6E AX210/AX1675 2x2 [Typhoon Peak] [8086:2725] rev 1a, iwlwifi
Quoted 7 lines
firmware 89.735b75a4.0 ty-a0-gf-a0-89.ucode,
op_modeiwlmvm
band at crash 2462 MHz (2.4 GHz, channel 11), roaming on a weak linkBoth core dumps are still on disk if anything else would help.
Thanks,
Muhammed Izzet Saglam
Thanks for reporting this. A quick fix is certainly to just add -ENOTCONN to station_neighbor_report_cb(). The only thing that makes me nervous is if -ENOTCONN is a possible return from netlink. I'm also questioning why netdev needs to call the callback in the first place (e.g. with -ENODEV). We just bail out and ignore it. I can see the argument for netdev completing the API contract with the caller, but it also feels like a waste of cycles to me.
Thanks,
James