AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/06 01:50 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": [
    "CFG80211",
    "CFG80211_WEXT",
    "MAC80211_HWSIM"
  ],
  "FocusSymbols": [
    "cfg80211_scan_done",
    "___cfg80211_scan_done",
    "reg_leave_invalid_chans_wk",
    "reg_leave_invalid_nan_wk",
    "reg_check_chans_work",
    "cfg80211_ibss_joined",
    "__cfg80211_ibss_joined",
    "cfg80211_classify8021d",
    "__cfg80211_bss_update",
    "cfg80211_update_assoc_bss_entry",
    "cfg80211_stop_p2p_device",
    "cfg80211_process_wdev_events"
  ],
  "KMSANReasoning": "The patch series addresses lifetime management, locking, and packet parsing across cfg80211:\n1. Scan request tracking (driver_owns/stale): Fixes use-after-free and premature frees of scan requests across driver cancellation paths. Any memory corruption or UAF issues are caught by KASAN. The scan request structures are allocated with kzalloc, so all new struct fields are zero-initialized.\n2. Regulatory workqueue split: Solves lock ordering/deadlocks between rtnl_lock and wiphy mutex during regulatory interface teardown. This falls strictly under LOCKDEP.\n3. IBSS event handling: Passes BSS references directly to event processing rather than re-looking them up; all event structs are allocated via kzalloc_obj and reference counting is properly managed.\n4. Packet classification (skb_header_pointer): Fixes potential out-of-bounds reads on non-linear skbs by safely checking header lengths with skb_header_pointer. Any potential buffer overreads are caught by KASAN.\n5. No uninitialized stack/heap memory, struct padding leaks, or uninitialized branch conditions are introduced.\n\nTherefore, dedicated KMSAN fuzzing is not required as KASAN and LOCKDEP fully cover the risks in these changes.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch introduces changes to cfg80211 scan request lifecycle management (handling asynchronous driver cancellation and stale request freeing), refactors regulatory channel leave workers and locking (splitting NAN teardown into separate work with RTNL lock vs wiphy work), updates BSS object refcounting during IBSS join events, and improves header parsing safety in cfg80211_classify8021d using skb_header_pointer. All modified paths are executable and reachable via nl80211/cfg80211 user-space netlink commands and simulated wireless hardware (mac80211_hwsim) in standard QEMU environments.",
  "WorthFuzzing": true
}

1/1 2026/09/06 01:50 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit bc6715e01ed071bb16441193b4cd75c547bd2b62\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Sun Sep 6 01:50:14 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/net/wireless/core.c b/net/wireless/core.c\nindex d13310fef691a..668380deec7da 100644\n--- a/net/wireless/core.c\n+++ b/net/wireless/core.c\n@@ -244,9 +244,8 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,\n \trdev-\u003eopencount--;\n \n \tif (rdev-\u003escan_req \u0026\u0026 rdev-\u003escan_req-\u003ereq.wdev == wdev) {\n-\t\tif (WARN_ON(!rdev-\u003escan_req-\u003enotified \u0026\u0026\n-\t\t\t    (!rdev-\u003eint_scan_req ||\n-\t\t\t     !rdev-\u003eint_scan_req-\u003enotified)))\n+\t\tif (!rdev-\u003escan_req-\u003enotified \u0026\u0026\n+\t\t    (!rdev-\u003eint_scan_req || !rdev-\u003eint_scan_req-\u003enotified))\n \t\t\trdev-\u003escan_req-\u003einfo.aborted = true;\n \t\t___cfg80211_scan_done(rdev, false);\n \t}\n@@ -646,6 +645,8 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,\n \tINIT_WORK(\u0026rdev-\u003edestroy_work, cfg80211_destroy_iface_wk);\n \twiphy_work_init(\u0026rdev-\u003esched_scan_stop_wk, cfg80211_sched_scan_stop_wk);\n \tINIT_WORK(\u0026rdev-\u003esched_scan_res_wk, cfg80211_sched_scan_results_wk);\n+\twiphy_work_init(\u0026rdev-\u003ereg_check_chans_wk, reg_leave_invalid_chans_wk);\n+\tINIT_WORK(\u0026rdev-\u003ereg_leave_nan_wk, reg_leave_invalid_nan_wk);\n \tINIT_WORK(\u0026rdev-\u003epropagate_radar_detect_wk,\n \t\t  cfg80211_propagate_radar_detect_wk);\n \tINIT_WORK(\u0026rdev-\u003epropagate_cac_done_wk, cfg80211_propagate_cac_done_wk);\n@@ -1345,6 +1346,7 @@ void wiphy_unregister(struct wiphy *wiphy)\n \tcancel_delayed_work_sync(\u0026rdev-\u003edfs_update_channels_wk);\n \tcancel_delayed_work_sync(\u0026rdev-\u003ebackground_cac_done_wk);\n \tflush_work(\u0026rdev-\u003edestroy_work);\n+\tflush_work(\u0026rdev-\u003ereg_leave_nan_wk);\n \tflush_work(\u0026rdev-\u003epropagate_radar_detect_wk);\n \tflush_work(\u0026rdev-\u003epropagate_cac_done_wk);\n \tflush_work(\u0026rdev-\u003emgmt_registrations_update_wk);\n@@ -1758,9 +1760,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,\n \t\twiphy_lock(\u0026rdev-\u003ewiphy);\n \t\tcfg80211_update_iface_num(rdev, wdev-\u003eiftype, -1);\n \t\tif (rdev-\u003escan_req \u0026\u0026 rdev-\u003escan_req-\u003ereq.wdev == wdev) {\n-\t\t\tif (WARN_ON(!rdev-\u003escan_req-\u003enotified \u0026\u0026\n-\t\t\t\t    (!rdev-\u003eint_scan_req ||\n-\t\t\t\t     !rdev-\u003eint_scan_req-\u003enotified)))\n+\t\t\tif (!rdev-\u003escan_req-\u003enotified \u0026\u0026\n+\t\t\t    (!rdev-\u003eint_scan_req ||\n+\t\t\t     !rdev-\u003eint_scan_req-\u003enotified))\n \t\t\t\trdev-\u003escan_req-\u003einfo.aborted = true;\n \t\t\t___cfg80211_scan_done(rdev, false);\n \t\t}\ndiff --git a/net/wireless/core.h b/net/wireless/core.h\nindex b4610f6685dcb..6138d207caf41 100644\n--- a/net/wireless/core.h\n+++ b/net/wireless/core.h\n@@ -24,6 +24,16 @@\n struct cfg80211_scan_request_int {\n \tstruct cfg80211_scan_info info;\n \tbool notified;\n+\t/*\n+\t * set while the request is handed to the driver, i.e. between\n+\t * rdev_scan() and cfg80211_scan_done()\n+\t */\n+\tbool driver_owns;\n+\t/*\n+\t * set when cfg80211 is done with the request but the driver still\n+\t * owns it, so that cfg80211_scan_done() knows to just free it\n+\t */\n+\tbool stale;\n \t/* must be last - variable members */\n \tstruct cfg80211_scan_request req;\n };\n@@ -104,6 +114,8 @@ struct cfg80211_registered_device {\n \tstruct work_struct destroy_work;\n \tstruct wiphy_work sched_scan_stop_wk;\n \tstruct work_struct sched_scan_res_wk;\n+\tstruct wiphy_work reg_check_chans_wk;\n+\tstruct work_struct reg_leave_nan_wk;\n \n \tstruct cfg80211_chan_def radar_chandef;\n \tstruct work_struct propagate_radar_detect_wk;\n@@ -280,8 +292,7 @@ struct cfg80211_event {\n \t\t\tbool locally_generated;\n \t\t} dc;\n \t\tstruct {\n-\t\t\tu8 bssid[ETH_ALEN];\n-\t\t\tstruct ieee80211_channel *channel;\n+\t\t\tstruct cfg80211_bss *bss;\n \t\t} ij;\n \t\tstruct {\n \t\t\tu8 peer_addr[ETH_ALEN];\n@@ -344,8 +355,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,\n void cfg80211_clear_ibss(struct net_device *dev, bool nowext);\n int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,\n \t\t\tstruct net_device *dev, bool nowext);\n-void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,\n-\t\t\t    struct ieee80211_channel *channel);\n+void __cfg80211_ibss_joined(struct net_device *dev, struct cfg80211_bss *bss);\n int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,\n \t\t\t    struct wireless_dev *wdev);\n \ndiff --git a/net/wireless/ibss.c b/net/wireless/ibss.c\nindex b1d748bdb504e..7f6779d326b81 100644\n--- a/net/wireless/ibss.c\n+++ b/net/wireless/ibss.c\n@@ -16,26 +16,18 @@\n #include \"rdev-ops.h\"\n \n \n-void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,\n-\t\t\t    struct ieee80211_channel *channel)\n+void __cfg80211_ibss_joined(struct net_device *dev, struct cfg80211_bss *bss)\n {\n \tstruct wireless_dev *wdev = dev-\u003eieee80211_ptr;\n-\tstruct cfg80211_bss *bss;\n #ifdef CONFIG_CFG80211_WEXT\n \tunion iwreq_data wrqu;\n #endif\n \n \tif (WARN_ON(wdev-\u003eiftype != NL80211_IFTYPE_ADHOC))\n-\t\treturn;\n+\t\tgoto put_bss;\n \n \tif (!wdev-\u003eu.ibss.ssid_len)\n-\t\treturn;\n-\n-\tbss = cfg80211_get_bss(wdev-\u003ewiphy, channel, bssid, NULL, 0,\n-\t\t\t       IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);\n-\n-\tif (WARN_ON(!bss))\n-\t\treturn;\n+\t\tgoto put_bss;\n \n \tif (wdev-\u003eu.ibss.current_bss) {\n \t\tcfg80211_unhold_bss(wdev-\u003eu.ibss.current_bss);\n@@ -43,17 +35,22 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,\n \t}\n \n \tcfg80211_hold_bss(bss_from_pub(bss));\n+\t/* the reference from the event is transferred to current_bss */\n \twdev-\u003eu.ibss.current_bss = bss_from_pub(bss);\n \n \tcfg80211_upload_connect_keys(wdev);\n \n-\tnl80211_send_ibss_bssid(wiphy_to_rdev(wdev-\u003ewiphy), dev, bssid,\n+\tnl80211_send_ibss_bssid(wiphy_to_rdev(wdev-\u003ewiphy), dev, bss-\u003ebssid,\n \t\t\t\tGFP_KERNEL);\n #ifdef CONFIG_CFG80211_WEXT\n \tmemset(\u0026wrqu, 0, sizeof(wrqu));\n-\tmemcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);\n+\tmemcpy(wrqu.ap_addr.sa_data, bss-\u003ebssid, ETH_ALEN);\n \twireless_send_event(dev, SIOCGIWAP, \u0026wrqu, NULL);\n #endif\n+\treturn;\n+\n+put_bss:\n+\tcfg80211_put_bss(wdev-\u003ewiphy, bss);\n }\n \n void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,\n@@ -62,6 +59,7 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,\n \tstruct wireless_dev *wdev = dev-\u003eieee80211_ptr;\n \tstruct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev-\u003ewiphy);\n \tstruct cfg80211_event *ev;\n+\tstruct cfg80211_bss *bss;\n \tunsigned long flags;\n \n \ttrace_cfg80211_ibss_joined(dev, bssid, channel);\n@@ -69,13 +67,19 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,\n \tif (WARN_ON(!channel))\n \t\treturn;\n \n+\tbss = cfg80211_get_bss(wdev-\u003ewiphy, channel, bssid, NULL, 0,\n+\t\t\t       IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);\n+\tif (WARN_ON(!bss))\n+\t\treturn;\n+\n \tev = kzalloc_obj(*ev, gfp);\n-\tif (!ev)\n+\tif (!ev) {\n+\t\tcfg80211_put_bss(wdev-\u003ewiphy, bss);\n \t\treturn;\n+\t}\n \n \tev-\u003etype = EVENT_IBSS_JOINED;\n-\tmemcpy(ev-\u003eij.bssid, bssid, ETH_ALEN);\n-\tev-\u003eij.channel = channel;\n+\tev-\u003eij.bss = bss;\n \n \tspin_lock_irqsave(\u0026wdev-\u003eevent_lock, flags);\n \tlist_add_tail(\u0026ev-\u003elist, \u0026wdev-\u003eevent_list);\ndiff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h\nindex 46849fe8d0b39..adcfd0278da32 100644\n--- a/net/wireless/rdev-ops.h\n+++ b/net/wireless/rdev-ops.h\n@@ -464,7 +464,10 @@ static inline int rdev_scan(struct cfg80211_registered_device *rdev,\n \t\treturn -EINVAL;\n \n \ttrace_rdev_scan(\u0026rdev-\u003ewiphy, request);\n+\trequest-\u003edriver_owns = true;\n \tret = rdev-\u003eops-\u003escan(\u0026rdev-\u003ewiphy, \u0026request-\u003ereq);\n+\tif (ret)\n+\t\trequest-\u003edriver_owns = false;\n \ttrace_rdev_return_int(\u0026rdev-\u003ewiphy, ret);\n \treturn ret;\n }\ndiff --git a/net/wireless/reg.c b/net/wireless/reg.c\nindex a8336baf85dc6..11665e0a7efc8 100644\n--- a/net/wireless/reg.c\n+++ b/net/wireless/reg.c\n@@ -2345,7 +2345,7 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)\n \tiftype = wdev-\u003eiftype;\n \n \t/* make sure the interface is active */\n-\tif (!wdev-\u003enetdev || !netif_running(wdev-\u003enetdev))\n+\tif (!wdev_running(wdev))\n \t\treturn true;\n \n \t/* NAN doesn't have links, handle it separately */\n@@ -2446,19 +2446,52 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)\n \treturn true;\n }\n \n-static void reg_leave_invalid_chans(struct wiphy *wiphy)\n+void reg_leave_invalid_nan_wk(struct work_struct *work)\n {\n+\tstruct cfg80211_registered_device *rdev;\n \tstruct wireless_dev *wdev;\n-\tstruct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);\n+\n+\trdev = container_of(work, struct cfg80211_registered_device,\n+\t\t\t    reg_leave_nan_wk);\n+\n+\t/* stopping NAN closes its data interfaces, which needs the RTNL */\n+\trtnl_lock();\n \n \tlist_for_each_entry(wdev, \u0026rdev-\u003ewiphy.wdev_list, list) {\n \t\tbool valid;\n \n-\t\tscoped_guard(wiphy, wiphy)\n-\t\t\tvalid = reg_wdev_chan_valid(wiphy, wdev);\n+\t\tif (wdev-\u003eiftype != NL80211_IFTYPE_NAN)\n+\t\t\tcontinue;\n+\n+\t\tscoped_guard(wiphy, \u0026rdev-\u003ewiphy)\n+\t\t\tvalid = reg_wdev_chan_valid(\u0026rdev-\u003ewiphy, wdev);\n \t\tif (!valid)\n \t\t\tcfg80211_leave(rdev, wdev, -1);\n \t}\n+\n+\trtnl_unlock();\n+}\n+\n+void reg_leave_invalid_chans_wk(struct wiphy *wiphy, struct wiphy_work *work)\n+{\n+\tstruct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);\n+\tstruct wireless_dev *wdev;\n+\n+\tlockdep_assert_held(\u0026wiphy-\u003emtx);\n+\n+\tlist_for_each_entry(wdev, \u0026rdev-\u003ewiphy.wdev_list, list) {\n+\t\tif (reg_wdev_chan_valid(wiphy, wdev))\n+\t\t\tcontinue;\n+\n+\t\t/*\n+\t\t * Tearing down NAN needs the RTNL for closing NAN_DATA\n+\t\t * interfaces, handle that separately.\n+\t\t */\n+\t\tif (wdev-\u003eiftype == NL80211_IFTYPE_NAN)\n+\t\t\tschedule_work(\u0026rdev-\u003ereg_leave_nan_wk);\n+\t\telse\n+\t\t\tcfg80211_leave_locked(rdev, wdev, -1);\n+\t}\n }\n \n static void reg_check_chans_work(struct work_struct *work)\n@@ -2466,12 +2499,13 @@ static void reg_check_chans_work(struct work_struct *work)\n \tstruct cfg80211_registered_device *rdev;\n \n \tpr_debug(\"Verifying active interfaces after reg change\\n\");\n-\trtnl_lock();\n \n-\tfor_each_rdev(rdev)\n-\t\treg_leave_invalid_chans(\u0026rdev-\u003ewiphy);\n+\trcu_read_lock();\n \n-\trtnl_unlock();\n+\tlist_for_each_entry_rcu(rdev, \u0026cfg80211_rdev_list, list)\n+\t\twiphy_work_queue(\u0026rdev-\u003ewiphy, \u0026rdev-\u003ereg_check_chans_wk);\n+\n+\trcu_read_unlock();\n }\n \n void reg_check_channels(void)\ndiff --git a/net/wireless/reg.h b/net/wireless/reg.h\nindex fc31c5f9a61ab..c587079ead8fb 100644\n--- a/net/wireless/reg.h\n+++ b/net/wireless/reg.h\n@@ -178,6 +178,22 @@ int reg_reload_regdb(void);\n  */\n void reg_check_channels(void);\n \n+/**\n+ * reg_leave_invalid_chans_wk - check if channels are no longer usable and leave\n+ * @wiphy: the wiphy to check\n+ * @work: the work struct\n+ */\n+void reg_leave_invalid_chans_wk(struct wiphy *wiphy, struct wiphy_work *work);\n+\n+/**\n+ * reg_leave_invalid_nan_wk - check channels and tear down NAN when unusable\n+ * @work: the work struct\n+ *\n+ * Stopping a NAN interface needs the RTNL, so it cannot be done from\n+ * reg_leave_invalid_chans_wk() which runs with the wiphy mutex held.\n+ */\n+void reg_leave_invalid_nan_wk(struct work_struct *work);\n+\n extern const u8 shipped_regdb_certs[];\n extern unsigned int shipped_regdb_certs_len;\n extern const u8 extra_regdb_certs[];\ndiff --git a/net/wireless/scan.c b/net/wireless/scan.c\nindex 9e934b185e341..caa9c6495f20c 100644\n--- a/net/wireless/scan.c\n+++ b/net/wireless/scan.c\n@@ -1114,6 +1114,21 @@ int cfg80211_scan(struct cfg80211_registered_device *rdev)\n \treturn 0;\n }\n \n+/*\n+ * Release the scan request, but free it only if the driver is also done,\n+ * e.g. mac80211 may cancel it asynchronously and still use it.\n+ */\n+static void cfg80211_put_scan_req(struct cfg80211_scan_request_int *req)\n+{\n+\tif (!req)\n+\t\treturn;\n+\n+\tif (req-\u003edriver_owns)\n+\t\treq-\u003estale = true;\n+\telse\n+\t\tkfree(req);\n+}\n+\n void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,\n \t\t\t   bool send_message)\n {\n@@ -1173,10 +1188,10 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,\n \n \tdev_put(wdev-\u003enetdev);\n \n-\tkfree(rdev-\u003eint_scan_req);\n+\tcfg80211_put_scan_req(rdev-\u003eint_scan_req);\n \trdev-\u003eint_scan_req = NULL;\n \n-\tkfree(rdev-\u003escan_req);\n+\tcfg80211_put_scan_req(rdev-\u003escan_req);\n \trdev-\u003escan_req = NULL;\n \n \tif (!send_message)\n@@ -1199,6 +1214,18 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request,\n \tstruct cfg80211_scan_info old_info = intreq-\u003einfo;\n \n \ttrace_cfg80211_scan_done(intreq, info);\n+\n+\tintreq-\u003edriver_owns = false;\n+\n+\tif (intreq-\u003estale) {\n+\t\t/*\n+\t\t * The scan is already completed as far as we're concerned,\n+\t\t * it was just kept around for the driver - done now, free it.\n+\t\t */\n+\t\tkfree(intreq);\n+\t\treturn;\n+\t}\n+\n \tWARN_ON(intreq != rdev-\u003escan_req \u0026\u0026\n \t\tintreq != rdev-\u003eint_scan_req);\n \n@@ -2050,6 +2077,13 @@ __cfg80211_bss_update(struct cfg80211_registered_device *rdev,\n \t\t\tif (!hidden)\n \t\t\t\thidden = rb_find_bss(rdev, tmp,\n \t\t\t\t\t\t     BSS_CMP_HIDE_NUL);\n+\t\t\t/*\n+\t\t\t * Only group with an entry with beacon data, otherwise\n+\t\t\t * beacon data can never be filled/updated.\n+\t\t\t */\n+\t\t\tif (hidden \u0026\u0026\n+\t\t\t    !rcu_access_pointer(hidden-\u003epub.beacon_ies))\n+\t\t\t\thidden = NULL;\n \t\t\tif (hidden) {\n \t\t\t\tnew-\u003epub.hidden_beacon_bss = \u0026hidden-\u003epub;\n \t\t\t\tlist_add(\u0026new-\u003ehidden_list,\n@@ -3468,11 +3502,6 @@ void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,\n \tcbss-\u003epub.channel = chan;\n \n \tlist_for_each_entry(bss, \u0026rdev-\u003ebss_list, list) {\n-\t\tif (!cfg80211_bss_type_match(bss-\u003epub.capability,\n-\t\t\t\t\t     bss-\u003epub.channel-\u003eband,\n-\t\t\t\t\t     wdev-\u003econn_bss_type))\n-\t\t\tcontinue;\n-\n \t\tif (bss == cbss)\n \t\t\tcontinue;\n \ndiff --git a/net/wireless/util.c b/net/wireless/util.c\nindex 3e584d0ca3e26..f2464d2ce0d5a 100644\n--- a/net/wireless/util.c\n+++ b/net/wireless/util.c\n@@ -1039,12 +1039,30 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb,\n \t}\n \n \tswitch (skb-\u003eprotocol) {\n-\tcase htons(ETH_P_IP):\n-\t\tdscp = ipv4_get_dsfield(ip_hdr(skb)) \u0026 0xfc;\n+\tcase htons(ETH_P_IP): {\n+\t\tconst struct iphdr *iph;\n+\t\tstruct iphdr _iph;\n+\n+\t\tiph = skb_header_pointer(skb, sizeof(struct ethhdr),\n+\t\t\t\t\t sizeof(*iph), \u0026_iph);\n+\t\tif (!iph)\n+\t\t\treturn 0;\n+\n+\t\tdscp = ipv4_get_dsfield(iph) \u0026 0xfc;\n \t\tbreak;\n-\tcase htons(ETH_P_IPV6):\n-\t\tdscp = ipv6_get_dsfield(ipv6_hdr(skb)) \u0026 0xfc;\n+\t}\n+\tcase htons(ETH_P_IPV6): {\n+\t\tconst struct ipv6hdr *ip6h;\n+\t\tstruct ipv6hdr _ip6h;\n+\n+\t\tip6h = skb_header_pointer(skb, sizeof(struct ethhdr),\n+\t\t\t\t\t  sizeof(*ip6h), \u0026_ip6h);\n+\t\tif (!ip6h)\n+\t\t\treturn 0;\n+\n+\t\tdscp = ipv6_get_dsfield(ip6h) \u0026 0xfc;\n \t\tbreak;\n+\t}\n \tcase htons(ETH_P_MPLS_UC):\n \tcase htons(ETH_P_MPLS_MC): {\n \t\tstruct mpls_label mpls_tmp, *mpls;\n@@ -1217,8 +1235,7 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)\n \t\t\t\t\t\t!ev-\u003edc.locally_generated);\n \t\t\tbreak;\n \t\tcase EVENT_IBSS_JOINED:\n-\t\t\t__cfg80211_ibss_joined(wdev-\u003enetdev, ev-\u003eij.bssid,\n-\t\t\t\t\t       ev-\u003eij.channel);\n+\t\t\t__cfg80211_ibss_joined(wdev-\u003enetdev, ev-\u003eij.bss);\n \t\t\tbreak;\n \t\tcase EVENT_STOPPED:\n \t\t\t/*\n@@ -2477,16 +2494,15 @@ static void cfg80211_calculate_bi_data(struct wiphy *wiphy, u32 new_beacon_int,\n \t\tif (wdev-\u003evalid_links)\n \t\t\tcontinue;\n \n+\t\twdev_bi = cfg80211_wdev_bi(wdev);\n+\t\tif (!wdev_bi)\n+\t\t\tcontinue;\n+\n \t\t/* skip wdevs not active on the given wiphy radio */\n \t\tif (radio_idx \u003e= 0 \u0026\u0026\n \t\t    !(rdev_get_radio_mask(rdev, wdev-\u003enetdev) \u0026 BIT(radio_idx)))\n \t\t\tcontinue;\n \n-\t\twdev_bi = cfg80211_wdev_bi(wdev);\n-\n-\t\tif (!wdev_bi)\n-\t\t\tcontinue;\n-\n \t\tif (!*beacon_int_gcd) {\n \t\t\t*beacon_int_gcd = wdev_bi;\n \t\t\tcontinue;\n"
}

