AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/25 10:21 flow patch-triage
4m Args:
null

Results:
{
  "EnableConfigs": [
    "RTW89",
    "RTW89_CORE",
    "RTW89_USB",
    "RTW89_8852CU",
    "MAC80211",
    "USB"
  ],
  "FocusSymbols": [
    "rtw89_fw_recognize_elements",
    "rtw89_board_elm_seek",
    "rtw89_chip_info_setup",
    "rtw89_load_firmware_work",
    "rtw89_unload_firmware",
    "__rtw89_unload_firmware",
    "rtw89_led_init",
    "rtw89_usb_probe"
  ],
  "KMSANReasoning": "The patch optimizes memory consumption in the Realtek rtw89 driver by introducing board element firmware support and optionally duplicating firmware elements/suits so that the primary firmware buffers can be released early after probing.\n\nFrom a memory safety perspective:\n1. KMSAN applicability: The patch does not introduce or expose uninitialized memory risks. All newly allocated buffers (via devm_kmemdup or vmalloc) are fully populated with memcpy for their entire specified length. Stack variables like `struct firmware pseudo_fw` are explicitly cleared with memset(..., 0, ...) before initialization. The new list head `dup_data_list` is initialized with INIT_LIST_HEAD. Furthermore, no kernel data structures, buffers, or uninitialized padding are copied or exposed to user space.\n2. KASAN applicability: The primary risks associated with this patch involve memory lifetimes and boundary calculations—specifically, potential Use-After-Free (UAF) if driver logic attempts to dereference firmware pointers after early release in `__rtw89_unload_firmware`, Double-Free if `rtw89_unload_firmware_dup_data` is invoked repeatedly without clearing `dup_data_list`, or out-of-bounds (OOB) memory access if 32-bit arithmetic overflow occurs in offset/size calculations (e.g. `ofst + size` in `rtw89_board_elm_seek`). These issues are all reliably detected by KASAN (including KASAN_VMALLOC) and standard kernel debugging checks (such as CONFIG_DEBUG_LIST).\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch introduces support for board firmware element parsing and early firmware unloading with element/fw_suit data duplication in the rtw89 wireless driver. Because rtw89 supports USB devices (such as RTW8852CU, explicitly modified in this patch series), this driver code is reachable via virtual USB buses (e.g. USB Raw Gadget in QEMU/KVM test environments). The changes introduce new binary parsing routines, memory duplication, and lifecycle deallocation logic worth fuzzing.",
  "WorthFuzzing": true
}

1/1 2026/09/25 10:21 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 0ffbabfaba5b514a37c2d74aa098f3cfc4bcc0c8\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Fri Sep 25 10:21:20 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c\nindex dacc2d889a467..e893235f962af 100644\n--- a/drivers/net/wireless/realtek/rtw89/core.c\n+++ b/drivers/net/wireless/realtek/rtw89/core.c\n@@ -6973,6 +6973,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)\n \t}\n \tINIT_LIST_HEAD(\u0026rtwdev-\u003escan_info.chan_list);\n \tINIT_LIST_HEAD(\u0026rtwdev-\u003etx_waits);\n+\tINIT_LIST_HEAD(\u0026rtwdev-\u003efw.dup_data_list);\n \tINIT_WORK(\u0026rtwdev-\u003eba_work, rtw89_core_ba_work);\n \tINIT_WORK(\u0026rtwdev-\u003etxq_work, rtw89_core_txq_work);\n \tINIT_DELAYED_WORK(\u0026rtwdev-\u003etxq_reinvoke_work, rtw89_core_txq_reinvoke_work);\n@@ -7372,6 +7373,7 @@ void rtw89_core_rfkill_poll(struct rtw89_dev *rtwdev, bool force)\n \n int rtw89_chip_info_setup(struct rtw89_dev *rtwdev)\n {\n+\tstruct rtw89_fw_req_info *fw_req = \u0026rtwdev-\u003efw.req;\n \tstruct rtw89_efuse *efuse = \u0026rtwdev-\u003eefuse;\n \tstruct rtw89_hal *hal = \u0026rtwdev-\u003ehal;\n \tint ret;\n@@ -7421,6 +7423,9 @@ int rtw89_chip_info_setup(struct rtw89_dev *rtwdev)\n \t\t   hal-\u003ecid, hal-\u003ecv, hal-\u003eaid, hal-\u003eacv, efuse-\u003erfe_type);\n \n out:\n+\tif (fw_req-\u003efree_after_probe)\n+\t\t__rtw89_unload_firmware(rtwdev);\n+\n \trtw89_mac_pwr_off(rtwdev);\n \n \treturn ret;\n@@ -7723,6 +7728,7 @@ void rtw89_free_ieee80211_hw(struct rtw89_dev *rtwdev)\n }\n EXPORT_SYMBOL(rtw89_free_ieee80211_hw);\n \n+MODULE_FIRMWARE(RTW89_FWNAME_BOARD_ELM);\n MODULE_AUTHOR(\"Realtek Corporation\");\n MODULE_DESCRIPTION(\"Realtek 802.11ax wireless core module\");\n MODULE_LICENSE(\"Dual BSD/GPL\");\ndiff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h\nindex 2b21d969ece7b..e942ee1fd21bb 100644\n--- a/drivers/net/wireless/realtek/rtw89/core.h\n+++ b/drivers/net/wireless/realtek/rtw89/core.h\n@@ -5274,6 +5274,8 @@ struct rtw89_reg_imr {\n #define RTW89_GEN_MODULE_FWNAME(basename, maxformat) \\\n \tbasename RTW89_GEN_MODULE_FWNAME_FMT(maxformat) \".bin\"\n \n+#define RTW89_FWNAME_BOARD_ELM \"rtw89/board_elm.bin\"\n+\n struct rtw89_fw_def {\n \tconst char *fw_basename;\n \tu8 fw_format_max;\n@@ -5865,7 +5867,19 @@ struct rtw89_chip_variant {\n \tconst struct rtw89_qta_def *qta_def_override;\n };\n \n+/* @pid can have extra suffix to describe the variant if necessary */\n+#define RTW89_BOARD_ID(vid, pid) \\\n+\tRTW89_BOARD_ID_##vid##_##pid\n+\n+enum rtw89_board_id {\n+\tRTW89_BOARD_ID(0000, 0000) = 0, /* reserve 0 for non-variant */\n+\tRTW89_BOARD_ID(28de, 2432) = 1,\n+\n+\tNUM_OF_RTW89_BOARD_IDS,\n+};\n+\n struct rtw89_board_variant {\n+\tenum rtw89_board_id id;\n \tconst struct rtw89_led_desc *led_desc;\n };\n \n@@ -6029,6 +6043,7 @@ enum rtw89_fw_feature {\n struct rtw89_fw_suit {\n \tenum rtw89_fw_type type;\n \tconst u8 *data;\n+\tstruct list_head list;\n \tu32 size;\n \tu8 major_ver;\n \tu8 minor_ver;\n@@ -6063,7 +6078,9 @@ struct rtw89_fw_suit {\n \n struct rtw89_fw_req_info {\n \tconst struct firmware *firmware;\n+\tconst struct firmware *board_elm;\n \tstruct completion completion;\n+\tbool free_after_probe;\n };\n \n struct rtw89_fw_log {\n@@ -6119,6 +6136,8 @@ struct rtw89_fw_info {\n \tstruct rtw89_fw_elm_info elm_info;\n \tstruct rtw89_fw_secure sec;\n \n+\tstruct list_head dup_data_list;\n+\n \tDECLARE_BITMAP(feature_map, NUM_OF_RTW89_FW_FEATURES);\n };\n \ndiff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c\nindex 0099c5c03e7a6..272d6219e6a66 100644\n--- a/drivers/net/wireless/realtek/rtw89/fw.c\n+++ b/drivers/net/wireless/realtek/rtw89/fw.c\n@@ -3,6 +3,7 @@\n  */\n \n #include \u003clinux/if_arp.h\u003e\n+#include \u003clinux/vmalloc.h\u003e\n #include \"cam.h\"\n #include \"chan.h\"\n #include \"coex.h\"\n@@ -137,6 +138,56 @@ int rtw89_fw_check_rdy(struct rtw89_dev *rtwdev, enum rtw89_fwdl_check_type type\n \treturn 0;\n }\n \n+static const void *__rtw89_fw_elem_dup_if_needed(struct rtw89_dev *rtwdev,\n+\t\t\t\t\t\t const void *src, size_t len,\n+\t\t\t\t\t\t bool is_fw_suit)\n+{\n+\tstruct rtw89_fw_req_info *fw_req = \u0026rtwdev-\u003efw.req;\n+\tvoid *dup;\n+\n+\tif (!fw_req-\u003efree_after_probe)\n+\t\treturn src;\n+\n+\tif (is_fw_suit) {\n+\t\tdup = vmalloc(len);\n+\t\tif (dup)\n+\t\t\tmemcpy(dup, src, len);\n+\t} else {\n+\t\tdup = devm_kmemdup(rtwdev-\u003edev, src, len, GFP_KERNEL);\n+\t}\n+\n+\tif (!dup) {\n+\t\t/* If failed to memdup, fallback to point to firmware-\u003edata. */\n+\t\tfw_req-\u003efree_after_probe = false;\n+\t\treturn src;\n+\t}\n+\n+\treturn dup;\n+}\n+\n+static const void *rtw89_fw_elem_dup_if_needed(struct rtw89_dev *rtwdev,\n+\t\t\t\t\t       const struct rtw89_fw_element_hdr *elm)\n+{\n+\tsize_t len = sizeof(*elm) + le32_to_cpu(elm-\u003esize);\n+\n+\treturn __rtw89_fw_elem_dup_if_needed(rtwdev, elm, len, false);\n+}\n+\n+static const void *rtw89_fw_suit_data_dup_if_needed(struct rtw89_dev *rtwdev,\n+\t\t\t\t\t\t    struct rtw89_fw_suit *fw_suit,\n+\t\t\t\t\t\t    const void *src)\n+{\n+\tstruct rtw89_fw_info *fw = \u0026rtwdev-\u003efw;\n+\tconst void *dup;\n+\n+\tdup = __rtw89_fw_elem_dup_if_needed(rtwdev, src, fw_suit-\u003esize, true);\n+\n+\tif (dup != src)\n+\t\tlist_add_tail(\u0026fw_suit-\u003elist, \u0026fw-\u003edup_data_list);\n+\n+\treturn dup;\n+}\n+\n static int rtw89_fw_hdr_parser_v0(struct rtw89_dev *rtwdev, const u8 *fw, u32 len,\n \t\t\t\t  struct rtw89_fw_bin_info *info)\n {\n@@ -609,6 +660,8 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,\n \tconst struct rtw89_mfw_hdr *mfw_hdr;\n \tconst u8 *mfw = firmware-\u003edata;\n \tu32 mfw_len = firmware-\u003esize;\n+\tconst u8 *mfw_info_ptr;\n+\tu32 mfw_info_size;\n \tint ret;\n \tint i;\n \n@@ -618,8 +671,8 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,\n \t\t/* legacy firmware support normal type only */\n \t\tif (type != RTW89_FW_NORMAL)\n \t\t\treturn -EINVAL;\n-\t\tfw_suit-\u003edata = mfw;\n \t\tfw_suit-\u003esize = mfw_len;\n+\t\tfw_suit-\u003edata = rtw89_fw_suit_data_dup_if_needed(rtwdev, fw_suit, mfw);\n \t\treturn 0;\n \t}\n \n@@ -654,14 +707,17 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,\n \treturn -ENOENT;\n \n found:\n-\tfw_suit-\u003edata = mfw + le32_to_cpu(mfw_info-\u003eshift);\n-\tfw_suit-\u003esize = le32_to_cpu(mfw_info-\u003esize);\n+\tmfw_info_ptr = mfw + le32_to_cpu(mfw_info-\u003eshift);\n+\tmfw_info_size = le32_to_cpu(mfw_info-\u003esize);\n \n-\tif (fw_suit-\u003edata + fw_suit-\u003esize \u003e mfw + mfw_len) {\n+\tif (mfw_info_ptr + mfw_info_size \u003e mfw + mfw_len) {\n \t\trtw89_err(rtwdev, \"fw_suit %d out of address\\n\", type);\n \t\treturn -EFAULT;\n \t}\n \n+\tfw_suit-\u003esize = mfw_info_size;\n+\tfw_suit-\u003edata = rtw89_fw_suit_data_dup_if_needed(rtwdev, fw_suit, mfw_info_ptr);\n+\n \treturn 0;\n }\n \n@@ -799,8 +855,9 @@ int __rtw89_fw_recognize_from_elm(struct rtw89_dev *rtwdev,\n \tif (fw_suit-\u003edata)\n \t\treturn 1; /* ignore this element (a firmware is taken already) */\n \n-\tfw_suit-\u003edata = elm-\u003eu.bbmcu.contents;\n \tfw_suit-\u003esize = le32_to_cpu(elm-\u003esize);\n+\tfw_suit-\u003edata =\n+\t\trtw89_fw_suit_data_dup_if_needed(rtwdev, fw_suit, elm-\u003eu.bbmcu.contents);\n \n \treturn rtw89_fw_update_ver(rtwdev, type, fw_suit);\n }\n@@ -1216,7 +1273,12 @@ int rtw89_fw_recognize_txpwr_from_elm(struct rtw89_dev *rtwdev,\n \tconf-\u003erfe_type = txpwr_elm-\u003erfe_type;\n \tconf-\u003eent_sz = txpwr_elm-\u003eent_sz;\n \tconf-\u003enum_ents = le32_to_cpu(txpwr_elm-\u003enum_ents);\n+\t/*\n+\t * The conf-\u003edata is used by rtw89_core_setup_rfe_parms() to do format\n+\t * conversion before releasing firmware. No need to duplicate.\n+\t */\n \tconf-\u003edata = txpwr_elm-\u003econtent;\n+\n \treturn 0;\n }\n \n@@ -1227,6 +1289,7 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev,\n {\n \tstruct rtw89_fw_elm_info *elm_info = \u0026rtwdev-\u003efw.elm_info;\n \tconst struct rtw89_chip_info *chip = rtwdev-\u003echip;\n+\tconst struct rtw89_fw_element_hdr *elm_dup;\n \tstruct rtw89_hal *hal = \u0026rtwdev-\u003ehal;\n \tu16 aid = le16_to_cpu(elm-\u003eaid);\n \tu32 needed_bitmap = 0;\n@@ -1257,6 +1320,8 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev,\n \tif (!elm_info-\u003etxpwr_trk)\n \t\treturn -ENOMEM;\n \n+\telm_dup = rtw89_fw_elem_dup_if_needed(rtwdev, elm);\n+\n \tfor (type = 0; bitmap; type++, bitmap \u003e\u003e= 1) {\n \t\tif (!(bitmap \u0026 BIT(0)))\n \t\t\tcontinue;\n@@ -1273,10 +1338,10 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev,\n \t\telse\n \t\t\tbreak;\n \n-\t\telm_info-\u003etxpwr_trk-\u003edelta[type] = \u0026elm-\u003eu.txpwr_trk.contents[offset];\n+\t\telm_info-\u003etxpwr_trk-\u003edelta[type] = \u0026elm_dup-\u003eu.txpwr_trk.contents[offset];\n \n \t\toffset += subband;\n-\t\tif (offset * DELTA_SWINGIDX_SIZE \u003e le32_to_cpu(elm-\u003esize))\n+\t\tif (offset * DELTA_SWINGIDX_SIZE \u003e le32_to_cpu(elm_dup-\u003esize))\n \t\t\tgoto err;\n \t}\n \n@@ -1311,7 +1376,7 @@ int rtw89_build_rfk_log_fmt_from_elm(struct rtw89_dev *rtwdev,\n \tif (rfk_id \u003e= RTW89_PHY_C2H_RFK_LOG_FUNC_NUM)\n \t\treturn 1;\n \n-\telm_info-\u003erfk_log_fmt-\u003eelm[rfk_id] = elm;\n+\telm_info-\u003erfk_log_fmt-\u003eelm[rfk_id] = rtw89_fw_elem_dup_if_needed(rtwdev, elm);\n \n \treturn 0;\n }\n@@ -1418,7 +1483,7 @@ int rtw89_build_afe_pwr_seq_from_elm(struct rtw89_dev *rtwdev,\n {\n \tstruct rtw89_fw_elm_info *elm_info = \u0026rtwdev-\u003efw.elm_info;\n \n-\telm_info-\u003eafe = elm;\n+\telm_info-\u003eafe = rtw89_fw_elem_dup_if_needed(rtwdev, elm);\n \n \treturn 0;\n }\n@@ -1430,7 +1495,7 @@ int rtw89_recognize_diag_mac_from_elm(struct rtw89_dev *rtwdev,\n {\n \tstruct rtw89_fw_elm_info *elm_info = \u0026rtwdev-\u003efw.elm_info;\n \n-\telm_info-\u003ediag_mac = elm;\n+\telm_info-\u003ediag_mac = rtw89_fw_elem_dup_if_needed(rtwdev, elm);\n \n \treturn 0;\n }\n@@ -1456,7 +1521,7 @@ int rtw89_build_tx_comp_from_elm(struct rtw89_dev *rtwdev,\n \telse if (elm_info-\u003etx_comp)\n \t\treturn 1; /* ignore if an element is existing */\n \n-\telm_info-\u003etx_comp = elm;\n+\telm_info-\u003etx_comp = rtw89_fw_elem_dup_if_needed(rtwdev, elm);\n \n \treturn 0;\n }\n@@ -1557,30 +1622,17 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {\n \t},\n };\n \n-int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)\n+static int __rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev,\n+\t\t\t\t\t const struct firmware *firmware,\n+\t\t\t\t\t u32 offset, u32 skipped_elements,\n+\t\t\t\t\t u32 *unrecognized_elements)\n {\n-\tstruct rtw89_fw_info *fw_info = \u0026rtwdev-\u003efw;\n-\tconst struct firmware *firmware = fw_info-\u003ereq.firmware;\n-\tconst struct rtw89_chip_info *chip = rtwdev-\u003echip;\n-\tu32 unrecognized_elements = chip-\u003eneeded_fw_elms;\n \tconst struct rtw89_fw_element_handler *handler;\n \tconst struct rtw89_fw_element_hdr *hdr;\n-\tbool transition;\n \tu32 elm_size;\n \tu32 elem_id;\n-\tu32 offset;\n \tint ret;\n \n-\tBUILD_BUG_ON(sizeof(chip-\u003eneeded_fw_elms) * 8 \u003c RTW89_FW_ELEMENT_ID_NUM);\n-\n-\ttransition = !!((chip-\u003eneeded_fw_elms \u0026 BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION)));\n-\tunrecognized_elements \u0026= ~BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION);\n-\n-\toffset = rtw89_mfw_get_size(rtwdev);\n-\toffset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN);\n-\tif (offset == 0)\n-\t\treturn -EINVAL;\n-\n \twhile (offset + sizeof(*hdr) \u003c firmware-\u003esize) {\n \t\thdr = (const struct rtw89_fw_element_hdr *)(firmware-\u003edata + offset);\n \n@@ -1593,6 +1645,8 @@ int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)\n \t\telem_id = le32_to_cpu(hdr-\u003eid);\n \t\tif (elem_id \u003e= ARRAY_SIZE(__fw_element_handlers))\n \t\t\tgoto next;\n+\t\tif (skipped_elements \u0026 BIT(elem_id))\n+\t\t\tgoto next;\n \n \t\thandler = \u0026__fw_element_handlers[elem_id];\n \t\tif (!handler-\u003efn)\n@@ -1608,12 +1662,105 @@ int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)\n \t\t\trtw89_info(rtwdev, \"Firmware element %s version: %4ph\\n\",\n \t\t\t\t   handler-\u003ename, hdr-\u003ever);\n \n-\t\tunrecognized_elements \u0026= ~BIT(elem_id);\n+\t\t*unrecognized_elements \u0026= ~BIT(elem_id);\n next:\n \t\toffset += sizeof(*hdr) + elm_size;\n \t\toffset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN);\n \t}\n \n+\treturn 0;\n+}\n+\n+static int rtw89_board_elm_seek(const struct firmware *board_elm,\n+\t\t\t\tenum rtw89_board_id board_id,\n+\t\t\t\tstruct firmware *pseudo_fw)\n+{\n+\tconst struct rtw89_board_elm_hdr *hdr;\n+\tconst struct rtw89_board_elm_ent *ent;\n+\tu32 ofst;\n+\tu32 size;\n+\tu16 num;\n+\n+\tif (board_elm-\u003esize \u003c sizeof(*hdr))\n+\t\treturn -EINVAL;\n+\n+\thdr = (const struct rtw89_board_elm_hdr *)board_elm-\u003edata;\n+\tif (hdr-\u003esig != RTW89_BOARD_ELM_SIG)\n+\t\treturn -EINVAL;\n+\n+\tBUILD_BUG_ON(U16_MAX \u003c NUM_OF_RTW89_BOARD_IDS);\n+\n+\tnum = le16_to_cpu(hdr-\u003enum);\n+\tif (unlikely(board_elm-\u003esize \u003c struct_size(hdr, ents, num)))\n+\t\treturn -EFAULT;\n+\tif (num \u003c= board_id)\n+\t\treturn -ENOENT;\n+\n+\tent = \u0026hdr-\u003eents[board_id];\n+\n+\tofst = le32_to_cpu(ent-\u003eofst);\n+\tsize = le32_to_cpu(ent-\u003esize);\n+\n+\tif (unlikely(board_elm-\u003esize \u003c ofst + size))\n+\t\treturn -EFAULT;\n+\n+\tmemset(pseudo_fw, 0, sizeof(*pseudo_fw));\n+\n+\tpseudo_fw-\u003edata = (const void *)board_elm-\u003edata + ofst;\n+\tpseudo_fw-\u003esize = size;\n+\n+\treturn 0;\n+}\n+\n+int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)\n+{\n+\tconst struct rtw89_board_variant *board = rtwdev-\u003eboard;\n+\tstruct rtw89_fw_req_info *fw_req = \u0026rtwdev-\u003efw.req;\n+\tconst struct firmware *firmware = fw_req-\u003efirmware;\n+\tconst struct rtw89_chip_info *chip = rtwdev-\u003echip;\n+\tu32 unrecognized_elements = chip-\u003eneeded_fw_elms;\n+\tu32 skipped_elements = 0;\n+\tbool transition;\n+\tu32 offset;\n+\tint ret;\n+\n+\tBUILD_BUG_ON(sizeof(chip-\u003eneeded_fw_elms) * 8 \u003c RTW89_FW_ELEMENT_ID_NUM);\n+\tBUILD_BUG_ON(!__same_type(unrecognized_elements, chip-\u003eneeded_fw_elms));\n+\tBUILD_BUG_ON(!__same_type(skipped_elements, chip-\u003eneeded_fw_elms));\n+\n+\ttransition = !!((chip-\u003eneeded_fw_elms \u0026 BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION)));\n+\tunrecognized_elements \u0026= ~BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION);\n+\n+\tif (fw_req-\u003eboard_elm) {\n+\t\tstruct firmware pseudo_fw;\n+\n+\t\tret = rtw89_board_elm_seek(fw_req-\u003eboard_elm, board-\u003eid, \u0026pseudo_fw);\n+\t\tif (ret)\n+\t\t\tgoto mfw;\n+\n+\t\tret = __rtw89_fw_recognize_elements(rtwdev, \u0026pseudo_fw, 0, 0,\n+\t\t\t\t\t\t    \u0026unrecognized_elements);\n+\t\tif (ret)\n+\t\t\tgoto mfw;\n+\n+\t\tskipped_elements = chip-\u003eneeded_fw_elms \u0026 ~unrecognized_elements;\n+\n+\t\trtw89_debug(rtwdev, RTW89_DBG_FW, \"apply elements 0x08%x from %s\\n\",\n+\t\t\t    skipped_elements, RTW89_FWNAME_BOARD_ELM);\n+\t}\n+\n+mfw:\n+\toffset = rtw89_mfw_get_size(rtwdev);\n+\toffset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN);\n+\tif (offset == 0)\n+\t\treturn -EINVAL;\n+\n+\tret = __rtw89_fw_recognize_elements(rtwdev, firmware, offset,\n+\t\t\t\t\t    skipped_elements,\n+\t\t\t\t\t    \u0026unrecognized_elements);\n+\tif (ret)\n+\t\treturn ret;\n+\n \tif (unrecognized_elements) {\n \t\tif (transition) {\n \t\t\trtw89_info(rtwdev, \"NOTE: This firmware is going to be obsolete!\\n\"\n@@ -2070,13 +2217,13 @@ static int rtw89_load_firmware_req(struct rtw89_dev *rtwdev,\n \t\t\t\t   struct rtw89_fw_req_info *req,\n \t\t\t\t   const char *fw_name, bool nowarn)\n {\n-\tint ret;\n+\tconst struct rtw89_board_variant *board = rtwdev-\u003eboard;\n+\tint ret = 0;\n \n \tif (req-\u003efirmware) {\n \t\trtw89_debug(rtwdev, RTW89_DBG_FW,\n \t\t\t    \"full firmware has been early requested\\n\");\n-\t\tcomplete_all(\u0026req-\u003ecompletion);\n-\t\treturn 0;\n+\t\tgoto out;\n \t}\n \n \tif (nowarn)\n@@ -2084,6 +2231,11 @@ static int rtw89_load_firmware_req(struct rtw89_dev *rtwdev,\n \telse\n \t\tret = request_firmware(\u0026req-\u003efirmware, fw_name, rtwdev-\u003edev);\n \n+out:\n+\tif (board \u0026\u0026 board-\u003eid)\n+\t\tfirmware_request_nowarn(\u0026req-\u003eboard_elm, RTW89_FWNAME_BOARD_ELM, rtwdev-\u003edev);\n+\n+\treq-\u003efree_after_probe = req-\u003efirmware \u0026\u0026 req-\u003efirmware-\u003esize \u003e 0x200000;\n \tcomplete_all(\u0026req-\u003ecompletion);\n \n \treturn ret;\n@@ -2126,23 +2278,41 @@ static void rtw89_unload_firmware_elements(struct rtw89_dev *rtwdev)\n \tkfree(elm_info-\u003erfk_log_fmt);\n }\n \n-void rtw89_unload_firmware(struct rtw89_dev *rtwdev)\n+static void rtw89_unload_firmware_dup_data(struct rtw89_dev *rtwdev)\n {\n \tstruct rtw89_fw_info *fw = \u0026rtwdev-\u003efw;\n+\tstruct rtw89_fw_suit *fw_suit;\n \n-\tcancel_work_sync(\u0026rtwdev-\u003eload_firmware_work);\n+\tlist_for_each_entry(fw_suit, \u0026fw-\u003edup_data_list, list)\n+\t\tvfree(fw_suit-\u003edata);\n+}\n \n-\tif (fw-\u003ereq.firmware) {\n-\t\trelease_firmware(fw-\u003ereq.firmware);\n+void __rtw89_unload_firmware(struct rtw89_dev *rtwdev)\n+{\n+\tstruct rtw89_fw_info *fw = \u0026rtwdev-\u003efw;\n \n-\t\t/* assign NULL back in case rtw89_free_ieee80211_hw()\n-\t\t * try to release the same one again.\n-\t\t */\n-\t\tfw-\u003ereq.firmware = NULL;\n-\t}\n+\trelease_firmware(fw-\u003ereq.firmware);\n+\trelease_firmware(fw-\u003ereq.board_elm);\n+\n+\t/*\n+\t * Directly call this to free firmware early in normal flow. Assign\n+\t * NULL back in case rtw89_free_ieee80211_hw() or tw89_core_deinit()\n+\t * try to release the same one again in error handling paths.\n+\t */\n+\tfw-\u003ereq.firmware = NULL;\n+\tfw-\u003ereq.board_elm = NULL;\n+}\n+\n+void rtw89_unload_firmware(struct rtw89_dev *rtwdev)\n+{\n+\tstruct rtw89_fw_info *fw = \u0026rtwdev-\u003efw;\n+\n+\tcancel_work_sync(\u0026rtwdev-\u003eload_firmware_work);\n+\t__rtw89_unload_firmware(rtwdev);\n \n \tkfree(fw-\u003elog.fmts);\n \trtw89_unload_firmware_elements(rtwdev);\n+\trtw89_unload_firmware_dup_data(rtwdev);\n }\n \n static u32 rtw89_fw_log_get_fmt_idx(struct rtw89_dev *rtwdev, u32 fmt_id)\ndiff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h\nindex 375ca0a243a65..0e2473ebe7564 100644\n--- a/drivers/net/wireless/realtek/rtw89/fw.h\n+++ b/drivers/net/wireless/realtek/rtw89/fw.h\n@@ -4346,6 +4346,7 @@ struct rtw89_h2c_ofld {\n #define RTW89_H2C_OFLD_W0_RX_TP GENMASK(27, 18)\n \n #define RTW89_MFW_SIG\t0xFF\n+#define RTW89_BOARD_ELM_SIG 0xFE\n \n struct rtw89_mfw_info {\n \tu8 cv;\n@@ -4371,6 +4372,19 @@ struct rtw89_mfw_hdr {\n \tstruct rtw89_mfw_info info[];\n } __packed;\n \n+struct rtw89_board_elm_ent {\n+\tu8 rsvd[8];\n+\t__le32 ofst; /* offset from beginning of rtw89_board_elm_hdr */\n+\t__le32 size;\n+} __packed;\n+\n+struct rtw89_board_elm_hdr {\n+\tu8 sig; /* RTW89_BOARD_ELM_SIG */\n+\tu8 rsvd[13];\n+\t__le16 num;\n+\tstruct rtw89_board_elm_ent ents[] __counted_by_le(num);\n+} __packed;\n+\n struct rtw89_fw_logsuit_hdr {\n \t__le32 rsvd;\n \t__le32 count;\n@@ -5370,6 +5384,7 @@ rtw89_early_fw_feature_recognize(struct device *device,\n int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,\n \t\t      bool include_bb);\n void rtw89_load_firmware_work(struct work_struct *work);\n+void __rtw89_unload_firmware(struct rtw89_dev *rtwdev);\n void rtw89_unload_firmware(struct rtw89_dev *rtwdev);\n int rtw89_wait_firmware_completion(struct rtw89_dev *rtwdev);\n int rtw89_fw_log_prepare(struct rtw89_dev *rtwdev);\ndiff --git a/drivers/net/wireless/realtek/rtw89/led.c b/drivers/net/wireless/realtek/rtw89/led.c\nindex ae74b7075d7cd..18bfe626ef50f 100644\n--- a/drivers/net/wireless/realtek/rtw89/led.c\n+++ b/drivers/net/wireless/realtek/rtw89/led.c\n@@ -104,7 +104,7 @@ void rtw89_led_init(struct rtw89_dev *rtwdev)\n \t/* single-GPIO monochrome LED is the only supported layout */\n \tBUILD_BUG_ON(ARRAY_SIZE(rtw89_common_led_gpios) != 1);\n \n-\tif (board)\n+\tif (board \u0026\u0026 board-\u003eled_desc)\n \t\tdesc = board-\u003eled_desc;\n \tif (!desc-\u003en_gpio || desc-\u003en_gpio \u003e RTW89_LED_MAX_NUM)\n \t\treturn;\ndiff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c\nindex 2dec9b8454815..a87ca8566d945 100644\n--- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c\n+++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c\n@@ -89,6 +89,7 @@ static const struct rtw89_led_desc rtw8852cu_valve_led_desc = {\n };\n \n static const struct rtw89_board_variant rtw89_8852cu_valve_board = {\n+\t.id = RTW89_BOARD_ID(28de, 2432),\n \t.led_desc = \u0026rtw8852cu_valve_led_desc,\n };\n \n"
}

2/1 2026/09/25 10:21 agent patch-evaluator
2m Model:
core

Args:
null

