Root cause:
mac80211_hwsim_link_info_changedcallsdo_div(tsf, bcn_int)withbcn_int = 0whenieee80211_offchannel_returnre-enables beaconing on an interface whose beacon interval was never set (or was set to zero by syzbot), causing an x86 #DE divide error atmac80211_hwsim.c:2734.
Crash report from syzbot (Google Compute Engine). MSGID: 69efb8dd.050a0220.18b4f.0006.GAE@google.com
| Field | Value | Implication |
|---|---|---|
| MSGID | <69efb8dd.050a0220.18b4f.0006.GAE@google.com> |
|
| MSGID_URL | 69efb8dd.050a0220.18b4f.0006.GAE@google.com | |
| UNAME | syzkaller |
|
| CRASH_TYPE | divide error: 0000 [#1] SMP KASAN PTI |
x86 #DE exception; integer divide-by-zero |
| PROCESS | kworker/u8:6 |
|
| PID | 193 | |
| CPU | 1 | |
| UID | 0 | |
| HARDWARE | Google Google Compute Engine |
|
| BIOS | Google 04/18/2026 |
|
| TAINT | L — softlockup | Indicates a system stall, thermal issues, or malicious software. |
| SOURCEDIR | oops-workdir/linux |
|
| VMLINUX | oops-workdir/syzbot/vmlinux-254f4963 |
|
| HEAD_COMMIT | 254f49634ee1 (Linux 7.1-rc1) | |
| WORKQUEUE | events_unbound cfg80211_wiphy_work |
Crash occurred inside a workqueue context |
| INTRODUCED-BY | c51f878379b1 (“mac80211_hwsim: fix beacon timing”, Thomas Pedersen, 2013-01-02) | Introduced do_div(tsf, bcn_int) without a zero
guard |
| PATCH_BASE | v7.1-rc1 (254f49634ee1) |
| Module | Flags | Backtrace | Location | Flag Implication |
|---|---|---|---|---|
| (module list not available in this report) |
| Address | Function | Offset | Size | Context | Module | Source location |
|---|---|---|---|---|---|---|
0xffffffff870a6c76 (0xffffffff870a6800 + 0x476) |
mac80211_hwsim_link_info_changed |
0x476 |
0xfc0 |
Task | (built-in) | mac80211_hwsim.c:2734 |
0xffffffff8b126765 (0xffffffff8b126260 + 0x505) |
drv_link_info_changed |
0x505 |
0x880 |
Task | (built-in) | driver-ops.c:497 |
0xffffffff8b163635 (0xffffffff8b1632c0 + 0x375) |
ieee80211_offchannel_return |
0x375 |
0x500 |
Task | (built-in) | offchannel.c:160 |
0xffffffff8b15db6a (0xffffffff8b15d410 + 0x75a) |
__ieee80211_scan_completed |
0x75a |
0xb40 |
Task | (built-in) | scan.c:519 |
0xffffffff8af01cbf (0xffffffff8af019f0 + 0x2cf) |
cfg80211_wiphy_work |
0x2cf |
0x460 |
Task | (built-in) | core.c:513 |
process_one_work (inlined) |
Task | (built-in) | workqueue.c:3302 | |||
0xffffffff818e85ad (0xffffffff818e7a50 + 0xb5d) |
process_scheduled_works |
0xb5d |
0x1860 |
Task | (built-in) | workqueue.c:3385 |
0xffffffff818f0613 (0xffffffff818efbc0 + 0xa53) |
worker_thread |
0xa53 |
0xfc0 |
Task | (built-in) | workqueue.c:3466 |
0xffffffff81908118 (0xffffffff81907d90 + 0x388) |
kthread |
0x388 |
0x470 |
Task | (built-in) | kthread.c:436 |
0xffffffff816bcae4 (0xffffffff816bc5d0 + 0x514) |
ret_from_fork |
0x514 |
0xb70 |
Task | (built-in) | process.c:158 |
0xffffffff813340aa (0xffffffff81334090 + 0x1a) |
ret_from_fork_asm |
0x1a |
0x30 |
Task | (built-in) | entry_64.S:245 |
RIP: 0010:mac80211_hwsim_link_info_changed+0x476/0xfc0 [drivers/net/wireless/virtual/mac80211_hwsim.c:2734]
RSP: 0000:ffffc90002ea7860 EFLAGS: 00010246
RAX: 000650757bb12572 RBX: 0000000000000200 RCX: 0000000000000000
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff88806fbe7b98
RBP: ffffc90002ea7958 R08: ffffffff903096f7 R09: 1ffffffff20612de
R10: dffffc0000000000 R11: fffffbfff20612df R12: 000650757bb12572
R13: ffff88806fbe7b18 R14: dffffc0000000000 R15: 1ffff920005d4f18
FS: 0000000000000000(0000) GS:ffff888125393000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00000000f540c000 CR3: 0000000064b3a000 CR4: 00000000003526f0
Register annotations:
| Register | Value | Annotation |
|---|---|---|
| RAX | 0x000650757bb12572 |
TSF value (u64 dividend for do_div); same as R12 |
| RBX | 0x0000000000000200 |
BSS_CHANGED_BEACON_ENABLED = 1<<9 =
0x200 |
| RCX | 0x0000000000000000 |
Result of mov (%rdi),%rcx —
link_data->beacon_int loaded as zero |
| RDX | 0x0000000000000000 |
Cleared by xor %edx,%edx before div
instruction |
| RSI | 0x0000000000000000 |
Divisor = bcn_int; zero → causes #DE |
| RDI | 0xffff88806fbe7b98 |
Address of link_data->beacon_int field (passed via
0x20(%rsp)) |
| R12 | 0x000650757bb12572 |
TSF value (u64); high 32 bits = 0x00065075 ≠ 0 →
long-division path taken |
| R13 | 0xffff88806fbe7b18 |
Likely link_data base pointer |
mac80211_hwsim_link_info_changed — crash site
(mac80211_hwsim.c:2734)drivers/net/wireless/virtual/mac80211_hwsim.c:2701
2701 static void mac80211_hwsim_link_info_changed(struct ieee80211_hw *hw,
2702 struct ieee80211_vif *vif,
2703 struct ieee80211_bss_conf *info,
2704 u64 changed)
2705 {
2706 struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
2707 struct mac80211_hwsim_data *data = hw->priv;
2708 unsigned int link_id = info->link_id;
2709 struct mac80211_hwsim_link_data *link_data = &data->link_data[link_id];
...
2722 if (changed & BSS_CHANGED_BEACON_ENABLED) {
2723 wiphy_dbg(hw->wiphy, " BCN EN: %d (BI=%u)\n",
2724 info->enable_beacon, info->beacon_int);
2725 vp->bcn_en = info->enable_beacon;
2726 if (data->started &&
2727 !hrtimer_active(&link_data->beacon_timer) &&
2728 info->enable_beacon) {
2729 u64 tsf, until_tbtt;
2730 u32 bcn_int;
2731 link_data->beacon_int = info->beacon_int * 1024;
2732 tsf = mac80211_hwsim_get_tsf(hw, vif);
2733 bcn_int = link_data->beacon_int;
→ 2734 until_tbtt = bcn_int - do_div(tsf, bcn_int); // ← bcn_int = 0; div %rsi crashes here
2735
2736 hrtimer_start(&link_data->beacon_timer,
2737 ns_to_ktime(until_tbtt * NSEC_PER_USEC),
2738 HRTIMER_MODE_REL_SOFT);
2739 } else if (!info->enable_beacon) {
...
2779 }mac80211_hwsim_link_data struct
(mac80211_hwsim.c:660):
660 struct mac80211_hwsim_link_data {
661 u32 link_id;
662 u64 beacon_int /* beacon interval in us */;
663 struct hrtimer beacon_timer;
664 };Disassembly window around crash (from
backtrace_resolve.py):
ffffffff870a6c3c: add 0x0(%r13),%r12
ffffffff870a6c40: movabs $0xdffffc0000000000,%rax
ffffffff870a6c4a: cmpb $0x0,(%r14,%rax,1)
ffffffff870a6c4f: mov %rax,%r14
ffffffff870a6c52: mov 0x20(%rsp),%rdi ← load ptr to link_data->beacon_int
ffffffff870a6c57: je ffffffff870a6c63 ← skip KASAN report if shadow ok
ffffffff870a6c59: call ffffffff8230dea0 <__asan_report_load8_noabort>
ffffffff870a6c5e: mov 0x20(%rsp),%rdi
ffffffff870a6c63: mov (%rdi),%rcx ← rcx = link_data->beacon_int (= 0)
ffffffff870a6c66: mov %ecx,%esi ← esi = low 32 bits = 0
ffffffff870a6c68: mov %r12,%rax ← rax = tsf
ffffffff870a6c6b: shr $0x20,%rax ← check high 32 bits of tsf
ffffffff870a6c6f: je ffffffff870a6cd1 ← tsf fits in 32 bits? use fast path
ffffffff870a6c71: mov %r12,%rax ← rax = tsf (full 64-bit)
ffffffff870a6c74: xor %edx,%edx ← rdx = 0
ffffffff870a6c76: div %rsi <<< crash — RSI = 0, divide error
ffffffff870a6c79: jmp ffffffff870a6cd8
git blame for lines 2731–2734:
| Line | Hash | Author | Subject |
|---|---|---|---|
| 2731 | e57f8a489c29 |
Shaul Triebitz (2022-06-06) | wifi: mac80211_hwsim: send a beacon per link |
| 2732 | c51f878379b1 |
Thomas Pedersen (2013-01-02) | mac80211_hwsim: fix beacon timing |
| 2733 | e57f8a489c29 |
Shaul Triebitz (2022-06-06) | wifi: mac80211_hwsim: send a beacon per link |
| 2734 | c51f878379b1 |
Thomas Pedersen (2013-01-02) | mac80211_hwsim: fix beacon timing |
drv_link_info_changed — driver-ops.c:497 460 void drv_link_info_changed(struct ieee80211_local *local,
461 struct ieee80211_sub_if_data *sdata,
462 struct ieee80211_bss_conf *info,
463 int link_id, u64 changed)
464 {
...
493 trace_drv_link_info_changed(local, sdata, info, changed);
494 if (local->ops->link_info_changed)
495 local->ops->link_info_changed(&local->hw, &sdata->vif,
496 info, changed);
→ 497 else if (local->ops->bss_info_changed)
498 local->ops->bss_info_changed(&local->hw, &sdata->vif,
499 info, changed);
500 trace_drv_return_void(local);
501 }ieee80211_offchannel_return —
offchannel.c:160 133 void ieee80211_offchannel_return(struct ieee80211_local *local)
134 {
135 struct ieee80211_sub_if_data *sdata;
136
137 lockdep_assert_wiphy(local->hw.wiphy);
...
157 if (test_and_clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED,
158 &sdata->state)) {
159 sdata->vif.bss_conf.enable_beacon = true;
→ 160 ieee80211_link_info_change_notify(
161 sdata, &sdata->deflink,
162 BSS_CHANGED_BEACON_ENABLED);
163 }
...
169 }__ieee80211_scan_completed — scan.c:519 516 if (!hw_scan && was_scanning) {
517 ieee80211_configure_filter(local);
518 drv_sw_scan_complete(local, scan_sdata);
→ 519 ieee80211_offchannel_return(local);
520 }cfg80211_wiphy_work — core.c:513(source at line 513 calls the scan completion work handler; this is the workqueue dispatch point)
process_one_work (inlined) —
workqueue.c:3302Inlined into process_scheduled_works. Source location:
kernel/workqueue.c:3302.
process_scheduled_works —
workqueue.c:3385Standard workqueue processing loop. Source:
kernel/workqueue.c:3385.
worker_thread —
workqueue.c:3466Standard workqueue worker thread. Source:
kernel/workqueue.c:3466.
kthread —
kthread.c:436Standard kernel thread entry. Source:
kernel/kthread.c:436.
ret_from_fork —
process.c:158Standard fork return path. Source:
arch/x86/kernel/process.c:158.
ret_from_fork_asm — entry_64.S:245Standard assembly fork return. Source:
arch/x86/entry/entry_64.S:245.
An x86 #DE (integer divide-by-zero) exception fires at
mac80211_hwsim.c:2734 inside
mac80211_hwsim_link_info_changed. The crashing expression
is:
until_tbtt = bcn_int - do_div(tsf, bcn_int);bcn_int is a u32 loaded from
link_data->beacon_int (line 2733), which was just set at
line 2731 to info->beacon_int * 1024.
info->beacon_int is the 802.11 beacon interval (in time
units) from ieee80211_bss_conf. In the crashing execution
info->beacon_int = 0, so
link_data->beacon_int = 0 and
bcn_int = 0.
The disassembly confirms: mov (%rdi),%rcx loads
link_data->beacon_int into RCX = 0;
mov %ecx,%esi truncates to RSI = 0 (the do_div
divisor); div %rsi with RSI = 0 triggers the #DE. This is
complete fact — the register dump shows RCX = RSI = 0 and RAX = R12 =
the TSF value 0x000650757bb12572.
drivers/net/wireless/virtual/mac80211_hwsim.c:2701
2701 static void mac80211_hwsim_link_info_changed(struct ieee80211_hw *hw,
2702 struct ieee80211_vif *vif,
2703 struct ieee80211_bss_conf *info,
2704 u64 changed)
2705 {
2706 struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
2707 struct mac80211_hwsim_data *data = hw->priv;
2708 unsigned int link_id = info->link_id;
2709 struct mac80211_hwsim_link_data *link_data = &data->link_data[link_id];
...
2722 if (changed & BSS_CHANGED_BEACON_ENABLED) {
2723 wiphy_dbg(hw->wiphy, " BCN EN: %d (BI=%u)\n",
2724 info->enable_beacon, info->beacon_int);
2725 vp->bcn_en = info->enable_beacon;
2726 if (data->started &&
2727 !hrtimer_active(&link_data->beacon_timer) &&
2728 info->enable_beacon) {
2729 u64 tsf, until_tbtt;
2730 u32 bcn_int;
2731 link_data->beacon_int = info->beacon_int * 1024;
2732 tsf = mac80211_hwsim_get_tsf(hw, vif);
2733 bcn_int = link_data->beacon_int;
→ 2734 until_tbtt = bcn_int - do_div(tsf, bcn_int); // ← bcn_int = 0; RSI = 0 → #DE
2735
2736 hrtimer_start(&link_data->beacon_timer,
2737 ns_to_ktime(until_tbtt * NSEC_PER_USEC),
2738 HRTIMER_MODE_REL_SOFT);
2739 } else if (!info->enable_beacon) {
...
2748 link_data->beacon_int = 0;
...
2750 }
2751 }Q1: How did bcn_int become zero?
A1: At line 2731, link_data->beacon_int is set to
info->beacon_int * 1024, converting the 802.11 beacon
interval from TUs (time units) to microseconds. info is
struct ieee80211_bss_conf *, and
info->beacon_int (type u16) carries
whatever value was in bss_conf.beacon_int at notification
time. When this field is zero, the multiplication produces zero,
link_data->beacon_int becomes zero, the local
bcn_int is loaded from this zero value at line 2733, and
do_div(tsf, bcn_int) faults.
Q2: How did info->beacon_int end up as zero
at the time of the call?
A2: The notification with BSS_CHANGED_BEACON_ENABLED was
sent by ieee80211_offchannel_return (offchannel.c:155–158)
when a software scan completed. That function sets
sdata->vif.bss_conf.enable_beacon = true and calls
ieee80211_link_info_change_notify(sdata, &sdata->deflink, BSS_CHANGED_BEACON_ENABLED).
It does not update bss_conf.beacon_int
before sending the notification. If bss_conf.beacon_int was
never set (syzbot can create AP interfaces with
beacon_int = 0 through fuzzing), the notification arrives
with info->beacon_int = 0.
The call chain (confirmed from backtrace):
cfg80211_wiphy_work (workqueue)
→ __ieee80211_scan_completed [scan.c:519]
→ ieee80211_offchannel_return [offchannel.c:160]
→ ieee80211_link_info_change_notify(BSS_CHANGED_BEACON_ENABLED)
→ drv_link_info_changed [driver-ops.c:497]
→ mac80211_hwsim_link_info_changed [mac80211_hwsim.c:2734] ← crash
The guard at lines 2726–2728 checks data->started,
!hrtimer_active, and info->enable_beacon —
all true — but does not check for
info->beacon_int == 0.
Comparison with existing guard:
An identical do_div(tsf, bcn_int) expression appears in
mac80211_hwsim_config (mac80211_hwsim.c:2635), protected
by:
2630 if (!data->started || !link_data->beacon_int) {
2631 hrtimer_cancel(&link_data->beacon_timer);
2632 } else if (!hrtimer_active(&link_data->beacon_timer)) {
2633 u64 tsf = mac80211_hwsim_get_tsf(hw, NULL);
2634 u32 bcn_int = link_data->beacon_int;
2635 u64 until_tbtt = bcn_int - do_div(tsf, bcn_int);The equivalent guard (!link_data->beacon_int →
cancel/skip) is absent from the BSS_CHANGED_BEACON_ENABLED
path in mac80211_hwsim_link_info_changed.
The fix belongs in mac80211_hwsim_link_info_changed in
drivers/net/wireless/virtual/mac80211_hwsim.c, immediately
after bcn_int = link_data->beacon_int (line 2733) and
before the do_div.
A zero beacon_int is invalid for beacon timer
scheduling. When info->beacon_int == 0, setting
link_data->beacon_int = 0 (line 2731) is consistent with
the cleanup path at line 2748 — both leave the field at zero to signal
“no active beacon”. The timer must not be started with a zero period;
returning early is correct.
Proposed fix:
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -2730,7 +2730,9 @@ static void mac80211_hwsim_link_info_changed(struct ieee80211_hw *hw,
u32 bcn_int;
link_data->beacon_int = info->beacon_int * 1024;
tsf = mac80211_hwsim_get_tsf(hw, vif);
bcn_int = link_data->beacon_int;
+if (!bcn_int)
+return;
until_tbtt = bcn_int - do_div(tsf, bcn_int);
hrtimer_start(&link_data->beacon_timer,The patch is saved as report.patch for the patch
agent.
PATCH_BASE: v7.1-rc1 (commit
254f49634ee1)
Bug introduction: Commit c51f878379b1
(“mac80211_hwsim: fix beacon timing”, Thomas Pedersen, 2013-01-02)
introduced do_div(tsf, bcn_int) without a zero guard.
Commit e57f8a489c29 (“wifi: mac80211_hwsim: send a beacon
per link”, Shaul Triebitz, 2022-06-06) refactored the beacon setup to
use per-link data, setting
link_data->beacon_int = info->beacon_int * 1024 —
this is what allows info->beacon_int = 0 from the
notification to reach the division. No upstream fix was found.
Security assessment: This is a locally-triggerable kernel crash (divide by zero) from userspace via a wireless subsystem syscall sequence. No memory corruption; no privilege escalation vector. Impact: local denial of service (kernel crash). CVSS 5.5 (local, low privilege, no interaction, high availability impact). Not suitable for CVE assignment as it requires root or CAP_NET_ADMIN to create the AP interface.
v7.1-rc1 (254f49634ee1) —
exact match (PATCH_BASE tag).git apply --check passed
cleanly against v7.1-rc1.patch-email.txt — LKML-ready patch emailgit-send-email.sh — send script
(--to linux-wireless@vger.kernel.org,
--in-reply-to <69efb8dd.050a0220.18b4f.0006.GAE@google.com>)All checked items verified.