2/1 2026/09/06 01:50 agent patch-evaluator
0m Model:
core

Args:
null

Results:
{
  "EnableConfigs": [
    "CFG80211",
    "CFG80211_WEXT",
    "MAC80211_HWSIM"
  ],
  "FocusSymbols": [
    "cfg80211_scan_done",
    "___cfg80211_scan_done",
    "reg_leave_invalid_chans_wk",
    "reg_leave_invalid_nan_wk",
    "reg_check_chans_work",
    "cfg80211_ibss_joined",
    "__cfg80211_ibss_joined",
    "cfg80211_classify8021d",
    "__cfg80211_bss_update",
    "cfg80211_update_assoc_bss_entry",
    "cfg80211_stop_p2p_device",
    "cfg80211_process_wdev_events"
  ],
  "Reasoning": "The patch introduces changes to cfg80211 scan request lifecycle management (handling asynchronous driver cancellation and stale request freeing), refactors regulatory channel leave workers and locking (splitting NAN teardown into separate work with RTNL lock vs wiphy work), updates BSS object refcounting during IBSS join events, and improves header parsing safety in cfg80211_classify8021d using skb_header_pointer. All modified paths are executable and reachable via nl80211/cfg80211 user-space netlink commands and simulated wireless hardware (mac80211_hwsim) in standard QEMU environments.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit bc6715e01ed071bb16441193b4cd75c547bd2b62
Author: syz-cluster <triage@syzkaller.com>
Date:   Sun Sep 6 01:50:14 2026 +0000

    syz-cluster: applied patch under review

diff --git a/net/wireless/core.c b/net/wireless/core.c
index d13310fef691a..668380deec7da 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -244,9 +244,8 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
 	rdev->opencount--;
 
 	if (rdev->scan_req && rdev->scan_req->req.wdev == wdev) {
-		if (WARN_ON(!rdev->scan_req->notified &&
-			    (!rdev->int_scan_req ||
-			     !rdev->int_scan_req->notified)))
+		if (!rdev->scan_req->notified &&
+		    (!rdev->int_scan_req || !rdev->int_scan_req->notified))
 			rdev->scan_req->info.aborted = true;
 		___cfg80211_scan_done(rdev, false);
 	}
@@ -646,6 +645,8 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
 	INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk);
 	wiphy_work_init(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk);
 	INIT_WORK(&rdev->sched_scan_res_wk, cfg80211_sched_scan_results_wk);
+	wiphy_work_init(&rdev->reg_check_chans_wk, reg_leave_invalid_chans_wk);
+	INIT_WORK(&rdev->reg_leave_nan_wk, reg_leave_invalid_nan_wk);
 	INIT_WORK(&rdev->propagate_radar_detect_wk,
 		  cfg80211_propagate_radar_detect_wk);
 	INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk);
@@ -1345,6 +1346,7 @@ void wiphy_unregister(struct wiphy *wiphy)
 	cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
 	cancel_delayed_work_sync(&rdev->background_cac_done_wk);
 	flush_work(&rdev->destroy_work);
+	flush_work(&rdev->reg_leave_nan_wk);
 	flush_work(&rdev->propagate_radar_detect_wk);
 	flush_work(&rdev->propagate_cac_done_wk);
 	flush_work(&rdev->mgmt_registrations_update_wk);
@@ -1758,9 +1760,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
 		wiphy_lock(&rdev->wiphy);
 		cfg80211_update_iface_num(rdev, wdev->iftype, -1);
 		if (rdev->scan_req && rdev->scan_req->req.wdev == wdev) {
-			if (WARN_ON(!rdev->scan_req->notified &&
-				    (!rdev->int_scan_req ||
-				     !rdev->int_scan_req->notified)))
+			if (!rdev->scan_req->notified &&
+			    (!rdev->int_scan_req ||
+			     !rdev->int_scan_req->notified))
 				rdev->scan_req->info.aborted = true;
 			___cfg80211_scan_done(rdev, false);
 		}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index b4610f6685dcb..6138d207caf41 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -24,6 +24,16 @@
 struct cfg80211_scan_request_int {
 	struct cfg80211_scan_info info;
 	bool notified;
+	/*
+	 * set while the request is handed to the driver, i.e. between
+	 * rdev_scan() and cfg80211_scan_done()
+	 */
+	bool driver_owns;
+	/*
+	 * set when cfg80211 is done with the request but the driver still
+	 * owns it, so that cfg80211_scan_done() knows to just free it
+	 */
+	bool stale;
 	/* must be last - variable members */
 	struct cfg80211_scan_request req;
 };