Results:
{
  "EnableConfigs": [
    "RTW89",
    "RTW89_CORE",
    "RTW89_USB",
    "RTW89_8852CU",
    "MAC80211",
    "USB"
  ],
  "FocusSymbols": [
    "rtw89_fw_recognize_elements",
    "rtw89_board_elm_seek",
    "rtw89_chip_info_setup",
    "rtw89_load_firmware_work",
    "rtw89_unload_firmware",
    "__rtw89_unload_firmware",
    "rtw89_led_init",
    "rtw89_usb_probe"
  ],
  "Reasoning": "The patch introduces support for board firmware element parsing and early firmware unloading with element/fw_suit data duplication in the rtw89 wireless driver. Because rtw89 supports USB devices (such as RTW8852CU, explicitly modified in this patch series), this driver code is reachable via virtual USB buses (e.g. USB Raw Gadget in QEMU/KVM test environments). The changes introduce new binary parsing routines, memory duplication, and lifecycle deallocation logic worth fuzzing.",
  "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 0ffbabfaba5b514a37c2d74aa098f3cfc4bcc0c8
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Sep 25 10:21:20 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index dacc2d889a467..e893235f962af 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -6973,6 +6973,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)
 	}
 	INIT_LIST_HEAD(&rtwdev->scan_info.chan_list);
 	INIT_LIST_HEAD(&rtwdev->tx_waits);
+	INIT_LIST_HEAD(&rtwdev->fw.dup_data_list);
 	INIT_WORK(&rtwdev->ba_work, rtw89_core_ba_work);
 	INIT_WORK(&rtwdev->txq_work, rtw89_core_txq_work);
 	INIT_DELAYED_WORK(&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work);
@@ -7372,6 +7373,7 @@ void rtw89_core_rfkill_poll(struct rtw89_dev *rtwdev, bool force)
 
 int rtw89_chip_info_setup(struct rtw89_dev *rtwdev)
 {
+	struct rtw89_fw_req_info *fw_req = &rtwdev->fw.req;
 	struct rtw89_efuse *efuse = &rtwdev->efuse;
 	struct rtw89_hal *hal = &rtwdev->hal;
 	int ret;
@@ -7421,6 +7423,9 @@ int rtw89_chip_info_setup(struct rtw89_dev *rtwdev)
 		   hal->cid, hal->cv, hal->aid, hal->acv, efuse->rfe_type);
 
 out:
+	if (fw_req->free_after_probe)
+		__rtw89_unload_firmware(rtwdev);
+
 	rtw89_mac_pwr_off(rtwdev);
 
 	return ret;
@@ -7723,6 +7728,7 @@ void rtw89_free_ieee80211_hw(struct rtw89_dev *rtwdev)
 }
 EXPORT_SYMBOL(rtw89_free_ieee80211_hw);
 
+MODULE_FIRMWARE(RTW89_FWNAME_BOARD_ELM);
 MODULE_AUTHOR("Realtek Corporation");
 MODULE_DESCRIPTION("Realtek 802.11ax wireless core module");
 MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 2b21d969ece7b..e942ee1fd21bb 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -5274,6 +5274,8 @@ struct rtw89_reg_imr {
 #define RTW89_GEN_MODULE_FWNAME(basename, maxformat) \
 	basename RTW89_GEN_MODULE_FWNAME_FMT(maxformat) ".bin"
 
+#define RTW89_FWNAME_BOARD_ELM "rtw89/board_elm.bin"
+
 struct rtw89_fw_def {
 	const char *fw_basename;
 	u8 fw_format_max;
@@ -5865,7 +5867,19 @@ struct rtw89_chip_variant {
 	const struct rtw89_qta_def *qta_def_override;
 };
 
+/* @pid can have extra suffix to describe the variant if necessary */
+#define RTW89_BOARD_ID(vid, pid) \
+	RTW89_BOARD_ID_##vid##_##pid
+
+enum rtw89_board_id {
+	RTW89_BOARD_ID(0000, 0000) = 0, /* reserve 0 for non-variant */
+	RTW89_BOARD_ID(28de, 2432) = 1,
+
+	NUM_OF_RTW89_BOARD_IDS,
+};
+
 struct rtw89_board_variant {
+	enum rtw89_board_id id;
 	const struct rtw89_led_desc *led_desc;
 };
 
@@ -6029,6 +6043,7 @@ enum rtw89_fw_feature {
 struct rtw89_fw_suit {
 	enum rtw89_fw_type type;
 	const u8 *data;
+	struct list_head list;
 	u32 size;
 	u8 major_ver;
 	u8 minor_ver;
@@ -6063,7 +6078,9 @@ struct rtw89_fw_suit {
 
 struct rtw89_fw_req_info {
 	const struct firmware *firmware;
+	const struct firmware *board_elm;
 	struct completion completion;
+	bool free_after_probe;
 };
 
 struct rtw89_fw_log {
@@ -6119,6 +6136,8 @@ struct rtw89_fw_info {
 	struct rtw89_fw_elm_info elm_info;
 	struct rtw89_fw_secure sec;
 
+	struct list_head dup_data_list;
+
 	DECLARE_BITMAP(feature_map, NUM_OF_RTW89_FW_FEATURES);
 };
 
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 0099c5c03e7a6..272d6219e6a66 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -3,6 +3,7 @@
  */
 
 #include <linux/if_arp.h>
+#include <linux/vmalloc.h>
 #include "cam.h"
 #include "chan.h"
 #include "coex.h"
@@ -137,6 +138,56 @@ int rtw89_fw_check_rdy(struct rtw89_dev *rtwdev, enum rtw89_fwdl_check_type type
 	return 0;
 }
 
+static const void *__rtw89_fw_elem_dup_if_needed(struct rtw89_dev *rtwdev,
+						 const void *src, size_t len,
+						 bool is_fw_suit)
+{
+	struct rtw89_fw_req_info *fw_req = &rtwdev->fw.req;
+	void *dup;
+
+	if (!fw_req->free_after_probe)
+		return src;
+
+	if (is_fw_suit) {
+		dup = vmalloc(len);
+		if (dup)
+			memcpy(dup, src, len);
+	} else {
+		dup = devm_kmemdup(rtwdev->dev, src, len, GFP_KERNEL);
+	}
+
+	if (!dup) {
+		/* If failed to memdup, fallback to point to firmware->data. */
+		fw_req->free_after_probe = false;
+		return src;
+	}
+
+	return dup;
+}
+
+static const void *rtw89_fw_elem_dup_if_needed(struct rtw89_dev *rtwdev,
+					       const struct rtw89_fw_element_hdr *elm)
+{
+	size_t len = sizeof(*elm) + le32_to_cpu(elm->size);
+
+	return __rtw89_fw_elem_dup_if_needed(rtwdev, elm, len, false);
+}
+
+static const void *rtw89_fw_suit_data_dup_if_needed(struct rtw89_dev *rtwdev,
+						    struct rtw89_fw_suit *fw_suit,
+						    const void *src)
+{
+	struct rtw89_fw_info *fw = &rtwdev->fw;
+	const void *dup;
+
+	dup = __rtw89_fw_elem_dup_if_needed(rtwdev, src, fw_suit->size, true);
+
+	if (dup != src)
+		list_add_tail(&fw_suit->list, &fw->dup_data_list);
+
+	return dup;
+}
+
 static int rtw89_fw_hdr_parser_v0(struct rtw89_dev *rtwdev, const u8 *fw, u32 len,
 				  struct rtw89_fw_bin_info *info)
 {
@@ -609,6 +660,8 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
 	const struct rtw89_mfw_hdr *mfw_hdr;
 	const u8 *mfw = firmware->data;
 	u32 mfw_len = firmware->size;
+	const u8 *mfw_info_ptr;
+	u32 mfw_info_size;
 	int ret;
 	int i;
 
@@ -618,8 +671,8 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
 		/* legacy firmware support normal type only */
 		if (type != RTW89_FW_NORMAL)
 			return -EINVAL;
-		fw_suit->data = mfw;
 		fw_suit->size = mfw_len;
+		fw_suit->data = rtw89_fw_suit_data_dup_if_needed(rtwdev, fw_suit, mfw);
 		return 0;
 	}
 
@@ -654,14 +707,17 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
 	return -ENOENT;
 
 found:
-	fw_suit->data = mfw + le32_to_cpu(mfw_info->shift);
-	fw_suit->size = le32_to_cpu(mfw_info->size);
+	mfw_info_ptr = mfw + le32_to_cpu(mfw_info->shift);
+	mfw_info_size = le32_to_cpu(mfw_info->size);
 
-	if (fw_suit->data + fw_suit->size > mfw + mfw_len) {
+	if (mfw_info_ptr + mfw_info_size > mfw + mfw_len) {
 		rtw89_err(rtwdev, "fw_suit %d out of address\n", type);
 		return -EFAULT;
 	}
 
+	fw_suit->size = mfw_info_size;
+	fw_suit->data = rtw89_fw_suit_data_dup_if_needed(rtwdev, fw_suit, mfw_info_ptr);
+
 	return 0;
 }
 
@@ -799,8 +855,9 @@ int __rtw89_fw_recognize_from_elm(struct rtw89_dev *rtwdev,
 	if (fw_suit->data)
 		return 1; /* ignore this element (a firmware is taken already) */
 
-	fw_suit->data = elm->u.bbmcu.contents;
 	fw_suit->size = le32_to_cpu(elm->size);
+	fw_suit->data =
+		rtw89_fw_suit_data_dup_if_needed(rtwdev, fw_suit, elm->u.bbmcu.contents);
 
 	return rtw89_fw_update_ver(rtwdev, type, fw_suit);
 }
@@ -1216,7 +1273,12 @@ int rtw89_fw_recognize_txpwr_from_elm(struct rtw89_dev *rtwdev,
 	conf->rfe_type = txpwr_elm->rfe_type;
 	conf->ent_sz = txpwr_elm->ent_sz;
 	conf->num_ents = le32_to_cpu(txpwr_elm->num_ents);
+	/*
+	 * The conf->data is used by rtw89_core_setup_rfe_parms() to do format
+	 * conversion before releasing firmware. No need to duplicate.
+	 */
 	conf->data = txpwr_elm->content;
+
 	return 0;
 }
 
@@ -1227,6 +1289,7 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev,
 {
 	struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
 	const struct rtw89_chip_info *chip = rtwdev->chip;
+	const struct rtw89_fw_element_hdr *elm_dup;
 	struct rtw89_hal *hal = &rtwdev->hal;
 	u16 aid = le16_to_cpu(elm->aid);
 	u32 needed_bitmap = 0;
@@ -1257,6 +1320,8 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev,
 	if (!elm_info->txpwr_trk)
 		return -ENOMEM;
 
+	elm_dup = rtw89_fw_elem_dup_if_needed(rtwdev, elm);
+
 	for (type = 0; bitmap; type++, bitmap >>= 1) {
 		if (!(bitmap & BIT(0)))
 			continue;
@@ -1273,10 +1338,10 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev,
 		else
 			break;
 
-		elm_info->txpwr_trk->delta[type] = &elm->u.txpwr_trk.contents[offset];
+		elm_info->txpwr_trk->delta[type] = &elm_dup->u.txpwr_trk.contents[offset];
 
 		offset += subband;
-		if (offset * DELTA_SWINGIDX_SIZE > le32_to_cpu(elm->size))
+		if (offset * DELTA_SWINGIDX_SIZE > le32_to_cpu(elm_dup->size))
 			goto err;
 	}
 
@@ -1311,7 +1376,7 @@ int rtw89_build_rfk_log_fmt_from_elm(struct rtw89_dev *rtwdev,
 	if (rfk_id >= RTW89_PHY_C2H_RFK_LOG_FUNC_NUM)
 		return 1;
 
-	elm_info->rfk_log_fmt->elm[rfk_id] = elm;
+	elm_info->rfk_log_fmt->elm[rfk_id] = rtw89_fw_elem_dup_if_needed(rtwdev, elm);
 
 	return 0;
 }
@@ -1418,7 +1483,7 @@ int rtw89_build_afe_pwr_seq_from_elm(struct rtw89_dev *rtwdev,
 {
 	struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
 
-	elm_info->afe = elm;
+	elm_info->afe = rtw89_fw_elem_dup_if_needed(rtwdev, elm);
 
 	return 0;
 }
@@ -1430,7 +1495,7 @@ int rtw89_recognize_diag_mac_from_elm(struct rtw89_dev *rtwdev,
 {
 	struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
 
-	elm_info->diag_mac = elm;
+	elm_info->diag_mac = rtw89_fw_elem_dup_if_needed(rtwdev, elm);
 
 	return 0;
 }
@@ -1456,7 +1521,7 @@ int rtw89_build_tx_comp_from_elm(struct rtw89_dev *rtwdev,
 	else if (elm_info->tx_comp)
 		return 1; /* ignore if an element is existing */
 
-	elm_info->tx_comp = elm;
+	elm_info->tx_comp = rtw89_fw_elem_dup_if_needed(rtwdev, elm);
 
 	return 0;
 }
@@ -1557,30 +1622,17 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
 	},
 };
 
-int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
+static int __rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev,
+					 const struct firmware *firmware,
+					 u32 offset, u32 skipped_elements,
+					 u32 *unrecognized_elements)
 {
-	struct rtw89_fw_info *fw_info = &rtwdev->fw;
-	const struct firmware *firmware = fw_info->req.firmware;
-	const struct rtw89_chip_info *chip = rtwdev->chip;
-	u32 unrecognized_elements = chip->needed_fw_elms;
 	const struct rtw89_fw_element_handler *handler;
 	const struct rtw89_fw_element_hdr *hdr;
-	bool transition;
 	u32 elm_size;
 	u32 elem_id;
-	u32 offset;
 	int ret;
 
-	BUILD_BUG_ON(sizeof(chip->needed_fw_elms) * 8 < RTW89_FW_ELEMENT_ID_NUM);
-
-	transition = !!((chip->needed_fw_elms & BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION)));
-	unrecognized_elements &= ~BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION);
-
-	offset = rtw89_mfw_get_size(rtwdev);
-	offset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN);
-	if (offset == 0)
-		return -EINVAL;
-
 	while (offset + sizeof(*hdr) < firmware->size) {
 		hdr = (const struct rtw89_fw_element_hdr *)(firmware->data + offset);
 
@@ -1593,6 +1645,8 @@ int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
 		elem_id = le32_to_cpu(hdr->id);
 		if (elem_id >= ARRAY_SIZE(__fw_element_handlers))
 			goto next;
+		if (skipped_elements & BIT(elem_id))
+			goto next;
 
 		handler = &__fw_element_handlers[elem_id];
 		if (!handler->fn)
@@ -1608,12 +1662,105 @@ int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
 			rtw89_info(rtwdev, "Firmware element %s version: %4ph\n",
 				   handler->name, hdr->ver);
 
-		unrecognized_elements &= ~BIT(elem_id);
+		*unrecognized_elements &= ~BIT(elem_id);
 next:
 		offset += sizeof(*hdr) + elm_size;
 		offset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN);
 	}
 
+	return 0;
+}
+
+static int rtw89_board_elm_seek(const struct firmware *board_elm,
+				enum rtw89_board_id board_id,
+				struct firmware *pseudo_fw)
+{
+	const struct rtw89_board_elm_hdr *hdr;
+	const struct rtw89_board_elm_ent *ent;
+	u32 ofst;
+	u32 size;
+	u16 num;
+
+	if (board_elm->size < sizeof(*hdr))
+		return -EINVAL;
+
+	hdr = (const struct rtw89_board_elm_hdr *)board_elm->data;
+	if (hdr->sig != RTW89_BOARD_ELM_SIG)
+		return -EINVAL;
+
+	BUILD_BUG_ON(U16_MAX < NUM_OF_RTW89_BOARD_IDS);
+
+	num = le16_to_cpu(hdr->num);
+	if (unlikely(board_elm->size < struct_size(hdr, ents, num)))
+		return -EFAULT;
+	if (num <= board_id)
+		return -ENOENT;
+
+	ent = &hdr->ents[board_id];
+
+	ofst = le32_to_cpu(ent->ofst);
+	size = le32_to_cpu(ent->size);
+
+	if (unlikely(board_elm->size < ofst + size))
+		return -EFAULT;
+
+	memset(pseudo_fw, 0, sizeof(*pseudo_fw));
+
+	pseudo_fw->data = (const void *)board_elm->data + ofst;
+	pseudo_fw->size = size;
+
+	return 0;
+}
+
+int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
+{
+	const struct rtw89_board_variant *board = rtwdev->board;
+	struct rtw89_fw_req_info *fw_req = &rtwdev->fw.req;
+	const struct firmware *firmware = fw_req->firmware;
+	const struct rtw89_chip_info *chip = rtwdev->chip;
+	u32 unrecognized_elements = chip->needed_fw_elms;
+	u32 skipped_elements = 0;
+	bool transition;
+	u32 offset;
+	int ret;
+
+	BUILD_BUG_ON(sizeof(chip->needed_fw_elms) * 8 < RTW89_FW_ELEMENT_ID_NUM);
+	BUILD_BUG_ON(!__same_type(unrecognized_elements, chip->needed_fw_elms));
+	BUILD_BUG_ON(!__same_type(skipped_elements, chip->needed_fw_elms));
+
+	transition = !!((chip->needed_fw_elms & BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION)));
+	unrecognized_elements &= ~BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION);
+
+	if (fw_req->board_elm) {
+		struct firmware pseudo_fw;
+
+		ret = rtw89_board_elm_seek(fw_req->board_elm, board->id, &pseudo_fw);
+		if (ret)
+			goto mfw;
+
+		ret = __rtw89_fw_recognize_elements(rtwdev, &pseudo_fw, 0, 0,
+						    &unrecognized_elements);
+		if (ret)
+			goto mfw;
+
+		skipped_elements = chip->needed_fw_elms & ~unrecognized_elements;
+
+		rtw89_debug(rtwdev, RTW89_DBG_FW, "apply elements 0x08%x from %s\n",
+			    skipped_elements, RTW89_FWNAME_BOARD_ELM);
+	}
+
+mfw:
+	offset = rtw89_mfw_get_size(rtwdev);
+	offset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN);
+	if (offset == 0)
+		return -EINVAL;
+
+	ret = __rtw89_fw_recognize_elements(rtwdev, firmware, offset,
+					    skipped_elements,
+					    &unrecognized_elements);
+	if (ret)
+		return ret;
+
 	if (unrecognized_elements) {
 		if (transition) {
 			rtw89_info(rtwdev, "NOTE: This firmware is going to be obsolete!\n"
@@ -2070,13 +2217,13 @@ static int rtw89_load_firmware_req(struct rtw89_dev *rtwdev,
 				   struct rtw89_fw_req_info *req,
 				   const char *fw_name, bool nowarn)
 {
-	int ret;
+	const struct rtw89_board_variant *board = rtwdev->board;
+	int ret = 0;
 
 	if (req->firmware) {
 		rtw89_debug(rtwdev, RTW89_DBG_FW,
 			    "full firmware has been early requested\n");
-		complete_all(&req->completion);
-		return 0;
+		goto out;
 	}
 
 	if (nowarn)
@@ -2084,6 +2231,11 @@ static int rtw89_load_firmware_req(struct rtw89_dev *rtwdev,
 	else
 		ret = request_firmware(&req->firmware, fw_name, rtwdev->dev);
 
+out:
+	if (board && board->id)
+		firmware_request_nowarn(&req->board_elm, RTW89_FWNAME_BOARD_ELM, rtwdev->dev);
+
+	req->free_after_probe = req->firmware && req->firmware->size > 0x200000;
 	complete_all(&req->completion);
 
 	return ret;
@@ -2126,23 +2278,41 @@ static void rtw89_unload_firmware_elements(struct rtw89_dev *rtwdev)
 	kfree(elm_info->rfk_log_fmt);
 }
 
-void rtw89_unload_firmware(struct rtw89_dev *rtwdev)
+static void rtw89_unload_firmware_dup_data(struct rtw89_dev *rtwdev)
 {
 	struct rtw89_fw_info *fw = &rtwdev->fw;
+	struct rtw89_fw_suit *fw_suit;
 
-	cancel_work_sync(&rtwdev->load_firmware_work);
+	list_for_each_entry(fw_suit, &fw->dup_data_list, list)
+		vfree(fw_suit->data);
+}
 
-	if (fw->req.firmware) {
-		release_firmware(fw->req.firmware);
+void __rtw89_unload_firmware(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_fw_info *fw = &rtwdev->fw;
 
-		/* assign NULL back in case rtw89_free_ieee80211_hw()
-		 * try to release the same one again.
-		 */
-		fw->req.firmware = NULL;
-	}
+	release_firmware(fw->req.firmware);
+	release_firmware(fw->req.board_elm);
+
+	/*
+	 * Directly call this to free firmware early in normal flow. Assign
+	 * NULL back in case rtw89_free_ieee80211_hw() or tw89_core_deinit()
+	 * try to release the same one again in error handling paths.
+	 */
+	fw->req.firmware = NULL;
+	fw->req.board_elm = NULL;
+}
+
+void rtw89_unload_firmware(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_fw_info *fw = &rtwdev->fw;
+
+	cancel_work_sync(&rtwdev->load_firmware_work);
+	__rtw89_unload_firmware(rtwdev);
 
 	kfree(fw->log.fmts);
 	rtw89_unload_firmware_elements(rtwdev);
+	rtw89_unload_firmware_dup_data(rtwdev);
 }
 
 static u32 rtw89_fw_log_get_fmt_idx(struct rtw89_dev *rtwdev, u32 fmt_id)
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index 375ca0a243a65..0e2473ebe7564 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -4346,6 +4346,7 @@ struct rtw89_h2c_ofld {
 #define RTW89_H2C_OFLD_W0_RX_TP GENMASK(27, 18)
 
 #define RTW89_MFW_SIG	0xFF
+#define RTW89_BOARD_ELM_SIG 0xFE
 
 struct rtw89_mfw_info {
 	u8 cv;
@@ -4371,6 +4372,19 @@ struct rtw89_mfw_hdr {
 	struct rtw89_mfw_info info[];
 } __packed;
 
+struct rtw89_board_elm_ent {
+	u8 rsvd[8];
+	__le32 ofst; /* offset from beginning of rtw89_board_elm_hdr */
+	__le32 size;
+} __packed;
+
+struct rtw89_board_elm_hdr {
+	u8 sig; /* RTW89_BOARD_ELM_SIG */
+	u8 rsvd[13];
+	__le16 num;
+	struct rtw89_board_elm_ent ents[] __counted_by_le(num);
+} __packed;
+
 struct rtw89_fw_logsuit_hdr {
 	__le32 rsvd;
 	__le32 count;
@@ -5370,6 +5384,7 @@ rtw89_early_fw_feature_recognize(struct device *device,
 int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
 		      bool include_bb);
 void rtw89_load_firmware_work(struct work_struct *work);
+void __rtw89_unload_firmware(struct rtw89_dev *rtwdev);
 void rtw89_unload_firmware(struct rtw89_dev *rtwdev);
 int rtw89_wait_firmware_completion(struct rtw89_dev *rtwdev);
 int rtw89_fw_log_prepare(struct rtw89_dev *rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw89/led.c b/drivers/net/wireless/realtek/rtw89/led.c
index ae74b7075d7cd..18bfe626ef50f 100644
--- a/drivers/net/wireless/realtek/rtw89/led.c
+++ b/drivers/net/wireless/realtek/rtw89/led.c
@@ -104,7 +104,7 @@ void rtw89_led_init(struct rtw89_dev *rtwdev)
 	/* single-GPIO monochrome LED is the only supported layout */
 	BUILD_BUG_ON(ARRAY_SIZE(rtw89_common_led_gpios) != 1);
 
-	if (board)
+	if (board && board->led_desc)
 		desc = board->led_desc;
 	if (!desc->n_gpio || desc->n_gpio > RTW89_LED_MAX_NUM)
 		return;
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
index 2dec9b8454815..a87ca8566d945 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
@@ -89,6 +89,7 @@ static const struct rtw89_led_desc rtw8852cu_valve_led_desc = {
 };
 
 static const struct rtw89_board_variant rtw89_8852cu_valve_board = {
+	.id = RTW89_BOARD_ID(28de, 2432),
 	.led_desc = &rtw8852cu_valve_led_desc,
 };
 

3/2 2026/09/25 10:21 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 11442
							output: 28
							thoughts: 493
						

Thoughts:
**Analyzing the Patch and Reachability**

Okay, let's dive into this patch series and the accompanying instructions. First, I need to understand the scope. It looks like we're dealing with the `drivers/net/wireless/realtek/rtw89/` directory, specifically modifications to `core.c`, `core.h`, `fw.c`, `fw.h`, `led.c`, and `rtw8852cu.c`. Right off the bat, I need to establish the type of driver. Is this a USB driver, PCI, or potentially both? Hold on… "rtw8852cu" looks promising. The "cu" suffix usually screams USB in Realtek naming conventions (like rtw8852ce being PCIe, and rtw8852cu being USB).

Now the key question is whether this USB device can be emulated within syzkaller using USB raw gadget or vusb. Does syzkaller even target the rtw89 driver or broader USB wifi devices? I need to clarify this.

Let's refer back to the instructions, specifically sections 1, 2, and 3. I'm focusing on "REACHABILITY IS THE PRIMARY GATE." The instructions are clear – if the modified code is unreachable within a standard virtualized environment (GCE or QEMU using emulated devices like USB gadgets), it's a no-go for fuzzing, no matter what it does. Section 2 confirms that vendor-specific PCIe switches, SmartNICs, or GPU drivers are structurally unreachable if they need physical hardware that is not emulated in QEMU. That is not the case for USB. Section 3 clarifies that reachable drivers include those accessed via virtual buses like virtio, USB gadget, loopback, netlink, binder, and sockets. That sounds promising for USB!

So, can we reach rtw89 / rtw8852cu via USB gadget or Raw Gadget? That's the million-dollar question. I need to get into the kernel and see what rtw8852cu is. What does the driver's probe function do? Does it require any specific firmware or, god forbid, physical hardware? I'm going to run some quick greps through the tree and start inspecting the code. Let's get to work!



4/2 2026/09/25 10:21 tool grepper
0m Args:
{
  "Expression": "rtw89_8852cu_valve_board"
}

Results:
{
  "Output": "drivers/net/wireless/realtek/rtw89/rtw8852cu.c=86=static const struct rtw89_led_desc rtw8852cu_valve_led_desc = {\n--\ndrivers/net/wireless/realtek/rtw89/rtw8852cu.c-90-\ndrivers/net/wireless/realtek/rtw89/rtw8852cu.c:91:static const struct rtw89_board_variant rtw89_8852cu_valve_board = {\ndrivers/net/wireless/realtek/rtw89/rtw8852cu.c-92-\t.id = RTW89_BOARD_ID(28de, 2432),\n--\ndrivers/net/wireless/realtek/rtw89/rtw8852cu.c=96=static const struct rtw89_driver_info rtw89_8852cu_valve_info = {\n--\ndrivers/net/wireless/realtek/rtw89/rtw8852cu.c-98-\t.variant = NULL,\ndrivers/net/wireless/realtek/rtw89/rtw8852cu.c:99:\t.board = \u0026rtw89_8852cu_valve_board,\ndrivers/net/wireless/realtek/rtw89/rtw8852cu.c-100-\t.quirks = NULL,\n"
}

5/2 2026/09/25 10:21 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 12840
							output: 50
							thoughts: 37
						

Thoughts:
Okay, let's dive in.

**Analyzing the rtw8852cu.c Driver Code**

Alright, I'm thinking I should take a look at `drivers/net/wireless/realtek/rtw89/rtw8852cu.c`. Time to get into the details, specifically focusing on a portion of that file. I want to see what's happening under the hood. Hopefully, this will give me some insight into its design.



6/2 2026/09/25 10:21 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/realtek/rtw89/rtw8852cu.c",
  "FirstLine": 80,
  "LineCount": 50
}

Results:
{
  "Contents": "  80:\t\t\t.mode = {.addr = R_AX_GPIO_16_TO_18_EXT_CTRL + 2,\n  81:\t\t\t\t .data = BIT(1) | BIT(9)},\n  82:\t\t\t.out = {.addr = R_AX_GPIO_16_TO_18_EXT_CTRL + 1, .data = BIT(1)},\n  83:\t\t},\n  84:\t};\n  85:\t\n  86:\tstatic const struct rtw89_led_desc rtw8852cu_valve_led_desc = {\n  87:\t\t.gpios = rtw8852cu_valve_led_gpios,\n  88:\t\t.n_gpio = ARRAY_SIZE(rtw8852cu_valve_led_gpios),\n  89:\t};\n  90:\t\n  91:\tstatic const struct rtw89_board_variant rtw89_8852cu_valve_board = {\n  92:\t\t.id = RTW89_BOARD_ID(28de, 2432),\n  93:\t\t.led_desc = \u0026rtw8852cu_valve_led_desc,\n  94:\t};\n  95:\t\n  96:\tstatic const struct rtw89_driver_info rtw89_8852cu_valve_info = {\n  97:\t\t.chip = \u0026rtw8852c_chip_info,\n  98:\t\t.variant = NULL,\n  99:\t\t.board = \u0026rtw89_8852cu_valve_board,\n 100:\t\t.quirks = NULL,\n 101:\t\t.dev_id_quirks = BIT(RTW89_QUIRK_HW_INFO_SYSFS) |\n 102:\t\t\t\t BIT(RTW89_QUIRK_DISABLE_2GHZ),\n 103:\t\t.bus = {\n 104:\t\t\t.usb = \u0026rtw8852c_usb_info,\n 105:\t\t},\n 106:\t};\n 107:\t\n 108:\tstatic const struct usb_device_id rtw_8852cu_id_table[] = {\n 109:\t\t{ USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03a6, 0xff, 0xff, 0xff),\n 110:\t\t  .driver_info = (kernel_ulong_t)\u0026rtw89_8852cu_info },\n 111:\t\t{ USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x4024, 0xff, 0xff, 0xff),\n 112:\t\t  .driver_info = (kernel_ulong_t)\u0026rtw89_8852cu_info },\n 113:\t\t{ USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xc832, 0xff, 0xff, 0xff),\n 114:\t\t  .driver_info = (kernel_ulong_t)\u0026rtw89_8852cu_info },\n 115:\t\t{ USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xc85a, 0xff, 0xff, 0xff),\n 116:\t\t  .driver_info = (kernel_ulong_t)\u0026rtw89_8852cu_info },\n 117:\t\t{ USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xc85d, 0xff, 0xff, 0xff),\n 118:\t\t  .driver_info = (kernel_ulong_t)\u0026rtw89_8852cu_info },\n 119:\t\t{ USB_DEVICE_AND_INTERFACE_INFO(0x0db0, 0x991d, 0xff, 0xff, 0xff),\n 120:\t\t  .driver_info = (kernel_ulong_t)\u0026rtw89_8852cu_info },\n 121:\t\t{ USB_DEVICE_AND_INTERFACE_INFO(0x28de, 0x2432, 0xff, 0xff, 0xff),\n 122:\t\t  .driver_info = (kernel_ulong_t)\u0026rtw89_8852cu_valve_info },\n 123:\t\t{ USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x8206, 0xff, 0xff, 0xff),\n 124:\t\t  .driver_info = (kernel_ulong_t)\u0026rtw89_8852cu_info },\n 125:\t\t{ USB_DEVICE_AND_INTERFACE_INFO(0x35b2, 0x0502, 0xff, 0xff, 0xff),\n 126:\t\t  .driver_info = (kernel_ulong_t)\u0026rtw89_8852cu_info },\n 127:\t\t{ USB_DEVICE_AND_INTERFACE_INFO(0x35bc, 0x0101, 0xff, 0xff, 0xff),\n 128:\t\t  .driver_info = (kernel_ulong_t)\u0026rtw89_8852cu_info },\n 129:\t\t{ USB_DEVICE_AND_INTERFACE_INFO(0x35bc, 0x0102, 0xff, 0xff, 0xff),\n"
}

7/2 2026/09/25 10:21 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 14420
							output: 41
							thoughts: 255
						

Thoughts:
**My Immediate Analysis of the USB Driver Code**

Okay, let's dive in. Line 121… there it is: `{ USB_DEVICE_AND_INTERFACE_INFO(0x28de, 0x2432, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_valve_info },`.  Aha! VID 0x28de, PID 0x2432! That's the Valve Steam Deck OLED Wi-Fi USB dongle, or at least the chipset identification. And there it is again: `RTW89_BOARD_ID(28de, 2432)`. Makes perfect sense given the context; it’s a USB device driver, confirmed by the structure name `rtw_8852cu_id_table`.

Now, the interesting question is: can syzkaller be used to probe this kind of USB device using a raw gadget? The key here is if syzkaller can provide arbitrary descriptors. Yes! Syzkaller has `syz_usb_connect` which should allow me to emulate any USB device with arbitrary VID/PID and descriptors. This is a potential vulnerability surface, so this is worth investigating.

Before I go further, I should check how the `rtw89` USB probe routine actually works. This driver, in particular, will be the focus of the investigation. Let's start by grepping the code for `rtw89_usb_probe` or a similar function within the rtw89 USB context to determine the probe function to use.



8/2 2026/09/25 10:21 tool grepper
0m Args:
{
  "Expression": "rtw89_usb_probe",
  "PathPrefix": "drivers/net/wireless/realtek/rtw89"
}

Results:
{
  "Output": "drivers/net/wireless/realtek/rtw89/rtw8851bu.c=62=static struct usb_driver rtw_8851bu_driver = {\n--\ndrivers/net/wireless/realtek/rtw89/rtw8851bu.c-64-\t.id_table = rtw_8851bu_id_table,\ndrivers/net/wireless/realtek/rtw89/rtw8851bu.c:65:\t.probe = rtw89_usb_probe,\ndrivers/net/wireless/realtek/rtw89/rtw8851bu.c-66-\t.disconnect = rtw89_usb_disconnect,\n--\ndrivers/net/wireless/realtek/rtw89/rtw8852au.c=76=static struct usb_driver rtw_8852au_driver = {\n--\ndrivers/net/wireless/realtek/rtw89/rtw8852au.c-78-\t.id_table = rtw_8852au_id_table,\ndrivers/net/wireless/realtek/rtw89/rtw8852au.c:79:\t.probe = rtw89_usb_probe,\ndrivers/net/wireless/realtek/rtw89/rtw8852au.c-80-\t.disconnect = rtw89_usb_disconnect,\n--\ndrivers/net/wireless/realtek/rtw89/rtw8852bu.c=76=static struct usb_driver rtw_8852bu_driver = {\n--\ndrivers/net/wireless/realtek/rtw89/rtw8852bu.c-78-\t.id_table = rtw_8852bu_id_table,\ndrivers/net/wireless/realtek/rtw89/rtw8852bu.c:79:\t.probe = rtw89_usb_probe,\ndrivers/net/wireless/realtek/rtw89/rtw8852bu.c-80-\t.disconnect = rtw89_usb_disconnect,\n--\ndrivers/net/wireless/realtek/rtw89/rtw8852cu.c=137=static struct usb_driver rtw_8852cu_driver = {\n--\ndrivers/net/wireless/realtek/rtw89/rtw8852cu.c-139-\t.id_table = rtw_8852cu_id_table,\ndrivers/net/wireless/realtek/rtw89/rtw8852cu.c:140:\t.probe = rtw89_usb_probe,\ndrivers/net/wireless/realtek/rtw89/rtw8852cu.c-141-\t.disconnect = rtw89_usb_disconnect,\n--\ndrivers/net/wireless/realtek/rtw89/rtw8922au.c=77=static struct usb_driver rtw_8922au_driver = {\n--\ndrivers/net/wireless/realtek/rtw89/rtw8922au.c-79-\t.id_table = rtw_8922au_id_table,\ndrivers/net/wireless/realtek/rtw89/rtw8922au.c:80:\t.probe = rtw89_usb_probe,\ndrivers/net/wireless/realtek/rtw89/rtw8922au.c-81-\t.disconnect = rtw89_usb_disconnect,\n--\ndrivers/net/wireless/realtek/rtw89/usb.c=1201=__ATTRIBUTE_GROUPS(rtw89_usb);\ndrivers/net/wireless/realtek/rtw89/usb.c-1202-\ndrivers/net/wireless/realtek/rtw89/usb.c:1203:int rtw89_usb_probe(struct usb_interface *intf,\ndrivers/net/wireless/realtek/rtw89/usb.c-1204-\t\t    const struct usb_device_id *id)\n--\ndrivers/net/wireless/realtek/rtw89/usb.c-1294-}\ndrivers/net/wireless/realtek/rtw89/usb.c:1295:EXPORT_SYMBOL(rtw89_usb_probe);\ndrivers/net/wireless/realtek/rtw89/usb.c-1296-\n--\ndrivers/net/wireless/realtek/rtw89/usb.h=92=static inline struct rtw89_usb *rtw89_usb_priv(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/usb.h-96-\ndrivers/net/wireless/realtek/rtw89/usb.h:97:int rtw89_usb_probe(struct usb_interface *intf,\ndrivers/net/wireless/realtek/rtw89/usb.h-98-\t\t    const struct usb_device_id *id);\n"
}

9/2 2026/09/25 10:21 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 16360
							output: 47
							thoughts: 42
						

Thoughts:
Here's my summary, as the expert:

**Diving into the Realtek USB Wireless Driver Probe**

Alright, let's get down to brass tacks. I need to understand what's happening during the `rtw89_usb_probe` function call. I'm going to head straight to the source, specifically `drivers/net/wireless/realtek/rtw89/usb.c`, and I'm targeting around line 1203. That's where the heart of the matter usually resides during the device probing process. I'm expecting to see the initialization sequence, the resource allocation, and the overall setup flow for this specific USB device. I'll be looking for how the driver interacts with the USB subsystem, how it identifies the hardware, and how it gets the device ready for operation. This is where the magic happens, so let's dig in.



10/2 2026/09/25 10:21 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/realtek/rtw89/usb.c",
  "FirstLine": 1200,
  "LineCount": 100
}

Results:
{
  "Contents": "1200:\t};\n1201:\t__ATTRIBUTE_GROUPS(rtw89_usb);\n1202:\t\n1203:\tint rtw89_usb_probe(struct usb_interface *intf,\n1204:\t\t\t    const struct usb_device_id *id)\n1205:\t{\n1206:\t\tconst struct rtw89_driver_info *info;\n1207:\t\tstruct rtw89_dev *rtwdev;\n1208:\t\tstruct rtw89_usb *rtwusb;\n1209:\t\tint ret;\n1210:\t\n1211:\t\tinfo = (const struct rtw89_driver_info *)id-\u003edriver_info;\n1212:\t\n1213:\t\trtwdev = rtw89_alloc_ieee80211_hw(\u0026intf-\u003edev,\n1214:\t\t\t\t\t\t  sizeof(struct rtw89_usb), info);\n1215:\t\tif (!rtwdev) {\n1216:\t\t\tdev_err(\u0026intf-\u003edev, \"failed to allocate hw\\n\");\n1217:\t\t\treturn -ENOMEM;\n1218:\t\t}\n1219:\t\n1220:\t\trtwusb = rtw89_usb_priv(rtwdev);\n1221:\t\trtwusb-\u003ertwdev = rtwdev;\n1222:\t\trtwusb-\u003einfo = info-\u003ebus.usb;\n1223:\t\n1224:\t\trtwdev-\u003ehw-\u003ewiphy-\u003edev.groups = rtw89_usb_groups;\n1225:\t\n1226:\t\trtwdev-\u003ehci.ops = \u0026rtw89_usb_ops;\n1227:\t\trtwdev-\u003ehci.type = RTW89_HCI_TYPE_USB;\n1228:\t\trtwdev-\u003ehci.tx_rpt_enabled = true;\n1229:\t\n1230:\t\tret = rtw89_usb_intf_init(rtwdev, intf);\n1231:\t\tif (ret) {\n1232:\t\t\trtw89_err(rtwdev, \"failed to initialise intf: %d\\n\", ret);\n1233:\t\t\tgoto err_free_hw;\n1234:\t\t}\n1235:\t\n1236:\t\tret = rtw89_usb_switch_mode(rtwdev);\n1237:\t\tif (ret) {\n1238:\t\t\t/* Not a fail, but we do need to skip rtw89_core_register. */\n1239:\t\t\tret = 0;\n1240:\t\t\tgoto err_intf_deinit;\n1241:\t\t}\n1242:\t\n1243:\t\tif (rtwusb-\u003eudev-\u003espeed == USB_SPEED_SUPER)\n1244:\t\t\trtwdev-\u003ehci.dle_type = RTW89_HCI_DLE_TYPE_USB3;\n1245:\t\telse\n1246:\t\t\trtwdev-\u003ehci.dle_type = RTW89_HCI_DLE_TYPE_USB2;\n1247:\t\n1248:\t\trtw89_usb_init_tx(rtwdev);\n1249:\t\n1250:\t\tret = rtw89_usb_alloc_rx_bufs(rtwusb);\n1251:\t\tif (ret)\n1252:\t\t\tgoto err_intf_deinit;\n1253:\t\n1254:\t\tret = rtw89_usb_init_rx(rtwdev);\n1255:\t\tif (ret)\n1256:\t\t\tgoto err_free_rx_bufs;\n1257:\t\n1258:\t\tret = rtw89_core_init(rtwdev);\n1259:\t\tif (ret) {\n1260:\t\t\trtw89_err(rtwdev, \"failed to initialise core: %d\\n\", ret);\n1261:\t\t\tgoto err_deinit_rx;\n1262:\t\t}\n1263:\t\n1264:\t\tret = rtw89_chip_info_setup(rtwdev);\n1265:\t\tif (ret) {\n1266:\t\t\trtw89_err(rtwdev, \"failed to setup chip information\\n\");\n1267:\t\t\tgoto err_core_deinit;\n1268:\t\t}\n1269:\t\n1270:\t\tret = rtw89_core_register(rtwdev);\n1271:\t\tif (ret) {\n1272:\t\t\trtw89_err(rtwdev, \"failed to register core\\n\");\n1273:\t\t\tgoto err_core_deinit;\n1274:\t\t}\n1275:\t\n1276:\t\trtw89_usb_start_rx(rtwdev);\n1277:\t\n1278:\t\tset_bit(RTW89_FLAG_PROBE_DONE, rtwdev-\u003eflags);\n1279:\t\n1280:\t\treturn 0;\n1281:\t\n1282:\terr_core_deinit:\n1283:\t\trtw89_core_deinit(rtwdev);\n1284:\terr_deinit_rx:\n1285:\t\trtw89_usb_deinit_rx(rtwdev);\n1286:\terr_free_rx_bufs:\n1287:\t\trtw89_usb_free_rx_bufs(rtwusb);\n1288:\terr_intf_deinit:\n1289:\t\trtw89_usb_intf_deinit(rtwdev, intf);\n1290:\terr_free_hw:\n1291:\t\trtw89_free_ieee80211_hw(rtwdev);\n1292:\t\n1293:\t\treturn ret;\n1294:\t}\n1295:\tEXPORT_SYMBOL(rtw89_usb_probe);\n1296:\t\n1297:\tvoid rtw89_usb_disconnect(struct usb_interface *intf)\n1298:\t{\n1299:\t\tstruct ieee80211_hw *hw = usb_get_intfdata(intf);\n"
}

11/2 2026/09/25 10:21 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 18279
							output: 46
							thoughts: 278
						

Thoughts:
**Analyzing the rtw89 USB Probe Sequence**

Alright, let's dive into this `rtw89_usb_probe` function. The code flow is pretty standard, looks like we're calling `rtw89_core_init`, then `rtw89_chip_info_setup`, and finally `rtw89_core_register`. Makes sense, initialize the core, set up the chip-specific information, and then register everything with the system.

Now, that patch... specifically, it added `INIT_LIST_HEAD(&rtwdev->fw.dup_data_list)` in `rtw89_core_init`. Okay, initializing a linked list for the firmware data. I wonder what that's used for.

And then, looking at `rtw89_chip_info_setup`, there's a structure `rtw89_fw_req_info` and some conditional firmware unloading with `__rtw89_unload_firmware`. The flow appears to determine whether we free the firmware after the probe or not, based on the `free_after_probe` flag.

I need to understand what's happening during `rtw89_chip_info_setup`! Let's examine the code in `core.c` and dig into how this setup works. I need to see how the list mentioned earlier, the firmware requests, and the firmware unload logic are tied together to get a clearer picture of this sequence.



12/2 2026/09/25 10:21 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/realtek/rtw89/core.c",
  "FirstLine": 7370,
  "LineCount": 65
}

Results:
{
  "Contents": "7370:\t\n7371:\t\twiphy_rfkill_set_hw_state(rtwdev-\u003ehw-\u003ewiphy, blocked);\n7372:\t}\n7373:\t\n7374:\tint rtw89_chip_info_setup(struct rtw89_dev *rtwdev)\n7375:\t{\n7376:\t\tstruct rtw89_fw_req_info *fw_req = \u0026rtwdev-\u003efw.req;\n7377:\t\tstruct rtw89_efuse *efuse = \u0026rtwdev-\u003eefuse;\n7378:\t\tstruct rtw89_hal *hal = \u0026rtwdev-\u003ehal;\n7379:\t\tint ret;\n7380:\t\n7381:\t\trtw89_read_chip_ver(rtwdev);\n7382:\t\n7383:\t\tret = rtw89_mac_pwr_on(rtwdev);\n7384:\t\tif (ret) {\n7385:\t\t\trtw89_err(rtwdev, \"failed to power on\\n\");\n7386:\t\t\treturn ret;\n7387:\t\t}\n7388:\t\n7389:\t\tret = rtw89_wait_firmware_completion(rtwdev);\n7390:\t\tif (ret) {\n7391:\t\t\trtw89_err(rtwdev, \"failed to wait firmware completion\\n\");\n7392:\t\t\tgoto out;\n7393:\t\t}\n7394:\t\n7395:\t\tret = rtw89_fw_recognize(rtwdev);\n7396:\t\tif (ret) {\n7397:\t\t\trtw89_err(rtwdev, \"failed to recognize firmware\\n\");\n7398:\t\t\tgoto out;\n7399:\t\t}\n7400:\t\n7401:\t\tret = rtw89_chip_efuse_info_setup(rtwdev);\n7402:\t\tif (ret)\n7403:\t\t\tgoto out;\n7404:\t\n7405:\t\tret = rtw89_fw_recognize_elements(rtwdev);\n7406:\t\tif (ret) {\n7407:\t\t\trtw89_err(rtwdev, \"failed to recognize firmware elements\\n\");\n7408:\t\t\tgoto out;\n7409:\t\t}\n7410:\t\n7411:\t\tret = rtw89_chip_board_info_setup(rtwdev);\n7412:\t\tif (ret)\n7413:\t\t\tgoto out;\n7414:\t\n7415:\t\tret = rtw89_chip_data_setup(rtwdev);\n7416:\t\tif (ret)\n7417:\t\t\tgoto out;\n7418:\t\n7419:\t\trtw89_core_setup_rfe_parms(rtwdev);\n7420:\t\trtwdev-\u003eps_mode = rtw89_update_ps_mode(rtwdev);\n7421:\t\n7422:\t\trtw89_info(rtwdev, \"chip info CID: %x, CV: %x, AID: %x, ACV: %x, RFE: %d\\n\",\n7423:\t\t\t   hal-\u003ecid, hal-\u003ecv, hal-\u003eaid, hal-\u003eacv, efuse-\u003erfe_type);\n7424:\t\n7425:\tout:\n7426:\t\tif (fw_req-\u003efree_after_probe)\n7427:\t\t\t__rtw89_unload_firmware(rtwdev);\n7428:\t\n7429:\t\trtw89_mac_pwr_off(rtwdev);\n7430:\t\n7431:\t\treturn ret;\n7432:\t}\n7433:\tEXPORT_SYMBOL(rtw89_chip_info_setup);\n7434:\t\n"
}

13/2 2026/09/25 10:21 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 19955
							output: 44
							thoughts: 56
						

Thoughts:
Here's my analysis of the provided text, rendered in the first person, assuming an expert user:

**Deep Dive into Firmware Loading**

Okay, so we need to understand the trigger for firmware loading in this `rtw89` context. I'll get right to it – the question is, *how* is `rtw89_load_firmware_req` called? Or, perhaps more generally, what's kicking off the firmware loading process itself? My immediate strategy is clear: I'm going to search the codebase for instances of `rtw89_load_firmware_req` directly. Alternatively, I'll broaden the net and hunt for any calls related to `load_firmware_work`. That should give me a solid view of the execution path. Let's see what we can find.



14/2 2026/09/25 10:21 tool grepper
0m Args:
{
  "Expression": "rtw89_load_firmware_req",
  "PathPrefix": "drivers/net/wireless/realtek/rtw89"
}

Results:
{
  "Output": "drivers/net/wireless/realtek/rtw89/fw.c=2205=int rtw89_wait_firmware_completion(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c-2215-\ndrivers/net/wireless/realtek/rtw89/fw.c:2216:static int rtw89_load_firmware_req(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/fw.c-2217-\t\t\t\t   struct rtw89_fw_req_info *req,\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=2244=void rtw89_load_firmware_work(struct work_struct *work)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c-2253-\ndrivers/net/wireless/realtek/rtw89/fw.c:2254:\trtw89_load_firmware_req(rtwdev, \u0026rtwdev-\u003efw.req, fw_name, false);\ndrivers/net/wireless/realtek/rtw89/fw.c-2255-}\n"
}

15/2 2026/09/25 10:21 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 20561
							output: 39
							thoughts: 21
						

Thoughts:
Okay, here's my summary, tailored for an expert audience, as if I'm thinking these things myself:

**Tracing the Firmware Loading Process**

Right, let's dive into this `rtw89_load_firmware_work` function. My immediate instinct is to figure out *where* this is actually being scheduled. It's crucial for understanding the timing and dependencies involved in the firmware loading sequence. Where does the kernel, or more specifically the driver, decide this work needs to happen? Is it triggered by an interrupt, a probe, some specific hardware event, or something else entirely? Knowing the queuing mechanism – is it a workqueue, a tasklet, a threaded IRQ – informs a lot about potential concurrency issues and error handling strategies within the driver. Time to track down the call path!



16/2 2026/09/25 10:21 tool grepper
0m Args:
{
  "Expression": "load_firmware_work",
  "PathPrefix": "drivers/net/wireless/realtek/rtw89"
}

Results:
{
  "Output": "drivers/net/wireless/realtek/rtw89/core.c=6954=int rtw89_core_init(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/core.c-7006-\twiphy_work_init(\u0026rtwdev-\u003ecancel_6ghz_probe_work, rtw89_cancel_6ghz_probe_work);\ndrivers/net/wireless/realtek/rtw89/core.c:7007:\tINIT_WORK(\u0026rtwdev-\u003eload_firmware_work, rtw89_load_firmware_work);\ndrivers/net/wireless/realtek/rtw89/core.c-7008-\n--\ndrivers/net/wireless/realtek/rtw89/core.c-7035-\ndrivers/net/wireless/realtek/rtw89/core.c:7036:\tschedule_work(\u0026rtwdev-\u003eload_firmware_work);\ndrivers/net/wireless/realtek/rtw89/core.c-7037-\n--\ndrivers/net/wireless/realtek/rtw89/core.h=7570=struct rtw89_dev {\n--\ndrivers/net/wireless/realtek/rtw89/core.h-7626-\tstruct wiphy_work cancel_6ghz_probe_work;\ndrivers/net/wireless/realtek/rtw89/core.h:7627:\tstruct work_struct load_firmware_work;\ndrivers/net/wireless/realtek/rtw89/core.h-7628-\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=2216=static int rtw89_load_firmware_req(struct rtw89_dev *rtwdev,\n--\ndrivers/net/wireless/realtek/rtw89/fw.c-2243-\ndrivers/net/wireless/realtek/rtw89/fw.c:2244:void rtw89_load_firmware_work(struct work_struct *work)\ndrivers/net/wireless/realtek/rtw89/fw.c-2245-{\ndrivers/net/wireless/realtek/rtw89/fw.c-2246-\tstruct rtw89_dev *rtwdev =\ndrivers/net/wireless/realtek/rtw89/fw.c:2247:\t\tcontainer_of(work, struct rtw89_dev, load_firmware_work);\ndrivers/net/wireless/realtek/rtw89/fw.c-2248-\tconst struct rtw89_fw_def *fw_def = rtw89_chip_get_fw_def(rtwdev);\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=2306=void rtw89_unload_firmware(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c-2309-\ndrivers/net/wireless/realtek/rtw89/fw.c:2310:\tcancel_work_sync(\u0026rtwdev-\u003eload_firmware_work);\ndrivers/net/wireless/realtek/rtw89/fw.c-2311-\t__rtw89_unload_firmware(rtwdev);\n--\ndrivers/net/wireless/realtek/rtw89/fw.h=5384=int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,\ndrivers/net/wireless/realtek/rtw89/fw.h-5385-\t\t      bool include_bb);\ndrivers/net/wireless/realtek/rtw89/fw.h:5386:void rtw89_load_firmware_work(struct work_struct *work);\ndrivers/net/wireless/realtek/rtw89/fw.h-5387-void __rtw89_unload_firmware(struct rtw89_dev *rtwdev);\n"
}

17/2 2026/09/25 10:21 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 21840
							output: 44
							thoughts: 345
						

Thoughts:
**My Initial Investigation into rtw89 Firmware Loading with syzkaller**

Okay, here's what's running through my mind as I dig into this. I see that within the `rtw89_core_init` function, `INIT_WORK` and `schedule_work` are being used for `rtwdev->load_firmware_work`. That's the key. This implies an asynchronous firmware loading process, and I need to understand if syzkaller is set up to handle it correctly.

First, in `rtw89_chip_info_setup`, there's a call to `rtw89_wait_firmware_completion(rtwdev)`. That wait implies we need the firmware to actually *be* loaded for things to proceed. The critical question here is whether syzkaller even *has* the necessary rtw89 firmware in its testing environment, given standard QEMU/GCE instances that syzkaller frequently uses. Is the firmware present on the disk image itself, or is there a mechanism to load it? Does syzkaller's test image incorporate the Realtek rtw89 firmware directly? And if not, is `request_firmware` going to fail outright, returning an `ENOENT` error?

If `request_firmware` fails, then is there a mechanism through which syzkaller can load the firmware using a userhelper or the fw_loader sysfs interface? Or will that also result in `ENOENT`?

Even more fundamentally, I need to know if syzkaller can even successfully probe rtw89 USB devices to begin with. We need to be able to get to `rtw89_mac_pwr_on(rtwdev)` and read those registers. We've got to start at the device detection level.

I need to dive into functions like `rtw89_usb_intf_init`, `rtw89_usb_switch_mode`, `rtw89_read_chip_ver`, and especially `rtw89_mac_pwr_on` to check the USB interactions, but first let's see what happens during `rtw89_usb_probe`! This seems like the crucial entry point to understanding this entire flow.



18/2 2026/09/25 10:21 tool grepper
0m Args:
{
  "Expression": "rtw89_usb_intf_init",
  "PathPrefix": "drivers/net/wireless/realtek/rtw89"
}

Results:
{
  "Output": "drivers/net/wireless/realtek/rtw89/usb.c=982=static int rtw89_usb_parse(struct rtw89_dev *rtwdev,\n--\ndrivers/net/wireless/realtek/rtw89/usb.c-1041-\ndrivers/net/wireless/realtek/rtw89/usb.c:1042:static int rtw89_usb_intf_init(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/usb.c-1043-\t\t\t       struct usb_interface *intf)\n--\ndrivers/net/wireless/realtek/rtw89/usb.c=1203=int rtw89_usb_probe(struct usb_interface *intf,\n--\ndrivers/net/wireless/realtek/rtw89/usb.c-1229-\ndrivers/net/wireless/realtek/rtw89/usb.c:1230:\tret = rtw89_usb_intf_init(rtwdev, intf);\ndrivers/net/wireless/realtek/rtw89/usb.c-1231-\tif (ret) {\n"
}

19/2 2026/09/25 10:21 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 23031
							output: 46
							thoughts: 30
						

Thoughts:
Okay, here's my interpretation of what's going through the "author's" mind, assuming an expert-level understanding of the codebase and likely, kernel modules:

**Diving into USB Interface Initialization and Mode Switching**

Right, time to get my hands dirty. I'm going to take a closer look at `rtw89_usb_intf_init` and `rtw89_usb_switch_mode`. These functions are central to how this USB wireless interface operates, so understanding their interplay is crucial for debugging, optimization, or even extending the functionality. I need to refresh my memory on the initialization sequence and how the system decides when and how to switch modes; presumably, this might include things like driver loading, device enumeration, power management handoff, and initial setup of the data transfer mechanisms. I want to see how these functions handle the initial device configuration, resource allocation, and any potential race conditions during mode changes. I'll probably start by tracing the function calls, examining variable assignments, and correlating that with the device states I know are important. My initial goal is to understand how the USB interface is set up and what events trigger a mode transition.



20/2 2026/09/25 10:21 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/realtek/rtw89/usb.c",
  "FirstLine": 1040,
  "LineCount": 80
}

Results:
{
  "Contents": "1040:\t}\n1041:\t\n1042:\tstatic int rtw89_usb_intf_init(struct rtw89_dev *rtwdev,\n1043:\t\t\t\t       struct usb_interface *intf)\n1044:\t{\n1045:\t\tstruct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);\n1046:\t\tint ret;\n1047:\t\n1048:\t\tinit_usb_anchor(\u0026rtwusb-\u003etx_submitted);\n1049:\t\n1050:\t\tret = rtw89_usb_parse(rtwdev, intf);\n1051:\t\tif (ret)\n1052:\t\t\treturn ret;\n1053:\t\n1054:\t\trtwusb-\u003evendor_req_buf = kmalloc_obj(*rtwusb-\u003evendor_req_buf);\n1055:\t\tif (!rtwusb-\u003evendor_req_buf)\n1056:\t\t\treturn -ENOMEM;\n1057:\t\n1058:\t\trtwusb-\u003eudev = interface_to_usbdev(intf);\n1059:\t\n1060:\t\tusb_set_intfdata(intf, rtwdev-\u003ehw);\n1061:\t\n1062:\t\tSET_IEEE80211_DEV(rtwdev-\u003ehw, \u0026intf-\u003edev);\n1063:\t\n1064:\t\treturn 0;\n1065:\t}\n1066:\t\n1067:\tstatic void rtw89_usb_intf_deinit(struct rtw89_dev *rtwdev,\n1068:\t\t\t\t\t  struct usb_interface *intf)\n1069:\t{\n1070:\t\tstruct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);\n1071:\t\n1072:\t\tkfree(rtwusb-\u003evendor_req_buf);\n1073:\t\tusb_set_intfdata(intf, NULL);\n1074:\t}\n1075:\t\n1076:\tstatic int rtw89_usb_switch_mode_ax(struct rtw89_dev *rtwdev)\n1077:\t{\n1078:\t\tu32 pad_ctrl2;\n1079:\t\n1080:\t\t/* No known USB 3 devices with this chip. */\n1081:\t\tif (rtwdev-\u003echip-\u003echip_id == RTL8851B)\n1082:\t\t\treturn 0;\n1083:\t\n1084:\t\tpad_ctrl2 = rtw89_usb_ops_read32(rtwdev, R_AX_PAD_CTRL2);\n1085:\t\n1086:\t\trtw89_debug(rtwdev, RTW89_DBG_HCI, \"%s: pad_ctrl2: %#x\\n\",\n1087:\t\t\t    __func__, pad_ctrl2);\n1088:\t\n1089:\t\t/* Already tried to switch but it's a USB 2 port. */\n1090:\t\tif (u32_get_bits(pad_ctrl2, B_AX_MATCH_CNT) == USB_SWITCH_DELAY)\n1091:\t\t\treturn 0;\n1092:\t\n1093:\t\t/* Add delay to prevent some platforms would not detect USB switch */\n1094:\t\tu32p_replace_bits(\u0026pad_ctrl2, USB_SWITCH_DELAY, B_AX_MATCH_CNT);\n1095:\t\n1096:\t\tpad_ctrl2 \u0026= ~(B_AX_FORCE_U3_CK | B_AX_USB2_FORCE |\n1097:\t\t\t       B_AX_USB3_FORCE | B_AX_USB3_USB2_TRANSITION);\n1098:\t\n1099:\t\tu32p_replace_bits(\u0026pad_ctrl2, USB_MODE_U3, B_AX_USB23_SW_MODE_V1);\n1100:\t\n1101:\t\tpad_ctrl2 |= B_AX_NO_PDN_CHIPOFF_V1 | B_AX_RSM_EN_V1;\n1102:\t\n1103:\t\trtw89_usb_write32_quiet(rtwdev, R_AX_PAD_CTRL2, pad_ctrl2);\n1104:\t\n1105:\t\treturn 1;\n1106:\t}\n1107:\t\n1108:\tstatic int rtw89_usb_switch_mode_be(struct rtw89_dev *rtwdev)\n1109:\t{\n1110:\t\tu32 pad_ctrl2;\n1111:\t\n1112:\t\tpad_ctrl2 = rtw89_usb_ops_read32(rtwdev, R_BE_PAD_CTRL2);\n1113:\t\n1114:\t\trtw89_debug(rtwdev, RTW89_DBG_HCI, \"%s: pad_ctrl2: %#x\\n\",\n1115:\t\t\t    __func__, pad_ctrl2);\n1116:\t\n1117:\t\t/* Already tried to switch but it's a USB 2 port. */\n1118:\t\tif (u32_get_bits(pad_ctrl2, B_BE_MATCH_CNT) == USB_SWITCH_DELAY)\n1119:\t\t\treturn 0;\n"
}

21/2 2026/09/25 10:21 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 24771
							output: 46
							thoughts: 142
						

Thoughts:
**Analyzing the USB Switch Mode Function**

Okay, let's dive into this. I'm looking at `rtw89_usb_switch_mode` here. The code jumps out at me because if it returns 1, we immediately jump out of the registration process, which seems a bit... abrupt. It sets the return code to 0 on exit, meaning failure isn't a *failure*, it's just something that needs to be worked around. I need to understand what constitutes a successful return (0) versus a flagged return (1). Clearly, the intent isn't a failure, but a necessary skipping of the `rtw89_core_register` part. Where is this behavior defined? Time to go hunting in `usb.c`, lines 1120-1160, and understand the logic behind this "switch mode" function. It has to be important, if it forces this branching behavior.



22/2 2026/09/25 10:22 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/realtek/rtw89/usb.c",
  "FirstLine": 1120,
  "LineCount": 50
}

Results:
{
  "Contents": "1120:\t\n1121:\t\t/* Add delay to prevent some platforms would not detect USB switch */\n1122:\t\tu32p_replace_bits(\u0026pad_ctrl2, USB_SWITCH_DELAY, B_BE_MATCH_CNT);\n1123:\t\n1124:\t\tpad_ctrl2 |= B_BE_RSM_EN_V1 | B_BE_NO_PDN_CHIPOFF_V1 |\n1125:\t\t\t     B_BE_USB_AUTO_INSTALL_MASK | B_BE_USB23_SW_MODE;\n1126:\t\n1127:\t\tpad_ctrl2 \u0026= ~(B_BE_USB3_FORCE | B_BE_USB2_FORCE |\n1128:\t\t\t       B_BE_FORCE_U3_CK | B_BE_FORCE_U2_CK |\n1129:\t\t\t       B_BE_FORCE_CLK_U2 | B_BE_USB3_GEN_MODE |\n1130:\t\t\t       B_BE_USB3_LANE_MODE);\n1131:\t\n1132:\t\trtw89_usb_write32_quiet(rtwdev, R_BE_PAD_CTRL2, pad_ctrl2);\n1133:\t\n1134:\t\treturn 1;\n1135:\t}\n1136:\t\n1137:\tstatic int rtw89_usb_switch_mode(struct rtw89_dev *rtwdev)\n1138:\t{\n1139:\t\tstruct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);\n1140:\t\n1141:\t\tif (rtwusb-\u003eudev-\u003espeed == USB_SPEED_SUPER) {\n1142:\t\t\trtw89_info(rtwdev,\n1143:\t\t\t\t   \"2.4 GHz performance may be better in a USB 2 port\\n\");\n1144:\t\t\treturn 0;\n1145:\t\t}\n1146:\t\n1147:\t\tif (rtwdev-\u003echip-\u003echip_gen == RTW89_CHIP_AX)\n1148:\t\t\treturn rtw89_usb_switch_mode_ax(rtwdev);\n1149:\t\n1150:\t\treturn rtw89_usb_switch_mode_be(rtwdev);\n1151:\t}\n1152:\t\n1153:\tstatic ssize_t serial_number_show(struct device *dev,\n1154:\t\t\t\t\t  struct device_attribute *attr, char *buf)\n1155:\t{\n1156:\t\tstruct wiphy *wiphy = container_of(dev, struct wiphy, dev);\n1157:\t\tstruct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);\n1158:\t\tstruct rtw89_dev *rtwdev = hw-\u003epriv;\n1159:\t\tstruct rtw89_efuse *efuse = \u0026rtwdev-\u003eefuse;\n1160:\t\n1161:\t\treturn sysfs_emit(buf, \"%*phN\\n\",\n1162:\t\t\t\t  (int)sizeof(efuse-\u003esn), efuse-\u003esn);\n1163:\t}\n1164:\tstatic DEVICE_ATTR_RO(serial_number);\n1165:\t\n1166:\tstatic ssize_t uuid_show(struct device *dev,\n1167:\t\t\t\t struct device_attribute *attr, char *buf)\n1168:\t{\n1169:\t\tstruct wiphy *wiphy = container_of(dev, struct wiphy, dev);\n"
}

23/2 2026/09/25 10:22 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 26102
							output: 44
							thoughts: 371
						

Thoughts:
**Analyzing the Driver Initialization Flow**

Okay, so I'm digging into this driver's initialization sequence. The initial conditions are important. First, I see a few quick checks: if the USB speed is super (USB_SPEED_SUPER) or if a specific bitfield, `B_AX_MATCH_CNT`, within `pad_ctrl2` matches `USB_SWITCH_DELAY`, it immediately returns 0. That’s a significant early-exit condition that deserves a closer look depending on the context.

Assuming those checks pass, the code calls `rtw89_core_init(rtwdev)`. Inside that function, the patch adds `INIT_LIST_HEAD(&rtwdev->fw.dup_data_list);` which immediately catches my attention. I need to understand the purpose of this linked list, probably related to firmware data duplication.

Following that, the `rtw89_chip_info_setup(rtwdev)` function is called. This function seems to be doing the heavy lifting. Within it, I see: `rtw89_read_chip_ver(rtwdev)`, `rtw89_mac_pwr_on(rtwdev)`, `rtw89_wait_firmware_completion(rtwdev)`, `rtw89_fw_recognize(rtwdev)`, `rtw89_chip_efuse_info_setup(rtwdev)`, and `rtw89_fw_recognize_elements(rtwdev)`. All pretty standard for initializing hardware and fetching firmware. The order looks sensible: chip version, MAC power up, firmware completion, firmware recognition and efuse info, which, logically, are all interrelated in the initialization of the device.

But then, I hit a potential problem. What happens if `rtw89_wait_firmware_completion(rtwdev)` *fails*? I can see the structure leading to `__rtw89_unload_firmware(rtwdev)` if the `fw_req->free_after_probe` is true, but I need to see if there is any error handling, or recovery steps if the firmware completion fails. It seems like a critical point that needs thorough investigation, so I'm going to track down the exact code and error handling within `rtw89_wait_firmware_completion(rtwdev)` immediately.



24/2 2026/09/25 10:22 tool grepper
0m Args:
{
  "Expression": "rtw89_wait_firmware_completion",
  "PathPrefix": "drivers/net/wireless/realtek/rtw89"
}

Results:
{
  "Output": "drivers/net/wireless/realtek/rtw89/core.c=7374=int rtw89_chip_info_setup(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/core.c-7388-\ndrivers/net/wireless/realtek/rtw89/core.c:7389:\tret = rtw89_wait_firmware_completion(rtwdev);\ndrivers/net/wireless/realtek/rtw89/core.c-7390-\tif (ret) {\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=2190=int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,\n--\ndrivers/net/wireless/realtek/rtw89/fw.c-2204-\ndrivers/net/wireless/realtek/rtw89/fw.c:2205:int rtw89_wait_firmware_completion(struct rtw89_dev *rtwdev)\ndrivers/net/wireless/realtek/rtw89/fw.c-2206-{\n--\ndrivers/net/wireless/realtek/rtw89/fw.h=5388=void rtw89_unload_firmware(struct rtw89_dev *rtwdev);\ndrivers/net/wireless/realtek/rtw89/fw.h:5389:int rtw89_wait_firmware_completion(struct rtw89_dev *rtwdev);\ndrivers/net/wireless/realtek/rtw89/fw.h-5390-int rtw89_fw_log_prepare(struct rtw89_dev *rtwdev);\n"
}

25/2 2026/09/25 10:22 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 27492
							output: 46
							thoughts: 23
						

Thoughts:
Okay, let's dive into this. Here's my take on understanding this code:

**Analyzing `rtw89_wait_firmware_completion` in `fw.c`**

Alright, so I'm focusing on `rtw89_wait_firmware_completion` within the `fw.c` file. Given my expertise, my initial assumption is that this function is crucial for ensuring the proper loading and initialization of the firmware for this particular wireless chip – the `rtw89`. It's likely a critical piece in the overall firmware bring-up process. I'm guessing it's responsible for synchronizing the driver with the hardware after a firmware image has been sent or loaded.

I expect this function to involve some kind of polling mechanism, waiting for a specific register or flag to change value, signaling that the firmware has successfully completed its initialization sequence. There's probably a timeout involved to prevent the driver from getting stuck indefinitely if the firmware encounters an error. The specific registers, flags, and timeout values will obviously be dependent on the hardware architecture and firmware design. I'm anticipating error handling too; there should be checks and actions taken if the firmware takes too long or reports an error. I'm wondering if there's any interrupt-based handling involved as well, potentially alongside the polling to optimize for responsiveness. Let's see how they do it. This sounds like it could be interesting!



26/2 2026/09/25 10:22 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/realtek/rtw89/fw.c",
  "FirstLine": 2200,
  "LineCount": 45
}

Results:
{
  "Contents": "2200:\t\t}\n2201:\t\n2202:\t\treturn ret;\n2203:\t}\n2204:\t\n2205:\tint rtw89_wait_firmware_completion(struct rtw89_dev *rtwdev)\n2206:\t{\n2207:\t\tstruct rtw89_fw_info *fw = \u0026rtwdev-\u003efw;\n2208:\t\n2209:\t\twait_for_completion(\u0026fw-\u003ereq.completion);\n2210:\t\tif (!fw-\u003ereq.firmware)\n2211:\t\t\treturn -EINVAL;\n2212:\t\n2213:\t\treturn 0;\n2214:\t}\n2215:\t\n2216:\tstatic int rtw89_load_firmware_req(struct rtw89_dev *rtwdev,\n2217:\t\t\t\t\t   struct rtw89_fw_req_info *req,\n2218:\t\t\t\t\t   const char *fw_name, bool nowarn)\n2219:\t{\n2220:\t\tconst struct rtw89_board_variant *board = rtwdev-\u003eboard;\n2221:\t\tint ret = 0;\n2222:\t\n2223:\t\tif (req-\u003efirmware) {\n2224:\t\t\trtw89_debug(rtwdev, RTW89_DBG_FW,\n2225:\t\t\t\t    \"full firmware has been early requested\\n\");\n2226:\t\t\tgoto out;\n2227:\t\t}\n2228:\t\n2229:\t\tif (nowarn)\n2230:\t\t\tret = firmware_request_nowarn(\u0026req-\u003efirmware, fw_name, rtwdev-\u003edev);\n2231:\t\telse\n2232:\t\t\tret = request_firmware(\u0026req-\u003efirmware, fw_name, rtwdev-\u003edev);\n2233:\t\n2234:\tout:\n2235:\t\tif (board \u0026\u0026 board-\u003eid)\n2236:\t\t\tfirmware_request_nowarn(\u0026req-\u003eboard_elm, RTW89_FWNAME_BOARD_ELM, rtwdev-\u003edev);\n2237:\t\n2238:\t\treq-\u003efree_after_probe = req-\u003efirmware \u0026\u0026 req-\u003efirmware-\u003esize \u003e 0x200000;\n2239:\t\tcomplete_all(\u0026req-\u003ecompletion);\n2240:\t\n2241:\t\treturn ret;\n2242:\t}\n2243:\t\n2244:\tvoid rtw89_load_firmware_work(struct work_struct *work)\n"
}

27/2 2026/09/25 10:22 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 28548
							output: 42
							thoughts: 749
						

Thoughts:
**Firmware Loading Flow: A Deep Dive**

Okay, let's break down this firmware loading sequence. I'm focusing on `rtw89_load_firmware_req` specifically. My initial observation is that it initiates a firmware request with `request_firmware` and then, regardless of the success or failure of that initial request, the code *always* proceeds to the `out:` label. Inside `out:`, there's a conditional `firmware_request_nowarn` for a board element, and importantly, it sets `req->free_after_probe` based on the firmware size. Finally, it completes the `req->completion`. This looks like the main entry point to initiate the process.

Now, I'm thinking about the execution context. I can see that `rtw89_load_firmware_req` is scheduled as a work item in `rtw89_core_init` via `INIT_WORK` and `schedule_work`. That means it's running asynchronously, probably triggered during the USB probe, right? Let's check... yes, confirmed! `rtw89_load_firmware_work` actually calls `rtw89_load_firmware_req`.

Now, the potential problem: What happens if the firmware file isn't found? If `request_firmware` fails, `req->firmware` is going to be `NULL`. Let's see how the code handles this. Following the execution path, I find `rtw89_wait_firmware_completion`, which does a `wait_for_completion` on the `req->completion`. Crucially, it then checks `!fw->req.firmware`. If the firmware is indeed not loaded, it returns `-EINVAL`. This error is then propagated to `rtw89_chip_info_setup`.

In `rtw89_chip_info_setup`, if `rtw89_wait_firmware_completion` fails, we have an error, which triggers some cleanup. And that failure is then propagated up to `rtw89_usb_probe`, which calls `rtw89_chip_info_setup`...and then *bam*... if it fails we get the cleanup: `rtw89_core_deinit` is called.

The key is `rtw89_core_deinit`, which *unloads* the firmware. I'll need to confirm that this is what happens on a firmware failure, but it looks like the fallback in case of a missing firmware file is to de-initialize the core and unload the firmware. This seems like a robust approach to handle the firmware loading failure.



28/2 2026/09/25 10:22 tool grepper
0m Args:
{
  "Expression": "rtw89_core_deinit",
  "PathPrefix": "drivers/net/wireless/realtek/rtw89"
}

Results:
{
  "Output": "drivers/net/wireless/realtek/rtw89/core.c=7045=EXPORT_SYMBOL(rtw89_core_init);\ndrivers/net/wireless/realtek/rtw89/core.c-7046-\ndrivers/net/wireless/realtek/rtw89/core.c:7047:void rtw89_core_deinit(struct rtw89_dev *rtwdev)\ndrivers/net/wireless/realtek/rtw89/core.c-7048-{\n--\ndrivers/net/wireless/realtek/rtw89/core.c-7054-}\ndrivers/net/wireless/realtek/rtw89/core.c:7055:EXPORT_SYMBOL(rtw89_core_deinit);\ndrivers/net/wireless/realtek/rtw89/core.c-7056-\n--\ndrivers/net/wireless/realtek/rtw89/core.h=9321=int rtw89_core_init(struct rtw89_dev *rtwdev);\ndrivers/net/wireless/realtek/rtw89/core.h:9322:void rtw89_core_deinit(struct rtw89_dev *rtwdev);\ndrivers/net/wireless/realtek/rtw89/core.h-9323-int rtw89_core_register(struct rtw89_dev *rtwdev);\n--\ndrivers/net/wireless/realtek/rtw89/pci.c=4769=int rtw89_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n--\ndrivers/net/wireless/realtek/rtw89/pci.c-4855-err_core_deinit:\ndrivers/net/wireless/realtek/rtw89/pci.c:4856:\trtw89_core_deinit(rtwdev);\ndrivers/net/wireless/realtek/rtw89/pci.c-4857-err_release_hw:\n--\ndrivers/net/wireless/realtek/rtw89/pci.c=4864=void rtw89_pci_remove(struct pci_dev *pdev)\n--\ndrivers/net/wireless/realtek/rtw89/pci.c-4875-\trtw89_pci_declaim_device(rtwdev, pdev);\ndrivers/net/wireless/realtek/rtw89/pci.c:4876:\trtw89_core_deinit(rtwdev);\ndrivers/net/wireless/realtek/rtw89/pci.c-4877-\trtw89_free_ieee80211_hw(rtwdev);\n--\ndrivers/net/wireless/realtek/rtw89/usb.c=1203=int rtw89_usb_probe(struct usb_interface *intf,\n--\ndrivers/net/wireless/realtek/rtw89/usb.c-1282-err_core_deinit:\ndrivers/net/wireless/realtek/rtw89/usb.c:1283:\trtw89_core_deinit(rtwdev);\ndrivers/net/wireless/realtek/rtw89/usb.c-1284-err_deinit_rx:\n--\ndrivers/net/wireless/realtek/rtw89/usb.c=1297=void rtw89_usb_disconnect(struct usb_interface *intf)\n--\ndrivers/net/wireless/realtek/rtw89/usb.c-1312-\trtw89_core_unregister(rtwdev);\ndrivers/net/wireless/realtek/rtw89/usb.c:1313:\trtw89_core_deinit(rtwdev);\ndrivers/net/wireless/realtek/rtw89/usb.c-1314-\trtw89_usb_deinit_rx(rtwdev);\n"
}

29/2 2026/09/25 10:22 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 30946
							output: 46
							thoughts: 29
						

Thoughts:
Okay, let's dive into this. I need to understand what's happening during the de-initialization of this system. I'm going to focus on `rtw89_core_deinit`, specifically looking at the code around line 7047 within `core.c`. I have a good grasp of the overall architecture, but details are key, especially during de-initialization. I'm expecting to see resource cleanup, potential shutdown sequences, and maybe some checks to ensure everything is in a known, stable state before we shut it all down.



30/2 2026/09/25 10:22 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/realtek/rtw89/core.c",
  "FirstLine": 7045,
  "LineCount": 20
}

Results:
{
  "Contents": "7045:\tEXPORT_SYMBOL(rtw89_core_init);\n7046:\t\n7047:\tvoid rtw89_core_deinit(struct rtw89_dev *rtwdev)\n7048:\t{\n7049:\t\trtw89_ser_deinit(rtwdev);\n7050:\t\trtw89_unload_firmware(rtwdev);\n7051:\t\t__rtw89_fw_free_all_early_h2c(rtwdev);\n7052:\t\n7053:\t\tdestroy_workqueue(rtwdev-\u003etxq_wq);\n7054:\t}\n7055:\tEXPORT_SYMBOL(rtw89_core_deinit);\n7056:\t\n7057:\tvoid rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link,\n7058:\t\t\t\t   const u8 *mac_addr, bool hw_scan)\n7059:\t{\n7060:\t\tconst struct rtw89_chan *chan = rtw89_chan_get(rtwdev,\n7061:\t\t\t\t\t\t\t       rtwvif_link-\u003echanctx_idx);\n7062:\t\tstruct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link-\u003ephy_idx);\n7063:\t\n7064:\t\trtwdev-\u003escanning = true;\n"
}

31/2 2026/09/25 10:22 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 31548
							output: 45
							thoughts: 400
						

Thoughts:
**Analyzing the rtw89 Driver Unload Sequence for Fuzzing Potential**

Okay, hold on a second! I'm looking at the `rtw89_core_deinit` function. The key here is that it calls `rtw89_unload_firmware(rtwdev)`. Now, let's dive into what *that* function does. It looks like it handles cancelling a firmware load work queue, calls `__rtw89_unload_firmware`, and then frees some allocated memory related to the firmware logs and data. The presence of functions like `rtw89_unload_firmware_elements` and `rtw89_unload_firmware_dup_data` implies we're dealing with a relatively complex unloading process, which makes it an interesting target.

The critical question now is, can we actually *reach* this function from a fuzzing perspective, specifically via USB gadget interaction? Since we're dealing with an 802.11 wireless driver, that's a plausible pathway. We can probe USB devices via USB gadget interfaces.

A quick check confirms that syzkaller *does* have USB device descriptors and response fuzzing capabilities with `syz_usb_connect`. Excellent! That's a good sign. The ability to craft specific USB descriptors is exactly what we need.

Now, can we make syzkaller drive execution to `rtw89_usb_probe`? Let's check the code:

```c
int rtw89_usb_probe(struct usb_interface *intf,
		    const struct usb_device_id *id)
{
...
	ret = rtw89_usb_intf_init(rtwdev, intf);
	if (ret) {
...
```

Right, we've got `rtw89_usb_probe`. Crucially, it calls `rtw89_usb_intf_init`, meaning we *can* get to the `rtw89_usb` code through USB. I need to understand `rtw89_usb_parse` in `drivers/net/wireless/realtek/rtw89/usb.c` better.



32/2 2026/09/25 10:22 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/realtek/rtw89/usb.c",
  "FirstLine": 980,
  "LineCount": 65
}