@@ -104,6 +114,8 @@ struct cfg80211_registered_device {
 	struct work_struct destroy_work;
 	struct wiphy_work sched_scan_stop_wk;
 	struct work_struct sched_scan_res_wk;
+	struct wiphy_work reg_check_chans_wk;
+	struct work_struct reg_leave_nan_wk;
 
 	struct cfg80211_chan_def radar_chandef;
 	struct work_struct propagate_radar_detect_wk;
@@ -280,8 +292,7 @@ struct cfg80211_event {
 			bool locally_generated;
 		} dc;
 		struct {
-			u8 bssid[ETH_ALEN];
-			struct ieee80211_channel *channel;
+			struct cfg80211_bss *bss;
 		} ij;
 		struct {
 			u8 peer_addr[ETH_ALEN];
@@ -344,8 +355,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 void cfg80211_clear_ibss(struct net_device *dev, bool nowext);
 int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
 			struct net_device *dev, bool nowext);
-void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
-			    struct ieee80211_channel *channel);
+void __cfg80211_ibss_joined(struct net_device *dev, struct cfg80211_bss *bss);
 int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
 			    struct wireless_dev *wdev);
 
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index b1d748bdb504e..7f6779d326b81 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -16,26 +16,18 @@
 #include "rdev-ops.h"
 
 
-void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
-			    struct ieee80211_channel *channel)
+void __cfg80211_ibss_joined(struct net_device *dev, struct cfg80211_bss *bss)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
-	struct cfg80211_bss *bss;
 #ifdef CONFIG_CFG80211_WEXT
 	union iwreq_data wrqu;
 #endif
 
 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
-		return;
+		goto put_bss;
 
 	if (!wdev->u.ibss.ssid_len)
-		return;
-
-	bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,
-			       IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
-
-	if (WARN_ON(!bss))
-		return;
+		goto put_bss;
 
 	if (wdev->u.ibss.current_bss) {
 		cfg80211_unhold_bss(wdev->u.ibss.current_bss);
@@ -43,17 +35,22 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
 	}
 
 	cfg80211_hold_bss(bss_from_pub(bss));
+	/* the reference from the event is transferred to current_bss */
 	wdev->u.ibss.current_bss = bss_from_pub(bss);
 
 	cfg80211_upload_connect_keys(wdev);
 
-	nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bssid,
+	nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bss->bssid,
 				GFP_KERNEL);
 #ifdef CONFIG_CFG80211_WEXT
 	memset(&wrqu, 0, sizeof(wrqu));
-	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
+	memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN);
 	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
 #endif
+	return;
+
+put_bss:
+	cfg80211_put_bss(wdev->wiphy, bss);
 }
 
 void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
@@ -62,6 +59,7 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 	struct cfg80211_event *ev;
+	struct cfg80211_bss *bss;
 	unsigned long flags;
 
 	trace_cfg80211_ibss_joined(dev, bssid, channel);
@@ -69,13 +67,19 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
 	if (WARN_ON(!channel))
 		return;
 
+	bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,
+			       IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
+	if (WARN_ON(!bss))
+		return;
+
 	ev = kzalloc_obj(*ev, gfp);
-	if (!ev)
+	if (!ev) {
+		cfg80211_put_bss(wdev->wiphy, bss);
 		return;
+	}
 
 	ev->type = EVENT_IBSS_JOINED;
-	memcpy(ev->ij.bssid, bssid, ETH_ALEN);
-	ev->ij.channel = channel;
+	ev->ij.bss = bss;
 
 	spin_lock_irqsave(&wdev->event_lock, flags);
 	list_add_tail(&ev->list, &wdev->event_list);
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 46849fe8d0b39..adcfd0278da32 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -464,7 +464,10 @@ static inline int rdev_scan(struct cfg80211_registered_device *rdev,
 		return -EINVAL;
 
 	trace_rdev_scan(&rdev->wiphy, request);
+	request->driver_owns = true;
 	ret = rdev->ops->scan(&rdev->wiphy, &request->req);
+	if (ret)
+		request->driver_owns = false;
 	trace_rdev_return_int(&rdev->wiphy, ret);
 	return ret;
 }
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index a8336baf85dc6..11665e0a7efc8 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -2345,7 +2345,7 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
 	iftype = wdev->iftype;
 
 	/* make sure the interface is active */
-	if (!wdev->netdev || !netif_running(wdev->netdev))
+	if (!wdev_running(wdev))
 		return true;
 
 	/* NAN doesn't have links, handle it separately */
@@ -2446,19 +2446,52 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
 	return true;
 }
 
-static void reg_leave_invalid_chans(struct wiphy *wiphy)
+void reg_leave_invalid_nan_wk(struct work_struct *work)
 {
+	struct cfg80211_registered_device *rdev;
 	struct wireless_dev *wdev;
-	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+	rdev = container_of(work, struct cfg80211_registered_device,
+			    reg_leave_nan_wk);
+
+	/* stopping NAN closes its data interfaces, which needs the RTNL */
+	rtnl_lock();
 
 	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
 		bool valid;
 
-		scoped_guard(wiphy, wiphy)
-			valid = reg_wdev_chan_valid(wiphy, wdev);
+		if (wdev->iftype != NL80211_IFTYPE_NAN)
+			continue;
+
+		scoped_guard(wiphy, &rdev->wiphy)
+			valid = reg_wdev_chan_valid(&rdev->wiphy, wdev);
 		if (!valid)
 			cfg80211_leave(rdev, wdev, -1);
 	}
+
+	rtnl_unlock();
+}
+
+void reg_leave_invalid_chans_wk(struct wiphy *wiphy, struct wiphy_work *work)
+{
+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+	struct wireless_dev *wdev;
+
+	lockdep_assert_held(&wiphy->mtx);
+
+	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+		if (reg_wdev_chan_valid(wiphy, wdev))
+			continue;
+
+		/*
+		 * Tearing down NAN needs the RTNL for closing NAN_DATA
+		 * interfaces, handle that separately.
+		 */
+		if (wdev->iftype == NL80211_IFTYPE_NAN)
+			schedule_work(&rdev->reg_leave_nan_wk);
+		else
+			cfg80211_leave_locked(rdev, wdev, -1);
+	}
 }
 
 static void reg_check_chans_work(struct work_struct *work)
@@ -2466,12 +2499,13 @@ static void reg_check_chans_work(struct work_struct *work)
 	struct cfg80211_registered_device *rdev;
 
 	pr_debug("Verifying active interfaces after reg change\n");
-	rtnl_lock();
 
-	for_each_rdev(rdev)
-		reg_leave_invalid_chans(&rdev->wiphy);
+	rcu_read_lock();
 
-	rtnl_unlock();
+	list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
+		wiphy_work_queue(&rdev->wiphy, &rdev->reg_check_chans_wk);
+
+	rcu_read_unlock();
 }
 
 void reg_check_channels(void)
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index fc31c5f9a61ab..c587079ead8fb 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -178,6 +178,22 @@ int reg_reload_regdb(void);
  */
 void reg_check_channels(void);
 
+/**
+ * reg_leave_invalid_chans_wk - check if channels are no longer usable and leave
+ * @wiphy: the wiphy to check
+ * @work: the work struct
+ */
+void reg_leave_invalid_chans_wk(struct wiphy *wiphy, struct wiphy_work *work);
+
+/**
+ * reg_leave_invalid_nan_wk - check channels and tear down NAN when unusable
+ * @work: the work struct
+ *
+ * Stopping a NAN interface needs the RTNL, so it cannot be done from
+ * reg_leave_invalid_chans_wk() which runs with the wiphy mutex held.
+ */
+void reg_leave_invalid_nan_wk(struct work_struct *work);
+
 extern const u8 shipped_regdb_certs[];
 extern unsigned int shipped_regdb_certs_len;
 extern const u8 extra_regdb_certs[];
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 9e934b185e341..caa9c6495f20c 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1114,6 +1114,21 @@ int cfg80211_scan(struct cfg80211_registered_device *rdev)
 	return 0;
 }
 