Results:
{
  "Contents": " 980:\t};\n 981:\t\n 982:\tstatic int rtw89_usb_parse(struct rtw89_dev *rtwdev,\n 983:\t\t\t\t   struct usb_interface *intf)\n 984:\t{\n 985:\t\tstruct usb_host_interface *host_interface = \u0026intf-\u003ealtsetting[0];\n 986:\t\tstruct usb_interface_descriptor *intf_desc = \u0026host_interface-\u003edesc;\n 987:\t\tstruct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);\n 988:\t\tstruct usb_endpoint_descriptor *endpoint;\n 989:\t\tint num_out_pipes = 0;\n 990:\t\tint num_in_pipes = 0;\n 991:\t\tu8 num;\n 992:\t\tint i;\n 993:\t\n 994:\t\tif (intf_desc-\u003ebNumEndpoints \u003e RTW89_MAX_ENDPOINT_NUM) {\n 995:\t\t\trtw89_err(rtwdev, \"found %d endpoints, expected %d max\\n\",\n 996:\t\t\t\t  intf_desc-\u003ebNumEndpoints, RTW89_MAX_ENDPOINT_NUM);\n 997:\t\t\treturn -EINVAL;\n 998:\t\t}\n 999:\t\n1000:\t\tfor (i = 0; i \u003c intf_desc-\u003ebNumEndpoints; i++) {\n1001:\t\t\tendpoint = \u0026host_interface-\u003eendpoint[i].desc;\n1002:\t\t\tnum = usb_endpoint_num(endpoint);\n1003:\t\n1004:\t\t\tif (usb_endpoint_dir_in(endpoint) \u0026\u0026\n1005:\t\t\t    usb_endpoint_xfer_bulk(endpoint)) {\n1006:\t\t\t\tif (num_in_pipes \u003e= RTW89_MAX_BULKIN_NUM) {\n1007:\t\t\t\t\trtw89_err(rtwdev,\n1008:\t\t\t\t\t\t  \"found more than %d bulk in endpoint\\n\",\n1009:\t\t\t\t\t\t  RTW89_MAX_BULKIN_NUM);\n1010:\t\t\t\t\treturn -EINVAL;\n1011:\t\t\t\t}\n1012:\t\n1013:\t\t\t\trtwusb-\u003ein_pipe[num_in_pipes++] = num;\n1014:\t\t\t}\n1015:\t\n1016:\t\t\tif (usb_endpoint_dir_out(endpoint) \u0026\u0026\n1017:\t\t\t    usb_endpoint_xfer_bulk(endpoint)) {\n1018:\t\t\t\tif (num_out_pipes \u003e= RTW89_MAX_BULKOUT_NUM) {\n1019:\t\t\t\t\trtw89_err(rtwdev,\n1020:\t\t\t\t\t\t  \"found more than %d bulk out endpoints\\n\",\n1021:\t\t\t\t\t\t  RTW89_MAX_BULKOUT_NUM);\n1022:\t\t\t\t\treturn -EINVAL;\n1023:\t\t\t\t}\n1024:\t\n1025:\t\t\t\trtwusb-\u003eout_pipe[num_out_pipes++] = num;\n1026:\t\t\t}\n1027:\t\t}\n1028:\t\n1029:\t\tif (num_in_pipes \u003c 1) {\n1030:\t\t\trtw89_err(rtwdev, \"no bulk in endpoints found\\n\");\n1031:\t\t\treturn -EINVAL;\n1032:\t\t}\n1033:\t\n1034:\t\tif (num_out_pipes \u003c 1) {\n1035:\t\t\trtw89_err(rtwdev, \"no bulk out endpoints found\\n\");\n1036:\t\t\treturn -EINVAL;\n1037:\t\t}\n1038:\t\n1039:\t\treturn 0;\n1040:\t}\n1041:\t\n1042:\tstatic int rtw89_usb_intf_init(struct rtw89_dev *rtwdev,\n1043:\t\t\t\t       struct usb_interface *intf)\n1044:\t{\n"
}

33/2 2026/09/25 10:22 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 33530
							output: 43
							thoughts: 1812
						

Thoughts:
**Firmware Loading and Syzkaller Reachability: A Deep Dive**

Okay, so the `rtw89_usb_parse` function seems straightforward enough – just endpoint descriptor checks. Then, `rtw89_usb_switch_mode` returns 0 when USB speed is super-speed.  Alright, let's keep going. `rtw89_usb_init_tx` allocates buffers, and so does `rtw89_usb_alloc_rx_bufs`, and `rtw89_usb_init_rx` initializes urbs, as expected. Now, `rtw89_core_init` sets up lists, workqueues, and that `INIT_LIST_HEAD` call – that was modified by a patch, interesting. And then, crucially, it schedules `rtwdev->load_firmware_work`.

Right, that work function, `rtw89_load_firmware_work`, retrieves firmware information and calls `rtw89_load_firmware_req`. Inside `rtw89_load_firmware_req`, we have the firmware request. The critical call is either `firmware_request_nowarn` or `request_firmware`. What happens if the firmware isn’t already loaded?  I need to pay attention to that logic, and specifically, the use of `firmware_request_nowarn`. And if the board ID is present (like with the Valve device), it also requests `board_elm.bin`. The big question is: what if these firmware files are NOT present in the syzkaller filesystem? This opens a huge rabbit hole.

Can syzkaller even *load* firmware, or does the kernel fallback to a userspace helper? Does syzkaller even have linux-firmware available? These are critical questions for fuzzing.  I need to check syzkaller's documentation and see how it handles firmware loading, especially for USB wifi drivers.

Okay, let's go back and carefully consider the triage guidelines. I need to know if the target code is reachable. The key is to see if USB devices are accessible. Section 3 explicitly mentions "Reachable Drivers and Protocols" and includes "Drivers accessible via virtual buses (virtio, USB gadget...)." The example "USB gadget" is very relevant! Section 2 is pretty clear about unreachable hardware.

So, USB gadget is a virtual bus. Then, the real question is: is rtw89 primarily a PCI driver or does it also do USB? Wait! I see that rtw89 actually supports both PCI and USB! Then I should check Kconfig options to enable rtw89 USB, to make sure it is enabled in the image.



34/2 2026/09/25 10:22 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/realtek/rtw89/Kconfig",
  "FirstLine": 1,
  "LineCount": 100
}

Results:
{
  "Contents": "   1:\t# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause\n   2:\tmenuconfig RTW89\n   3:\t\ttristate \"Realtek 802.11ax wireless chips support\"\n   4:\t\tdepends on MAC80211\n   5:\t\thelp\n   6:\t\t  This module adds support for mac80211-based wireless drivers that\n   7:\t\t  enables Realtek IEEE 802.11ax wireless chipsets.\n   8:\t\n   9:\t\t  If you choose to build a module, it'll be called rtw89.\n  10:\t\n  11:\tif RTW89\n  12:\t\n  13:\tconfig RTW89_CORE\n  14:\t\ttristate\n  15:\t\tselect WANT_DEV_COREDUMP\n  16:\t\n  17:\tconfig RTW89_PCI\n  18:\t\ttristate\n  19:\t\n  20:\tconfig RTW89_USB\n  21:\t\ttristate\n  22:\t\n  23:\tconfig RTW89_8851B\n  24:\t\ttristate\n  25:\t\n  26:\tconfig RTW89_8852A\n  27:\t\ttristate\n  28:\t\n  29:\tconfig RTW89_8852B_COMMON\n  30:\t\ttristate\n  31:\t\n  32:\tconfig RTW89_8852B\n  33:\t\ttristate\n  34:\t\n  35:\tconfig RTW89_8852BT\n  36:\t\ttristate\n  37:\t\n  38:\tconfig RTW89_8852C\n  39:\t\ttristate\n  40:\t\n  41:\tconfig RTW89_8922A\n  42:\t\ttristate\n  43:\t\n  44:\tconfig RTW89_8922D\n  45:\t\ttristate\n  46:\t\n  47:\tconfig RTW89_8851BE\n  48:\t\ttristate \"Realtek 8851BE PCI wireless network (Wi-Fi 6) adapter\"\n  49:\t\tdepends on PCI\n  50:\t\tselect RTW89_CORE\n  51:\t\tselect RTW89_PCI\n  52:\t\tselect RTW89_8851B\n  53:\t\thelp\n  54:\t\t  Select this option will enable support for 8851BE chipset\n  55:\t\n  56:\t\t  802.11ax PCIe wireless network (Wi-Fi 6) adapter\n  57:\t\n  58:\tconfig RTW89_8851BU\n  59:\t\ttristate \"Realtek 8851BU USB wireless network (Wi-Fi 6) adapter\"\n  60:\t\tdepends on USB\n  61:\t\tselect RTW89_CORE\n  62:\t\tselect RTW89_USB\n  63:\t\tselect RTW89_8851B\n  64:\t\thelp\n  65:\t\t  Select this option will enable support for 8851BU chipset\n  66:\t\n  67:\t\t  802.11ax USB wireless network (Wi-Fi 6) adapter\n  68:\t\n  69:\tconfig RTW89_8852AE\n  70:\t\ttristate \"Realtek 8852AE PCI wireless network (Wi-Fi 6) adapter\"\n  71:\t\tdepends on PCI\n  72:\t\tselect RTW89_CORE\n  73:\t\tselect RTW89_PCI\n  74:\t\tselect RTW89_8852A\n  75:\t\thelp\n  76:\t\t  Select this option will enable support for 8852AE chipset\n  77:\t\n  78:\t\t  802.11ax PCIe wireless network (Wi-Fi 6) adapter\n  79:\t\n  80:\tconfig RTW89_8852AU\n  81:\t\ttristate \"Realtek 8852AU USB wireless network (Wi-Fi 6) adapter\"\n  82:\t\tdepends on USB\n  83:\t\tselect RTW89_CORE\n  84:\t\tselect RTW89_USB\n  85:\t\tselect RTW89_8852A\n  86:\t\thelp\n  87:\t\t  Select this option will enable support for 8852AU chipset\n  88:\t\n  89:\t\t  802.11ax USB wireless network (Wi-Fi 6) adapter\n  90:\t\n  91:\tconfig RTW89_8852BE\n  92:\t\ttristate \"Realtek 8852BE PCI wireless network (Wi-Fi 6) adapter\"\n  93:\t\tdepends on PCI\n  94:\t\tselect RTW89_CORE\n  95:\t\tselect RTW89_PCI\n  96:\t\tselect RTW89_8852B\n  97:\t\tselect RTW89_8852B_COMMON\n  98:\t\thelp\n  99:\t\t  Select this option will enable support for 8852BE chipset\n 100:\t\n"
}

35/2 2026/09/25 10:22 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 37314
							output: 44
							thoughts: 10
						

Thoughts:
Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:

**Diving into the Configuration Landscape**

Alright, the objective here is to understand the current situation within the configuration system. The use of the word "further" implies we've already engaged with some initial aspects of this system. We are clearly attempting to analyze the `Kconfig` file. This tells me we are dealing with configuration options, dependencies, and potentially conditional compilation within a project, likely a kernel, embedded system, or complex software build environment.

The fact that I'm thinking about this implies I likely am preparing to evaluate or modify this specific Kconfig file, maybe to change a configuration, debug, integrate a new piece of hardware or software. I'm going to want to understand what the system knows, to what's defined and how it relates to other parts of the project. I need to get into the details of the configuration and the dependencies that are expressed within it.



36/2 2026/09/25 10:22 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/realtek/rtw89/Kconfig",
  "FirstLine": 101,
  "LineCount": 60
}

Results:
{
  "Contents": " 101:\t\t  802.11ax PCIe wireless network (Wi-Fi 6) adapter\n 102:\t\n 103:\tconfig RTW89_8852BU\n 104:\t\ttristate \"Realtek 8852BU USB wireless network (Wi-Fi 6) adapter\"\n 105:\t\tdepends on USB\n 106:\t\tselect RTW89_CORE\n 107:\t\tselect RTW89_USB\n 108:\t\tselect RTW89_8852B\n 109:\t\tselect RTW89_8852B_COMMON\n 110:\t\thelp\n 111:\t\t  Select this option will enable support for 8852BU chipset\n 112:\t\n 113:\t\t  802.11ax USB wireless network (Wi-Fi 6) adapter\n 114:\t\n 115:\tconfig RTW89_8852BTE\n 116:\t\ttristate \"Realtek 8852BE-VT PCI wireless network (Wi-Fi 6) adapter\"\n 117:\t\tdepends on PCI\n 118:\t\tselect RTW89_CORE\n 119:\t\tselect RTW89_PCI\n 120:\t\tselect RTW89_8852BT\n 121:\t\tselect RTW89_8852B_COMMON\n 122:\t\thelp\n 123:\t\t  Select this option will enable support for 8852BE-VT chipset\n 124:\t\n 125:\t\t  802.11ax PCIe wireless network (Wi-Fi 6) adapter\n 126:\t\n 127:\tconfig RTW89_8852CE\n 128:\t\ttristate \"Realtek 8852CE PCI wireless network (Wi-Fi 6E) adapter\"\n 129:\t\tdepends on PCI\n 130:\t\tselect RTW89_CORE\n 131:\t\tselect RTW89_PCI\n 132:\t\tselect RTW89_8852C\n 133:\t\thelp\n 134:\t\t  Select this option will enable support for 8852CE chipset\n 135:\t\n 136:\t\t  802.11ax PCIe wireless network (Wi-Fi 6E) adapter\n 137:\t\n 138:\tconfig RTW89_8852CU\n 139:\t\ttristate \"Realtek 8852CU USB wireless network (Wi-Fi 6E) adapter\"\n 140:\t\tdepends on USB\n 141:\t\tselect RTW89_CORE\n 142:\t\tselect RTW89_USB\n 143:\t\tselect RTW89_8852C\n 144:\t\thelp\n 145:\t\t  Select this option will enable support for 8852CU chipset\n 146:\t\n 147:\t\t  802.11ax USB wireless network (Wi-Fi 6E) adapter\n 148:\t\n 149:\tconfig RTW89_8922AE\n 150:\t\ttristate \"Realtek 8922AE/8922AE-VS PCI wireless network (Wi-Fi 7) adapter\"\n 151:\t\tdepends on PCI\n 152:\t\tselect RTW89_CORE\n 153:\t\tselect RTW89_PCI\n 154:\t\tselect RTW89_8922A\n 155:\t\thelp\n 156:\t\t  Select this option will enable support for 8922AE/8922AE-VS chipset\n 157:\t\n 158:\t\t  802.11be PCIe wireless network (Wi-Fi 7) adapter\n 159:\t\t  supporting 2x2 2GHz/5GHz/6GHz 4096-QAM 160MHz channels.\n 160:\t\n"
}