+/*
+ * Release the scan request, but free it only if the driver is also done,
+ * e.g. mac80211 may cancel it asynchronously and still use it.
+ */
+static void cfg80211_put_scan_req(struct cfg80211_scan_request_int *req)
+{
+	if (!req)
+		return;
+
+	if (req->driver_owns)
+		req->stale = true;
+	else
+		kfree(req);
+}
+
 void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
 			   bool send_message)
 {
@@ -1173,10 +1188,10 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
 
 	dev_put(wdev->netdev);
 
-	kfree(rdev->int_scan_req);
+	cfg80211_put_scan_req(rdev->int_scan_req);
 	rdev->int_scan_req = NULL;
 
-	kfree(rdev->scan_req);
+	cfg80211_put_scan_req(rdev->scan_req);
 	rdev->scan_req = NULL;
 
 	if (!send_message)
@@ -1199,6 +1214,18 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request,
 	struct cfg80211_scan_info old_info = intreq->info;
 
 	trace_cfg80211_scan_done(intreq, info);
+
+	intreq->driver_owns = false;
+
+	if (intreq->stale) {
+		/*
+		 * The scan is already completed as far as we're concerned,
+		 * it was just kept around for the driver - done now, free it.
+		 */
+		kfree(intreq);
+		return;
+	}
+
 	WARN_ON(intreq != rdev->scan_req &&
 		intreq != rdev->int_scan_req);
 
@@ -2050,6 +2077,13 @@ __cfg80211_bss_update(struct cfg80211_registered_device *rdev,
 			if (!hidden)
 				hidden = rb_find_bss(rdev, tmp,
 						     BSS_CMP_HIDE_NUL);
+			/*
+			 * Only group with an entry with beacon data, otherwise
+			 * beacon data can never be filled/updated.
+			 */
+			if (hidden &&
+			    !rcu_access_pointer(hidden->pub.beacon_ies))
+				hidden = NULL;
 			if (hidden) {
 				new->pub.hidden_beacon_bss = &hidden->pub;
 				list_add(&new->hidden_list,
@@ -3468,11 +3502,6 @@ void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
 	cbss->pub.channel = chan;
 
 	list_for_each_entry(bss, &rdev->bss_list, list) {
-		if (!cfg80211_bss_type_match(bss->pub.capability,
-					     bss->pub.channel->band,
-					     wdev->conn_bss_type))
-			continue;
-
 		if (bss == cbss)
 			continue;
 
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 3e584d0ca3e26..f2464d2ce0d5a 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1039,12 +1039,30 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb,
 	}
 
 	switch (skb->protocol) {
-	case htons(ETH_P_IP):
-		dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc;
+	case htons(ETH_P_IP): {
+		const struct iphdr *iph;
+		struct iphdr _iph;
+
+		iph = skb_header_pointer(skb, sizeof(struct ethhdr),
+					 sizeof(*iph), &_iph);
+		if (!iph)
+			return 0;
+
+		dscp = ipv4_get_dsfield(iph) & 0xfc;
 		break;
-	case htons(ETH_P_IPV6):
-		dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & 0xfc;
+	}
+	case htons(ETH_P_IPV6): {
+		const struct ipv6hdr *ip6h;
+		struct ipv6hdr _ip6h;
+
+		ip6h = skb_header_pointer(skb, sizeof(struct ethhdr),
+					  sizeof(*ip6h), &_ip6h);
+		if (!ip6h)
+			return 0;
+
+		dscp = ipv6_get_dsfield(ip6h) & 0xfc;
 		break;
+	}
 	case htons(ETH_P_MPLS_UC):
 	case htons(ETH_P_MPLS_MC): {
 		struct mpls_label mpls_tmp, *mpls;
@@ -1217,8 +1235,7 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)
 						!ev->dc.locally_generated);
 			break;
 		case EVENT_IBSS_JOINED:
-			__cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid,
-					       ev->ij.channel);
+			__cfg80211_ibss_joined(wdev->netdev, ev->ij.bss);
 			break;
 		case EVENT_STOPPED:
 			/*
@@ -2477,16 +2494,15 @@ static void cfg80211_calculate_bi_data(struct wiphy *wiphy, u32 new_beacon_int,
 		if (wdev->valid_links)
 			continue;
 
+		wdev_bi = cfg80211_wdev_bi(wdev);
+		if (!wdev_bi)
+			continue;
+
 		/* skip wdevs not active on the given wiphy radio */
 		if (radio_idx >= 0 &&
 		    !(rdev_get_radio_mask(rdev, wdev->netdev) & BIT(radio_idx)))
 			continue;
 
-		wdev_bi = cfg80211_wdev_bi(wdev);
-
-		if (!wdev_bi)
-			continue;
-
 		if (!*beacon_int_gcd) {
 			*beacon_int_gcd = wdev_bi;
 			continue;

3/2 2026/09/06 01:50 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 9367
							output: 31
							thoughts: 813
						

Thoughts:
**Analyzing the Patch for Fuzzing Potential**

Okay, so the task is to determine if this patch is a good candidate for fuzzing with syzkaller. My initial thought is to dive into the details, as always. Looking at the changes, I see we're dealing with the `net/wireless` subsystem, specifically `cfg80211`. That's interesting, and also a good starting point for evaluating the vulnerability potential of the patch. The files touched span a decent chunk of the `cfg80211` code, which always warrants a closer look.

Let's break down the changes: First, the removal of `WARN_ON` in scan abort handling. That's a classic signal of potentially relaxed error checks; my spidey senses are tingling a bit there. Second, the initialization/flushing of `reg_check_chans_wk` and `reg_leave_nan_wk` in `wiphy_new_nm` suggests changes to work queue management, which is always a place to watch closely, especially in a network context. Third, the scan request lifetime management is being overhauled with `driver_owns` and `stale` flags. That sounds like a potential race condition or use-after-free scenario waiting to happen. The logic around allocating and freeing scan requests is often tricky.

The fourth change, which has to do with how `cfg80211_bss` references are being handled and their related reference counting being tightened, is another area to focus on. Fifth, the channel invalidation split and workqueue change from RTNL locking to RCU in `reg.c` is significant. That's a good move to improve concurrency, but it also opens the door to potential subtle issues if not implemented perfectly. The use of `wdev_running(wdev)` suggests some related changes in the runtime state of a wireless device. Sixth and seventh the changes in `scan.c` and `util.c` look like incremental changes with potential impact.

Now, the crucial question: Can we actually fuzz this with syzkaller? The good news is, from what I know, `cfg80211`, `mac80211`, and associated components are indeed fuzzable in a standard QEMU/syzkaller environment. The `mac80211_hwsim` interface, NL80211, and simulated wireless interfaces are all heavily used in syzkaller. That's a big plus, but it is important to double-check the Kconfig options.

Okay, I'm going to start running some greps or code inspections to confirm. I think this patch is definitely worth a shot, and potentially quite interesting to throw some fuzzing at.



4/2 2026/09/06 01:50 tool grepper
0m Args:
{
  "Expression": "config CFG80211",
  "PathPrefix": "net/wireless"
}

Results:
{
  "Output": "net/wireless/Kconfig=14=config WEXT_PRIV\n--\nnet/wireless/Kconfig-16-\nnet/wireless/Kconfig:17:config CFG80211\nnet/wireless/Kconfig-18-\ttristate \"cfg80211 - wireless configuration API\"\n--\nnet/wireless/Kconfig=38=config NL80211_TESTMODE\n--\nnet/wireless/Kconfig-51-\nnet/wireless/Kconfig:52:config CFG80211_DEVELOPER_WARNINGS\nnet/wireless/Kconfig-53-\tbool \"enable developer warnings\"\n--\nnet/wireless/Kconfig-67-\nnet/wireless/Kconfig:68:config CFG80211_CERTIFICATION_ONUS\nnet/wireless/Kconfig-69-\tbool \"cfg80211 certification onus\"\n--\nnet/wireless/Kconfig-88-\nnet/wireless/Kconfig:89:config CFG80211_REQUIRE_SIGNED_REGDB\nnet/wireless/Kconfig-90-\tbool \"require regdb signature\" if CFG80211_CERTIFICATION_ONUS\n--\nnet/wireless/Kconfig-98-\nnet/wireless/Kconfig:99:config CFG80211_USE_KERNEL_REGDB_KEYS\nnet/wireless/Kconfig-100-\tbool \"allow regdb keys shipped with the kernel\" if CFG80211_CERTIFICATION_ONUS\n--\nnet/wireless/Kconfig-110-\nnet/wireless/Kconfig:111:config CFG80211_EXTRA_REGDB_KEYDIR\nnet/wireless/Kconfig-112-\tstring \"additional regdb key directory\" if CFG80211_CERTIFICATION_ONUS\n--\nnet/wireless/Kconfig-121-\nnet/wireless/Kconfig:122:config CFG80211_REG_CELLULAR_HINTS\nnet/wireless/Kconfig-123-\tbool \"cfg80211 regulatory support for cellular base station hints\"\n--\nnet/wireless/Kconfig-134-\nnet/wireless/Kconfig:135:config CFG80211_REG_RELAX_NO_IR\nnet/wireless/Kconfig-136-\tbool \"cfg80211 support for NO_IR relaxation\"\n--\nnet/wireless/Kconfig-158-\nnet/wireless/Kconfig:159:config CFG80211_DEFAULT_PS\nnet/wireless/Kconfig-160-\tbool \"enable powersave by default\"\n--\nnet/wireless/Kconfig-168-\nnet/wireless/Kconfig:169:config CFG80211_DEBUGFS\nnet/wireless/Kconfig-170-\tbool \"cfg80211 DebugFS entries\"\n--\nnet/wireless/Kconfig-176-\nnet/wireless/Kconfig:177:config CFG80211_CRDA_SUPPORT\nnet/wireless/Kconfig-178-\tbool \"support CRDA\" if EXPERT\n--\nnet/wireless/Kconfig-186-\nnet/wireless/Kconfig:187:config CFG80211_WEXT\nnet/wireless/Kconfig-188-\tbool \"cfg80211 wireless extensions compatibility\"\n--\nnet/wireless/Kconfig-193-\nnet/wireless/Kconfig:194:config CFG80211_KUNIT_TEST\nnet/wireless/Kconfig-195-\ttristate \"KUnit tests for cfg80211\" if !KUNIT_ALL_TESTS\n"
}

5/2 2026/09/06 01:50 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 11707
							output: 32
							thoughts: 16
						

Thoughts:
Here's a summary of my thought process:

**Kconfig Dependency Check**

Okay, time to dive in. I need to make sure I haven't missed anything, so my first instinct is to thoroughly check the Kconfig files. Specifically, I need to understand the configuration dependencies of CFG80211. What features rely on it? Are there any hidden, indirect dependencies I might overlook? A detailed review here will help ensure the entire system is configured as expected and avoid any runtime surprises later. This is a critical step in nailing down all the connections.



6/2 2026/09/06 01:50 tool read-file
0m Args:
{
  "File": "net/wireless/Kconfig",
  "FirstLine": 1,
  "LineCount": 40
}

Results:
{
  "Contents": "   1:\t# SPDX-License-Identifier: GPL-2.0-only\n   2:\tconfig WIRELESS_EXT\n   3:\t\tbool\n   4:\t\n   5:\tconfig WEXT_CORE\n   6:\t\tdef_bool y\n   7:\t\tdepends on CFG80211_WEXT || WIRELESS_EXT\n   8:\t\n   9:\tconfig WEXT_PROC\n  10:\t\tdef_bool y\n  11:\t\tdepends on PROC_FS\n  12:\t\tdepends on WEXT_CORE\n  13:\t\n  14:\tconfig WEXT_PRIV\n  15:\t\tbool\n  16:\t\n  17:\tconfig CFG80211\n  18:\t\ttristate \"cfg80211 - wireless configuration API\"\n  19:\t\tdepends on RFKILL || !RFKILL\n  20:\t\tselect FW_LOADER\n  21:\t\tselect CRC32\n  22:\t\t# may need to update this when certificates are changed and are\n  23:\t\t# using a different algorithm, though right now they shouldn't\n  24:\t\t# (this is here rather than below to allow it to be a module)\n  25:\t\tselect CRYPTO_SHA256 if CFG80211_USE_KERNEL_REGDB_KEYS\n  26:\t\thelp\n  27:\t\t  cfg80211 is the Linux wireless LAN (802.11) configuration API.\n  28:\t\t  Enable this if you have a wireless device.\n  29:\t\n  30:\t\t  For more information refer to documentation on the wireless wiki:\n  31:\t\n  32:\t\t  https://wireless.wiki.kernel.org/en/developers/Documentation/cfg80211\n  33:\t\n  34:\t\t  When built as a module it will be called cfg80211.\n  35:\t\n  36:\tif CFG80211\n  37:\t\n  38:\tconfig NL80211_TESTMODE\n  39:\t\tbool \"nl80211 testmode command\"\n  40:\t\thelp\n"
}

7/2 2026/09/06 01:50 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 12413
							output: 36
							thoughts: 21
						

Thoughts:
Here's my take on those thoughts:

**Checking the Hardware Simulation Configuration**

Okay, before I go any further down this troubleshooting path, I should really double-check the `mac80211_hwsim` Kconfig settings. I've been focusing on the higher-level drivers, but a misconfiguration there could be the root cause of the issue. A simple oversight in the simulation environment's build options could explain the odd behavior I'm seeing. It's a fundamental step, but one that could save me a lot of time and potential headaches. Let's make sure that's all squared away before digging deeper. That hardware simulation configuration needs to be verified.



8/2 2026/09/06 01:50 tool grepper
0m Args:
{
  "Expression": "config MAC80211_HWSIM",
  "PathPrefix": "drivers/net/wireless"
}

Results:
{
  "Output": "drivers/net/wireless/virtual/Kconfig:1:config MAC80211_HWSIM\ndrivers/net/wireless/virtual/Kconfig-2-\ttristate \"Simulated radio testing tool for mac80211\"\n"
}

9/2 2026/09/06 01:50 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 12684
							output: 35
							thoughts: 598
						

Thoughts:
**Focus Symbol Analysis for the Patch**

Okay, let's break this down systematically. My immediate goal is to identify the most relevant functions to include as FocusSymbols for this patch. I'll need to focus on the symbols that are either public or otherwise significant for understanding the patch's changes.

First, I've got a comprehensive list of modified functions. This looks like a reasonable starting point:

*   `cfg80211_stop_p2p_device`
*   `wiphy_new_nm`
*   `wiphy_unregister`
*   `cfg80211_netdev_notifier_call`
*   `__cfg80211_ibss_joined`
*   `cfg80211_ibss_joined`
*   `rdev_scan` (inline) - I'll need to check `scan.c` for call sites or related functions.
*   `reg_wdev_chan_valid`
*   `reg_leave_invalid_nan_wk`
*   `reg_leave_invalid_chans_wk`
*   `reg_check_chans_work`
*   `cfg80211_put_scan_req` (static) - I need to double-check this one.
*   `___cfg80211_scan_done`
*   `cfg80211_scan_done`
*   `__cfg80211_bss_update`
*   `cfg80211_update_assoc_bss_entry`
*   `cfg80211_classify8021d`
*   `cfg80211_process_wdev_events`
*   `cfg80211_calculate_bi_data`

Now, I'm going to quickly filter this list. I'll flag the static ones, like `cfg80211_put_scan_req` and `reg_check_chans_work`, as potentially less important unless they have significant internal dependencies or are called from outside. The inline `rdev_scan` will need further investigation.

Next, I'll turn my attention to the source files themselves to see where these functions reside: `scan.c`, `ibss.c`, `reg.c`, `util.c`, and `core.c` appear to be the key areas. I'm going to look at related functions to see what I might be missing.

Based on that, a smaller set of candidates emerges as particularly critical to highlight:

*   `cfg80211_scan_done`
*   `___cfg80211_scan_done`
*   `cfg80211_scan` (I need to ensure this is in the patch scope)
*   `reg_leave_invalid_nan_wk`
*   `reg_leave_invalid_chans_wk`
*   `reg_check_chans_work` / `reg_check_channels`
*   `cfg80211_ibss_joined`
*   `__cfg80211_ibss_joined`
*   `cfg80211_classify8021d`
*   `__cfg80211_bss_update`
*   `cfg80211_update_assoc_bss_entry`
*   `cfg80211_stop_p2p_device`
*   `cfg80211_netdev_notifier_call`

Finally, I'll do a quick check to make absolutely sure these symbols are actually present and defined within the updated codebase. That's the last verification I need to make before I'm satisfied.



10/2 2026/09/06 01:50 tool grepper
0m Args:
{
  "Expression": "void cfg80211_scan_done\\(",
  "PathPrefix": "net/wireless"
}

Results:
{
  "Output": "net/wireless/scan.c=1203=void __cfg80211_scan_done(struct wiphy *wiphy, struct wiphy_work *wk)\n--\nnet/wireless/scan.c-1207-\nnet/wireless/scan.c:1208:void cfg80211_scan_done(struct cfg80211_scan_request *request,\nnet/wireless/scan.c-1209-\t\t\tstruct cfg80211_scan_info *info)\n"
}

11/2 2026/09/06 01:50 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 14302
							output: 29
							thoughts: 28
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person:

**Investigating Potential Data Anomalies in Leave Registration**

Okay, time to dig into the leave registration data. I need to make sure we're handling missing or invalid values correctly, especially around leave requests. My initial focus has to be on two specific variables: `reg_leave_invalid_nan_wk` and `reg_leave_invalid_chans_wk`. I suspect these variables are key in flagging potentially problematic leave entries. I expect `reg_leave_invalid_nan_wk` to address weeks with missing numerical values that should be present and I also expect `reg_leave_invalid_chans_wk` to address weeks with incorrect or missing channel information tied to the leave request. Let's see what the data actually tells us about these variables. This needs a thorough investigation.



12/2 2026/09/06 01:50 tool grepper
0m Args:
{
  "Expression": "reg_leave_invalid_",
  "PathPrefix": "net/wireless"
}

Results:
{
  "Output": "net/wireless/core.c=548=struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,\n--\nnet/wireless/core.c-647-\tINIT_WORK(\u0026rdev-\u003esched_scan_res_wk, cfg80211_sched_scan_results_wk);\nnet/wireless/core.c:648:\twiphy_work_init(\u0026rdev-\u003ereg_check_chans_wk, reg_leave_invalid_chans_wk);\nnet/wireless/core.c:649:\tINIT_WORK(\u0026rdev-\u003ereg_leave_nan_wk, reg_leave_invalid_nan_wk);\nnet/wireless/core.c-650-\tINIT_WORK(\u0026rdev-\u003epropagate_radar_detect_wk,\n--\nnet/wireless/reg.c=2337=static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)\n--\nnet/wireless/reg.c-2448-\nnet/wireless/reg.c:2449:void reg_leave_invalid_nan_wk(struct work_struct *work)\nnet/wireless/reg.c-2450-{\n--\nnet/wireless/reg.c-2474-\nnet/wireless/reg.c:2475:void reg_leave_invalid_chans_wk(struct wiphy *wiphy, struct wiphy_work *work)\nnet/wireless/reg.c-2476-{\n--\nnet/wireless/reg.h=179=void reg_check_channels(void);\n--\nnet/wireless/reg.h-181-/**\nnet/wireless/reg.h:182: * reg_leave_invalid_chans_wk - check if channels are no longer usable and leave\nnet/wireless/reg.h-183- * @wiphy: the wiphy to check\n--\nnet/wireless/reg.h-185- */\nnet/wireless/reg.h:186:void reg_leave_invalid_chans_wk(struct wiphy *wiphy, struct wiphy_work *work);\nnet/wireless/reg.h-187-\nnet/wireless/reg.h-188-/**\nnet/wireless/reg.h:189: * reg_leave_invalid_nan_wk - check channels and tear down NAN when unusable\nnet/wireless/reg.h-190- * @work: the work struct\n--\nnet/wireless/reg.h-192- * Stopping a NAN interface needs the RTNL, so it cannot be done from\nnet/wireless/reg.h:193: * reg_leave_invalid_chans_wk() which runs with the wiphy mutex held.\nnet/wireless/reg.h-194- */\nnet/wireless/reg.h:195:void reg_leave_invalid_nan_wk(struct work_struct *work);\nnet/wireless/reg.h-196-\n"
}

13/2 2026/09/06 01:50 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 15293
							output: 34
							thoughts: 34
						

Thoughts:
Here's my take on those functions, thinking through the process:

**Diving Deep into BSS Updates**

Okay, so I need to refresh my understanding of how BSS entries are handled in the wireless stack, especially concerning association updates. Let's start by digging into `__cfg80211_bss_update` and `cfg80211_update_assoc_bss_entry`. These are critical for managing the information about access points and how our device associates with them, so I want a clear picture. I'm imagining `__cfg80211_bss_update` as the foundational function, the lower-level workhorse that likely does the bulk of the dirty work. I expect it to handle the core data structure modifications, the actual update of the BSS entry with new information. It's the point where things get concrete. From there I'm guessing `cfg80211_update_assoc_bss_entry` probably sits a level higher and makes decisions about what needs updating, calls `__cfg80211_bss_update` as needed, and perhaps deals with more association-specific logic. I'm going to want to understand what triggers the call to `__cfg80211_bss_update` and how the function handles concurrent access. Are there locks involved? What data structures are involved and how are those structures protected from race conditions? What if the same BSS entry is being updated from multiple sources? Then I'll have to see if the second function makes a separate check. It is most likely that this function is called from the association process or a related connection state. I'll need to focus on where the information comes from that is used to update the BSS entry: Is it from the firmware, the driver, or from the scan results? Then I'll check how they relate to the underlying regulatory database.



14/2 2026/09/06 01:50 tool grepper
0m Args:
{
  "Expression": "__cfg80211_bss_update",
  "PathPrefix": "net/wireless"
}

Results:
{
  "Output": "net/wireless/scan.c=2034=static struct cfg80211_internal_bss *\nnet/wireless/scan.c:2035:__cfg80211_bss_update(struct cfg80211_registered_device *rdev,\nnet/wireless/scan.c-2036-\t\t      struct cfg80211_internal_bss *tmp,\n--\nnet/wireless/scan.c=2145=cfg80211_bss_update(struct cfg80211_registered_device *rdev,\n--\nnet/wireless/scan.c-2151-\tspin_lock_bh(\u0026rdev-\u003ebss_lock);\nnet/wireless/scan.c:2152:\tres = __cfg80211_bss_update(rdev, tmp, signal_valid, ts);\nnet/wireless/scan.c-2153-\tspin_unlock_bh(\u0026rdev-\u003ebss_lock);\n--\nnet/wireless/scan.c=2325=cfg80211_inform_single_bss_data(struct wiphy *wiphy,\n--\nnet/wireless/scan.c-2439-\tspin_lock_bh(\u0026rdev-\u003ebss_lock);\nnet/wireless/scan.c:2440:\tres = __cfg80211_bss_update(rdev, \u0026tmp, signal_valid, ts);\nnet/wireless/scan.c-2441-\tif (!res)\n--\nnet/wireless/scan.c-2462-\ttrace_cfg80211_return_bss(\u0026res-\u003epub);\nnet/wireless/scan.c:2463:\t/* __cfg80211_bss_update gives us a referenced result */\nnet/wireless/scan.c-2464-\treturn \u0026res-\u003epub;\n"
}

15/2 2026/09/06 01:50 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 16228
							output: 36
							thoughts: 19
						

Thoughts:
Here's my summary, as though I'm thinking through this process:

**Diving into `cfg80211_classify8021d`**

Okay, so I need to take a look at `cfg80211_classify8021d`. This function is likely involved in the 802.1D classification process within the cfg80211 framework. Given the name, I'm betting it handles classifying traffic based on the 802.1D standard, probably related to VLAN tagging, priority bits, or some kind of quality of service (QoS) determination. I'll need to figure out *how* it's doing the classification. Is it a straightforward bitwise check? Does it utilize lookup tables or some more complex algorithm? What data structures are involved?

I should start by understanding the inputs to this function: what kind of packet information is it receiving? Is it the entire Ethernet frame, just the VLAN header, or something else? Then I need to see what the outputs are – is it returning a classification result, a priority value, or modifying some kind of internal state? I'll probably look for where it's called and the context in which it's used to get the complete picture. Hopefully, there are some decent comments within the function or surrounding code that will help. Knowing the internals will let me understand its interactions with the rest of the QoS flow, and I can be confident in any modifications I might need to make, or any potential performance bottlenecks. Let's see what we've got.



16/2 2026/09/06 01:50 tool grepper
0m Args:
{
  "Expression": "cfg80211_classify8021d",
  "PathPrefix": "net/wireless"
}

Results:
{
  "Output": "net/wireless/util.c=1012=EXPORT_SYMBOL(ieee80211_amsdu_to_8023s);\n--\nnet/wireless/util.c-1014-/* Given a data frame determine the 802.1p/1d tag to use. */\nnet/wireless/util.c:1015:unsigned int cfg80211_classify8021d(struct sk_buff *skb,\nnet/wireless/util.c-1016-\t\t\t\t    struct cfg80211_qos_map *qos_map)\n--\nnet/wireless/util.c-1166-}\nnet/wireless/util.c:1167:EXPORT_SYMBOL(cfg80211_classify8021d);\nnet/wireless/util.c-1168-\n"
}

17/2 2026/09/06 01:50 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 16836
							output: 36
							thoughts: 19
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Diving into `cfg80211_process_wdev_events`**

Okay, let's dissect `cfg80211_process_wdev_events`. I'm starting here because this is likely the central point for handling asynchronous wireless device events within the cfg80211 core, right? The goal is to understand how these events are queued, processed, and ultimately delivered to the relevant user-space components or kernel modules. I need to figure out the flow. I expect it to involve some sort of workqueue or tasklet mechanism, considering the need to decouple event generation from the context where the event occurs. I'm keen to see how concurrency is managed, especially when dealing with multiple wireless devices. Is there a lock protecting the event queue, and if so, what's its granularity? What about event ordering? Is there a guarantee events will be processed in a specific sequence, or is it handled in a "best effort" style? I need to analyze how different event types (e.g., scan results, connection status updates, regulatory domain changes) are handled and if there is a distinction between synchronous and asynchronous events. Finally, I'll need to confirm that this is where the `wiphy` structures come into play, as this is the central data structure in cfg80211 that represents the wireless device, and needs to be properly updated after processing.



18/2 2026/09/06 01:50 tool grepper
0m Args:
{
  "Expression": "cfg80211_process_wdev_events",
  "PathPrefix": "net/wireless"
}

Results:
{
  "Output": "net/wireless/core.c=1410=static void _cfg80211_unregister_wdev(struct wireless_dev *wdev,\n--\nnet/wireless/core.c-1464-\t */\nnet/wireless/core.c:1465:\tcfg80211_process_wdev_events(wdev);\nnet/wireless/core.c-1466-\n--\nnet/wireless/core.h=481=void cfg80211_process_wiphy_works(struct cfg80211_registered_device *rdev,\nnet/wireless/core.h-482-\t\t\t\t  struct wiphy_work *end);\nnet/wireless/core.h:483:void cfg80211_process_wdev_events(struct wireless_dev *wdev);\nnet/wireless/core.h-484-\n--\nnet/wireless/util.c=1181=void cfg80211_upload_connect_keys(struct wireless_dev *wdev)\n--\nnet/wireless/util.c-1208-\nnet/wireless/util.c:1209:void cfg80211_process_wdev_events(struct wireless_dev *wdev)\nnet/wireless/util.c-1210-{\n--\nnet/wireless/util.c=1265=void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev)\n--\nnet/wireless/util.c-1271-\tlist_for_each_entry(wdev, \u0026rdev-\u003ewiphy.wdev_list, list)\nnet/wireless/util.c:1272:\t\tcfg80211_process_wdev_events(wdev);\nnet/wireless/util.c-1273-}\n"
}

19/2 2026/09/06 01:50 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 17673
							output: 35
							thoughts: 18
						

Thoughts:
Here's a summary of my thought process, as if I were the one thinking it:

**Diving into `cfg80211_calculate_bi_data`**

Okay, time to dissect `cfg80211_calculate_bi_data`. This function is likely responsible for calculating the beacon interval data, which is fundamental for Wi-Fi communication – ensuring devices know when to listen for management frames and synchronize their transmissions. Given my expertise, I already have a pretty good mental model of how beacon intervals, DTIM periods, and related parameters are interconnected within the broader 802.11 framework. I'm immediately going to be looking for:

*   **Input parameters:** What data does it take as input? I expect things like channel information, supported rates, capabilities, and potentially settings related to power management or virtual interfaces to be critical. Knowing the input context is crucial to understanding the algorithm. I'll need to confirm my assumptions and mentally validate that the expected arguments are correctly supplied.
*   **Calculations:** What actual computations are performed? I'll be looking for where beacon interval, DTIM period, and other related fields are calculated and populated, and how these values are derived from any external configuration. Also, any optimization considerations will be in my focus.
*   **Error handling/Edge cases:** What does it do in anomalous situations? For example, what happens if the input is inconsistent, or if there's an unsupported rate combination? How does it handle zero values or unreasonable requests for intervals? How robust is the logic, specifically regarding the handling of exceptional or unexpected input?
*   **Return values/Side effects:** What does it return or modify? I assume it might be returning the calculated beacon data in a specific structure. Are there any side effects on any global variables? Does it interact with any other parts of the driver or kernel?
*   **Optimization Goals:** Is there any work done to improve performance or reduce power consumption?

I need to quickly grasp the core logic to understand its implications for overall performance and stability. I'll probably step through the code mentally, or with a debugger, if needed, making notes on anything that seems non-obvious or potentially problematic. Understanding the nuances of this function is going to be key to debugging any wireless issues.



20/2 2026/09/06 01:50 tool grepper
0m Args:
{
  "Expression": "cfg80211_calculate_bi_data",
  "PathPrefix": "net/wireless"
}

Results:
{
  "Output": "net/wireless/util.c=2460=static int cfg80211_wdev_bi(struct wireless_dev *wdev)\n--\nnet/wireless/util.c-2477-\nnet/wireless/util.c:2478:static void cfg80211_calculate_bi_data(struct wiphy *wiphy, u32 new_beacon_int,\nnet/wireless/util.c-2479-\t\t\t\t       u32 *beacon_int_gcd,\n--\nnet/wireless/util.c=2525=int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,\n--\nnet/wireless/util.c-2531-\t * to cfg80211_check_combinations(), in which case we'll validate more\nnet/wireless/util.c:2532:\t * through the cfg80211_calculate_bi_data() call and code in\nnet/wireless/util.c-2533-\t * cfg80211_iter_combinations().\n--\nnet/wireless/util.c=2542=int cfg80211_iter_combinations(struct wiphy *wiphy,\n--\nnet/wireless/util.c-2570-\t */\nnet/wireless/util.c:2571:\tcfg80211_calculate_bi_data(wiphy, params-\u003enew_beacon_int,\nnet/wireless/util.c-2572-\t\t\t\t   \u0026beacon_int_gcd, \u0026beacon_int_different,\n"
}

21/2 2026/09/06 01:50 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 18610
							output: 375
							thoughts: 1215
						

Thoughts:
**Analysis of Proposed cfg80211 Patch for Fuzzing Potential**

Okay, let's dive into this patch and assess its fuzzing potential within the `cfg80211` subsystem. My first instinct is to see where this code is called. I'm going to look at `cfg80211_iter_combinations` and other callers as a starting point. Then I'll meticulously review the changes this patch makes.

First, within `net/wireless/core.c`, the patch removes some `WARN_ON` checks in `cfg80211_stop_p2p_device` and `cfg80211_netdev_notifier_call` - probably worth a closer look to see why these checks were deemed unnecessary. It also initializes and flushes `reg_check_chans_wk` and `reg_leave_nan_wk` in the registered device lifecycle.

`net/wireless/core.h` shows struct changes: adding `driver_owns` and `stale` to `cfg80211_scan_request_int`, and adding `reg_check_chans_wk` and `reg_leave_nan_wk` to the `cfg80211_registered_device` struct. Also, the `cfg80211_event` union for `ij` now holds a `struct cfg80211_bss *bss`.

`net/wireless/ibss.c` modifies `__cfg80211_ibss_joined` and `cfg80211_ibss_joined` related to BSS reference counting and passing a BSS pointer across events.

`net/wireless/rdev-ops.h` has the `rdev_scan` function setting `driver_owns` to true, and clearing it on error.

`net/wireless/reg.c` introduces async workers `reg_leave_invalid_nan_wk` and `reg_leave_invalid_chans_wk` for regulatory channel/NAN leaves, and `reg_check_chans_work` to queue `reg_check_chans_wk` for all rdevs under RCU. It also uses `wdev_running(wdev)` in `reg_wdev_chan_valid`.

`net/wireless/scan.c` concerns itself with the lifetime management of scan requests via `stale` and `driver_owns` in `cfg80211_put_scan_req`, `___cfg80211_scan_done`, and `cfg80211_scan_done`. The patch also validates beacon data for hidden BSS via `__cfg80211_bss_update` and simplifies a check in `cfg80211_update_assoc_bss_entry`.

Finally, `net/wireless/util.c` uses `skb_header_pointer` safely access packet headers in `cfg80211_classify8021d`, handles an updated `EVENT_IBSS_JOINED` with the BSS pointer in `cfg80211_process_wdev_events`, and fixes beacon interval calculation in `cfg80211_calculate_bi_data`.

The key question is whether this code is reachable within a fuzzing context like QEMU and syzkaller. Since `cfg80211` is the core, and can be accessed through nl80211 sockets, mac80211/mac80211_hwsim, wireless extensions, packet sockets, the answer is a resounding yes! Syzkaller is known to target nl80211 extensively.

Next, the configurations are key. We need `CFG80211`, obviously. We also need `MAC80211_HWSIM` and or `NL80211`. I'll confirm that `CFG80211` doesn't require anything else, but I saw a mention of `CFG80211_WEXT` in `ibss.c`, so I'll include it. So, `CFG80211`, `CFG80211_WEXT`, and `MAC80211_HWSIM` should be the configurations to include.

Now for the targeted symbols: We should focus our fuzzing efforts on the modified or newly implemented functions: `cfg80211_scan_done`, `___cfg80211_scan_done`, `reg_leave_invalid_chans_wk`, `reg_leave_invalid_nan_wk`, `reg_check_


22/2 2026/09/06 01:51 tool set-results
0m Args:
{
  "EnableConfigs": [
    "CFG80211",
    "CFG80211_WEXT",
    "MAC80211_HWSIM"
  ],
  "FocusSymbols": [
    "cfg80211_scan_done",
    "___cfg80211_scan_done",
    "reg_leave_invalid_chans_wk",
    "reg_leave_invalid_nan_wk",
    "reg_check_chans_work",
    "cfg80211_ibss_joined",
    "__cfg80211_ibss_joined",
    "cfg80211_classify8021d",
    "__cfg80211_bss_update",
    "cfg80211_update_assoc_bss_entry",
    "cfg80211_stop_p2p_device",
    "cfg80211_process_wdev_events"
  ],
  "Reasoning": "The patch introduces changes to cfg80211 scan request lifecycle management (handling asynchronous driver cancellation and stale request freeing), refactors regulatory channel leave workers and locking (splitting NAN teardown into separate work with RTNL lock vs wiphy work), updates BSS object refcounting during IBSS join events, and improves header parsing safety in cfg80211_classify8021d using skb_header_pointer. All modified paths are executable and reachable via nl80211/cfg80211 user-space netlink commands and simulated wireless hardware (mac80211_hwsim) in standard QEMU environments.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "CFG80211",
    "CFG80211_WEXT",
    "MAC80211_HWSIM"
  ],
  "FocusSymbols": [
    "cfg80211_scan_done",
    "___cfg80211_scan_done",
    "reg_leave_invalid_chans_wk",
    "reg_leave_invalid_nan_wk",
    "reg_check_chans_work",
    "cfg80211_ibss_joined",
    "__cfg80211_ibss_joined",
    "cfg80211_classify8021d",
    "__cfg80211_bss_update",
    "cfg80211_update_assoc_bss_entry",
    "cfg80211_stop_p2p_device",
    "cfg80211_process_wdev_events"
  ],
  "Reasoning": "The patch introduces changes to cfg80211 scan request lifecycle management (handling asynchronous driver cancellation and stale request freeing), refactors regulatory channel leave workers and locking (splitting NAN teardown into separate work with RTNL lock vs wiphy work), updates BSS object refcounting during IBSS join events, and improves header parsing safety in cfg80211_classify8021d using skb_header_pointer. All modified paths are executable and reachable via nl80211/cfg80211 user-space netlink commands and simulated wireless hardware (mac80211_hwsim) in standard QEMU environments.",
  "WorthFuzzing": true
}

23/1 2026/09/06 01:51 action If
0m Args:
{
  "WorthFuzzing": true
}

Results:
null

24/2 2026/09/06 01:51 agent kmsan-evaluator
0m Model:
core

Args:
null

Results:
{
  "KMSANReasoning": "The patch series addresses lifetime management, locking, and packet parsing across cfg80211:\n1. Scan request tracking (driver_owns/stale): Fixes use-after-free and premature frees of scan requests across driver cancellation paths. Any memory corruption or UAF issues are caught by KASAN. The scan request structures are allocated with kzalloc, so all new struct fields are zero-initialized.\n2. Regulatory workqueue split: Solves lock ordering/deadlocks between rtnl_lock and wiphy mutex during regulatory interface teardown. This falls strictly under LOCKDEP.\n3. IBSS event handling: Passes BSS references directly to event processing rather than re-looking them up; all event structs are allocated via kzalloc_obj and reference counting is properly managed.\n4. Packet classification (skb_header_pointer): Fixes potential out-of-bounds reads on non-linear skbs by safely checking header lengths with skb_header_pointer. Any potential buffer overreads are caught by KASAN.\n5. No uninitialized stack/heap memory, struct padding leaks, or uninitialized branch conditions are introduced.\n\nTherefore, dedicated KMSAN fuzzing is not required as KASAN and LOCKDEP fully cover the risks in these changes.",
  "NeedsKMSAN": false
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit bc6715e01ed071bb16441193b4cd75c547bd2b62
Author: syz-cluster <triage@syzkaller.com>
Date:   Sun Sep 6 01:50:14 2026 +0000

    syz-cluster: applied patch under review

diff --git a/net/wireless/core.c b/net/wireless/core.c
index d13310fef691a..668380deec7da 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -244,9 +244,8 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
 	rdev->opencount--;
 
 	if (rdev->scan_req && rdev->scan_req->req.wdev == wdev) {
-		if (WARN_ON(!rdev->scan_req->notified &&
-			    (!rdev->int_scan_req ||
-			     !rdev->int_scan_req->notified)))
+		if (!rdev->scan_req->notified &&
+		    (!rdev->int_scan_req || !rdev->int_scan_req->notified))
 			rdev->scan_req->info.aborted = true;
 		___cfg80211_scan_done(rdev, false);
 	}
@@ -646,6 +645,8 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
 	INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk);
 	wiphy_work_init(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk);
 	INIT_WORK(&rdev->sched_scan_res_wk, cfg80211_sched_scan_results_wk);
+	wiphy_work_init(&rdev->reg_check_chans_wk, reg_leave_invalid_chans_wk);
+	INIT_WORK(&rdev->reg_leave_nan_wk, reg_leave_invalid_nan_wk);
 	INIT_WORK(&rdev->propagate_radar_detect_wk,
 		  cfg80211_propagate_radar_detect_wk);
 	INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk);
@@ -1345,6 +1346,7 @@ void wiphy_unregister(struct wiphy *wiphy)
 	cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
 	cancel_delayed_work_sync(&rdev->background_cac_done_wk);
 	flush_work(&rdev->destroy_work);
+	flush_work(&rdev->reg_leave_nan_wk);
 	flush_work(&rdev->propagate_radar_detect_wk);
 	flush_work(&rdev->propagate_cac_done_wk);
 	flush_work(&rdev->mgmt_registrations_update_wk);
@@ -1758,9 +1760,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
 		wiphy_lock(&rdev->wiphy);
 		cfg80211_update_iface_num(rdev, wdev->iftype, -1);
 		if (rdev->scan_req && rdev->scan_req->req.wdev == wdev) {
-			if (WARN_ON(!rdev->scan_req->notified &&
-				    (!rdev->int_scan_req ||
-				     !rdev->int_scan_req->notified)))
+			if (!rdev->scan_req->notified &&
+			    (!rdev->int_scan_req ||
+			     !rdev->int_scan_req->notified))
 				rdev->scan_req->info.aborted = true;
 			___cfg80211_scan_done(rdev, false);
 		}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index b4610f6685dcb..6138d207caf41 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -24,6 +24,16 @@
 struct cfg80211_scan_request_int {
 	struct cfg80211_scan_info info;
 	bool notified;
+	/*
+	 * set while the request is handed to the driver, i.e. between
+	 * rdev_scan() and cfg80211_scan_done()
+	 */
+	bool driver_owns;
+	/*
+	 * set when cfg80211 is done with the request but the driver still
+	 * owns it, so that cfg80211_scan_done() knows to just free it
+	 */
+	bool stale;
 	/* must be last - variable members */
 	struct cfg80211_scan_request req;
 };
@@ -104,6 +114,8 @@ struct cfg80211_registered_device {
 	struct work_struct destroy_work;
 	struct wiphy_work sched_scan_stop_wk;
 	struct work_struct sched_scan_res_wk;
+	struct wiphy_work reg_check_chans_wk;
+	struct work_struct reg_leave_nan_wk;
 
 	struct cfg80211_chan_def radar_chandef;
 	struct work_struct propagate_radar_detect_wk;
@@ -280,8 +292,7 @@ struct cfg80211_event {
 			bool locally_generated;
 		} dc;
 		struct {
-			u8 bssid[ETH_ALEN];
-			struct ieee80211_channel *channel;
+			struct cfg80211_bss *bss;
 		} ij;
 		struct {
 			u8 peer_addr[ETH_ALEN];
@@ -344,8 +355,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 void cfg80211_clear_ibss(struct net_device *dev, bool nowext);
 int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
 			struct net_device *dev, bool nowext);
-void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
-			    struct ieee80211_channel *channel);
+void __cfg80211_ibss_joined(struct net_device *dev, struct cfg80211_bss *bss);
 int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
 			    struct wireless_dev *wdev);
 
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index b1d748bdb504e..7f6779d326b81 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -16,26 +16,18 @@
 #include "rdev-ops.h"
 
 
-void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
-			    struct ieee80211_channel *channel)
+void __cfg80211_ibss_joined(struct net_device *dev, struct cfg80211_bss *bss)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
-	struct cfg80211_bss *bss;
 #ifdef CONFIG_CFG80211_WEXT
 	union iwreq_data wrqu;
 #endif
 
 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
-		return;
+		goto put_bss;
 
 	if (!wdev->u.ibss.ssid_len)
-		return;
-
-	bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,
-			       IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
-
-	if (WARN_ON(!bss))
-		return;
+		goto put_bss;
 
 	if (wdev->u.ibss.current_bss) {
 		cfg80211_unhold_bss(wdev->u.ibss.current_bss);
@@ -43,17 +35,22 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
 	}
 
 	cfg80211_hold_bss(bss_from_pub(bss));
+	/* the reference from the event is transferred to current_bss */
 	wdev->u.ibss.current_bss = bss_from_pub(bss);
 
 	cfg80211_upload_connect_keys(wdev);
 
-	nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bssid,
+	nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bss->bssid,
 				GFP_KERNEL);
 #ifdef CONFIG_CFG80211_WEXT
 	memset(&wrqu, 0, sizeof(wrqu));
-	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
+	memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN);
 	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
 #endif
+	return;
+
+put_bss:
+	cfg80211_put_bss(wdev->wiphy, bss);
 }
 
 void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
@@ -62,6 +59,7 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 	struct cfg80211_event *ev;
+	struct cfg80211_bss *bss;
 	unsigned long flags;
 
 	trace_cfg80211_ibss_joined(dev, bssid, channel);
@@ -69,13 +67,19 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
 	if (WARN_ON(!channel))
 		return;
 
+	bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,
+			       IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
+	if (WARN_ON(!bss))
+		return;
+
 	ev = kzalloc_obj(*ev, gfp);
-	if (!ev)
+	if (!ev) {
+		cfg80211_put_bss(wdev->wiphy, bss);
 		return;
+	}
 
 	ev->type = EVENT_IBSS_JOINED;
-	memcpy(ev->ij.bssid, bssid, ETH_ALEN);
-	ev->ij.channel = channel;
+	ev->ij.bss = bss;
 
 	spin_lock_irqsave(&wdev->event_lock, flags);
 	list_add_tail(&ev->list, &wdev->event_list);
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 46849fe8d0b39..adcfd0278da32 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -464,7 +464,10 @@ static inline int rdev_scan(struct cfg80211_registered_device *rdev,
 		return -EINVAL;
 
 	trace_rdev_scan(&rdev->wiphy, request);
+	request->driver_owns = true;
 	ret = rdev->ops->scan(&rdev->wiphy, &request->req);
+	if (ret)
+		request->driver_owns = false;
 	trace_rdev_return_int(&rdev->wiphy, ret);
 	return ret;
 }
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index a8336baf85dc6..11665e0a7efc8 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -2345,7 +2345,7 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
 	iftype = wdev->iftype;
 
 	/* make sure the interface is active */
-	if (!wdev->netdev || !netif_running(wdev->netdev))
+	if (!wdev_running(wdev))
 		return true;
 
 	/* NAN doesn't have links, handle it separately */
@@ -2446,19 +2446,52 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
 	return true;
 }
 
-static void reg_leave_invalid_chans(struct wiphy *wiphy)
+void reg_leave_invalid_nan_wk(struct work_struct *work)
 {
+	struct cfg80211_registered_device *rdev;
 	struct wireless_dev *wdev;
-	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+	rdev = container_of(work, struct cfg80211_registered_device,
+			    reg_leave_nan_wk);
+
+	/* stopping NAN closes its data interfaces, which needs the RTNL */
+	rtnl_lock();
 
 	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
 		bool valid;
 
-		scoped_guard(wiphy, wiphy)
-			valid = reg_wdev_chan_valid(wiphy, wdev);
+		if (wdev->iftype != NL80211_IFTYPE_NAN)
+			continue;
+
+		scoped_guard(wiphy, &rdev->wiphy)
+			valid = reg_wdev_chan_valid(&rdev->wiphy, wdev);
 		if (!valid)
 			cfg80211_leave(rdev, wdev, -1);
 	}
+
+	rtnl_unlock();
+}
+
+void reg_leave_invalid_chans_wk(struct wiphy *wiphy, struct wiphy_work *work)
+{
+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+	struct wireless_dev *wdev;
+
+	lockdep_assert_held(&wiphy->mtx);
+
+	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+		if (reg_wdev_chan_valid(wiphy, wdev))
+			continue;
+
+		/*
+		 * Tearing down NAN needs the RTNL for closing NAN_DATA
+		 * interfaces, handle that separately.
+		 */
+		if (wdev->iftype == NL80211_IFTYPE_NAN)
+			schedule_work(&rdev->reg_leave_nan_wk);
+		else
+			cfg80211_leave_locked(rdev, wdev, -1);
+	}
 }
 
 static void reg_check_chans_work(struct work_struct *work)
@@ -2466,12 +2499,13 @@ static void reg_check_chans_work(struct work_struct *work)
 	struct cfg80211_registered_device *rdev;
 
 	pr_debug("Verifying active interfaces after reg change\n");
-	rtnl_lock();
 
-	for_each_rdev(rdev)
-		reg_leave_invalid_chans(&rdev->wiphy);
+	rcu_read_lock();
 
-	rtnl_unlock();
+	list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
+		wiphy_work_queue(&rdev->wiphy, &rdev->reg_check_chans_wk);
+
+	rcu_read_unlock();
 }
 
 void reg_check_channels(void)
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index fc31c5f9a61ab..c587079ead8fb 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -178,6 +178,22 @@ int reg_reload_regdb(void);
  */
 void reg_check_channels(void);
 
+/**
+ * reg_leave_invalid_chans_wk - check if channels are no longer usable and leave
+ * @wiphy: the wiphy to check
+ * @work: the work struct
+ */
+void reg_leave_invalid_chans_wk(struct wiphy *wiphy, struct wiphy_work *work);
+
+/**
+ * reg_leave_invalid_nan_wk - check channels and tear down NAN when unusable
+ * @work: the work struct
+ *
+ * Stopping a NAN interface needs the RTNL, so it cannot be done from
+ * reg_leave_invalid_chans_wk() which runs with the wiphy mutex held.
+ */
+void reg_leave_invalid_nan_wk(struct work_struct *work);
+
 extern const u8 shipped_regdb_certs[];
 extern unsigned int shipped_regdb_certs_len;
 extern const u8 extra_regdb_certs[];
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 9e934b185e341..caa9c6495f20c 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1114,6 +1114,21 @@ int cfg80211_scan(struct cfg80211_registered_device *rdev)
 	return 0;
 }
 
+/*
+ * Release the scan request, but free it only if the driver is also done,
+ * e.g. mac80211 may cancel it asynchronously and still use it.
+ */
+static void cfg80211_put_scan_req(struct cfg80211_scan_request_int *req)
+{
+	if (!req)
+		return;
+
+	if (req->driver_owns)
+		req->stale = true;
+	else
+		kfree(req);
+}
+
 void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
 			   bool send_message)
 {
@@ -1173,10 +1188,10 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
 
 	dev_put(wdev->netdev);
 
-	kfree(rdev->int_scan_req);
+	cfg80211_put_scan_req(rdev->int_scan_req);
 	rdev->int_scan_req = NULL;
 
-	kfree(rdev->scan_req);
+	cfg80211_put_scan_req(rdev->scan_req);
 	rdev->scan_req = NULL;
 
 	if (!send_message)
@@ -1199,6 +1214,18 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request,
 	struct cfg80211_scan_info old_info = intreq->info;
 
 	trace_cfg80211_scan_done(intreq, info);
+
+	intreq->driver_owns = false;
+
+	if (intreq->stale) {
+		/*
+		 * The scan is already completed as far as we're concerned,
+		 * it was just kept around for the driver - done now, free it.
+		 */
+		kfree(intreq);
+		return;
+	}
+
 	WARN_ON(intreq != rdev->scan_req &&
 		intreq != rdev->int_scan_req);
 
@@ -2050,6 +2077,13 @@ __cfg80211_bss_update(struct cfg80211_registered_device *rdev,
 			if (!hidden)
 				hidden = rb_find_bss(rdev, tmp,
 						     BSS_CMP_HIDE_NUL);
+			/*
+			 * Only group with an entry with beacon data, otherwise
+			 * beacon data can never be filled/updated.
+			 */
+			if (hidden &&
+			    !rcu_access_pointer(hidden->pub.beacon_ies))
+				hidden = NULL;
 			if (hidden) {
 				new->pub.hidden_beacon_bss = &hidden->pub;
 				list_add(&new->hidden_list,
@@ -3468,11 +3502,6 @@ void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
 	cbss->pub.channel = chan;
 
 	list_for_each_entry(bss, &rdev->bss_list, list) {
-		if (!cfg80211_bss_type_match(bss->pub.capability,
-					     bss->pub.channel->band,
-					     wdev->conn_bss_type))
-			continue;
-
 		if (bss == cbss)
 			continue;
 
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 3e584d0ca3e26..f2464d2ce0d5a 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1039,12 +1039,30 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb,
 	}
 
 	switch (skb->protocol) {
-	case htons(ETH_P_IP):
-		dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc;
+	case htons(ETH_P_IP): {
+		const struct iphdr *iph;
+		struct iphdr _iph;
+
+		iph = skb_header_pointer(skb, sizeof(struct ethhdr),
+					 sizeof(*iph), &_iph);
+		if (!iph)
+			return 0;
+
+		dscp = ipv4_get_dsfield(iph) & 0xfc;
 		break;
-	case htons(ETH_P_IPV6):
-		dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & 0xfc;
+	}
+	case htons(ETH_P_IPV6): {
+		const struct ipv6hdr *ip6h;
+		struct ipv6hdr _ip6h;
+
+		ip6h = skb_header_pointer(skb, sizeof(struct ethhdr),
+					  sizeof(*ip6h), &_ip6h);
+		if (!ip6h)
+			return 0;
+
+		dscp = ipv6_get_dsfield(ip6h) & 0xfc;
 		break;
+	}
 	case htons(ETH_P_MPLS_UC):
 	case htons(ETH_P_MPLS_MC): {
 		struct mpls_label mpls_tmp, *mpls;
@@ -1217,8 +1235,7 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)
 						!ev->dc.locally_generated);
 			break;
 		case EVENT_IBSS_JOINED:
-			__cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid,
-					       ev->ij.channel);
+			__cfg80211_ibss_joined(wdev->netdev, ev->ij.bss);
 			break;
 		case EVENT_STOPPED:
 			/*
@@ -2477,16 +2494,15 @@ static void cfg80211_calculate_bi_data(struct wiphy *wiphy, u32 new_beacon_int,
 		if (wdev->valid_links)
 			continue;
 
+		wdev_bi = cfg80211_wdev_bi(wdev);
+		if (!wdev_bi)
+			continue;
+
 		/* skip wdevs not active on the given wiphy radio */
 		if (radio_idx >= 0 &&
 		    !(rdev_get_radio_mask(rdev, wdev->netdev) & BIT(radio_idx)))
 			continue;
 
-		wdev_bi = cfg80211_wdev_bi(wdev);
-
-		if (!wdev_bi)
-			continue;
-
 		if (!*beacon_int_gcd) {
 			*beacon_int_gcd = wdev_bi;
 			continue;

25/3 2026/09/06 01:51 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 8579
							output: 36
							thoughts: 1277
						

Thoughts:
**KMSAN Fuzzing Session Evaluation**

Okay, let's assess whether this patch series warrants a dedicated KMSAN fuzzing session, in addition to our standard KASAN efforts. My first step is always a thorough review of the diff.

I'm seeing a number of changes, and my initial scan suggests the wireless core is being touched heavily. The most interesting areas are likely those that touch memory allocation, data structures, and anything that could potentially lead to uninitialized memory access.

Specifically, I'm focusing on these aspects:

1.  **Data Structure Changes:** The addition of `driver_owns`, `stale`, `reg_check_chans_wk`, and `reg_leave_nan_wk` to various structs is interesting. I'll need to check how these are initialized and used to ensure no use-after-free or double-free scenarios are possible. The changes to `cfg80211_event` and `__cfg80211_ibss_joined` are definitely worth a closer look, especially the allocation of `bss` and the reference handling. Any time a `bss` is manipulated, I'm already cautious.

2.  **Workqueue Introductions:** The introduction of workqueues (`reg_check_chans_wk`, `reg_leave_invalid_nan_wk`, `reg_leave_invalid_chans_wk`) always demands scrutiny. Workqueues often involve asynchronous operations and can introduce subtle race conditions or memory management issues if not handled carefully. Locking improvements are good, but I'll make sure they are correct and robust.

3.  **Memory Management:** I need to closely investigate `cfg80211_put_scan_req` (with `driver_owns` and `stale`) and ensure scan requests are handled correctly, avoiding memory leaks or double-frees.

4.  **`skb_header_pointer` Usage:** The usage of `skb_header_pointer` in `cfg80211_classify8021d` is a prime suspect. This looks like a potential area for uninitialized memory usage, particularly since it involves copying data from the SKB. I need to make sure the buffer is properly sized and that the copy doesn't read past the end of the SKB, leading to out-of-bounds reads. I know the function's internal details. However, I want to check how the data is handled after the `skb_header_pointer` call. I will use `read-file` or `grepper` and look at line 1039 in `net/wireless/util.c`. This is my primary area of concern because it is touching memory.

5. **WARN_ON removals:** While this is generally not a security vulnerability, removing `WARN_ON` statements is also something that warrants attention. WARN_ON statements are generally there for a reason, namely to stop things from proceeding that the developer didn't expect to happen. Without it, you are more likely to run into undefined behavior.

My immediate plan is to delve deeper into these areas. I will specifically check for any potential uninitialized memory reads or leaks. I’ll be paying close attention to any changes in memory management or synchronization. Based on my findings from this more in-depth analysis, I will make the final decision about spinning up a dedicated KMSAN fuzzing session.



26/3 2026/09/06 01:51 tool read-file
0m Args:
{
  "File": "net/wireless/util.c",
  "FirstLine": 1030,
  "LineCount": 45
}

Results:
{
  "Contents": "1030:\t\t}\n1031:\t\n1032:\t\tif (skb_vlan_tag_present(skb)) {\n1033:\t\t\tvlan_priority = (skb_vlan_tag_get(skb) \u0026 VLAN_PRIO_MASK)\n1034:\t\t\t\t\u003e\u003e VLAN_PRIO_SHIFT;\n1035:\t\t\tif (vlan_priority \u003e 0) {\n1036:\t\t\t\tret = vlan_priority;\n1037:\t\t\t\tgoto out;\n1038:\t\t\t}\n1039:\t\t}\n1040:\t\n1041:\t\tswitch (skb-\u003eprotocol) {\n1042:\t\tcase htons(ETH_P_IP): {\n1043:\t\t\tconst struct iphdr *iph;\n1044:\t\t\tstruct iphdr _iph;\n1045:\t\n1046:\t\t\tiph = skb_header_pointer(skb, sizeof(struct ethhdr),\n1047:\t\t\t\t\t\t sizeof(*iph), \u0026_iph);\n1048:\t\t\tif (!iph)\n1049:\t\t\t\treturn 0;\n1050:\t\n1051:\t\t\tdscp = ipv4_get_dsfield(iph) \u0026 0xfc;\n1052:\t\t\tbreak;\n1053:\t\t}\n1054:\t\tcase htons(ETH_P_IPV6): {\n1055:\t\t\tconst struct ipv6hdr *ip6h;\n1056:\t\t\tstruct ipv6hdr _ip6h;\n1057:\t\n1058:\t\t\tip6h = skb_header_pointer(skb, sizeof(struct ethhdr),\n1059:\t\t\t\t\t\t  sizeof(*ip6h), \u0026_ip6h);\n1060:\t\t\tif (!ip6h)\n1061:\t\t\t\treturn 0;\n1062:\t\n1063:\t\t\tdscp = ipv6_get_dsfield(ip6h) \u0026 0xfc;\n1064:\t\t\tbreak;\n1065:\t\t}\n1066:\t\tcase htons(ETH_P_MPLS_UC):\n1067:\t\tcase htons(ETH_P_MPLS_MC): {\n1068:\t\t\tstruct mpls_label mpls_tmp, *mpls;\n1069:\t\n1070:\t\t\tmpls = skb_header_pointer(skb, sizeof(struct ethhdr),\n1071:\t\t\t\t\t\t  sizeof(*mpls), \u0026mpls_tmp);\n1072:\t\t\tif (!mpls)\n1073:\t\t\t\treturn 0;\n1074:\t\n"
}

27/3 2026/09/06 01:51 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 11278
							output: 30
							thoughts: 406
						

Thoughts:
**My Analysis of Recent Code Changes**

Okay, let's dive into these recent code modifications. First, regarding `cfg80211_classify8021d`, I noticed a change in how the IP header is accessed. Previously, it relied on direct access via `ip_hdr(skb)` and `ipv6_hdr(skb)`. Now, it's been updated to use `skb_header_pointer` instead. The rationale seems sound, addressing potential issues where `skb->network_header` might not be initialized or the header hasn't been pulled yet. This replacement aligns with the approach used in the MPLS scenario, employing `skb_header_pointer` for safe copying or referencing of data within the skb, with length validation. Good catch, mitigating potential crashes due to invalid memory access.

Next, I'll examine the IBSS join process, specifically `cfg80211_ibss_joined` and `__cfg80211_ibss_joined`. In `cfg80211_ibss_joined`, the use of `kzalloc_obj` ensures a zeroed allocation for the event structure. This is followed by assigning the event type and BSS pointer. The subsequent call to `__cfg80211_ibss_joined` involves sending the IBSS BSSID and copying it to a `wrqu` structure. The BSSID, critical here, is sourced from the `cfg80211_bss` structure, which is populated during BSS creation or discovery. Everything seems straightforward in terms of data flow.

Finally, I'll trace the scan request lifecycle. The allocation of `struct cfg80211_scan_request_int` is initiated in `cfg80211_scan` and `nl80211_trigger_scan`. I'll delve into the code to trace the exact allocation path and understand how the scan request structure is managed throughout its lifetime, including the implications of `driver_owns` and `stale` states. This is important for understanding potential concurrency and resource management issues.



28/3 2026/09/06 01:51 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "kzalloc.*scan_req",
  "PathPrefix": "net/wireless/"
}

Results:
{
  "error": "no matches"
}

29/3 2026/09/06 01:51 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 12185
							output: 29
							thoughts: 36
						

Thoughts:
Here's my thought process, as if I were considering this issue:

**Delving into the Scan Request Allocation**

Okay, so I need to understand how these `cfg80211_scan_request_int` structures, or specifically the `scan_req` instances, are being allocated. My immediate instinct is to jump into the `net/wireless/scan.c` file. That's the logical place to start looking for the core logic surrounding wireless scanning, so I'll begin my investigation there. I'll focus my search within that file for the memory allocation calls, such as `kmalloc`, `kzalloc`, or any custom allocation functions the wireless subsystem might employ. I’m thinking there might be specific allocation strategies or pools in place for this. From there, I'll trace how these `scan_req` structures are initialized, populated, and ultimately released.



30/3 2026/09/06 01:51 tool grepper
0m Args:
{
  "Expression": "scan_req.*=",
  "PathPrefix": "net/wireless/"
}

Results:
{
  "Output": "net/wireless/core.c=229=void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,\n--\nnet/wireless/core.c-245-\nnet/wireless/core.c:246:\tif (rdev-\u003escan_req \u0026\u0026 rdev-\u003escan_req-\u003ereq.wdev == wdev) {\nnet/wireless/core.c-247-\t\tif (!rdev-\u003escan_req-\u003enotified \u0026\u0026\nnet/wireless/core.c-248-\t\t    (!rdev-\u003eint_scan_req || !rdev-\u003eint_scan_req-\u003enotified))\nnet/wireless/core.c:249:\t\t\trdev-\u003escan_req-\u003einfo.aborted = true;\nnet/wireless/core.c-250-\t\t___cfg80211_scan_done(rdev, false);\n--\nnet/wireless/core.c=1709=static int cfg80211_netdev_notifier_call(struct notifier_block *nb,\n--\nnet/wireless/core.c-1761-\t\tcfg80211_update_iface_num(rdev, wdev-\u003eiftype, -1);\nnet/wireless/core.c:1762:\t\tif (rdev-\u003escan_req \u0026\u0026 rdev-\u003escan_req-\u003ereq.wdev == wdev) {\nnet/wireless/core.c-1763-\t\t\tif (!rdev-\u003escan_req-\u003enotified \u0026\u0026\n--\nnet/wireless/core.c-1765-\t\t\t     !rdev-\u003eint_scan_req-\u003enotified))\nnet/wireless/core.c:1766:\t\t\t\trdev-\u003escan_req-\u003einfo.aborted = true;\nnet/wireless/core.c-1767-\t\t\t___cfg80211_scan_done(rdev, false);\n--\nnet/wireless/nl80211.c=11272=static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)\n--\nnet/wireless/nl80211.c-11503-\nnet/wireless/nl80211.c:11504:\trdev-\u003escan_req = request;\nnet/wireless/nl80211.c-11505-\terr = cfg80211_scan(rdev);\n--\nnet/wireless/nl80211.c-11515- out_free:\nnet/wireless/nl80211.c:11516:\trdev-\u003escan_req = NULL;\nnet/wireless/nl80211.c-11517-\tkfree(request);\n--\nnet/wireless/nl80211.c=11965=static int nl80211_start_sched_scan(struct sk_buff *skb,\n--\nnet/wireless/nl80211.c-11982-\nnet/wireless/nl80211.c:11983:\tsched_scan_req = nl80211_parse_sched_scan(\u0026rdev-\u003ewiphy, wdev,\nnet/wireless/nl80211.c-11984-\t\t\t\t\t\t  info-\u003eattrs,\n--\nnet/wireless/nl80211.c-11994-\tif (want_multi \u0026\u0026 rdev-\u003ewiphy.max_sched_scan_reqs \u003e 1)\nnet/wireless/nl80211.c:11995:\t\tsched_scan_req-\u003ereqid = cfg80211_assign_cookie(rdev);\nnet/wireless/nl80211.c-11996-\n--\nnet/wireless/nl80211.c-12000-\nnet/wireless/nl80211.c:12001:\tsched_scan_req-\u003edev = dev;\nnet/wireless/nl80211.c:12002:\tsched_scan_req-\u003ewiphy = \u0026rdev-\u003ewiphy;\nnet/wireless/nl80211.c-12003-\nnet/wireless/nl80211.c-12004-\tif (info-\u003eattrs[NL80211_ATTR_SOCKET_OWNER])\nnet/wireless/nl80211.c:12005:\t\tsched_scan_req-\u003eowner_nlportid = info-\u003esnd_portid;\nnet/wireless/nl80211.c-12006-\n--\nnet/wireless/nl80211.c=20687=static int nl80211_add_scan_req(struct sk_buff *msg,\n--\nnet/wireless/nl80211.c-20689-{\nnet/wireless/nl80211.c:20690:\tstruct cfg80211_scan_request_int *req = rdev-\u003escan_req;\nnet/wireless/nl80211.c-20691-\tstruct nlattr *nest;\n--\nnet/wireless/nl80211.c=23127=static int nl80211_netlink_notify(struct notifier_block * nb,\n--\nnet/wireless/nl80211.c-23146-\t\t\t\t\tlist) {\nnet/wireless/nl80211.c:23147:\t\t\tif (sched_scan_req-\u003eowner_nlportid == notify-\u003eportid) {\nnet/wireless/nl80211.c:23148:\t\t\t\tsched_scan_req-\u003enl_owner_dead = true;\nnet/wireless/nl80211.c-23149-\t\t\t\twiphy_work_queue(\u0026rdev-\u003ewiphy,\n--\nnet/wireless/scan.c=844=static int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev,\n--\nnet/wireless/scan.c-849-\tint n_channels, count = 0, err;\nnet/wireless/scan.c:850:\tstruct cfg80211_scan_request_int *request, *rdev_req = rdev-\u003escan_req;\nnet/wireless/scan.c-851-\tLIST_HEAD(coloc_ap_list);\n--\nnet/wireless/scan.c-1045-\tif (request-\u003ereq.n_channels) {\nnet/wireless/scan.c:1046:\t\tstruct cfg80211_scan_request_int *old = rdev-\u003eint_scan_req;\nnet/wireless/scan.c-1047-\nnet/wireless/scan.c:1048:\t\trdev-\u003eint_scan_req = request;\nnet/wireless/scan.c-1049-\n--\nnet/wireless/scan.c-1054-\t\tif (!first_part \u0026\u0026 !WARN_ON(!old))\nnet/wireless/scan.c:1055:\t\t\trdev-\u003eint_scan_req-\u003einfo = old-\u003einfo;\nnet/wireless/scan.c-1056-\n--\nnet/wireless/scan.c-1058-\t\tif (err) {\nnet/wireless/scan.c:1059:\t\t\trdev-\u003eint_scan_req = old;\nnet/wireless/scan.c-1060-\t\t\tkfree(request);\n--\nnet/wireless/scan.c=1072=int cfg80211_scan(struct cfg80211_registered_device *rdev)\n--\nnet/wireless/scan.c-1074-\tstruct cfg80211_scan_request_int *request;\nnet/wireless/scan.c:1075:\tstruct cfg80211_scan_request_int *rdev_req = rdev-\u003escan_req;\nnet/wireless/scan.c-1076-\tu32 n_channels = 0, idx, i;\n--\nnet/wireless/scan.c-1112-\nnet/wireless/scan.c:1113:\trdev-\u003eint_scan_req = request;\nnet/wireless/scan.c-1114-\treturn 0;\n--\nnet/wireless/scan.c=1132=void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,\n--\nnet/wireless/scan.c-1191-\tcfg80211_put_scan_req(rdev-\u003eint_scan_req);\nnet/wireless/scan.c:1192:\trdev-\u003eint_scan_req = NULL;\nnet/wireless/scan.c-1193-\nnet/wireless/scan.c-1194-\tcfg80211_put_scan_req(rdev-\u003escan_req);\nnet/wireless/scan.c:1195:\trdev-\u003escan_req = NULL;\nnet/wireless/scan.c-1196-\n--\nnet/wireless/scan.c=1208=void cfg80211_scan_done(struct cfg80211_scan_request *request,\n--\nnet/wireless/scan.c-1210-{\nnet/wireless/scan.c:1211:\tstruct cfg80211_scan_request_int *intreq =\nnet/wireless/scan.c-1212-\t\tcontainer_of(request, struct cfg80211_scan_request_int, req);\n--\nnet/wireless/scan.c=1396=int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,\n--\nnet/wireless/scan.c-1402-\nnet/wireless/scan.c:1403:\tsched_scan_req = cfg80211_find_sched_scan_req(rdev, reqid);\nnet/wireless/scan.c-1404-\tif (!sched_scan_req)\n--\nnet/wireless/scan.c=3566=int cfg80211_wext_siwscan(struct net_device *dev,\n--\nnet/wireless/scan.c-3571-\tstruct wiphy *wiphy;\nnet/wireless/scan.c:3572:\tstruct iw_scan_req *wreq = NULL;\nnet/wireless/scan.c-3573-\tstruct cfg80211_scan_request_int *creq;\n--\nnet/wireless/scan.c-3694-\tscoped_guard(wiphy, \u0026rdev-\u003ewiphy) {\nnet/wireless/scan.c:3695:\t\trdev-\u003escan_req = creq;\nnet/wireless/scan.c-3696-\t\terr = rdev_scan(rdev, creq);\nnet/wireless/scan.c-3697-\t\tif (err) {\nnet/wireless/scan.c:3698:\t\t\trdev-\u003escan_req = NULL;\nnet/wireless/scan.c-3699-\t\t\t/* creq will be freed below */\n--\nnet/wireless/sme.c=64=static int cfg80211_conn_scan(struct wireless_dev *wdev)\n--\nnet/wireless/sme.c-131-\nnet/wireless/sme.c:132:\trdev-\u003escan_req = request;\nnet/wireless/sme.c-133-\n--\nnet/wireless/sme.c-139-\t} else {\nnet/wireless/sme.c:140:\t\trdev-\u003escan_req = NULL;\nnet/wireless/sme.c-141-\t\tkfree(request);\n"
}

31/3 2026/09/06 01:51 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 15399
							output: 42
							thoughts: 73
						

Thoughts:
**Diving into the Scan Request Allocation**

Okay, time to get my hands dirty. I need to understand exactly how the `cfg80211_scan_request_int` structure is allocated within the scan process, specifically when triggered via `nl80211_trigger_scan` or directly through `cfg80211_scan`. This is key to understanding the control flow and potential points of contention.

I'll start by digging into `nl80211.c`, and I'm going to jump straight to the area around line 11300. That's usually where these kinds of allocations happen, so hopefully, the creation of the request will be right there in front of me. This should give me a solid grasp of how the memory is handled during the scan initiation. Let's see what we've got!



32/3 2026/09/06 01:51 tool read-file
0m Args:
{
  "File": "net/wireless/nl80211.c",
  "FirstLine": 11300,
  "LineCount": 50
}

Results:
{
  "Contents": "11300:\t\t\t\treturn -EOPNOTSUPP;\n11301:\t\t\tscan_freqs = info-\u003eattrs[NL80211_ATTR_SCAN_FREQ_KHZ];\n11302:\t\t\tscan_freqs_khz = true;\n11303:\t\t} else if (info-\u003eattrs[NL80211_ATTR_SCAN_FREQUENCIES])\n11304:\t\t\tscan_freqs = info-\u003eattrs[NL80211_ATTR_SCAN_FREQUENCIES];\n11305:\t\n11306:\t\tif (scan_freqs) {\n11307:\t\t\tn_channels = validate_scan_freqs(scan_freqs);\n11308:\t\t\tif (!n_channels)\n11309:\t\t\t\treturn -EINVAL;\n11310:\t\t} else {\n11311:\t\t\tn_channels = ieee80211_get_num_supported_channels(wiphy);\n11312:\t\t}\n11313:\t\n11314:\t\tif (info-\u003eattrs[NL80211_ATTR_SCAN_SSIDS])\n11315:\t\t\tnla_for_each_nested(attr, info-\u003eattrs[NL80211_ATTR_SCAN_SSIDS], tmp)\n11316:\t\t\t\tn_ssids++;\n11317:\t\n11318:\t\tif (n_ssids \u003e wiphy-\u003emax_scan_ssids)\n11319:\t\t\treturn -EINVAL;\n11320:\t\n11321:\t\tif (info-\u003eattrs[NL80211_ATTR_IE])\n11322:\t\t\tie_len = nla_len(info-\u003eattrs[NL80211_ATTR_IE]);\n11323:\t\telse\n11324:\t\t\tie_len = 0;\n11325:\t\n11326:\t\tif (ie_len \u003e wiphy-\u003emax_scan_ie_len)\n11327:\t\t\treturn -EINVAL;\n11328:\t\n11329:\t\tsize = struct_size(request, req.channels, n_channels);\n11330:\t\tssids_offset = size;\n11331:\t\tsize = size_add(size, array_size(sizeof(*request-\u003ereq.ssids), n_ssids));\n11332:\t\tie_offset = size;\n11333:\t\tsize = size_add(size, ie_len);\n11334:\t\trequest = kzalloc(size, GFP_KERNEL);\n11335:\t\tif (!request)\n11336:\t\t\treturn -ENOMEM;\n11337:\t\n11338:\t\tif (n_ssids)\n11339:\t\t\trequest-\u003ereq.ssids = (void *)request + ssids_offset;\n11340:\t\trequest-\u003ereq.n_ssids = n_ssids;\n11341:\t\tif (ie_len)\n11342:\t\t\trequest-\u003ereq.ie = (void *)request + ie_offset;\n11343:\t\n11344:\t\ti = 0;\n11345:\t\tif (scan_freqs) {\n11346:\t\t\t/* user specified, bail out if channel not found */\n11347:\t\t\tnla_for_each_nested(attr, scan_freqs, tmp) {\n11348:\t\t\t\tstruct ieee80211_channel *chan;\n11349:\t\t\t\tint freq = nla_get_u32(attr);\n"
}

33/3 2026/09/06 01:51 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 16633
							output: 292
							thoughts: 995
						

Thoughts:
**KMSAN Assessment: No Concerns Identified**

Okay, let's break this down systematically. I see the core focus of this patch series is on hardening the cfg80211 subsystem, specifically around concurrency issues, memory safety, and potential data corruption. My initial thought process is to trace the flow and identify any potential vulnerabilities that KMSAN might catch, despite this being a kernel code review and not a fuzzing exercise.

First, I note the use of `kzalloc` for the `request` structure. This immediately sets `driver_owns` and `stale` to false (0), which is a good starting point for ensuring a known state. Then, I examined the `cfg80211_event` union, specifically the transition from `ij` to `bss` in `cfg80211_ibss_joined`. The `kzalloc` allocation and subsequent assignment (`ev->ij.bss = bss;`) should, in principle, prevent uninitialized memory issues. The subsequent usage in `cfg80211_process_wdev_events` seems safe given this initialization.

I then looked at the workqueue changes in `reg.c` and their relation to `cfg80211_leave` calls. The use of `wiphy_work_init` and `INIT_WORK` coupled with the checks within `reg_wdev_chan_valid` suggests the intention is to avoid locking inversions. The comments about LOCKDEP and standard bug detectors reinforce this. I see this as a standard approach to avoid deadlocks; this is more an issue for LOCKDEP and less a KMSAN concern.

The removal of the `WARN_ON(...)` wrapper in `cfg80211_stop_p2p_device` and `cfg80211_netdev_notifier_call` is mostly a cosmetic change, but I still take the time to consider the condition within.

The scan request reference management in `scan.c` and `rdev-ops.h` is promising, as they prevent use-after-free and double free. KASAN is the relevant tool here.

The `cfg80211_classify8021d` change using `skb_header_pointer` is a good practice. Using `skb_header_pointer` to handle packet data reduces the risk of out-of-bounds reads and data leaks, which are primarily handled by KASAN.

Finally, the BSS handling changes (`__cfg80211_bss_update` and `cfg80211_update_assoc_bss_entry`) appear to be minor refinements regarding data access and redundant checks. `rcu_access_pointer` is properly used, addressing potential issues in a concurrent environment.

Now, to explicitly address the KMSAN questions:

1.  **Kernel structures to userspace?** No, there are no concerns about info-leaks.

2.  **Conditional logic based on uninitialized variables?** No. `kzalloc` is used and local variables are well handled.

3.  **Allocation/Initialization of complex data structures?** No uninitialized fields are present.

4.  **Bounds checks exposing uninitialized bytes?** No, `skb_header_pointer` is used correctly and defensively.

Given this thorough review, I'm confident in saying that this patch series doesn't introduce any new KMSAN concerns. All identified issues relate to concurrency, lifetime management, locking, and boundary checks, which are adequately addressed by KASAN and LOCKDEP. No uninitialized memory risks or kernel info-leaks are present.

Therefore, the `NeedsKMSAN` flag should be set to `false`. I'll double-check everything, as always, to be certain, and then invoke the `set-results` tool.



34/3 2026/09/06 01:51 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch series addresses lifetime management, locking, and packet parsing across cfg80211:\n1. Scan request tracking (driver_owns/stale): Fixes use-after-free and premature frees of scan requests across driver cancellation paths. Any memory corruption or UAF issues are caught by KASAN. The scan request structures are allocated with kzalloc, so all new struct fields are zero-initialized.\n2. Regulatory workqueue split: Solves lock ordering/deadlocks between rtnl_lock and wiphy mutex during regulatory interface teardown. This falls strictly under LOCKDEP.\n3. IBSS event handling: Passes BSS references directly to event processing rather than re-looking them up; all event structs are allocated via kzalloc_obj and reference counting is properly managed.\n4. Packet classification (skb_header_pointer): Fixes potential out-of-bounds reads on non-linear skbs by safely checking header lengths with skb_header_pointer. Any potential buffer overreads are caught by KASAN.\n5. No uninitialized stack/heap memory, struct padding leaks, or uninitialized branch conditions are introduced.\n\nTherefore, dedicated KMSAN fuzzing is not required as KASAN and LOCKDEP fully cover the risks in these changes.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch series addresses lifetime management, locking, and packet parsing across cfg80211:\n1. Scan request tracking (driver_owns/stale): Fixes use-after-free and premature frees of scan requests across driver cancellation paths. Any memory corruption or UAF issues are caught by KASAN. The scan request structures are allocated with kzalloc, so all new struct fields are zero-initialized.\n2. Regulatory workqueue split: Solves lock ordering/deadlocks between rtnl_lock and wiphy mutex during regulatory interface teardown. This falls strictly under LOCKDEP.\n3. IBSS event handling: Passes BSS references directly to event processing rather than re-looking them up; all event structs are allocated via kzalloc_obj and reference counting is properly managed.\n4. Packet classification (skb_header_pointer): Fixes potential out-of-bounds reads on non-linear skbs by safely checking header lengths with skb_header_pointer. Any potential buffer overreads are caught by KASAN.\n5. No uninitialized stack/heap memory, struct padding leaks, or uninitialized branch conditions are introduced.\n\nTherefore, dedicated KMSAN fuzzing is not required as KASAN and LOCKDEP fully cover the risks in these changes.",
  "NeedsKMSAN": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)