37/2 2026/09/25 10:22 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 38601
							output: 41
							thoughts: 4280
						

Thoughts:
**Triage Analysis: Realtek 8852CU USB Driver Patch**

Alright, let's break this down. We're looking at a patch for the Realtek 8852CU USB Wi-Fi driver (`CONFIG_RTW89_8852CU`). The patch's primary modification is the addition of a board ID (`RTW89_BOARD_ID(28de, 2432)`) to `rtw89_8852cu_valve_board` within `rtw8852cu.c`. This suggests a new device variant is being introduced or supported. The changes to `core.c`, `fw.c`, and `led.c` touch upon firmware loading, data duplication related to firmware elements, and LED initialization respectively. The critical aspect here is how syzkaller, our fuzzer, interacts with this driver.

The initial USB probe (`rtw89_usb_probe`) goes through a series of steps: allocation, initialization, mode switching, memory allocation, and ultimately the `rtw89_core_init` function. During this process, we encounter a key dependency on firmware loading. Syzkaller, operating as root in the fuzzer VM, has the ability to write to `/lib/firmware`. The question is, does syzkaller provide a firmware file? We need to ascertain if syzkaller's setup involves supplying firmware or relying on fallback mechanisms. Given the triage guidelines, and the driver's accessibility via USB gadget emulation, this is certainly worth investigating.

Even if firmware loading fails, important functions are called. Such as: `firmware_request_nowarn(&req->board_elm, RTW89_FWNAME_BOARD_ELM, rtwdev->dev);`. Then, `__rtw89_unload_firmware`, `rtw89_unload_firmware` and `rtw89_unload_firmware_dup_data`, all these functions are executed. It's important to analyze these "unload" functions in case the firmware fails to load. Moreover, if syzkaller can provide the necessary firmware, the code paths related to parsing and recognizing firmware elements are fully executed, which increases the likelihood of finding bugs. Furthermore, since the changes touch upon core functionality like firmware handling, these modifications are likely to introduce or expose new failure modes. The patch introduces several new paths, which may potentially crash the program. The LED initialization is also potentially affected by the new `board` field.

Finally, the triage guidelines are clear: since this driver is accessible via virtual buses (USB gadget), and the patch modifies reachable executable code, it *must* be considered for fuzzing. We have to fuzz it.



38/2 2026/09/25 10:23 tool grepper
0m Args:
{
  "Expression": "rtw89_led_init",
  "PathPrefix": "drivers/net/wireless/realtek/rtw89"
}

Results:
{
  "Output": "drivers/net/wireless/realtek/rtw89/core.c=7456=static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/core.c-7587-\trtw89_btc_init(rtwdev);\ndrivers/net/wireless/realtek/rtw89/core.c:7588:\trtw89_led_init(rtwdev);\ndrivers/net/wireless/realtek/rtw89/core.c-7589-\n--\ndrivers/net/wireless/realtek/rtw89/led.c=89=static void rtw89_led_sc_deinit(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/led.c-95-\ndrivers/net/wireless/realtek/rtw89/led.c:96:void rtw89_led_init(struct rtw89_dev *rtwdev)\ndrivers/net/wireless/realtek/rtw89/led.c-97-{\n--\ndrivers/net/wireless/realtek/rtw89/led.h=12=void rtw89_led_gpio_set(struct rtw89_dev *rtwdev,\n--\ndrivers/net/wireless/realtek/rtw89/led.h-16-#ifdef CONFIG_RTW89_LEDS\ndrivers/net/wireless/realtek/rtw89/led.h:17:void rtw89_led_init(struct rtw89_dev *rtwdev);\ndrivers/net/wireless/realtek/rtw89/led.h-18-void rtw89_led_deinit(struct rtw89_dev *rtwdev);\ndrivers/net/wireless/realtek/rtw89/led.h-19-#else\ndrivers/net/wireless/realtek/rtw89/led.h:20:static inline void rtw89_led_init(struct rtw89_dev *rtwdev) {}\ndrivers/net/wireless/realtek/rtw89/led.h-21-static inline void rtw89_led_deinit(struct rtw89_dev *rtwdev) {}\n"
}

39/2 2026/09/25 10:23 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 44099
							output: 45
							thoughts: 1518
						

Thoughts:
**Analysis of the rtw89 Wireless Driver Patch**

Okay, let's break this down. First, I see a fix in `rtw89_led_init`. It looks like the original code had a potential null pointer dereference issue. If a `board` structure was present but didn't have a `led_desc`, the code would dereference `board->led_desc` without checking if it was null, leading to a crash. The patch adds a safety check: `if (board && board->led_desc)`. Smart. This is consistent with `rtw89_board_variant` potentially not having an `led_desc`.

Now, the juicy stuff. This patch seems to be about supporting "board firmware element loading" (`rtw89/board_elm.bin`). This is interesting. The code now parses a board element header (`struct rtw89_board_elm_hdr`, `struct rtw89_board_elm_ent`) to find specific elements for a given `board->id`. `__rtw89_fw_recognize_elements` and `rtw89_fw_recognize_elements` have been modified. It first parses the board element file if present and skips those elements in the firmware.

The patch includes memory duplication logic with `__rtw89_fw_elem_dup_if_needed`, `rtw89_fw_elem_dup_if_needed`, and `rtw89_fw_suit_data_dup_if_needed`. If the firmware is large (`req->firmware->size > 0x200000`) and `fw_req->free_after_probe` is true, the elements and firmware suits are duplicated into dynamically allocated memory. Crucially, the original firmware is then freed early after probe! This suggests a memory optimization strategy, probably to free up larger memory blocks. This is a common practice when dealing with large firmware images. `rtw89_chip_info_setup` and `rtw89_unload_firmware` are involved in this process.

The `rtw8852cu.c` file assigns `.id = RTW89_BOARD_ID(28de, 2432)` to `rtw89_8852cu_valve_board`. This is a board ID.

Now, the key question for me: Is this code path reachable for fuzzing? Looking at the criteria, I need to determine if `rtw89` falls into any of the *unreachable* categories. Specifically, are the `rtw89_fw_recognize_elements` and `rtw89_chip_info_setup` functions reachable?

I rule out "Non-kernel and non-executable changes" and "Unreachable execution paths".

The critical check is "Structurally unreachable hardware: ... requiring physical ASIC/PCIe cards not emulated in standard QEMU". My understanding is that `rtw89` is a driver for various Realtek wireless devices, *including* USB devices (`rtw89_8852cu`, `rtw89_8851bu`, `rtw89_8852au`, `rtw89_8852bu`, `rtw89_8922au`).  USB devices *can* be attached via USB gadget (Raw Gadget / Dummy HCD), so the driver *is* accessible via a virtual bus. Therefore, the driver is NOT structurally unreachable.

However, I need to check whether `rtw89_fw_recognize_elements` or `rtw89_chip_info_setup` can execute if `rtw89_mac_pwr_on(rtwdev)` fails. I need to investigate the failure paths of `rtw89_mac_pwr_on`.



40/2 2026/09/25 10:23 tool grepper
0m Args:
{
  "Expression": "int rtw89_mac_pwr_on",
  "PathPrefix": "drivers/net/wireless/realtek/rtw89"
}

Results:
{
  "Output": "drivers/net/wireless/realtek/rtw89/mac.c=1521=static int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on)\n--\ndrivers/net/wireless/realtek/rtw89/mac.c-1587-\ndrivers/net/wireless/realtek/rtw89/mac.c:1588:int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev)\ndrivers/net/wireless/realtek/rtw89/mac.c-1589-{\n--\ndrivers/net/wireless/realtek/rtw89/mac.h=1294=rtw89_write32_port_set(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link,\n--\ndrivers/net/wireless/realtek/rtw89/mac.h-1303-\ndrivers/net/wireless/realtek/rtw89/mac.h:1304:int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev);\ndrivers/net/wireless/realtek/rtw89/mac.h-1305-void rtw89_mac_pwr_off(struct rtw89_dev *rtwdev);\n"
}

41/2 2026/09/25 10:23 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 46860
							output: 46
							thoughts: 23
						

Thoughts:
Okay, let's dive into this. Here's my thought process on the matter:

**Analyzing `rtw89_mac_pwr_on` in `mac.c`**

Alright, the next step is to examine the `rtw89_mac_pwr_on` function. This is critical. Given my expertise, I know this is likely where the MAC layer of this Realtek 802.11 device's power-on sequence is orchestrated. I'm expecting to see the initialization steps for the MAC controller, perhaps some register writes to enable clocks, configure internal power domains, and potentially initiate some hardware reset sequences or firmware loading if it's a co-processor scenario. I'm mentally parsing the filename `mac.c`; that's a good starting point. I want to quickly understand the power-on sequence from a software perspective. This is where the core logic will reside. I'll be looking for any indications of hardware dependencies and potential race conditions in this critical power-up path. I’m thinking I will probably need to review the registers for the corresponding hardware to know what they are trying to do. This function is likely the linchpin.



42/2 2026/09/25 10:23 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/realtek/rtw89/mac.c",
  "FirstLine": 1580,
  "LineCount": 40
}

Results:
{
  "Contents": "1580:\t\t\trtw89_mac_update_scoreboard(rtwdev, MAC_AX_NOTIFY_PWR_MAJOR);\n1581:\t\t\trtw89_set_entity_state(rtwdev, RTW89_PHY_0, false);\n1582:\t\t\trtw89_set_entity_state(rtwdev, RTW89_PHY_1, false);\n1583:\t\t}\n1584:\t\n1585:\t\treturn 0;\n1586:\t}\n1587:\t\n1588:\tint rtw89_mac_pwr_on(struct rtw89_dev *rtwdev)\n1589:\t{\n1590:\t\tint ret;\n1591:\t\n1592:\t\tret = rtw89_mac_power_switch(rtwdev, true);\n1593:\t\tif (ret) {\n1594:\t\t\trtw89_mac_power_switch(rtwdev, false);\n1595:\t\t\tret = rtw89_mac_power_switch(rtwdev, true);\n1596:\t\t\tif (ret)\n1597:\t\t\t\treturn ret;\n1598:\t\t}\n1599:\t\n1600:\t\treturn 0;\n1601:\t}\n1602:\t\n1603:\tvoid rtw89_mac_pwr_off(struct rtw89_dev *rtwdev)\n1604:\t{\n1605:\t\trtw89_mac_set_vcore_reset(rtwdev);\n1606:\t\n1607:\t\trtw89_mac_power_switch(rtwdev, false);\n1608:\t}\n1609:\t\n1610:\tstatic int cmac_func_en_ax(struct rtw89_dev *rtwdev, u8 mac_idx, bool en)\n1611:\t{\n1612:\t\tu32 func_en = 0;\n1613:\t\tu32 ck_en = 0;\n1614:\t\tu32 c1pc_en = 0;\n1615:\t\tu32 addrl_func_en[] = {R_AX_CMAC_FUNC_EN, R_AX_CMAC_FUNC_EN_C1};\n1616:\t\tu32 addrl_ck_en[] = {R_AX_CK_EN, R_AX_CK_EN_C1};\n1617:\t\n1618:\t\tfunc_en = B_AX_CMAC_EN | B_AX_CMAC_TXEN | B_AX_CMAC_RXEN |\n1619:\t\t\t\tB_AX_PHYINTF_EN | B_AX_CMAC_DMA_EN | B_AX_PTCLTOP_EN |\n"
}

43/2 2026/09/25 10:23 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 47921
							output: 46
							thoughts: 35
						

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

**Diving into the Realtek Wireless Driver Power Management**

Okay, I need to get into the nitty-gritty of how the Realtek wireless driver, specifically for the rtw89 series, handles power switching. My immediate focus is on the `rtw89_mac_power_switch` function. Let's see what's happening at a granular level. I'm going to jump straight into the `mac.c` file and zero in on lines 1520 through 1586. This section should give me a good handle on the core logic used in this critical function. From these lines I expect to gain an understanding of the transitions and the implications of this core power switch functionality. This direct access to the source code is the fastest and most efficient way to understand the underlying implementation and identify potential areas of interest, potential problems, or optimizations, and the implications of certain configurations.



44/2 2026/09/25 10:23 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/realtek/rtw89/mac.c",
  "FirstLine": 1520,
  "LineCount": 60
}

Results:
{
  "Contents": "1520:\t\n1521:\tstatic int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on)\n1522:\t{\n1523:\t\tconst struct rtw89_mac_gen_def *mac = rtwdev-\u003echip-\u003emac_def;\n1524:\t\tconst struct rtw89_chip_info *chip = rtwdev-\u003echip;\n1525:\t\tconst struct rtw89_pwr_cfg * const *cfg_seq;\n1526:\t\tint (*cfg_func)(struct rtw89_dev *rtwdev);\n1527:\t\tint ret;\n1528:\t\n1529:\t\trtw89_mac_power_switch_boot_mode(rtwdev);\n1530:\t\n1531:\t\tif (on) {\n1532:\t\t\tcfg_seq = chip-\u003epwr_on_seq;\n1533:\t\t\tcfg_func = chip-\u003eops-\u003epwr_on_func;\n1534:\t\t} else {\n1535:\t\t\tif (test_bit(RTW89_FLAG_UNPLUGGED, rtwdev-\u003eflags)) {\n1536:\t\t\t\tcfg_seq = NULL;\n1537:\t\t\t\tcfg_func = rtw89_mac_pwr_off_func_for_unplugged;\n1538:\t\t\t} else {\n1539:\t\t\t\tcfg_seq = chip-\u003epwr_off_seq;\n1540:\t\t\t\tcfg_func = chip-\u003eops-\u003epwr_off_func;\n1541:\t\t\t}\n1542:\t\t}\n1543:\t\n1544:\t\tif (test_bit(RTW89_FLAG_FW_RDY, rtwdev-\u003eflags))\n1545:\t\t\t__rtw89_leave_ps_mode(rtwdev);\n1546:\t\n1547:\t\tif (on) {\n1548:\t\t\tret = mac-\u003ereset_pwr_state(rtwdev);\n1549:\t\t\tif (ret)\n1550:\t\t\t\treturn ret;\n1551:\t\t}\n1552:\t\n1553:\t\tret = cfg_func ? cfg_func(rtwdev) : rtw89_mac_pwr_seq(rtwdev, cfg_seq);\n1554:\t\tif (ret)\n1555:\t\t\treturn ret;\n1556:\t\n1557:\t\tif (on) {\n1558:\t\t\tif (!test_bit(RTW89_FLAG_PROBE_DONE, rtwdev-\u003eflags)) {\n1559:\t\t\t\trtw89_mac_efuse_read_ecv(rtwdev);\n1560:\t\t\t\tmac-\u003eefuse_read_fw_secure(rtwdev);\n1561:\t\t\t\trtw89_mac_efuse_read_thermal_k(rtwdev);\n1562:\t\t\t\trtw89_mac_efuse_read_pwr_data(rtwdev);\n1563:\t\t\t}\n1564:\t\n1565:\t\t\tset_bit(RTW89_FLAG_POWERON, rtwdev-\u003eflags);\n1566:\t\t\tset_bit(RTW89_FLAG_DMAC_FUNC, rtwdev-\u003eflags);\n1567:\t\t\tset_bit(RTW89_FLAG_CMAC0_FUNC, rtwdev-\u003eflags);\n1568:\t\n1569:\t\t\trtw89_mac_update_scoreboard(rtwdev, MAC_AX_NOTIFY_TP_MAJOR);\n1570:\t\t\trtw89_mac_clr_aon_intr(rtwdev);\n1571:\t\t} else {\n1572:\t\t\tclear_bit(RTW89_FLAG_POWERON, rtwdev-\u003eflags);\n1573:\t\t\tclear_bit(RTW89_FLAG_DMAC_FUNC, rtwdev-\u003eflags);\n1574:\t\t\tclear_bit(RTW89_FLAG_CMAC0_FUNC, rtwdev-\u003eflags);\n1575:\t\t\tclear_bit(RTW89_FLAG_CMAC1_FUNC, rtwdev-\u003eflags);\n1576:\t\t\tclear_bit(RTW89_FLAG_CMAC0_PWR, rtwdev-\u003eflags);\n1577:\t\t\tclear_bit(RTW89_FLAG_CMAC1_PWR, rtwdev-\u003eflags);\n1578:\t\t\tclear_bit(RTW89_FLAG_FW_RDY, rtwdev-\u003eflags);\n1579:\t\n"
}

45/2 2026/09/25 10:23 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 49377
							output: 35
							thoughts: 2641
						

Thoughts:
**USB Wi-Fi Driver Analysis and Fuzzing Feasibility**

Alright, let's break this down. The `rtw89` USB Wi-Fi driver, specifically its interactions with USB reads and writes through `rtw89_usb_ops`, looks like a good target. USB control messages are the key entry points here, handled by `usb_control_msg` or `usb_bulk_msg`. Syzkaller's USB raw gadget allows us to emulate the device, making this path accessible for fuzzing. If we feed controlled responses via syzkaller, `rtw89_usb_probe` gets triggered, even during error scenarios within `rtw89_chip_info_setup`. This is where it gets interesting, as firmware unloading occurs through `__rtw89_unload_firmware` and `rtw89_unload_firmware_dup_data` that calls `vmalloc` on untrusted binary data from firmware.

Critically, if the firmware *is* loaded, the driver executes code paths involving binary parsing logic like `rtw89_board_elm_seek`, `rtw89_fw_elem_dup_if_needed`, and potentially others. These functions deal with parsing data from firmware or board_elm, using structures like `rtw89_fw_req_info` and `rtw89_fw_suit`, which could be exploited through memory corruption. Specifically, `rtw89_board_elm_seek` parses an untrusted binary, and `__rtw89_fw_elem_dup_if_needed` can `vmalloc` data from untrusted binary data. This parsing logic looks like a prime target for fuzzing. `rtw89_unload_firmware_dup_data` also frees memory that was vmalloc'ed, which is a possible use-after-free situation if it is double-freed.

We've got to determine if this code is reachable. The key here is Section 3 of the fuzzing documentation: "Reachable Drivers and Protocols: Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.)". This driver, *specifically* the `rtw8852cu` USB Wi-Fi adapter driver, is configured with `CONFIG_RTW89_8852CU` and *is* accessible via USB gadget. So, it's NOT structurally unreachable. We need to verify the configs, and confirm `RTW89_8852CU` depends on USB.

So, in conclusion, this USB Wi-Fi driver appears to be *highly* suitable for fuzzing. The logic involved in parsing firmware data and the interactions with the USB bus make it a prime target for finding security vulnerabilities.



46/2 2026/09/25 10:23 tool grepper
0m Args:
{
  "Expression": "config MAC80211",
  "PathPrefix": "net/mac80211"
}

Results:
{
  "Output": "net/mac80211/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nnet/mac80211/Kconfig:2:config MAC80211\nnet/mac80211/Kconfig-3-\ttristate \"Generic IEEE 802.11 Networking Stack (mac80211)\"\n--\nnet/mac80211/Kconfig=19=if MAC80211 != n\nnet/mac80211/Kconfig-20-\nnet/mac80211/Kconfig:21:config MAC80211_HAS_RC\nnet/mac80211/Kconfig-22-\tbool\nnet/mac80211/Kconfig-23-\nnet/mac80211/Kconfig:24:config MAC80211_RC_MINSTREL\nnet/mac80211/Kconfig-25-\tbool \"Minstrel\" if EXPERT\n--\nnet/mac80211/Kconfig=31=choice\n--\nnet/mac80211/Kconfig-40-\nnet/mac80211/Kconfig:41:config MAC80211_RC_DEFAULT_MINSTREL\nnet/mac80211/Kconfig-42-\tbool \"Minstrel\"\n--\nnet/mac80211/Kconfig=48=endchoice\nnet/mac80211/Kconfig-49-\nnet/mac80211/Kconfig:50:config MAC80211_RC_DEFAULT\nnet/mac80211/Kconfig-51-\tstring\n--\nnet/mac80211/Kconfig=57=comment \"Some wireless drivers require a rate control algorithm\"\n--\nnet/mac80211/Kconfig-59-\nnet/mac80211/Kconfig:60:config MAC80211_KUNIT_TEST\nnet/mac80211/Kconfig-61-\ttristate \"KUnit tests for mac80211\" if !KUNIT_ALL_TESTS\n--\nnet/mac80211/Kconfig-69-\nnet/mac80211/Kconfig:70:config MAC80211_MESH\nnet/mac80211/Kconfig-71-\tbool \"Enable mac80211 mesh networking support\"\n--\nnet/mac80211/Kconfig-78-\nnet/mac80211/Kconfig:79:config MAC80211_LEDS\nnet/mac80211/Kconfig-80-\tbool \"Enable LED triggers\"\n--\nnet/mac80211/Kconfig-87-\nnet/mac80211/Kconfig:88:config MAC80211_DEBUGFS\nnet/mac80211/Kconfig-89-\tbool \"Export mac80211 internals in DebugFS\"\n--\nnet/mac80211/Kconfig-96-\nnet/mac80211/Kconfig:97:config MAC80211_MESSAGE_TRACING\nnet/mac80211/Kconfig-98-\tbool \"Trace all mac80211 debug messages\"\n--\nnet/mac80211/Kconfig-109-\nnet/mac80211/Kconfig:110:menuconfig MAC80211_DEBUG_MENU\nnet/mac80211/Kconfig-111-\tbool \"Select mac80211 debugging features\"\n--\nnet/mac80211/Kconfig-115-\nnet/mac80211/Kconfig:116:config MAC80211_NOINLINE\nnet/mac80211/Kconfig-117-\tbool \"Do not inline TX/RX handlers\"\n--\nnet/mac80211/Kconfig-131-\nnet/mac80211/Kconfig:132:config MAC80211_VERBOSE_DEBUG\nnet/mac80211/Kconfig-133-\tbool \"Verbose debugging output\"\n--\nnet/mac80211/Kconfig-142-\nnet/mac80211/Kconfig:143:config MAC80211_MLME_DEBUG\nnet/mac80211/Kconfig-144-\tbool \"Verbose managed MLME output\"\n--\nnet/mac80211/Kconfig-153-\nnet/mac80211/Kconfig:154:config MAC80211_STA_DEBUG\nnet/mac80211/Kconfig-155-\tbool \"Verbose station debugging\"\n--\nnet/mac80211/Kconfig-162-\nnet/mac80211/Kconfig:163:config MAC80211_HT_DEBUG\nnet/mac80211/Kconfig-164-\tbool \"Verbose HT debugging\"\n--\nnet/mac80211/Kconfig-174-\nnet/mac80211/Kconfig:175:config MAC80211_OCB_DEBUG\nnet/mac80211/Kconfig-176-\tbool \"Verbose OCB debugging\"\n--\nnet/mac80211/Kconfig-185-\nnet/mac80211/Kconfig:186:config MAC80211_IBSS_DEBUG\nnet/mac80211/Kconfig-187-\tbool \"Verbose IBSS debugging\"\n--\nnet/mac80211/Kconfig-196-\nnet/mac80211/Kconfig:197:config MAC80211_PS_DEBUG\nnet/mac80211/Kconfig-198-\tbool \"Verbose powersave mode debugging\"\n--\nnet/mac80211/Kconfig-208-\nnet/mac80211/Kconfig:209:config MAC80211_MPL_DEBUG\nnet/mac80211/Kconfig-210-\tbool \"Verbose mesh peer link debugging\"\n--\nnet/mac80211/Kconfig-221-\nnet/mac80211/Kconfig:222:config MAC80211_MPATH_DEBUG\nnet/mac80211/Kconfig-223-\tbool \"Verbose mesh path debugging\"\n--\nnet/mac80211/Kconfig-234-\nnet/mac80211/Kconfig:235:config MAC80211_MHWMP_DEBUG\nnet/mac80211/Kconfig-236-\tbool \"Verbose mesh HWMP routing debugging\"\n--\nnet/mac80211/Kconfig-247-\nnet/mac80211/Kconfig:248:config MAC80211_MESH_SYNC_DEBUG\nnet/mac80211/Kconfig-249-\tbool \"Verbose mesh synchronization debugging\"\n--\nnet/mac80211/Kconfig-258-\nnet/mac80211/Kconfig:259:config MAC80211_MESH_CSA_DEBUG\nnet/mac80211/Kconfig-260-\tbool \"Verbose mesh channel switch debugging\"\n--\nnet/mac80211/Kconfig-269-\nnet/mac80211/Kconfig:270:config MAC80211_MESH_PS_DEBUG\nnet/mac80211/Kconfig-271-\tbool \"Verbose mesh powersave debugging\"\n--\nnet/mac80211/Kconfig-280-\nnet/mac80211/Kconfig:281:config MAC80211_TDLS_DEBUG\nnet/mac80211/Kconfig-282-\tbool \"Verbose TDLS debugging\"\n--\nnet/mac80211/Kconfig-292-\nnet/mac80211/Kconfig:293:config MAC80211_DEBUG_COUNTERS\nnet/mac80211/Kconfig-294-\tbool \"Extra statistics for TX/RX debugging\"\n--\nnet/mac80211/Kconfig-307-\nnet/mac80211/Kconfig:308:config MAC80211_STA_HASH_MAX_SIZE\nnet/mac80211/Kconfig-309-\tint \"Station hash table maximum size\" if MAC80211_DEBUG_MENU\n"
}

47/2 2026/09/25 10:23 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 54835
							output: 37
							thoughts: 24
						

Thoughts:
Okay, here's my take on those lines from `net/mac80211/Kconfig`, summarized from my perspective:

**Diving into mac80211 Kconfig - Initial Assessment**

Alright, let's get down to brass tacks. I need to understand what's being configured in the first 18 lines of the `net/mac80211/Kconfig` file. I'm assuming this section probably sets some core, high-level configurations for the mac80211 wireless stack. Given this is the Kconfig, it’s probably setting up dependencies, introducing the initial menu entries, or defining some fundamental booleans or tristates that drive the entire wireless subsystem. I'm looking for the foundational building blocks - what features are *always* available, and what are conditional? And which are dependent on other modules? I'll be looking to see how the code is structured in this section.



48/2 2026/09/25 10:23 tool read-file
0m Args:
{
  "File": "net/mac80211/Kconfig",
  "FirstLine": 1,
  "LineCount": 20
}

Results:
{
  "Contents": "   1:\t# SPDX-License-Identifier: GPL-2.0-only\n   2:\tconfig MAC80211\n   3:\t\ttristate \"Generic IEEE 802.11 Networking Stack (mac80211)\"\n   4:\t\tdepends on CFG80211\n   5:\t\tselect CRYPTO\n   6:\t\tselect CRYPTO_LIB_AES_CBC_MACS\n   7:\t\tselect CRYPTO_LIB_ARC4\n   8:\t\tselect CRYPTO_AES\n   9:\t\tselect CRYPTO_CCM\n  10:\t\tselect CRYPTO_GCM\n  11:\t\tselect CRC32\n  12:\t\thelp\n  13:\t\t  This option enables the hardware independent IEEE 802.11\n  14:\t\t  networking stack.\n  15:\t\n  16:\tcomment \"CFG80211 needs to be enabled for MAC80211\"\n  17:\t\tdepends on CFG80211=n\n  18:\t\n  19:\tif MAC80211 != n\n  20:\t\n"
}

49/2 2026/09/25 10:23 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 55381
							output: 41
							thoughts: 45
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own:

**Investigating Driver Dependencies**

Right, so `MAC80211` obviously builds on `CFG80211` – that's fundamental. But I need to be more thorough, so I wonder about the interaction with `RTW89_LEDS`.  Is `RTW89_LEDS` another prerequisite, something `MAC80211` leverages, or perhaps something that ties into the overall system in a related way? Let's check `RTW89_LEDS` specifically, and see where that fits into the bigger picture. Understanding these driver interdependencies is key to making sure everything is playing nicely together.



50/2 2026/09/25 10:23 tool grepper
0m Args:
{
  "Expression": "config RTW89_LEDS",
  "PathPrefix": "drivers/net/wireless/realtek/rtw89"
}

Results:
{
  "Output": "drivers/net/wireless/realtek/rtw89/Kconfig=199=config RTW89_DEBUGFS\n--\ndrivers/net/wireless/realtek/rtw89/Kconfig-207-\ndrivers/net/wireless/realtek/rtw89/Kconfig:208:config RTW89_LEDS\ndrivers/net/wireless/realtek/rtw89/Kconfig-209-\tbool\n--\ndrivers/net/wireless/realtek/rtw89/Kconfig-214-\ndrivers/net/wireless/realtek/rtw89/Kconfig:215:config RTW89_LEDS_MC\ndrivers/net/wireless/realtek/rtw89/Kconfig-216-\tbool\n"
}

51/2 2026/09/25 10:23 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 55824
							output: 44
							thoughts: 29
						

Thoughts:
Okay, here's my interpretation of that thought process, assuming I'm already deep in kernel development and familiar with the Realtek WiFi driver:

**Investigating the rtw89 Kconfig**

Alright, the next step is to dive into the `drivers/net/wireless/realtek/rtw89/Kconfig` file, specifically around line 208. I'm probably looking at this because something isn't behaving as expected with the rtw89 driver. My initial thought is likely focused on configuration options. I need to understand the available compile-time choices, and line 208 is probably where I believe a critical selection lives. I'm expecting to find a conditional statement, a dependency, or a default setting related to a feature I'm investigating. I want to know exactly how the driver's build process responds to certain configurations. Understanding the Kconfig's logic is crucial to debugging or configuring the driver effectively. Line 208 should give me some key insight.



52/2 2026/09/25 10:23 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/realtek/rtw89/Kconfig",
  "FirstLine": 200,
  "LineCount": 25
}

Results:
{
  "Contents": " 200:\t\tbool \"Realtek rtw89 debugfs support\"\n 201:\t\tdepends on RTW89_CORE \u0026\u0026 CFG80211_DEBUGFS\n 202:\t\tselect RTW89_DEBUG\n 203:\t\thelp\n 204:\t\t  Enable debugfs support\n 205:\t\n 206:\t\t  If unsure, say Y to simplify debug problems\n 207:\t\n 208:\tconfig RTW89_LEDS\n 209:\t\tbool\n 210:\t\tdepends on RTW89_CORE\n 211:\t\tdepends on LEDS_CLASS=y || LEDS_CLASS=MAC80211\n 212:\t\timply MAC80211_LEDS\n 213:\t\tdefault y\n 214:\t\n 215:\tconfig RTW89_LEDS_MC\n 216:\t\tbool\n 217:\t\tdepends on RTW89_LEDS\n 218:\t\tdepends on LEDS_CLASS_MULTICOLOR\n 219:\t\timply LEDS_TRIGGER_TIMER\n 220:\t\tdefault y\n 221:\t\n 222:\tendif\n"
}

53/2 2026/09/25 10:23 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 56430
							output: 285
							thoughts: 1619
						

Thoughts:
**Focusing on the Realtek RTW89 Wireless Driver Patch**

Alright, let's break this down. First, I see that `RTW89_LEDS` defaults to "y" if `LEDS_CLASS` is enabled. Also, the `RTW89_8852CU` configuration enables `RTW89_CORE`, `RTW89_USB`, and `RTW89_8852C`, depending on `USB` and `RTW89`, which in turn depends on `MAC80211` and ultimately, `CFG80211`. Now, my focus is to identify the crucial `FocusSymbols` for fuzzing this patch effectively.

I need to analyze the functions that were added or modified. Scanning the code, I've identified several areas of interest:

In `core.c`, `rtw89_core_init` initializes a list, and `rtw89_chip_info_setup` now calls `__rtw89_unload_firmware` if `fw_req->free_after_probe` is set.  This strongly suggests a connection to the firmware loading and unloading process.

In `fw.c`, there's a significant batch of new functions related to firmware elements. I see numerous static functions related to element duplication and recognition: `__rtw89_fw_elem_dup_if_needed`, `rtw89_fw_elem_dup_if_needed`, `rtw89_fw_suit_data_dup_if_needed`, `rtw89_board_elm_seek`, `__rtw89_fw_recognize_elements`, `rtw89_load_firmware_req`, and `rtw89_unload_firmware_dup_data`. Then there are the non-static functions like `rtw89_fw_recognize_elements`, `rtw89_load_firmware_work`, `__rtw89_unload_firmware`, `rtw89_unload_firmware`, `rtw89_mfw_recognize`, `__rtw89_fw_recognize_from_elm`, `rtw89_build_txpwr_trk_tbl_from_elm`, `rtw89_build_rfk_log_fmt_from_elm`, `rtw89_build_afe_pwr_seq_from_elm`, `rtw89_recognize_diag_mac_from_elm`, `rtw89_build_tx_comp_from_elm` all seem to be central to element handling.

In `led.c`, `rtw89_led_init` is a key initialization function.

Finally, `rtw89_8852cu.c` includes the `rtw89_8852cu_valve_board` data and, crucially, the entry point for USB probing: `rtw89_usb_probe`.

Now, sticking to the guidance, I need to filter these functions against the fuzzing guidelines. Ubiquitous functions are out, and I want to target specific feature logic and entry points. Looking at this list, `rtw89_fw_recognize_elements`, `rtw89_board_elm_seek`, `rtw89_fw_elem_dup_if_needed`, `rtw89_fw_suit_data_dup_if_needed`, `__rtw89_unload_firmware`, `rtw89_unload_firmware`, `rtw89_unload_firmware_dup_data`, `rtw89_load_firmware_req`, `rtw89_chip_info_setup`, and `rtw89_led_init` seem to fit the bill.

A quick check reveals that some of these functions are declared `static` within their respective `.c` files. Important. Static functions within `.c` files still have symbols. However static inline functions in headers would not, which isn't the case here. This is great, as I want functions with distinct symbols. So, I need to be careful to choose from the non-static functions, and the non-inlined static functions.

Therefore, the key functions to include are:
- `rtw89_fw_recognize_elements`
- `rtw89_board_elm_seek`
- `rtw89_chip_info_setup`
- `rtw89_load_firmware_work`
- `rtw89_unload_firmware`
- `__rt


54/2 2026/09/25 10:23 tool set-results
0m Args:
{
  "EnableConfigs": [
    "RTW89",
    "RTW89_CORE",
    "RTW89_USB",
    "RTW89_8852CU",
    "MAC80211",
    "USB"
  ],
  "FocusSymbols": [
    "rtw89_fw_recognize_elements",
    "rtw89_board_elm_seek",
    "rtw89_chip_info_setup",
    "rtw89_load_firmware_work",
    "rtw89_unload_firmware",
    "__rtw89_unload_firmware",
    "rtw89_led_init",
    "rtw89_usb_probe"
  ],
  "Reasoning": "The patch introduces support for board firmware element parsing and early firmware unloading with element/fw_suit data duplication in the rtw89 wireless driver. Because rtw89 supports USB devices (such as RTW8852CU, explicitly modified in this patch series), this driver code is reachable via virtual USB buses (e.g. USB Raw Gadget in QEMU/KVM test environments). The changes introduce new binary parsing routines, memory duplication, and lifecycle deallocation logic worth fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "RTW89",
    "RTW89_CORE",
    "RTW89_USB",
    "RTW89_8852CU",
    "MAC80211",
    "USB"
  ],
  "FocusSymbols": [
    "rtw89_fw_recognize_elements",
    "rtw89_board_elm_seek",
    "rtw89_chip_info_setup",
    "rtw89_load_firmware_work",
    "rtw89_unload_firmware",
    "__rtw89_unload_firmware",
    "rtw89_led_init",
    "rtw89_usb_probe"
  ],
  "Reasoning": "The patch introduces support for board firmware element parsing and early firmware unloading with element/fw_suit data duplication in the rtw89 wireless driver. Because rtw89 supports USB devices (such as RTW8852CU, explicitly modified in this patch series), this driver code is reachable via virtual USB buses (e.g. USB Raw Gadget in QEMU/KVM test environments). The changes introduce new binary parsing routines, memory duplication, and lifecycle deallocation logic worth fuzzing.",
  "WorthFuzzing": true
}

55/1 2026/09/25 10:23 action If
1m Args:
{
  "WorthFuzzing": true
}

Results:
null

56/2 2026/09/25 10:23 agent kmsan-evaluator
1m Model:
core

Args:
null

Results:
{
  "KMSANReasoning": "The patch optimizes memory consumption in the Realtek rtw89 driver by introducing board element firmware support and optionally duplicating firmware elements/suits so that the primary firmware buffers can be released early after probing.\n\nFrom a memory safety perspective:\n1. KMSAN applicability: The patch does not introduce or expose uninitialized memory risks. All newly allocated buffers (via devm_kmemdup or vmalloc) are fully populated with memcpy for their entire specified length. Stack variables like `struct firmware pseudo_fw` are explicitly cleared with memset(..., 0, ...) before initialization. The new list head `dup_data_list` is initialized with INIT_LIST_HEAD. Furthermore, no kernel data structures, buffers, or uninitialized padding are copied or exposed to user space.\n2. KASAN applicability: The primary risks associated with this patch involve memory lifetimes and boundary calculations—specifically, potential Use-After-Free (UAF) if driver logic attempts to dereference firmware pointers after early release in `__rtw89_unload_firmware`, Double-Free if `rtw89_unload_firmware_dup_data` is invoked repeatedly without clearing `dup_data_list`, or out-of-bounds (OOB) memory access if 32-bit arithmetic overflow occurs in offset/size calculations (e.g. `ofst + size` in `rtw89_board_elm_seek`). These issues are all reliably detected by KASAN (including KASAN_VMALLOC) and standard kernel debugging checks (such as CONFIG_DEBUG_LIST).\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
  "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 0ffbabfaba5b514a37c2d74aa098f3cfc4bcc0c8
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Sep 25 10:21:20 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index dacc2d889a467..e893235f962af 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -6973,6 +6973,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)
 	}
 	INIT_LIST_HEAD(&rtwdev->scan_info.chan_list);
 	INIT_LIST_HEAD(&rtwdev->tx_waits);
+	INIT_LIST_HEAD(&rtwdev->fw.dup_data_list);
 	INIT_WORK(&rtwdev->ba_work, rtw89_core_ba_work);
 	INIT_WORK(&rtwdev->txq_work, rtw89_core_txq_work);
 	INIT_DELAYED_WORK(&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work);
@@ -7372,6 +7373,7 @@ void rtw89_core_rfkill_poll(struct rtw89_dev *rtwdev, bool force)
 
 int rtw89_chip_info_setup(struct rtw89_dev *rtwdev)
 {
+	struct rtw89_fw_req_info *fw_req = &rtwdev->fw.req;
 	struct rtw89_efuse *efuse = &rtwdev->efuse;
 	struct rtw89_hal *hal = &rtwdev->hal;
 	int ret;
@@ -7421,6 +7423,9 @@ int rtw89_chip_info_setup(struct rtw89_dev *rtwdev)
 		   hal->cid, hal->cv, hal->aid, hal->acv, efuse->rfe_type);
 
 out:
+	if (fw_req->free_after_probe)
+		__rtw89_unload_firmware(rtwdev);
+
 	rtw89_mac_pwr_off(rtwdev);
 
 	return ret;
@@ -7723,6 +7728,7 @@ void rtw89_free_ieee80211_hw(struct rtw89_dev *rtwdev)
 }
 EXPORT_SYMBOL(rtw89_free_ieee80211_hw);
 
+MODULE_FIRMWARE(RTW89_FWNAME_BOARD_ELM);
 MODULE_AUTHOR("Realtek Corporation");
 MODULE_DESCRIPTION("Realtek 802.11ax wireless core module");
 MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 2b21d969ece7b..e942ee1fd21bb 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -5274,6 +5274,8 @@ struct rtw89_reg_imr {
 #define RTW89_GEN_MODULE_FWNAME(basename, maxformat) \
 	basename RTW89_GEN_MODULE_FWNAME_FMT(maxformat) ".bin"
 
+#define RTW89_FWNAME_BOARD_ELM "rtw89/board_elm.bin"
+
 struct rtw89_fw_def {
 	const char *fw_basename;
 	u8 fw_format_max;
@@ -5865,7 +5867,19 @@ struct rtw89_chip_variant {
 	const struct rtw89_qta_def *qta_def_override;
 };
 
+/* @pid can have extra suffix to describe the variant if necessary */
+#define RTW89_BOARD_ID(vid, pid) \
+	RTW89_BOARD_ID_##vid##_##pid
+
+enum rtw89_board_id {
+	RTW89_BOARD_ID(0000, 0000) = 0, /* reserve 0 for non-variant */
+	RTW89_BOARD_ID(28de, 2432) = 1,
+
+	NUM_OF_RTW89_BOARD_IDS,
+};
+
 struct rtw89_board_variant {
+	enum rtw89_board_id id;
 	const struct rtw89_led_desc *led_desc;
 };
 
@@ -6029,6 +6043,7 @@ enum rtw89_fw_feature {
 struct rtw89_fw_suit {
 	enum rtw89_fw_type type;
 	const u8 *data;
+	struct list_head list;
 	u32 size;
 	u8 major_ver;
 	u8 minor_ver;
@@ -6063,7 +6078,9 @@ struct rtw89_fw_suit {
 
 struct rtw89_fw_req_info {
 	const struct firmware *firmware;
+	const struct firmware *board_elm;
 	struct completion completion;
+	bool free_after_probe;
 };
 
 struct rtw89_fw_log {
@@ -6119,6 +6136,8 @@ struct rtw89_fw_info {
 	struct rtw89_fw_elm_info elm_info;
 	struct rtw89_fw_secure sec;
 
+	struct list_head dup_data_list;
+
 	DECLARE_BITMAP(feature_map, NUM_OF_RTW89_FW_FEATURES);
 };
 
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 0099c5c03e7a6..272d6219e6a66 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -3,6 +3,7 @@
  */
 
 #include <linux/if_arp.h>
+#include <linux/vmalloc.h>
 #include "cam.h"
 #include "chan.h"
 #include "coex.h"
@@ -137,6 +138,56 @@ int rtw89_fw_check_rdy(struct rtw89_dev *rtwdev, enum rtw89_fwdl_check_type type
 	return 0;
 }
 
+static const void *__rtw89_fw_elem_dup_if_needed(struct rtw89_dev *rtwdev,
+						 const void *src, size_t len,
+						 bool is_fw_suit)
+{
+	struct rtw89_fw_req_info *fw_req = &rtwdev->fw.req;
+	void *dup;
+
+	if (!fw_req->free_after_probe)
+		return src;
+
+	if (is_fw_suit) {
+		dup = vmalloc(len);
+		if (dup)
+			memcpy(dup, src, len);
+	} else {
+		dup = devm_kmemdup(rtwdev->dev, src, len, GFP_KERNEL);
+	}
+
+	if (!dup) {
+		/* If failed to memdup, fallback to point to firmware->data. */
+		fw_req->free_after_probe = false;
+		return src;
+	}
+
+	return dup;
+}
+
+static const void *rtw89_fw_elem_dup_if_needed(struct rtw89_dev *rtwdev,
+					       const struct rtw89_fw_element_hdr *elm)
+{
+	size_t len = sizeof(*elm) + le32_to_cpu(elm->size);
+
+	return __rtw89_fw_elem_dup_if_needed(rtwdev, elm, len, false);
+}
+
+static const void *rtw89_fw_suit_data_dup_if_needed(struct rtw89_dev *rtwdev,
+						    struct rtw89_fw_suit *fw_suit,
+						    const void *src)
+{
+	struct rtw89_fw_info *fw = &rtwdev->fw;
+	const void *dup;
+
+	dup = __rtw89_fw_elem_dup_if_needed(rtwdev, src, fw_suit->size, true);
+
+	if (dup != src)
+		list_add_tail(&fw_suit->list, &fw->dup_data_list);
+
+	return dup;
+}
+
 static int rtw89_fw_hdr_parser_v0(struct rtw89_dev *rtwdev, const u8 *fw, u32 len,
 				  struct rtw89_fw_bin_info *info)
 {
@@ -609,6 +660,8 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
 	const struct rtw89_mfw_hdr *mfw_hdr;
 	const u8 *mfw = firmware->data;
 	u32 mfw_len = firmware->size;
+	const u8 *mfw_info_ptr;
+	u32 mfw_info_size;
 	int ret;
 	int i;
 
@@ -618,8 +671,8 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
 		/* legacy firmware support normal type only */
 		if (type != RTW89_FW_NORMAL)
 			return -EINVAL;
-		fw_suit->data = mfw;
 		fw_suit->size = mfw_len;
+		fw_suit->data = rtw89_fw_suit_data_dup_if_needed(rtwdev, fw_suit, mfw);
 		return 0;
 	}
 
@@ -654,14 +707,17 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
 	return -ENOENT;
 
 found:
-	fw_suit->data = mfw + le32_to_cpu(mfw_info->shift);
-	fw_suit->size = le32_to_cpu(mfw_info->size);
+	mfw_info_ptr = mfw + le32_to_cpu(mfw_info->shift);
+	mfw_info_size = le32_to_cpu(mfw_info->size);
 
-	if (fw_suit->data + fw_suit->size > mfw + mfw_len) {
+	if (mfw_info_ptr + mfw_info_size > mfw + mfw_len) {
 		rtw89_err(rtwdev, "fw_suit %d out of address\n", type);
 		return -EFAULT;
 	}
 
+	fw_suit->size = mfw_info_size;
+	fw_suit->data = rtw89_fw_suit_data_dup_if_needed(rtwdev, fw_suit, mfw_info_ptr);
+
 	return 0;
 }
 
@@ -799,8 +855,9 @@ int __rtw89_fw_recognize_from_elm(struct rtw89_dev *rtwdev,
 	if (fw_suit->data)
 		return 1; /* ignore this element (a firmware is taken already) */
 
-	fw_suit->data = elm->u.bbmcu.contents;
 	fw_suit->size = le32_to_cpu(elm->size);
+	fw_suit->data =
+		rtw89_fw_suit_data_dup_if_needed(rtwdev, fw_suit, elm->u.bbmcu.contents);
 
 	return rtw89_fw_update_ver(rtwdev, type, fw_suit);
 }
@@ -1216,7 +1273,12 @@ int rtw89_fw_recognize_txpwr_from_elm(struct rtw89_dev *rtwdev,
 	conf->rfe_type = txpwr_elm->rfe_type;
 	conf->ent_sz = txpwr_elm->ent_sz;
 	conf->num_ents = le32_to_cpu(txpwr_elm->num_ents);
+	/*
+	 * The conf->data is used by rtw89_core_setup_rfe_parms() to do format
+	 * conversion before releasing firmware. No need to duplicate.
+	 */
 	conf->data = txpwr_elm->content;
+
 	return 0;
 }
 
@@ -1227,6 +1289,7 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev,
 {
 	struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
 	const struct rtw89_chip_info *chip = rtwdev->chip;
+	const struct rtw89_fw_element_hdr *elm_dup;
 	struct rtw89_hal *hal = &rtwdev->hal;
 	u16 aid = le16_to_cpu(elm->aid);
 	u32 needed_bitmap = 0;
@@ -1257,6 +1320,8 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev,
 	if (!elm_info->txpwr_trk)
 		return -ENOMEM;
 
+	elm_dup = rtw89_fw_elem_dup_if_needed(rtwdev, elm);
+
 	for (type = 0; bitmap; type++, bitmap >>= 1) {
 		if (!(bitmap & BIT(0)))
 			continue;
@@ -1273,10 +1338,10 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev,
 		else
 			break;
 
-		elm_info->txpwr_trk->delta[type] = &elm->u.txpwr_trk.contents[offset];
+		elm_info->txpwr_trk->delta[type] = &elm_dup->u.txpwr_trk.contents[offset];
 
 		offset += subband;
-		if (offset * DELTA_SWINGIDX_SIZE > le32_to_cpu(elm->size))
+		if (offset * DELTA_SWINGIDX_SIZE > le32_to_cpu(elm_dup->size))
 			goto err;
 	}
 
@@ -1311,7 +1376,7 @@ int rtw89_build_rfk_log_fmt_from_elm(struct rtw89_dev *rtwdev,
 	if (rfk_id >= RTW89_PHY_C2H_RFK_LOG_FUNC_NUM)
 		return 1;
 
-	elm_info->rfk_log_fmt->elm[rfk_id] = elm;
+	elm_info->rfk_log_fmt->elm[rfk_id] = rtw89_fw_elem_dup_if_needed(rtwdev, elm);
 
 	return 0;
 }
@@ -1418,7 +1483,7 @@ int rtw89_build_afe_pwr_seq_from_elm(struct rtw89_dev *rtwdev,
 {
 	struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
 
-	elm_info->afe = elm;
+	elm_info->afe = rtw89_fw_elem_dup_if_needed(rtwdev, elm);
 
 	return 0;
 }
@@ -1430,7 +1495,7 @@ int rtw89_recognize_diag_mac_from_elm(struct rtw89_dev *rtwdev,
 {
 	struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
 
-	elm_info->diag_mac = elm;
+	elm_info->diag_mac = rtw89_fw_elem_dup_if_needed(rtwdev, elm);
 
 	return 0;
 }
@@ -1456,7 +1521,7 @@ int rtw89_build_tx_comp_from_elm(struct rtw89_dev *rtwdev,
 	else if (elm_info->tx_comp)
 		return 1; /* ignore if an element is existing */
 
-	elm_info->tx_comp = elm;
+	elm_info->tx_comp = rtw89_fw_elem_dup_if_needed(rtwdev, elm);
 
 	return 0;
 }
@@ -1557,30 +1622,17 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
 	},
 };
 
-int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
+static int __rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev,
+					 const struct firmware *firmware,
+					 u32 offset, u32 skipped_elements,
+					 u32 *unrecognized_elements)
 {
-	struct rtw89_fw_info *fw_info = &rtwdev->fw;
-	const struct firmware *firmware = fw_info->req.firmware;
-	const struct rtw89_chip_info *chip = rtwdev->chip;
-	u32 unrecognized_elements = chip->needed_fw_elms;
 	const struct rtw89_fw_element_handler *handler;
 	const struct rtw89_fw_element_hdr *hdr;
-	bool transition;
 	u32 elm_size;
 	u32 elem_id;
-	u32 offset;
 	int ret;
 
-	BUILD_BUG_ON(sizeof(chip->needed_fw_elms) * 8 < RTW89_FW_ELEMENT_ID_NUM);
-
-	transition = !!((chip->needed_fw_elms & BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION)));
-	unrecognized_elements &= ~BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION);
-
-	offset = rtw89_mfw_get_size(rtwdev);
-	offset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN);
-	if (offset == 0)
-		return -EINVAL;
-
 	while (offset + sizeof(*hdr) < firmware->size) {
 		hdr = (const struct rtw89_fw_element_hdr *)(firmware->data + offset);
 
@@ -1593,6 +1645,8 @@ int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
 		elem_id = le32_to_cpu(hdr->id);
 		if (elem_id >= ARRAY_SIZE(__fw_element_handlers))
 			goto next;
+		if (skipped_elements & BIT(elem_id))
+			goto next;
 
 		handler = &__fw_element_handlers[elem_id];
 		if (!handler->fn)
@@ -1608,12 +1662,105 @@ int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
 			rtw89_info(rtwdev, "Firmware element %s version: %4ph\n",
 				   handler->name, hdr->ver);
 
-		unrecognized_elements &= ~BIT(elem_id);
+		*unrecognized_elements &= ~BIT(elem_id);
 next:
 		offset += sizeof(*hdr) + elm_size;
 		offset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN);
 	}
 
+	return 0;
+}
+
+static int rtw89_board_elm_seek(const struct firmware *board_elm,
+				enum rtw89_board_id board_id,
+				struct firmware *pseudo_fw)
+{
+	const struct rtw89_board_elm_hdr *hdr;
+	const struct rtw89_board_elm_ent *ent;
+	u32 ofst;
+	u32 size;
+	u16 num;
+
+	if (board_elm->size < sizeof(*hdr))
+		return -EINVAL;
+
+	hdr = (const struct rtw89_board_elm_hdr *)board_elm->data;
+	if (hdr->sig != RTW89_BOARD_ELM_SIG)
+		return -EINVAL;
+
+	BUILD_BUG_ON(U16_MAX < NUM_OF_RTW89_BOARD_IDS);
+
+	num = le16_to_cpu(hdr->num);
+	if (unlikely(board_elm->size < struct_size(hdr, ents, num)))
+		return -EFAULT;
+	if (num <= board_id)
+		return -ENOENT;
+
+	ent = &hdr->ents[board_id];
+
+	ofst = le32_to_cpu(ent->ofst);
+	size = le32_to_cpu(ent->size);
+
+	if (unlikely(board_elm->size < ofst + size))
+		return -EFAULT;
+
+	memset(pseudo_fw, 0, sizeof(*pseudo_fw));
+
+	pseudo_fw->data = (const void *)board_elm->data + ofst;
+	pseudo_fw->size = size;
+
+	return 0;
+}
+
+int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
+{
+	const struct rtw89_board_variant *board = rtwdev->board;
+	struct rtw89_fw_req_info *fw_req = &rtwdev->fw.req;
+	const struct firmware *firmware = fw_req->firmware;
+	const struct rtw89_chip_info *chip = rtwdev->chip;
+	u32 unrecognized_elements = chip->needed_fw_elms;
+	u32 skipped_elements = 0;
+	bool transition;
+	u32 offset;
+	int ret;
+
+	BUILD_BUG_ON(sizeof(chip->needed_fw_elms) * 8 < RTW89_FW_ELEMENT_ID_NUM);
+	BUILD_BUG_ON(!__same_type(unrecognized_elements, chip->needed_fw_elms));
+	BUILD_BUG_ON(!__same_type(skipped_elements, chip->needed_fw_elms));
+
+	transition = !!((chip->needed_fw_elms & BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION)));
+	unrecognized_elements &= ~BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION);
+
+	if (fw_req->board_elm) {
+		struct firmware pseudo_fw;
+
+		ret = rtw89_board_elm_seek(fw_req->board_elm, board->id, &pseudo_fw);
+		if (ret)
+			goto mfw;
+
+		ret = __rtw89_fw_recognize_elements(rtwdev, &pseudo_fw, 0, 0,
+						    &unrecognized_elements);
+		if (ret)
+			goto mfw;
+
+		skipped_elements = chip->needed_fw_elms & ~unrecognized_elements;
+
+		rtw89_debug(rtwdev, RTW89_DBG_FW, "apply elements 0x08%x from %s\n",
+			    skipped_elements, RTW89_FWNAME_BOARD_ELM);
+	}
+
+mfw:
+	offset = rtw89_mfw_get_size(rtwdev);
+	offset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN);
+	if (offset == 0)
+		return -EINVAL;
+
+	ret = __rtw89_fw_recognize_elements(rtwdev, firmware, offset,
+					    skipped_elements,
+					    &unrecognized_elements);
+	if (ret)
+		return ret;
+
 	if (unrecognized_elements) {
 		if (transition) {
 			rtw89_info(rtwdev, "NOTE: This firmware is going to be obsolete!\n"
@@ -2070,13 +2217,13 @@ static int rtw89_load_firmware_req(struct rtw89_dev *rtwdev,
 				   struct rtw89_fw_req_info *req,
 				   const char *fw_name, bool nowarn)
 {
-	int ret;
+	const struct rtw89_board_variant *board = rtwdev->board;
+	int ret = 0;
 
 	if (req->firmware) {
 		rtw89_debug(rtwdev, RTW89_DBG_FW,
 			    "full firmware has been early requested\n");
-		complete_all(&req->completion);
-		return 0;
+		goto out;
 	}
 
 	if (nowarn)
@@ -2084,6 +2231,11 @@ static int rtw89_load_firmware_req(struct rtw89_dev *rtwdev,
 	else
 		ret = request_firmware(&req->firmware, fw_name, rtwdev->dev);
 
+out:
+	if (board && board->id)
+		firmware_request_nowarn(&req->board_elm, RTW89_FWNAME_BOARD_ELM, rtwdev->dev);
+
+	req->free_after_probe = req->firmware && req->firmware->size > 0x200000;
 	complete_all(&req->completion);
 
 	return ret;
@@ -2126,23 +2278,41 @@ static void rtw89_unload_firmware_elements(struct rtw89_dev *rtwdev)
 	kfree(elm_info->rfk_log_fmt);
 }
 
-void rtw89_unload_firmware(struct rtw89_dev *rtwdev)
+static void rtw89_unload_firmware_dup_data(struct rtw89_dev *rtwdev)
 {
 	struct rtw89_fw_info *fw = &rtwdev->fw;
+	struct rtw89_fw_suit *fw_suit;
 
-	cancel_work_sync(&rtwdev->load_firmware_work);
+	list_for_each_entry(fw_suit, &fw->dup_data_list, list)
+		vfree(fw_suit->data);
+}
 
-	if (fw->req.firmware) {
-		release_firmware(fw->req.firmware);
+void __rtw89_unload_firmware(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_fw_info *fw = &rtwdev->fw;
 
-		/* assign NULL back in case rtw89_free_ieee80211_hw()
-		 * try to release the same one again.
-		 */
-		fw->req.firmware = NULL;
-	}
+	release_firmware(fw->req.firmware);
+	release_firmware(fw->req.board_elm);
+
+	/*
+	 * Directly call this to free firmware early in normal flow. Assign
+	 * NULL back in case rtw89_free_ieee80211_hw() or tw89_core_deinit()
+	 * try to release the same one again in error handling paths.
+	 */
+	fw->req.firmware = NULL;
+	fw->req.board_elm = NULL;
+}
+
+void rtw89_unload_firmware(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_fw_info *fw = &rtwdev->fw;
+
+	cancel_work_sync(&rtwdev->load_firmware_work);
+	__rtw89_unload_firmware(rtwdev);
 
 	kfree(fw->log.fmts);
 	rtw89_unload_firmware_elements(rtwdev);
+	rtw89_unload_firmware_dup_data(rtwdev);
 }
 
 static u32 rtw89_fw_log_get_fmt_idx(struct rtw89_dev *rtwdev, u32 fmt_id)
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index 375ca0a243a65..0e2473ebe7564 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -4346,6 +4346,7 @@ struct rtw89_h2c_ofld {
 #define RTW89_H2C_OFLD_W0_RX_TP GENMASK(27, 18)
 
 #define RTW89_MFW_SIG	0xFF
+#define RTW89_BOARD_ELM_SIG 0xFE
 
 struct rtw89_mfw_info {
 	u8 cv;
@@ -4371,6 +4372,19 @@ struct rtw89_mfw_hdr {
 	struct rtw89_mfw_info info[];
 } __packed;
 
+struct rtw89_board_elm_ent {
+	u8 rsvd[8];
+	__le32 ofst; /* offset from beginning of rtw89_board_elm_hdr */
+	__le32 size;
+} __packed;
+
+struct rtw89_board_elm_hdr {
+	u8 sig; /* RTW89_BOARD_ELM_SIG */
+	u8 rsvd[13];
+	__le16 num;
+	struct rtw89_board_elm_ent ents[] __counted_by_le(num);
+} __packed;
+
 struct rtw89_fw_logsuit_hdr {
 	__le32 rsvd;
 	__le32 count;
@@ -5370,6 +5384,7 @@ rtw89_early_fw_feature_recognize(struct device *device,
 int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
 		      bool include_bb);
 void rtw89_load_firmware_work(struct work_struct *work);
+void __rtw89_unload_firmware(struct rtw89_dev *rtwdev);
 void rtw89_unload_firmware(struct rtw89_dev *rtwdev);
 int rtw89_wait_firmware_completion(struct rtw89_dev *rtwdev);
 int rtw89_fw_log_prepare(struct rtw89_dev *rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw89/led.c b/drivers/net/wireless/realtek/rtw89/led.c
index ae74b7075d7cd..18bfe626ef50f 100644
--- a/drivers/net/wireless/realtek/rtw89/led.c
+++ b/drivers/net/wireless/realtek/rtw89/led.c
@@ -104,7 +104,7 @@ void rtw89_led_init(struct rtw89_dev *rtwdev)
 	/* single-GPIO monochrome LED is the only supported layout */
 	BUILD_BUG_ON(ARRAY_SIZE(rtw89_common_led_gpios) != 1);
 
-	if (board)
+	if (board && board->led_desc)
 		desc = board->led_desc;
 	if (!desc->n_gpio || desc->n_gpio > RTW89_LED_MAX_NUM)
 		return;
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
index 2dec9b8454815..a87ca8566d945 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
@@ -89,6 +89,7 @@ static const struct rtw89_led_desc rtw8852cu_valve_led_desc = {
 };
 
 static const struct rtw89_board_variant rtw89_8852cu_valve_board = {
+	.id = RTW89_BOARD_ID(28de, 2432),
 	.led_desc = &rtw8852cu_valve_led_desc,
 };
 

57/3 2026/09/25 10:23 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 10654
							output: 42
							thoughts: 2668
						

Thoughts:
**Analysis of Realtek rtw89 Wireless Driver Patch**

Alright, let's break this down. This patch is touching the Realtek `rtw89` wireless driver, and the goal seems to be memory optimization, specifically unloading the main firmware image early after probing if it's large enough. The core idea is to duplicate specific firmware elements as needed and unload the main firmware.

My initial thought is that we need to ensure that this doesn't introduce any uninitialized memory reads or other memory safety issues.

First, I see the introduction of `fw->dup_data_list`, which is a list to keep track of duplicated firmware data. It is used to free the duplicated data. Then it seems that the code tries to duplicate firmware suits or elements, using either `vmalloc` and `memcpy`, or `devm_kmemdup` if `req->free_after_probe` is true. `devm_kmemdup` is a managed `kmemdup`. If duplication fails, it falls back to using the original firmware data but sets `free_after_probe` to false, so the original firmware is never unloaded.

I need to meticulously check memory operations, because `memcpy` on uninitialized data is a bad thing.  `vmalloc` followed by `memcpy` makes me nervous, so I want to make sure the source is valid. The `src` comes from `firmware->data`, which is loaded by the kernel firmware loader (`request_firmware`).  Okay, that seems safe *if* the firmware loader does its job correctly.

Then, there's `__rtw89_fw_elem_dup_if_needed`, the core function. It calls `vmalloc` with the size of the element, which I need to validate. Or, it calls `devm_kmemdup`, which is a managed version of `kmemdup`.

Now, the `rtw89_fw_elem_dup_if_needed` function uses information from the `rtw89_fw_element_hdr`. Let's confirm: the function is called with `elm`, and it constructs the length using `sizeof(*elm) + le32_to_cpu(elm->size)`. But, I have to ensure that `elm->size` is within bounds! I need to trace where the `elm` comes from and if the bounds are checked. `rtw89_fw_recognize_elements` iterates through the firmware data, reads `hdr->size`, and checks if the offset + size goes beyond the firmware boundary *before* calling handlers. That seems sound. It returns -EFAULT if out of bounds, preventing any read beyond the size.

Aha! There's also `pseudo_fw`, and this is interesting. The `rtw89_board_elm_seek` function is responsible for setting `pseudo_fw->data` and `pseudo_fw->size` from a board element file. It calculates an offset and size using the `ofst` and `size` fields from `board_elm->data`, and checks that `board_elm->size < ofst + size`.  But wait a minute… what about integer overflow? If `ofst + size` overflows the u32, you might bypass the check, potentially causing an out-of-bounds read! The check becomes `board_elm->size < 0`! That could trigger a KASAN error. So that's a potential vulnerability to investigate further.

However, `pseudo_fw` seems to be properly initialized with `memset` *before* being used. Also, I see that the `pseudo_fw` is not actually used if there's an error in `rtw89_board_elm_seek`!

Now let's go back and investigate the allocation and usage of the `fw_suit->list` where `fw_suit` is a `struct rtw89_fw_suit`. I need to figure out how `fw_suit` is allocated and deallocated to assess if there are any use-after-free, double-free, or memory leak issues. Let's see how that is allocated... Time to go search through the code to figure this out!



58/3 2026/09/25 10:24 tool grepper
0m Args:
{
  "Expression": "struct rtw89_fw_suit",
  "PathPrefix": "drivers/net/wireless/realtek/rtw89"
}

Results:
{
  "Output": "drivers/net/wireless/realtek/rtw89/coex.c=13320=void rtw89_coex_recognize_ver(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/coex.c-13324-\tconst struct rtw89_btc_ver *btc_ver_def;\ndrivers/net/wireless/realtek/rtw89/coex.c:13325:\tconst struct rtw89_fw_suit *fw_suit;\ndrivers/net/wireless/realtek/rtw89/coex.c-13326-\tu32 suit_ver_code;\n--\ndrivers/net/wireless/realtek/rtw89/core.h=5991=enum rtw89_fw_feature {\n--\ndrivers/net/wireless/realtek/rtw89/core.h-6042-\ndrivers/net/wireless/realtek/rtw89/core.h:6043:struct rtw89_fw_suit {\ndrivers/net/wireless/realtek/rtw89/core.h-6044-\tenum rtw89_fw_type type;\n--\ndrivers/net/wireless/realtek/rtw89/core.h=6086=struct rtw89_fw_log {\ndrivers/net/wireless/realtek/rtw89/core.h:6087:\tstruct rtw89_fw_suit suit;\ndrivers/net/wireless/realtek/rtw89/core.h-6088-\tbool enable;\n--\ndrivers/net/wireless/realtek/rtw89/core.h=6124=struct rtw89_fw_info {\n--\ndrivers/net/wireless/realtek/rtw89/core.h-6130-\tu8 c2h_counter;\ndrivers/net/wireless/realtek/rtw89/core.h:6131:\tstruct rtw89_fw_suit normal;\ndrivers/net/wireless/realtek/rtw89/core.h:6132:\tstruct rtw89_fw_suit wowlan;\ndrivers/net/wireless/realtek/rtw89/core.h:6133:\tstruct rtw89_fw_suit bbmcu0;\ndrivers/net/wireless/realtek/rtw89/core.h:6134:\tstruct rtw89_fw_suit bbmcu1;\ndrivers/net/wireless/realtek/rtw89/core.h-6135-\tstruct rtw89_fw_log log;\n--\ndrivers/net/wireless/realtek/rtw89/core.h=9106=bool rtw89_sta_link_has_er_su_4xhe08(struct ieee80211_link_sta *link_sta)\n--\ndrivers/net/wireless/realtek/rtw89/core.h-9114-\ndrivers/net/wireless/realtek/rtw89/core.h:9115:static inline struct rtw89_fw_suit *rtw89_fw_suit_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/core.h-9116-\t\t\t\t\t\t      enum rtw89_fw_type type)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=176=static const void *rtw89_fw_suit_data_dup_if_needed(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/fw.c:177:\t\t\t\t\t\t    struct rtw89_fw_suit *fw_suit,\ndrivers/net/wireless/realtek/rtw89/fw.c-178-\t\t\t\t\t\t    const void *src)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=590=static int rtw89_fw_hdr_parser(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/fw.c:591:\t\t\t       const struct rtw89_fw_suit *fw_suit,\ndrivers/net/wireless/realtek/rtw89/fw.c-592-\t\t\t       struct rtw89_fw_bin_info *info)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=654=int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,\ndrivers/net/wireless/realtek/rtw89/fw.c:655:\t\t\tstruct rtw89_fw_suit *fw_suit, bool nowarn)\ndrivers/net/wireless/realtek/rtw89/fw.c-656-{\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=749=static void rtw89_fw_update_ver_v0(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/fw.c:750:\t\t\t\t   struct rtw89_fw_suit *fw_suit,\ndrivers/net/wireless/realtek/rtw89/fw.c-751-\t\t\t\t   const struct rtw89_fw_hdr *hdr)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=766=static void rtw89_fw_update_ver_v1(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/fw.c:767:\t\t\t\t   struct rtw89_fw_suit *fw_suit,\ndrivers/net/wireless/realtek/rtw89/fw.c-768-\t\t\t\t   const struct rtw89_fw_hdr_v1 *hdr)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=783=static int rtw89_fw_update_ver(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/fw.c-784-\t\t\t       enum rtw89_fw_type type,\ndrivers/net/wireless/realtek/rtw89/fw.c:785:\t\t\t       struct rtw89_fw_suit *fw_suit)\ndrivers/net/wireless/realtek/rtw89/fw.c-786-{\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=826=int __rtw89_fw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,\n--\ndrivers/net/wireless/realtek/rtw89/fw.c-828-{\ndrivers/net/wireless/realtek/rtw89/fw.c:829:\tstruct rtw89_fw_suit *fw_suit = rtw89_fw_suit_get(rtwdev, type);\ndrivers/net/wireless/realtek/rtw89/fw.c-830-\tint ret;\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=840=int __rtw89_fw_recognize_from_elm(struct rtw89_dev *rtwdev,\n--\ndrivers/net/wireless/realtek/rtw89/fw.c-845-\tstruct rtw89_hal *hal = \u0026rtwdev-\u003ehal;\ndrivers/net/wireless/realtek/rtw89/fw.c:846:\tstruct rtw89_fw_suit *fw_suit;\ndrivers/net/wireless/realtek/rtw89/fw.c-847-\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=1038=static void rtw89_fw_recognize_features(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c-1040-\tconst struct rtw89_chip_info *chip = rtwdev-\u003echip;\ndrivers/net/wireless/realtek/rtw89/fw.c:1041:\tconst struct rtw89_fw_suit *fw_suit;\ndrivers/net/wireless/realtek/rtw89/fw.c-1042-\tu32 suit_ver_code;\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=1092=static int rtw89_fw_validate_ver_required(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c-1094-\tconst struct rtw89_chip_variant *variant = rtwdev-\u003evariant;\ndrivers/net/wireless/realtek/rtw89/fw.c:1095:\tconst struct rtw89_fw_suit *fw_suit;\ndrivers/net/wireless/realtek/rtw89/fw.c-1096-\tu32 suit_ver_code;\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=1876=static int __rtw89_fw_download_hdr(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/fw.c:1877:\t\t\t\t   const struct rtw89_fw_suit *fw_suit,\ndrivers/net/wireless/realtek/rtw89/fw.c-1878-\t\t\t\t   struct rtw89_fw_bin_info *info)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=1932=static int rtw89_fw_download_hdr(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/fw.c:1933:\t\t\t\t const struct rtw89_fw_suit *fw_suit,\ndrivers/net/wireless/realtek/rtw89/fw.c-1934-\t\t\t\t struct rtw89_fw_bin_info *info)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=2021=rtw89_fw_get_fwdl_chk_type_from_suit(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/fw.c:2022:\t\t\t\t     const struct rtw89_fw_suit *fw_suit)\ndrivers/net/wireless/realtek/rtw89/fw.c-2023-{\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=2034=static int rtw89_fw_download_main(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/fw.c:2035:\t\t\t\t  const struct rtw89_fw_suit *fw_suit,\ndrivers/net/wireless/realtek/rtw89/fw.c-2036-\t\t\t\t  struct rtw89_fw_bin_info *info)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=2104=static int rtw89_fw_download_suit(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/fw.c:2105:\t\t\t\t  struct rtw89_fw_suit *fw_suit)\ndrivers/net/wireless/realtek/rtw89/fw.c-2106-{\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=2141=int __rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,\n--\ndrivers/net/wireless/realtek/rtw89/fw.c-2145-\tstruct rtw89_fw_info *fw_info = \u0026rtwdev-\u003efw;\ndrivers/net/wireless/realtek/rtw89/fw.c:2146:\tstruct rtw89_fw_suit *fw_suit = rtw89_fw_suit_get(rtwdev, type);\ndrivers/net/wireless/realtek/rtw89/fw.c-2147-\tu8 bbmcu_nr = rtwdev-\u003echip-\u003ebbmcu_nr;\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=2281=static void rtw89_unload_firmware_dup_data(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c-2283-\tstruct rtw89_fw_info *fw = \u0026rtwdev-\u003efw;\ndrivers/net/wireless/realtek/rtw89/fw.c:2284:\tstruct rtw89_fw_suit *fw_suit;\ndrivers/net/wireless/realtek/rtw89/fw.c-2285-\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=2333=static int rtw89_fw_log_create_fmts_dict(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c-2336-\tconst struct rtw89_fw_logsuit_hdr *suit_hdr;\ndrivers/net/wireless/realtek/rtw89/fw.c:2337:\tstruct rtw89_fw_suit *suit = \u0026log-\u003esuit;\ndrivers/net/wireless/realtek/rtw89/fw.c-2338-\tconst void *fmts_ptr, *fmts_end_ptr;\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=2365=int rtw89_fw_log_prepare(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/fw.c-2367-\tstruct rtw89_fw_log *log = \u0026rtwdev-\u003efw.log;\ndrivers/net/wireless/realtek/rtw89/fw.c:2368:\tstruct rtw89_fw_suit *suit = \u0026log-\u003esuit;\ndrivers/net/wireless/realtek/rtw89/fw.c-2369-\n"
}

59/3 2026/09/25 10:24 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 17946
							output: 52
							thoughts: 99
						

Thoughts:
Okay, here's my breakdown of what I'm looking at, from my perspective as someone deeply familiar with wireless driver development:

**Analyzing the Realtek rtw89 Device Initialization**

Alright, I need to get a clear picture of how this `rtwdev` structure is handled. My primary concern right now is understanding its allocation and initialization flow within this driver. Let's start with where it's allocated. I see a few key points: `rtw89_alloc_ieee80211_hw`, `devm_kzalloc`, and `ieee80211_alloc_hw`. I'll need to trace those functions to figure out which is being used in *this* particular path and see exactly how memory is being set aside. This is the foundation, I can't proceed without a concrete understanding of this.

Next, I need to understand what happens *after* allocation. I know that `rtw89_core_init` is involved somewhere along the way. I'll need to dive into `drivers/net/wireless/realtek/rtw89/core.c` and specifically trace that function. That's likely where the bulk of the device's internal state is set up. I expect to see register initialization, interrupt configuration, potentially DMA setup, and general hardware bring-up in there. Understanding this sequence is going to give me a solid grasp of how the Realtek rtw89 device is actually brought online.



60/3 2026/09/25 10:24 tool grepper
0m Args:
{
  "Expression": "INIT_LIST_HEAD\\(\u0026rtwdev-\u003efw.dup_data_list\\)",
  "PathPrefix": "drivers/net/wireless/realtek/rtw89"
}

Results:
{
  "Output": "drivers/net/wireless/realtek/rtw89/core.c=6954=int rtw89_core_init(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/core.c-6975-\tINIT_LIST_HEAD(\u0026rtwdev-\u003etx_waits);\ndrivers/net/wireless/realtek/rtw89/core.c:6976:\tINIT_LIST_HEAD(\u0026rtwdev-\u003efw.dup_data_list);\ndrivers/net/wireless/realtek/rtw89/core.c-6977-\tINIT_WORK(\u0026rtwdev-\u003eba_work, rtw89_core_ba_work);\n"
}

61/3 2026/09/25 10:24 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 18605
							output: 45
							thoughts: 1017
						

Thoughts:
**KASAN vs. KMSAN: A Deep Dive into Firmware Data Duplication**

Okay, let's break this down. I'm looking at how firmware data duplication is handled in `rtw89`, and it's making me a little nervous.  Specifically, the addition of `INIT_LIST_HEAD(&rtwdev->fw.dup_data_list)` in `rtw89_core_init` caught my attention. I need to understand what happens to the `list` member within each `fw_suit` element – that's the key.  Remember, `fw_suit` can be one of several firmware types: normal, wowlan, bbmcu0, bbmcu1, or the log suit.

The critical function is `rtw89_fw_suit_data_dup_if_needed`. It seems that data might be duplicated and, if so, the `fw_suit`'s `list` is added to `rtwdev->fw.dup_data_list`. This list management is a potential hotspot. The `__rtw89_fw_elem_dup_if_needed` part is where the actual duplication happens, but the list manipulation is what's giving me pause.

Then, there's `rtw89_unload_firmware_dup_data`. This function iterates through `dup_data_list` and calls `vfree` on `fw_suit->data`.  Here's where the red flags are multiplying! What if `rtw89_unload_firmware` gets called multiple times? Or what if  `rtw89_fw_suit_data_dup_if_needed` is called multiple times on the *same* `fw_suit`? Can a `fw_suit` be added to the list twice?

The immediate concern is double-free or even list corruption.  But, with KASAN enabled, I should be able to catch these easily. KASAN is already going to flag OOB, UAF, double free, invalid free, and memory corruption checks. The standard kernel setup should catch the critical bugs.  The question is, does the list handling *prevent* potential problems. The fact that the list isn't re-initialized or that elements aren't removed after freeing is concerning.

The critical issue is that `list_for_each_entry(fw_suit, &fw->dup_data_list, list) vfree(fw_suit->data);` doesn't include any `list_del` calls, nor does it re-initialize the list. The potential is that if `rtw89_unload_firmware` gets called twice, `vfree` will be called again on the freed pointers, which is a textbook Use-After-Free/Double-Free. KASAN *should* detect this right away.

So, am I dealing with uninitialized memory here, or just list management errors? That brings me to the KMSAN question.  My rule of thumb is:  **if KASAN can catch it, I don't need KMSAN**.

To determine if KMSAN is necessary, I need to methodically check the changes against the KMSAN criteria. Remember the guidelines: KMSAN detects *reads* of uninitialized memory (stack, heap, or page allocations) and potential kernel-to-user memory information leaks.

The KMSAN check means I need to confirm:

1.  **Kernel structures sent to user space?** Specifically, anything touched by this patch. Anything in the firmware elements or board data? I need to search for things like `copy_to_user`, `netlink`, `ethtool`, or `debugfs` calls in this area, especially related to the `fw` elements or board variants. I'll need to go through the code carefully.

If I find any of those things, then I need to figure out if there's uninitialized memory leaking to user space. If not, then it sounds like KASAN is the tool I need.



62/3 2026/09/25 10:24 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "(copy_to_user|nla_put)",
  "PathPrefix": "drivers/net/wireless/realtek/rtw89"
}

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

63/3 2026/09/25 10:24 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 20547
							output: 35
							thoughts: 12
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person:

**Investigating rtw89 via debugfs**

Alright, let's dive in. My immediate thought is, "Okay, let's leverage `debugfs` for the `rtw89` driver." We've hit an issue or we're troubleshooting, and `debugfs` is usually the go-to when you need low-level visibility and control over a kernel module. It's the standard for digging into the internal state of these kinds of drivers, especially those as complex as modern wireless implementations. I'm expecting to find a wealth of information in there: register dumps, internal statistics, potentially control knobs for tweaking parameters that aren't exposed through standard interfaces. The challenge, as always with `debugfs`, will be navigating the tree and figuring out which entries are relevant to the specific problem I'm investigating. I'll be looking for any hints about potential causes of the issue, like unexpected error codes, misconfigured registers, or unusual traffic patterns. Time to get my hands dirty and see what `debugfs` reveals!



64/3 2026/09/25 10:25 tool grepper
0m Args:
{
  "Expression": "debugfs",
  "PathPrefix": "drivers/net/wireless/realtek/rtw89"
}

Results:
{
  "Output": "\nFull output is too long, showing 500 out of 573 lines.\nUse more precise expression if possible.\n\ndrivers/net/wireless/realtek/rtw89/Kconfig=199=config RTW89_DEBUGFS\ndrivers/net/wireless/realtek/rtw89/Kconfig:200:\tbool \"Realtek rtw89 debugfs support\"\ndrivers/net/wireless/realtek/rtw89/Kconfig-201-\tdepends on RTW89_CORE \u0026\u0026 CFG80211_DEBUGFS\n--\ndrivers/net/wireless/realtek/rtw89/Kconfig-203-\thelp\ndrivers/net/wireless/realtek/rtw89/Kconfig:204:\t  Enable debugfs support\ndrivers/net/wireless/realtek/rtw89/Kconfig-205-\n--\ndrivers/net/wireless/realtek/rtw89/core.c=7607=int rtw89_core_register(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/core.c-7617-\trtw89_phy_dm_init_data(rtwdev);\ndrivers/net/wireless/realtek/rtw89/core.c:7618:\trtw89_debugfs_init(rtwdev);\ndrivers/net/wireless/realtek/rtw89/core.c-7619-\n--\ndrivers/net/wireless/realtek/rtw89/core.c=7624=void rtw89_core_unregister(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/core.c-7627-\ndrivers/net/wireless/realtek/rtw89/core.c:7628:\trtw89_debugfs_deinit(rtwdev);\ndrivers/net/wireless/realtek/rtw89/core.c-7629-}\n--\ndrivers/net/wireless/realtek/rtw89/core.h=31=struct rtw89_phy_sts_ie10;\ndrivers/net/wireless/realtek/rtw89/core.h:32:struct rtw89_debugfs;\ndrivers/net/wireless/realtek/rtw89/core.h-33-struct rtw89_regd_data;\n--\ndrivers/net/wireless/realtek/rtw89/core.h=7570=struct rtw89_dev {\n--\ndrivers/net/wireless/realtek/rtw89/core.h-7710-\ndrivers/net/wireless/realtek/rtw89/core.h:7711:\tstruct rtw89_debugfs *debugfs;\ndrivers/net/wireless/realtek/rtw89/core.h-7712-\tstruct rtw89_vif *pure_monitor_mode_vif;\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=23=MODULE_PARM_DESC(debug_mask, \"Debugging mask\");\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-26-#ifdef CONFIG_RTW89_DEBUGFS\ndrivers/net/wireless/realtek/rtw89/debug.c:27:struct rtw89_debugfs_priv_opt {\ndrivers/net/wireless/realtek/rtw89/debug.c-28-\tbool rlock:1;\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-32-\ndrivers/net/wireless/realtek/rtw89/debug.c:33:struct rtw89_debugfs_priv {\ndrivers/net/wireless/realtek/rtw89/debug.c-34-\tstruct rtw89_dev *rtwdev;\ndrivers/net/wireless/realtek/rtw89/debug.c-35-\tssize_t (*cb_read)(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:36:\t\t\t   struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-37-\t\t\t   char *buf, size_t bufsz);\ndrivers/net/wireless/realtek/rtw89/debug.c-38-\tssize_t (*cb_write)(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:39:\t\t\t    struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-40-\t\t\t    const char *buf, size_t count);\ndrivers/net/wireless/realtek/rtw89/debug.c:41:\tstruct rtw89_debugfs_priv_opt opt;\ndrivers/net/wireless/realtek/rtw89/debug.c-42-\tunion {\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-69-\ndrivers/net/wireless/realtek/rtw89/debug.c:70:struct rtw89_debugfs {\ndrivers/net/wireless/realtek/rtw89/debug.c:71:\tstruct rtw89_debugfs_priv read_reg;\ndrivers/net/wireless/realtek/rtw89/debug.c:72:\tstruct rtw89_debugfs_priv write_reg;\ndrivers/net/wireless/realtek/rtw89/debug.c:73:\tstruct rtw89_debugfs_priv read_rf;\ndrivers/net/wireless/realtek/rtw89/debug.c:74:\tstruct rtw89_debugfs_priv write_rf;\ndrivers/net/wireless/realtek/rtw89/debug.c:75:\tstruct rtw89_debugfs_priv rf_reg_dump;\ndrivers/net/wireless/realtek/rtw89/debug.c:76:\tstruct rtw89_debugfs_priv txpwr_table;\ndrivers/net/wireless/realtek/rtw89/debug.c:77:\tstruct rtw89_debugfs_priv mac_reg_dump;\ndrivers/net/wireless/realtek/rtw89/debug.c:78:\tstruct rtw89_debugfs_priv mac_mem_dump;\ndrivers/net/wireless/realtek/rtw89/debug.c:79:\tstruct rtw89_debugfs_priv mac_dbg_port_dump;\ndrivers/net/wireless/realtek/rtw89/debug.c:80:\tstruct rtw89_debugfs_priv send_h2c;\ndrivers/net/wireless/realtek/rtw89/debug.c:81:\tstruct rtw89_debugfs_priv early_h2c;\ndrivers/net/wireless/realtek/rtw89/debug.c:82:\tstruct rtw89_debugfs_priv fw_crash;\ndrivers/net/wireless/realtek/rtw89/debug.c:83:\tstruct rtw89_debugfs_priv ser_counters;\ndrivers/net/wireless/realtek/rtw89/debug.c:84:\tstruct rtw89_debugfs_priv btc_info;\ndrivers/net/wireless/realtek/rtw89/debug.c:85:\tstruct rtw89_debugfs_priv btc_manual;\ndrivers/net/wireless/realtek/rtw89/debug.c:86:\tstruct rtw89_debugfs_priv fw_log_manual;\ndrivers/net/wireless/realtek/rtw89/debug.c:87:\tstruct rtw89_debugfs_priv phy_info;\ndrivers/net/wireless/realtek/rtw89/debug.c:88:\tstruct rtw89_debugfs_priv bb_info;\ndrivers/net/wireless/realtek/rtw89/debug.c:89:\tstruct rtw89_debugfs_priv stations;\ndrivers/net/wireless/realtek/rtw89/debug.c:90:\tstruct rtw89_debugfs_priv disable_dm;\ndrivers/net/wireless/realtek/rtw89/debug.c:91:\tstruct rtw89_debugfs_priv static_pd_th;\ndrivers/net/wireless/realtek/rtw89/debug.c:92:\tstruct rtw89_debugfs_priv mlo_mode;\ndrivers/net/wireless/realtek/rtw89/debug.c:93:\tstruct rtw89_debugfs_priv beacon_info;\ndrivers/net/wireless/realtek/rtw89/debug.c:94:\tstruct rtw89_debugfs_priv diag_mac;\ndrivers/net/wireless/realtek/rtw89/debug.c:95:\tstruct rtw89_debugfs_priv diag_bb;\ndrivers/net/wireless/realtek/rtw89/debug.c:96:\tstruct rtw89_debugfs_priv diag_rf;\ndrivers/net/wireless/realtek/rtw89/debug.c:97:\tstruct rtw89_debugfs_priv monitor_opts;\ndrivers/net/wireless/realtek/rtw89/debug.c-98-};\ndrivers/net/wireless/realtek/rtw89/debug.c-99-\ndrivers/net/wireless/realtek/rtw89/debug.c:100:struct rtw89_debugfs_iter_data {\ndrivers/net/wireless/realtek/rtw89/debug.c-101-\tchar *buf;\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-105-\ndrivers/net/wireless/realtek/rtw89/debug.c:106:static void rtw89_debugfs_iter_data_setup(struct rtw89_debugfs_iter_data *iter_data,\ndrivers/net/wireless/realtek/rtw89/debug.c-107-\t\t\t\t\t  char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-113-\ndrivers/net/wireless/realtek/rtw89/debug.c:114:static void rtw89_debugfs_iter_data_next(struct rtw89_debugfs_iter_data *iter_data,\ndrivers/net/wireless/realtek/rtw89/debug.c-115-\t\t\t\t\t char *buf, size_t bufsz, int written_sz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=130=static u16 rtw89_rate_info_bw_to_mhz(enum rate_info_bw bw)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-137-\ndrivers/net/wireless/realtek/rtw89/debug.c:138:static ssize_t rtw89_debugfs_file_read_helper(struct wiphy *wiphy, struct file *file,\ndrivers/net/wireless/realtek/rtw89/debug.c-139-\t\t\t\t\t      char *buf, size_t bufsz, void *data)\ndrivers/net/wireless/realtek/rtw89/debug.c-140-{\ndrivers/net/wireless/realtek/rtw89/debug.c:141:\tstruct rtw89_debugfs_priv *debugfs_priv = data;\ndrivers/net/wireless/realtek/rtw89/debug.c:142:\tstruct rtw89_dev *rtwdev = debugfs_priv-\u003ertwdev;\ndrivers/net/wireless/realtek/rtw89/debug.c-143-\tssize_t n;\ndrivers/net/wireless/realtek/rtw89/debug.c-144-\ndrivers/net/wireless/realtek/rtw89/debug.c:145:\tn = debugfs_priv-\u003ecb_read(rtwdev, debugfs_priv, buf, bufsz);\ndrivers/net/wireless/realtek/rtw89/debug.c-146-\trtw89_might_trailing_ellipsis(buf, bufsz, n);\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-150-\ndrivers/net/wireless/realtek/rtw89/debug.c:151:static ssize_t rtw89_debugfs_file_read(struct file *file, char __user *userbuf,\ndrivers/net/wireless/realtek/rtw89/debug.c-152-\t\t\t\t       size_t count, loff_t *ppos)\ndrivers/net/wireless/realtek/rtw89/debug.c-153-{\ndrivers/net/wireless/realtek/rtw89/debug.c:154:\tstruct rtw89_debugfs_priv *debugfs_priv = file-\u003eprivate_data;\ndrivers/net/wireless/realtek/rtw89/debug.c:155:\tstruct rtw89_debugfs_priv_opt *opt = \u0026debugfs_priv-\u003eopt;\ndrivers/net/wireless/realtek/rtw89/debug.c:156:\tstruct rtw89_dev *rtwdev = debugfs_priv-\u003ertwdev;\ndrivers/net/wireless/realtek/rtw89/debug.c-157-\tsize_t bufsz = opt-\u003ersize ? opt-\u003ersize : PAGE_SIZE;\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-160-\ndrivers/net/wireless/realtek/rtw89/debug.c:161:\tif (!debugfs_priv-\u003erbuf)\ndrivers/net/wireless/realtek/rtw89/debug.c:162:\t\tdebugfs_priv-\u003erbuf = devm_kzalloc(rtwdev-\u003edev, bufsz, GFP_KERNEL);\ndrivers/net/wireless/realtek/rtw89/debug.c-163-\ndrivers/net/wireless/realtek/rtw89/debug.c:164:\tbuf = debugfs_priv-\u003erbuf;\ndrivers/net/wireless/realtek/rtw89/debug.c-165-\tif (!buf)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-168-\tif (*ppos) {\ndrivers/net/wireless/realtek/rtw89/debug.c:169:\t\tn = debugfs_priv-\u003erused;\ndrivers/net/wireless/realtek/rtw89/debug.c-170-\t\tgoto out;\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-173-\tif (opt-\u003erlock) {\ndrivers/net/wireless/realtek/rtw89/debug.c:174:\t\tn = wiphy_locked_debugfs_read(rtwdev-\u003ehw-\u003ewiphy, file, buf, bufsz,\ndrivers/net/wireless/realtek/rtw89/debug.c-175-\t\t\t\t\t      userbuf, count, ppos,\ndrivers/net/wireless/realtek/rtw89/debug.c:176:\t\t\t\t\t      rtw89_debugfs_file_read_helper,\ndrivers/net/wireless/realtek/rtw89/debug.c:177:\t\t\t\t\t      debugfs_priv);\ndrivers/net/wireless/realtek/rtw89/debug.c:178:\t\tdebugfs_priv-\u003erused = n;\ndrivers/net/wireless/realtek/rtw89/debug.c-179-\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-182-\ndrivers/net/wireless/realtek/rtw89/debug.c:183:\tn = rtw89_debugfs_file_read_helper(rtwdev-\u003ehw-\u003ewiphy, file, buf, bufsz,\ndrivers/net/wireless/realtek/rtw89/debug.c:184:\t\t\t\t\t   debugfs_priv);\ndrivers/net/wireless/realtek/rtw89/debug.c:185:\tdebugfs_priv-\u003erused = n;\ndrivers/net/wireless/realtek/rtw89/debug.c-186-\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-190-\ndrivers/net/wireless/realtek/rtw89/debug.c:191:static ssize_t rtw89_debugfs_file_write_helper(struct wiphy *wiphy, struct file *file,\ndrivers/net/wireless/realtek/rtw89/debug.c-192-\t\t\t\t\t       char *buf, size_t count, void *data)\ndrivers/net/wireless/realtek/rtw89/debug.c-193-{\ndrivers/net/wireless/realtek/rtw89/debug.c:194:\tstruct rtw89_debugfs_priv *debugfs_priv = data;\ndrivers/net/wireless/realtek/rtw89/debug.c:195:\tstruct rtw89_dev *rtwdev = debugfs_priv-\u003ertwdev;\ndrivers/net/wireless/realtek/rtw89/debug.c-196-\ndrivers/net/wireless/realtek/rtw89/debug.c:197:\treturn debugfs_priv-\u003ecb_write(rtwdev, debugfs_priv, buf, count);\ndrivers/net/wireless/realtek/rtw89/debug.c-198-}\ndrivers/net/wireless/realtek/rtw89/debug.c-199-\ndrivers/net/wireless/realtek/rtw89/debug.c:200:static ssize_t rtw89_debugfs_file_write(struct file *file,\ndrivers/net/wireless/realtek/rtw89/debug.c-201-\t\t\t\t\tconst char __user *userbuf,\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-203-{\ndrivers/net/wireless/realtek/rtw89/debug.c:204:\tstruct rtw89_debugfs_priv *debugfs_priv = file-\u003eprivate_data;\ndrivers/net/wireless/realtek/rtw89/debug.c:205:\tstruct rtw89_debugfs_priv_opt *opt = \u0026debugfs_priv-\u003eopt;\ndrivers/net/wireless/realtek/rtw89/debug.c:206:\tstruct rtw89_dev *rtwdev = debugfs_priv-\u003ertwdev;\ndrivers/net/wireless/realtek/rtw89/debug.c-207-\tchar *buf __free(kfree) = kmalloc(count + 1, GFP_KERNEL);\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-213-\tif (opt-\u003ewlock) {\ndrivers/net/wireless/realtek/rtw89/debug.c:214:\t\tn = wiphy_locked_debugfs_write(rtwdev-\u003ehw-\u003ewiphy,\ndrivers/net/wireless/realtek/rtw89/debug.c-215-\t\t\t\t\t       file, buf, count + 1,\ndrivers/net/wireless/realtek/rtw89/debug.c-216-\t\t\t\t\t       userbuf, count,\ndrivers/net/wireless/realtek/rtw89/debug.c:217:\t\t\t\t\t       rtw89_debugfs_file_write_helper,\ndrivers/net/wireless/realtek/rtw89/debug.c:218:\t\t\t\t\t       debugfs_priv);\ndrivers/net/wireless/realtek/rtw89/debug.c-219-\t\treturn n;\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-226-\ndrivers/net/wireless/realtek/rtw89/debug.c:227:\treturn debugfs_priv-\u003ecb_write(rtwdev, debugfs_priv, buf, count);\ndrivers/net/wireless/realtek/rtw89/debug.c-228-}\ndrivers/net/wireless/realtek/rtw89/debug.c-229-\ndrivers/net/wireless/realtek/rtw89/debug.c:230:static const struct debugfs_short_fops file_ops_single_r = {\ndrivers/net/wireless/realtek/rtw89/debug.c:231:\t.read = rtw89_debugfs_file_read,\ndrivers/net/wireless/realtek/rtw89/debug.c-232-\t.llseek = generic_file_llseek,\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-234-\ndrivers/net/wireless/realtek/rtw89/debug.c:235:static const struct debugfs_short_fops file_ops_common_rw = {\ndrivers/net/wireless/realtek/rtw89/debug.c:236:\t.read = rtw89_debugfs_file_read,\ndrivers/net/wireless/realtek/rtw89/debug.c:237:\t.write = rtw89_debugfs_file_write,\ndrivers/net/wireless/realtek/rtw89/debug.c-238-\t.llseek = generic_file_llseek,\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-240-\ndrivers/net/wireless/realtek/rtw89/debug.c:241:static const struct debugfs_short_fops file_ops_single_w = {\ndrivers/net/wireless/realtek/rtw89/debug.c:242:\t.write = rtw89_debugfs_file_write,\ndrivers/net/wireless/realtek/rtw89/debug.c-243-\t.llseek = generic_file_llseek,\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=247=rtw89_debug_priv_read_reg_select(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:248:\t\t\t\t struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-249-\t\t\t\t const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-259-\ndrivers/net/wireless/realtek/rtw89/debug.c:260:\tdebugfs_priv-\u003eread_reg.addr = addr;\ndrivers/net/wireless/realtek/rtw89/debug.c:261:\tdebugfs_priv-\u003eread_reg.len = len;\ndrivers/net/wireless/realtek/rtw89/debug.c-262-\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=269=ssize_t rtw89_debug_priv_read_reg_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:270:\t\t\t\t      struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-271-\t\t\t\t      char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-276-\ndrivers/net/wireless/realtek/rtw89/debug.c:277:\tlen = debugfs_priv-\u003eread_reg.len;\ndrivers/net/wireless/realtek/rtw89/debug.c:278:\taddr = debugfs_priv-\u003eread_reg.addr;\ndrivers/net/wireless/realtek/rtw89/debug.c-279-\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=319=ssize_t rtw89_debug_priv_write_reg_set(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:320:\t\t\t\t       struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-321-\t\t\t\t       const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=354=rtw89_debug_priv_read_rf_select(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:355:\t\t\t\tstruct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-356-\t\t\t\tconst char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-371-\t}\ndrivers/net/wireless/realtek/rtw89/debug.c:372:\tdebugfs_priv-\u003eread_rf.addr = addr;\ndrivers/net/wireless/realtek/rtw89/debug.c:373:\tdebugfs_priv-\u003eread_rf.mask = mask;\ndrivers/net/wireless/realtek/rtw89/debug.c:374:\tdebugfs_priv-\u003eread_rf.path = path;\ndrivers/net/wireless/realtek/rtw89/debug.c-375-\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=382=ssize_t rtw89_debug_priv_read_rf_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:383:\t\t\t\t     struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-384-\t\t\t\t     char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-389-\ndrivers/net/wireless/realtek/rtw89/debug.c:390:\taddr = debugfs_priv-\u003eread_rf.addr;\ndrivers/net/wireless/realtek/rtw89/debug.c:391:\tmask = debugfs_priv-\u003eread_rf.mask;\ndrivers/net/wireless/realtek/rtw89/debug.c:392:\tpath = debugfs_priv-\u003eread_rf.path;\ndrivers/net/wireless/realtek/rtw89/debug.c-393-\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=403=ssize_t rtw89_debug_priv_write_rf_set(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:404:\t\t\t\t      struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-405-\t\t\t\t      const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=430=ssize_t rtw89_debug_priv_rf_reg_dump_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:431:\t\t\t\t\t struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-432-\t\t\t\t\t char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=978=ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:979:\t\t\t\t\t struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-980-\t\t\t\t\t char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=1063=rtw89_debug_priv_mac_reg_dump_select(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:1064:\t\t\t\t     struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-1065-\t\t\t\t     const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-1085-\ndrivers/net/wireless/realtek/rtw89/debug.c:1086:\tdebugfs_priv-\u003ecb_data = sel;\ndrivers/net/wireless/realtek/rtw89/debug.c:1087:\trtw89_info(rtwdev, \"select mac page dump %d\\n\", debugfs_priv-\u003ecb_data);\ndrivers/net/wireless/realtek/rtw89/debug.c-1088-\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=1095=ssize_t rtw89_debug_priv_mac_reg_dump_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:1096:\t\t\t\t\t  struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-1097-\t\t\t\t\t  char *buf, size_t bufsz)\ndrivers/net/wireless/realtek/rtw89/debug.c-1098-{\ndrivers/net/wireless/realtek/rtw89/debug.c:1099:\tenum rtw89_debug_mac_reg_sel reg_sel = debugfs_priv-\u003ecb_data;\ndrivers/net/wireless/realtek/rtw89/debug.c-1100-\tchar *p = buf, *end = buf + bufsz;\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=1172=rtw89_debug_priv_mac_mem_dump_select(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:1173:\t\t\t\t     struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-1174-\t\t\t\t     const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-1184-\ndrivers/net/wireless/realtek/rtw89/debug.c:1185:\tdebugfs_priv-\u003emac_mem.sel = sel;\ndrivers/net/wireless/realtek/rtw89/debug.c:1186:\tdebugfs_priv-\u003emac_mem.start = start_addr;\ndrivers/net/wireless/realtek/rtw89/debug.c:1187:\tdebugfs_priv-\u003emac_mem.len = len;\ndrivers/net/wireless/realtek/rtw89/debug.c-1188-\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=1238=rtw89_debug_priv_mac_mem_dump_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:1239:\t\t\t\t  struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-1240-\t\t\t\t  char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-1246-\ndrivers/net/wireless/realtek/rtw89/debug.c:1247:\tif (debugfs_priv-\u003emac_mem.sel \u003e= RTW89_MAC_MEM_NUM)\ndrivers/net/wireless/realtek/rtw89/debug.c-1248-\t\treturn -ENOENT;\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-1250-\tif (rtwdev-\u003echip-\u003echip_id == RTL8852C) {\ndrivers/net/wireless/realtek/rtw89/debug.c:1251:\t\tswitch (debugfs_priv-\u003emac_mem.sel) {\ndrivers/net/wireless/realtek/rtw89/debug.c-1252-\t\tcase RTW89_MAC_MEM_TXD_FIFO_0_V1:\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-1266-\tp += rtw89_debug_dump_mac_mem(rtwdev, p, end - p,\ndrivers/net/wireless/realtek/rtw89/debug.c:1267:\t\t\t\t      debugfs_priv-\u003emac_mem.sel,\ndrivers/net/wireless/realtek/rtw89/debug.c:1268:\t\t\t\t      debugfs_priv-\u003emac_mem.start,\ndrivers/net/wireless/realtek/rtw89/debug.c:1269:\t\t\t\t      debugfs_priv-\u003emac_mem.len);\ndrivers/net/wireless/realtek/rtw89/debug.c-1270-\tif (grant_read)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=1277=rtw89_debug_priv_mac_dbg_port_dump_select(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:1278:\t\t\t\t\t  struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-1279-\t\t\t\t\t  const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-1293-\tcase 0:\ndrivers/net/wireless/realtek/rtw89/debug.c:1294:\t\tdebugfs_priv-\u003edbgpkg_en.ss_dbg = enable;\ndrivers/net/wireless/realtek/rtw89/debug.c-1295-\t\tbreak;\ndrivers/net/wireless/realtek/rtw89/debug.c-1296-\tcase 1:\ndrivers/net/wireless/realtek/rtw89/debug.c:1297:\t\tdebugfs_priv-\u003edbgpkg_en.dle_dbg = enable;\ndrivers/net/wireless/realtek/rtw89/debug.c-1298-\t\tbreak;\ndrivers/net/wireless/realtek/rtw89/debug.c-1299-\tcase 2:\ndrivers/net/wireless/realtek/rtw89/debug.c:1300:\t\tdebugfs_priv-\u003edbgpkg_en.dmac_dbg = enable;\ndrivers/net/wireless/realtek/rtw89/debug.c-1301-\t\tbreak;\ndrivers/net/wireless/realtek/rtw89/debug.c-1302-\tcase 3:\ndrivers/net/wireless/realtek/rtw89/debug.c:1303:\t\tdebugfs_priv-\u003edbgpkg_en.cmac_dbg = enable;\ndrivers/net/wireless/realtek/rtw89/debug.c-1304-\t\tbreak;\ndrivers/net/wireless/realtek/rtw89/debug.c-1305-\tcase 4:\ndrivers/net/wireless/realtek/rtw89/debug.c:1306:\t\tdebugfs_priv-\u003edbgpkg_en.dbg_port = enable;\ndrivers/net/wireless/realtek/rtw89/debug.c-1307-\t\tbreak;\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=3507=rtw89_debug_priv_mac_dbg_port_dump_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:3508:\t\t\t\t       struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-3509-\t\t\t\t       char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-3512-\ndrivers/net/wireless/realtek/rtw89/debug.c:3513:\tif (debugfs_priv-\u003edbgpkg_en.ss_dbg)\ndrivers/net/wireless/realtek/rtw89/debug.c-3514-\t\tp += rtw89_debug_mac_dump_ss_dbg(rtwdev, p, end - p);\ndrivers/net/wireless/realtek/rtw89/debug.c:3515:\tif (debugfs_priv-\u003edbgpkg_en.dle_dbg)\ndrivers/net/wireless/realtek/rtw89/debug.c-3516-\t\tp += rtw89_debug_mac_dump_dle_dbg(rtwdev, p, end - p);\ndrivers/net/wireless/realtek/rtw89/debug.c:3517:\tif (debugfs_priv-\u003edbgpkg_en.dmac_dbg)\ndrivers/net/wireless/realtek/rtw89/debug.c-3518-\t\tp += rtw89_debug_mac_dump_dmac_dbg(rtwdev, p, end - p);\ndrivers/net/wireless/realtek/rtw89/debug.c:3519:\tif (debugfs_priv-\u003edbgpkg_en.cmac_dbg)\ndrivers/net/wireless/realtek/rtw89/debug.c-3520-\t\tp += rtw89_debug_mac_dump_cmac_dbg(rtwdev, p, end - p);\ndrivers/net/wireless/realtek/rtw89/debug.c:3521:\tif (debugfs_priv-\u003edbgpkg_en.dbg_port)\ndrivers/net/wireless/realtek/rtw89/debug.c-3522-\t\tp += rtw89_debug_mac_dump_dbg_port(rtwdev, p, end - p);\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=3550=static ssize_t rtw89_debug_priv_send_h2c_set(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:3551:\t\t\t\t\t     struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-3552-\t\t\t\t\t     const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=3570=rtw89_debug_priv_early_h2c_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:3571:\t\t\t       struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-3572-\t\t\t       char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=3588=rtw89_debug_priv_early_h2c_set(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:3589:\t\t\t       struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-3590-\t\t\t       const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=3763=rtw89_debug_priv_fw_crash_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:3764:\t\t\t      struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-3765-\t\t\t      char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=3781=rtw89_debug_priv_fw_crash_set(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:3782:\t\t\t      struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-3783-\t\t\t      const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=3854=static ssize_t rtw89_debug_priv_ser_counters_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:3855:\t\t\t\t\t\t struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-3856-\t\t\t\t\t\t char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=3892=static ssize_t rtw89_debug_priv_btc_info_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:3893:\t\t\t\t\t     struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-3894-\t\t\t\t\t     char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=3899=static ssize_t rtw89_debug_priv_btc_manual_set(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:3900:\t\t\t\t\t       struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-3901-\t\t\t\t\t       const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=3915=static ssize_t rtw89_debug_priv_fw_log_manual_set(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:3916:\t\t\t\t\t\t  struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-3917-\t\t\t\t\t\t  const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=4070=static void rtw89_sta_info_get_iter(void *data, struct ieee80211_sta *sta)\ndrivers/net/wireless/realtek/rtw89/debug.c-4071-{\ndrivers/net/wireless/realtek/rtw89/debug.c:4072:\tstruct rtw89_debugfs_iter_data *iter_data =\ndrivers/net/wireless/realtek/rtw89/debug.c:4073:\t\t(struct rtw89_debugfs_iter_data *)data;\ndrivers/net/wireless/realtek/rtw89/debug.c-4074-\tstruct rtw89_sta *rtwsta = sta_to_rtwsta(sta);\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-4084-\ndrivers/net/wireless/realtek/rtw89/debug.c:4085:\trtw89_debugfs_iter_data_next(iter_data, p, end - p, p - buf);\ndrivers/net/wireless/realtek/rtw89/debug.c-4086-}\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=4173=static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:4174:\t\t\t\t\t     struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-4175-\t\t\t\t\t     char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-4177-\tstruct rtw89_traffic_stats *stats = \u0026rtwdev-\u003estats;\ndrivers/net/wireless/realtek/rtw89/debug.c:4178:\tstruct rtw89_debugfs_iter_data iter_data;\ndrivers/net/wireless/realtek/rtw89/debug.c-4179-\tstruct rtw89_hal *hal = \u0026rtwdev-\u003ehal;\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-4202-\ndrivers/net/wireless/realtek/rtw89/debug.c:4203:\trtw89_debugfs_iter_data_setup(\u0026iter_data, p, end - p);\ndrivers/net/wireless/realtek/rtw89/debug.c-4204-\tieee80211_iterate_stations_atomic(rtwdev-\u003ehw, rtw89_sta_info_get_iter, \u0026iter_data);\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=4470=rtw89_debug_priv_bb_info_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:4471:\t\t\t     struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-4472-\t\t\t     char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-4477-\tconst struct rtw89_tx_rate_cnt_info *info;\ndrivers/net/wireless/realtek/rtw89/debug.c:4478:\tstruct rtw89_debugfs_iter_data iter_data;\ndrivers/net/wireless/realtek/rtw89/debug.c-4479-\tchar *p = buf, *end = buf + bufsz;\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-4509-\ndrivers/net/wireless/realtek/rtw89/debug.c:4510:\trtw89_debugfs_iter_data_setup(\u0026iter_data, p, end - p);\ndrivers/net/wireless/realtek/rtw89/debug.c-4511-\tieee80211_iterate_stations_atomic(rtwdev-\u003ehw, rtw89_sta_info_get_iter, \u0026iter_data);\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=4518=rtw89_debug_priv_bb_info_set(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:4519:\t\t\t     struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-4520-\t\t\t     const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=4620=void rtw89_vif_ids_get_iter(void *data, u8 *mac, struct ieee80211_vif *vif)\ndrivers/net/wireless/realtek/rtw89/debug.c-4621-{\ndrivers/net/wireless/realtek/rtw89/debug.c:4622:\tstruct rtw89_debugfs_iter_data *iter_data =\ndrivers/net/wireless/realtek/rtw89/debug.c:4623:\t\t(struct rtw89_debugfs_iter_data *)data;\ndrivers/net/wireless/realtek/rtw89/debug.c-4624-\tstruct rtw89_vif *rtwvif = vif_to_rtwvif(vif);\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-4639-\ndrivers/net/wireless/realtek/rtw89/debug.c:4640:\trtw89_debugfs_iter_data_next(iter_data, p, end - p, p - buf);\ndrivers/net/wireless/realtek/rtw89/debug.c-4641-}\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=4691=static void rtw89_sta_ids_get_iter(void *data, struct ieee80211_sta *sta)\ndrivers/net/wireless/realtek/rtw89/debug.c-4692-{\ndrivers/net/wireless/realtek/rtw89/debug.c:4693:\tstruct rtw89_debugfs_iter_data *iter_data =\ndrivers/net/wireless/realtek/rtw89/debug.c:4694:\t\t(struct rtw89_debugfs_iter_data *)data;\ndrivers/net/wireless/realtek/rtw89/debug.c-4695-\tstruct rtw89_sta *rtwsta = sta_to_rtwsta(sta);\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-4711-\ndrivers/net/wireless/realtek/rtw89/debug.c:4712:\trtw89_debugfs_iter_data_next(iter_data, p, end - p, p - buf);\ndrivers/net/wireless/realtek/rtw89/debug.c-4713-}\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=4715=static ssize_t rtw89_debug_priv_stations_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:4716:\t\t\t\t\t     struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-4717-\t\t\t\t\t     char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-4719-\tstruct rtw89_cam_info *cam_info = \u0026rtwdev-\u003ecam_info;\ndrivers/net/wireless/realtek/rtw89/debug.c:4720:\tstruct rtw89_debugfs_iter_data iter_data;\ndrivers/net/wireless/realtek/rtw89/debug.c-4721-\tchar *p = buf, *end = buf + bufsz;\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-4752-\ndrivers/net/wireless/realtek/rtw89/debug.c:4753:\trtw89_debugfs_iter_data_setup(\u0026iter_data, p, end - p);\ndrivers/net/wireless/realtek/rtw89/debug.c-4754-\tieee80211_iterate_active_interfaces_atomic(rtwdev-\u003ehw,\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-4757-\ndrivers/net/wireless/realtek/rtw89/debug.c:4758:\trtw89_debugfs_iter_data_setup(\u0026iter_data, p, end - p);\ndrivers/net/wireless/realtek/rtw89/debug.c-4759-\tieee80211_iterate_stations_atomic(rtwdev-\u003ehw, rtw89_sta_ids_get_iter, \u0026iter_data);\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=4782=rtw89_debug_priv_disable_dm_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:4783:\t\t\t\tstruct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-4784-\t\t\t\tchar *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=4808=rtw89_debug_priv_disable_dm_set(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:4809:\t\t\t\tstruct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-4810-\t\t\t\tconst char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=4839=rtw89_debug_priv_static_pd_th_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:4840:\t\t\t\t  struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-4841-\t\t\t\t  char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=4870=rtw89_debug_priv_static_pd_th_set(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:4871:\t\t\t\t  struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-4872-\t\t\t\t  const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=4930=rtw89_debug_priv_mlo_mode_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:4931:\t\t\t      struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-4932-\t\t\t      char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=4961=rtw89_debug_priv_mlo_mode_set(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:4962:\t\t\t      struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-4963-\t\t\t      const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=5006=rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:5007:\t\t\t\t struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-5008-\t\t\t\t char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=5346=rtw89_debug_priv_diag_mac_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:5347:\t\t\t      struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-5348-\t\t\t      char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=5382=rtw89_debug_priv_diag_bb_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:5383:\t\t\t     struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-5384-\t\t\t     char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=5398=rtw89_debug_priv_diag_rf_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:5399:\t\t\t     struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-5400-\t\t\t     char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=5482=rtw89_debug_priv_diag_rf_set(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:5483:\t\t\t     struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-5484-\t\t\t     const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=5510=rtw89_debug_priv_monitor_opts_get(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:5511:\t\t\t\t  struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-5512-\t\t\t\t  char *buf, size_t bufsz)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c=5534=rtw89_debug_priv_monitor_opts_set(struct rtw89_dev *rtwdev,\ndrivers/net/wireless/realtek/rtw89/debug.c:5535:\t\t\t\t  struct rtw89_debugfs_priv *debugfs_priv,\ndrivers/net/wireless/realtek/rtw89/debug.c-5536-\t\t\t\t  const char *buf, size_t count)\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-5594-\ndrivers/net/wireless/realtek/rtw89/debug.c:5595:static const struct rtw89_debugfs rtw89_debugfs_templ = {\ndrivers/net/wireless/realtek/rtw89/debug.c-5596-\t.read_reg = rtw89_debug_priv_select_and_get(read_reg),\n--\ndrivers/net/wireless/realtek/rtw89/debug.c-5624-\ndrivers/net/wireless/realtek/rtw89/debug.c:5625:#define rtw89_debugfs_add(name, mode, fopname, parent)\t\t\t\t\\\ndrivers/net/wireless/realtek/rtw89/debug.c-5626-\tdo {\t\t\t\t\t\t\t\t\t\\\ndrivers/net/wireless/realtek/rtw89/debug.c:5627:\t\tstruct rtw89_debugfs_priv *priv = \u0026rtwdev-\u003edebugfs-\u003ename;\t\\\ndrivers/net/wireless/realtek/rtw89/debug.c-5628-\t\tpriv-\u003ertwdev = rtwdev;\t\t\t\t\t\t\\\ndrivers/net/wireless/realtek/rtw89/debug.c:5629:\t\tif (IS_ERR(debugfs_create_file(#name, mode, parent, priv,\t\\\ndrivers/net/wireless/realtek/rtw89/debug.c-5630-\t\t\t\t\t       \u0026file_ops_ ##fopname)))\t\t\\\ndrivers/net/wireless/realtek/rtw89/debug.c:5631:\t\t\tpr_debug(\"Unable to initialize debugfs:%s\\n\", #name);\t\\\ndrivers/net/wireless/realtek/rtw89/debug.c-5632-\t} while (0)\ndrivers/net/wireless/realtek/rtw89/debug.c-5633-\ndrivers/net/wireless/realtek/rtw89/debug.c:5634:#define rtw89_debugfs_add_w(name)\t\t\t\t\t\t\\\ndrivers/net/wireless/realtek/rtw89/debug.c:5635:\trtw89_debugfs_add(name, S_IFREG | 0222, single_w, debugfs_topdir)\ndrivers/net/wireless/realtek/rtw89/debug.c:5636:#define rtw89_debugfs_add_rw(name)\t\t\t\t\t\t\\\ndrivers/net/wireless/realtek/rtw89/debug.c:5637:\trtw89_debugfs_add(name, S_IFREG | 0666, common_rw, debugfs_topdir)\ndrivers/net/wireless/realtek/rtw89/debug.c:5638:#define rtw89_debugfs_add_r(name)\t\t\t\t\t\t\\\ndrivers/net/wireless/realtek/rtw89/debug.c:5639:\trtw89_debugfs_add(name, S_IFREG | 0444, single_r, debugfs_topdir)\ndrivers/net/wireless/realtek/rtw89/debug.c-5640-\n\n"
}

65/3 2026/09/25 10:25 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 36811
							output: 41
							thoughts: 1303
						

Thoughts:
**Analyzing Firmware Element Duplication**

Okay, let's dive into this firmware element duplication logic. My initial thought is to understand how this `__rtw89_fw_elem_dup_if_needed` function handles the copying of firmware data. The core of this seems to hinge on whether `is_fw_suit` is true or false.

If `is_fw_suit` is true, we have a `vmalloc` allocation. Hmm, `vmalloc` allocates whole pages, right? And the subsequent `memcpy` copies exactly `len` bytes. So, `dup` has initialized `len` bytes, and the code reading it, like in the firmware download routine for `fw_suit`, should only access `fw_suit->size` bytes, which equals `len`. No problem there. The critical case would be if `vmalloc` fails; it returns NULL, and the code correctly falls back. But, what if we call it with `len = 0`? That might be an edge case to check. In `rtw89_mfw_recognize` and `__rtw89_fw_recognize_from_elm`, `fw_suit->size` is the firmware binary size.

Now, if `is_fw_suit` is false, `devm_kmemdup` is called. Ah, `kmemdup` is based on `kmalloc`, so it will allocate only the requested amount, `len` bytes, then copies the data. So again, `len` bytes are fully initialized with the contents of `src`. This looks safe.

Let's look at `rtw89_fw_elem_dup_if_needed`. In this specific function, `is_fw_suit` is false. So we use `devm_kmemdup`.

Moving onto `rtw89_board_elm_seek`: This looks like an image parsing function for board firmware elements. We can see that the `pseudo_fw` structure gets `memset` to zero, which is good.

Finally, what about `rtw89_board_variant`, specifically `rtw89_8852cu_valve_board`. Are there other variants? Let's verify that.



66/3 2026/09/25 10:25 tool grepper
0m Args:
{
  "Expression": "rtw89_board_variant",
  "PathPrefix": "drivers/net/wireless/realtek/rtw89"
}

Results:
{
  "Output": "drivers/net/wireless/realtek/rtw89/core.h=5874=enum rtw89_board_id {\n--\ndrivers/net/wireless/realtek/rtw89/core.h-5880-\ndrivers/net/wireless/realtek/rtw89/core.h:5881:struct rtw89_board_variant {\ndrivers/net/wireless/realtek/rtw89/core.h-5882-\tenum rtw89_board_id id;\n--\ndrivers/net/wireless/realtek/rtw89/core.h=5891=struct rtw89_driver_info {\n--\ndrivers/net/wireless/realtek/rtw89/core.h-5893-\tconst struct rtw89_chip_variant *variant;\ndrivers/net/wireless/realtek/rtw89/core.h:5894:\tconst struct rtw89_board_variant *board;\ndrivers/net/wireless/realtek/rtw89/core.h-5895-\tconst struct dmi_system_id *quirks;\n--\ndrivers/net/wireless/realtek/rtw89/core.h=7570=struct rtw89_dev {\n--\ndrivers/net/wireless/realtek/rtw89/core.h-7581-\tconst struct rtw89_chip_variant *variant;\ndrivers/net/wireless/realtek/rtw89/core.h:7582:\tconst struct rtw89_board_variant *board;\ndrivers/net/wireless/realtek/rtw89/core.h-7583-\tconst struct rtw89_pci_info *pci_info;\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=1715=int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)\ndrivers/net/wireless/realtek/rtw89/fw.c-1716-{\ndrivers/net/wireless/realtek/rtw89/fw.c:1717:\tconst struct rtw89_board_variant *board = rtwdev-\u003eboard;\ndrivers/net/wireless/realtek/rtw89/fw.c-1718-\tstruct rtw89_fw_req_info *fw_req = \u0026rtwdev-\u003efw.req;\n--\ndrivers/net/wireless/realtek/rtw89/fw.c=2216=static int rtw89_load_firmware_req(struct rtw89_dev *rtwdev,\n--\ndrivers/net/wireless/realtek/rtw89/fw.c-2219-{\ndrivers/net/wireless/realtek/rtw89/fw.c:2220:\tconst struct rtw89_board_variant *board = rtwdev-\u003eboard;\ndrivers/net/wireless/realtek/rtw89/fw.c-2221-\tint ret = 0;\n--\ndrivers/net/wireless/realtek/rtw89/led.c=96=void rtw89_led_init(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/led.c-99-\tstruct rtw89_led *rtw_led = \u0026rtwdev-\u003eled;\ndrivers/net/wireless/realtek/rtw89/led.c:100:\tconst struct rtw89_board_variant *board = rtwdev-\u003eboard;\ndrivers/net/wireless/realtek/rtw89/led.c-101-\tint ret;\n--\ndrivers/net/wireless/realtek/rtw89/rtw8852cu.c=86=static const struct rtw89_led_desc rtw8852cu_valve_led_desc = {\n--\ndrivers/net/wireless/realtek/rtw89/rtw8852cu.c-90-\ndrivers/net/wireless/realtek/rtw89/rtw8852cu.c:91:static const struct rtw89_board_variant rtw89_8852cu_valve_board = {\ndrivers/net/wireless/realtek/rtw89/rtw8852cu.c-92-\t.id = RTW89_BOARD_ID(28de, 2432),\n"
}

67/3 2026/09/25 10:25 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 39759
							output: 371
							thoughts: 4604
						

Thoughts:
**Analysis of Realtek rtw89 Firmware Loading Optimization**

Okay, here's the breakdown of my thought process regarding this code. The core idea seems to be optimizing memory usage in the Realtek rtw89 wireless driver by allowing for early release of the main firmware after the driver probes the hardware.  The code selectively duplicates firmware data structures (suits and elements) to manage this early release and still have the data needed.

Initially, I see that the `rtw89_8852cu_valve_board` is a critical structure, with a NULL check to other board types. Then I check `rtw89_load_firmware_req` which is key for firmware loading.  I note that if `req->firmware` already exists, we skip the request. There's a check for `nowarn` and then a request for firmware, with an optional request for a `board_elm` file.  The `req->free_after_probe` flag is then set based on the firmware size. I mentally trace how the completion of the request and the return of an error code. I'm focusing on the condition where `req->firmware` fails to load.

Next up, `rtw89_chip_info_setup` gets my attention. This function is responsible for the actual setup after the firmware is loaded. Here's where `fw_req->free_after_probe` comes into play. If that flag is set, the driver unloads the firmware.  I delve into `__rtw89_unload_firmware`. This function releases the firmware and `board_elm` files, which sets `req->firmware` and `req->board_elm` to NULL.  The question is: what is `req->free_after_probe` is true, but pointers inside `rtw89` still point to freed memory (duplicates, in most cases). I start to look at what gets duplicated and when the pointers in the structs are set.

The `rtw89_fw_recognize_txpwr_from_elm` function looks interesting next because it copies data from the `board_elm` file into a configuration structure. I'm immediately concerned about a potential use-after-free situation if the firmware is released too early. I note the comment in the code that the `data` pointer is for format conversion before release. I can see KASAN is the most important tool here, for catching the use-after-free.

I then move on to `rtw89_fw_elem_dup_if_needed`, where firmware elements are conditionally duplicated. There's a calculation of a `len` variable which relies on the `elm->size` field. My mind jumps to the possibility of an integer overflow in that calculation.  If the size is large enough, the addition of `sizeof(*elm)` could wrap around, resulting in a small allocation via `devm_kmemdup` or even `vmalloc`. This leads to another KASAN flag, `KASAN_VMALLOC`. Then, there is the question if `vmalloc` zeros the memory.

I then check `__rtw89_fw_elem_dup_if_needed`, and note the `memcpy`. Then, I note that if `vmalloc` is used, KMSAN isn't needed here. All data gets copied.

Finally, I reach the KMSAN assessment. I carefully review the KMSAN conditions. My conclusion: none of the conditions for KMSAN are met.  There is no transfer of kernel structures to userspace, no conditional logic depending on uninitialized variables, no allocation of complex data structures with potentially uninitialized fields that are later read, and no modifications to bounds checks that could result in uninitialized memory accesses.

I list the types of vulnerabilities that *are* introduced: double-free and use-after-free, potential integer overflow resulting in out-of-bounds reads/writes, and list corruption. But all these memory safety issues are detectable by KASAN and CONFIG_DEBUG_LIST and standard kernel debugging facilities. No uninitialized memory is read or leaked; all allocated memory, whether by kmalloc, vmalloc, or devm_kmemdup, is initialized before use via memset or memcpy.

Therefore, this patch does not require a dedicated KMSAN fuzzing session. I make a detailed argument about why KMSAN is *not* needed, and then contrast it with how KASAN is the appropriate tool. This will be an easy 'Reviewed-by'.



68/3 2026/09/25 10:25 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch optimizes memory consumption in the Realtek rtw89 driver by introducing board element firmware support and optionally duplicating firmware elements/suits so that the primary firmware buffers can be released early after probing.\n\nFrom a memory safety perspective:\n1. KMSAN applicability: The patch does not introduce or expose uninitialized memory risks. All newly allocated buffers (via devm_kmemdup or vmalloc) are fully populated with memcpy for their entire specified length. Stack variables like `struct firmware pseudo_fw` are explicitly cleared with memset(..., 0, ...) before initialization. The new list head `dup_data_list` is initialized with INIT_LIST_HEAD. Furthermore, no kernel data structures, buffers, or uninitialized padding are copied or exposed to user space.\n2. KASAN applicability: The primary risks associated with this patch involve memory lifetimes and boundary calculations—specifically, potential Use-After-Free (UAF) if driver logic attempts to dereference firmware pointers after early release in `__rtw89_unload_firmware`, Double-Free if `rtw89_unload_firmware_dup_data` is invoked repeatedly without clearing `dup_data_list`, or out-of-bounds (OOB) memory access if 32-bit arithmetic overflow occurs in offset/size calculations (e.g. `ofst + size` in `rtw89_board_elm_seek`). These issues are all reliably detected by KASAN (including KASAN_VMALLOC) and standard kernel debugging checks (such as CONFIG_DEBUG_LIST).\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch optimizes memory consumption in the Realtek rtw89 driver by introducing board element firmware support and optionally duplicating firmware elements/suits so that the primary firmware buffers can be released early after probing.\n\nFrom a memory safety perspective:\n1. KMSAN applicability: The patch does not introduce or expose uninitialized memory risks. All newly allocated buffers (via devm_kmemdup or vmalloc) are fully populated with memcpy for their entire specified length. Stack variables like `struct firmware pseudo_fw` are explicitly cleared with memset(..., 0, ...) before initialization. The new list head `dup_data_list` is initialized with INIT_LIST_HEAD. Furthermore, no kernel data structures, buffers, or uninitialized padding are copied or exposed to user space.\n2. KASAN applicability: The primary risks associated with this patch involve memory lifetimes and boundary calculations—specifically, potential Use-After-Free (UAF) if driver logic attempts to dereference firmware pointers after early release in `__rtw89_unload_firmware`, Double-Free if `rtw89_unload_firmware_dup_data` is invoked repeatedly without clearing `dup_data_list`, or out-of-bounds (OOB) memory access if 32-bit arithmetic overflow occurs in offset/size calculations (e.g. `ofst + size` in `rtw89_board_elm_seek`). These issues are all reliably detected by KASAN (including KASAN_VMALLOC) and standard kernel debugging checks (such as CONFIG_DEBUG_LIST).\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
  "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)