| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/13 10:02 | flow | patch-triage |
2mArgs:null Results: {
"EnableConfigs": [
"MT7925U",
"NL80211_TESTMODE"
],
"FocusSymbols": [
"mt7925_testmode_dump"
],
"KMSANReasoning": "The patch fixes a bug in `mt7925_tm_query()` where `memset(pcmd, 0, sizeof(*pcmd))` was used to initialize a stack-allocated `struct mt7925_rftest_cmd cmd`. Because `pcmd` was declared as a `char *`, `sizeof(*pcmd)` evaluated to 1, meaning only the first byte of the structure was zeroed. The remaining 3 bytes of the `padding` field were left uninitialized. This structure was then sent to the MCU via `mt76_mcu_send_and_get_msg()`, resulting in a leak of uninitialized kernel stack memory to the device.\n\nAdditionally, the patch modifies the response handling to allow shorter responses from the MCU. To prevent leaking uninitialized stack memory to user space via `nla_put()` when the response is shorter than `MT7925_EVT_RSP_LEN`, the patch zero-initializes the `evt_resp` buffer (`char evt_resp[MT7925_EVT_RSP_LEN] = {};`).\n\nStandard KASAN detects memory corruption, out-of-bounds accesses, and use-after-free bugs, but it does not track uninitialized memory. KMSAN (KernelMemorySanitizer) is specifically designed to detect reads of uninitialized memory and info-leaks (both to user space and to hardware devices). Since this patch addresses uninitialized stack memory leaks, a dedicated KMSAN fuzzing session is required to verify the fix and ensure no other uninitialized memory issues exist in this code path.",
"NeedsKMSAN": true,
"Reasoning": "The patch fixes an out-of-bounds access and uninitialized memory issue in the mt7925 testmode query handler. The code is reachable via nl80211 testmode commands for the MT7925 USB wireless driver, which can be fuzzed using USB emulation.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/13 10:02 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 9e4ea6bbecf451e6dec70021d56db919a459c84f\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Aug 13 10:02:42 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c\nindex 22a8f1ddc321d..dbf1159effeb9 100644\n--- a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c\n+++ b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c\n@@ -87,13 +87,14 @@ static int\n mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req,\n \t\tchar *evt_resp)\n {\n+\tconst unsigned int hdr = offsetof(struct uni_cmd_testmode_ctrl, data);\n \tstruct mt7925_rftest_cmd cmd;\n-\tchar *pcmd = (char *)\u0026cmd;\n+\tstruct mt7925_rftest_cmd *pcmd = \u0026cmd;\n \tstruct sk_buff *skb = NULL;\n-\tint ret = 1;\n+\tint ret;\n \n \tmemset(pcmd, 0, sizeof(*pcmd));\n-\tmemcpy(pcmd + 4, (char *)\u0026req-\u003ec, sizeof(struct uni_cmd_testmode_ctrl));\n+\tmemcpy(\u0026pcmd-\u003ectrl, \u0026req-\u003ec, sizeof(struct uni_cmd_testmode_ctrl));\n \n \tif (*((uint16_t *)req-\u003epadding) == MCU_UNI_CMD_TESTMODE_CTRL)\n \t\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_UNI_QUERY(TESTMODE_CTRL),\n@@ -101,16 +102,18 @@ mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req,\n \telse if (*((uint16_t *)req-\u003epadding) == MCU_UNI_CMD_TESTMODE_RX_STAT)\n \t\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_UNI_QUERY(TESTMODE_RX_STAT),\n \t\t\t\t\t\t\u0026cmd, sizeof(cmd), true, \u0026skb);\n+\telse\n+\t\tret = -EINVAL;\n \n \tif (ret)\n \t\tgoto out;\n \n-\tif (skb-\u003elen \u003c MT7925_EVT_RSP_LEN + 8) {\n+\tif (!skb_pull(skb, hdr)) {\n \t\tret = -EINVAL;\n \t\tgoto out;\n \t}\n \n-\tmemcpy((char *)evt_resp, (char *)skb-\u003edata + 8, MT7925_EVT_RSP_LEN);\n+\tmemcpy(evt_resp, skb-\u003edata, min_t(u32, skb-\u003elen, MT7925_EVT_RSP_LEN));\n \n out:\n \tdev_kfree_skb(skb);\n@@ -190,7 +193,7 @@ int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,\n \n \t\tdata = drv_tb[MT7925_TM_ATTR_QUERY];\n \t\tif (data) {\n-\t\t\tchar evt_resp[MT7925_EVT_RSP_LEN];\n+\t\t\tchar evt_resp[MT7925_EVT_RSP_LEN] = {};\n \n \t\t\terr = mt7925_tm_query(phy-\u003edev, nla_data(data),\n \t\t\t\t\t evt_resp);\n"
} |
| 2/1 | 2026/08/13 10:02 | agent | patch-evaluator |
0mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"MT7925U",
"NL80211_TESTMODE"
],
"FocusSymbols": [
"mt7925_testmode_dump"
],
"Reasoning": "The patch fixes an out-of-bounds access and uninitialized memory issue in the mt7925 testmode query handler. The code is reachable via nl80211 testmode commands for the MT7925 USB wireless driver, which can be fuzzed using USB emulation.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a '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: For your convenience, here is the diff of the changes:
commit 9e4ea6bbecf451e6dec70021d56db919a459c84f
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Aug 13 10:02:42 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c
index 22a8f1ddc321d..dbf1159effeb9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c
@@ -87,13 +87,14 @@ static int
mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req,
char *evt_resp)
{
+ const unsigned int hdr = offsetof(struct uni_cmd_testmode_ctrl, data);
struct mt7925_rftest_cmd cmd;
- char *pcmd = (char *)&cmd;
+ struct mt7925_rftest_cmd *pcmd = &cmd;
struct sk_buff *skb = NULL;
- int ret = 1;
+ int ret;
memset(pcmd, 0, sizeof(*pcmd));
- memcpy(pcmd + 4, (char *)&req->c, sizeof(struct uni_cmd_testmode_ctrl));
+ memcpy(&pcmd->ctrl, &req->c, sizeof(struct uni_cmd_testmode_ctrl));
if (*((uint16_t *)req->padding) == MCU_UNI_CMD_TESTMODE_CTRL)
ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_QUERY(TESTMODE_CTRL),
@@ -101,16 +102,18 @@ mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req,
else if (*((uint16_t *)req->padding) == MCU_UNI_CMD_TESTMODE_RX_STAT)
ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_QUERY(TESTMODE_RX_STAT),
&cmd, sizeof(cmd), true, &skb);
+ else
+ ret = -EINVAL;
if (ret)
goto out;
- if (skb->len < MT7925_EVT_RSP_LEN + 8) {
+ if (!skb_pull(skb, hdr)) {
ret = -EINVAL;
goto out;
}
- memcpy((char *)evt_resp, (char *)skb->data + 8, MT7925_EVT_RSP_LEN);
+ memcpy(evt_resp, skb->data, min_t(u32, skb->len, MT7925_EVT_RSP_LEN));
out:
dev_kfree_skb(skb);
@@ -190,7 +193,7 @@ int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
data = drv_tb[MT7925_TM_ATTR_QUERY];
if (data) {
- char evt_resp[MT7925_EVT_RSP_LEN];
+ char evt_resp[MT7925_EVT_RSP_LEN] = {};
err = mt7925_tm_query(phy->dev, nla_data(data),
evt_resp);
|
| 3/2 | 2026/08/13 10:02 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 2713 output: 37 thoughts: 226 Thoughts: **Analyzing a Driver Patch for the MT7925 Test Mode** Okay, so I'm looking at a patch here that's tweaking the `mt7925_tm_query` function within `drivers/net/wireless/mediatek/mt76/mt7925/testmode.c`. It looks like they're fiddling with the initialization of `pcmd` and the parsing of the `skb` (socket buffer). I see they've added a check for the results of `skb_pull` as well as a new call to `min_t(u32, skb->len, MT7925_EVT_RSP_LEN)` and they're initializing `evt_resp` to zero. This is pretty standard stuff, but I need to understand the impact. First, I need to figure out where this function is even used. Let's see... `mt7925_testmode_dump` is part of the `nl80211` test mode for the mt7925 chipset. Right, the mt7925 driver is for MediaTek's MT7925 wireless devices. Now I need to quickly recall if these devices are primarily PCIe or USB based. I'm going to quickly check in the relevant directory `/drivers/net/wireless/mediatek/mt76/mt7925/` to confirm my understanding of the hardware interface. |
| 4/2 | 2026/08/13 10:02 | tool | codesearch-dir-index |
0mArgs:{
"Dir": "drivers/net/wireless/mediatek/mt76/mt7925/"
}Results: {
"Files": [
"debugfs.c",
"init.c",
"mac.c",
"mac.h",
"main.c",
"mcu.c",
"mcu.h",
"mt7925.h",
"nan.c",
"nan.h",
"pci.c",
"pci_mac.c",
"pci_mcu.c",
"regd.c",
"regd.h",
"regs.h",
"testmode.c",
"usb.c"
],
"Subdirs": null
} |
| 5/2 | 2026/08/13 10:02 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 3430 output: 47 thoughts: 212 Thoughts: **Analyzing the Code's Reachability** Okay, so I'm looking at this code and I see `usb.c` and `pci.c`. My initial thought is, this could be a USB device, right? And if it's a USB device, we could potentially emulate it using a USB gadget like `dummy_hcd` or `vhci`. But then, hang on a second, that prompt specifically calls out "impossible to reach in virtualized environments... even when utilizing software-emulated hardware." That's the kicker. Wait, it is! Syzkaller's USB fuzzing can leverage `vhci` to fuzz USB devices, so it should be reachable after all. Okay, so if it's a USB device, we can absolutely hit this code. Now, the next question becomes: is `mt7925_testmode_dump` reachable? It looks like it's part of the testmode functionality, which in turn hinges on `CONFIG_NL80211_TESTMODE`. Time to go digging in `drivers/net/wireless/mediatek/mt76/mt7925/Kconfig` and see if we can get to it. |
| 6/2 | 2026/08/13 10:02 | tool | grepper |
0mArgs:{
"Expression": "config MT7925",
"PathPrefix": "drivers/net/wireless/mediatek/mt76/mt7925/Kconfig"
}Results: {
"Output": "drivers/net/wireless/mediatek/mt76/mt7925/Kconfig-1-# SPDX-License-Identifier: BSD-3-Clause-Clear\ndrivers/net/wireless/mediatek/mt76/mt7925/Kconfig:2:config MT7925_COMMON\ndrivers/net/wireless/mediatek/mt76/mt7925/Kconfig-3-\ttristate\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/Kconfig-6-\ndrivers/net/wireless/mediatek/mt76/mt7925/Kconfig:7:config MT7925E\ndrivers/net/wireless/mediatek/mt76/mt7925/Kconfig-8-\ttristate \"MediaTek MT7925E (PCIe) support\"\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/Kconfig-18-\ndrivers/net/wireless/mediatek/mt76/mt7925/Kconfig:19:config MT7925U\ndrivers/net/wireless/mediatek/mt76/mt7925/Kconfig-20-\ttristate \"MediaTek MT7925U (USB) support\"\n"
} |
| 7/2 | 2026/08/13 10:02 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 4267 output: 47 thoughts: 29 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Checking the USB Integration** Right, `MT7925U`... that's the USB variant. Got it. Now, before I go any further down this rabbit hole, I need to understand its dependencies. What other components or drivers is this thing leaning on? I've got to dig into the relationship between this and the other subsystems. Need to make sure I don't overlook a critical link. I'll need to check the broader framework dependencies to make sure there are no conflicts or overlooked requirements. Time to dive into the specs and documentation to map this out. |
| 8/2 | 2026/08/13 10:02 | tool | read-file |
0mArgs:{
"File": "drivers/net/wireless/mediatek/mt76/mt7925/Kconfig",
"FirstLine": 1,
"LineCount": 50
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: BSD-3-Clause-Clear\n 2:\tconfig MT7925_COMMON\n 3:\t\ttristate\n 4:\t\tselect MT792x_LIB\n 5:\t\tselect WANT_DEV_COREDUMP\n 6:\t\n 7:\tconfig MT7925E\n 8:\t\ttristate \"MediaTek MT7925E (PCIe) support\"\n 9:\t\tselect MT7925_COMMON\n 10:\t\tdepends on MAC80211\n 11:\t\tdepends on PCI\n 12:\t\thelp\n 13:\t\t This adds support for MT7925-based wireless PCIe devices,\n 14:\t\t which support operation at 6GHz, 5GHz, and 2.4GHz IEEE 802.11be\n 15:\t\t 2x2:2SS 4096-QAM, 160MHz channels.\n 16:\t\n 17:\t\t To compile this driver as a module, choose M here.\n 18:\t\n 19:\tconfig MT7925U\n 20:\t\ttristate \"MediaTek MT7925U (USB) support\"\n 21:\t\tselect MT792x_USB\n 22:\t\tselect MT7925_COMMON\n 23:\t\tdepends on MAC80211\n 24:\t\tdepends on USB\n 25:\t\thelp\n 26:\t\t This adds support for MT7925-based wireless USB devices,\n 27:\t\t which support operation at 6GHz, 5GHz, and 2.4GHz IEEE 802.11be\n 28:\t\t 2x2:2SS 4096-QAM, 160MHz channels.\n 29:\t\n 30:\t\t To compile this driver as a module, choose M here.\n"
} |
| 9/2 | 2026/08/13 10:02 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 4968 output: 46 thoughts: 93 Thoughts: **Analyzing the MT7925U Driver Dependencies** Okay, so I'm looking at this `MT7925U` driver and immediately I see it relies on `MAC80211` and `USB`. That's standard for a wireless USB device, so no surprises there. Let's make sure `MT7925_COMMON` is also selected; that's probably got some core definitions. I suspect that the `testmode.c` file is only built if the `CONFIG_NL80211_TESTMODE` option is turned on; that's pretty common for debugging functionality. To confirm these inferences, I'm going to jump into the `drivers/net/wireless/mediatek/mt76/mt7925/Makefile` file to check the dependencies and build rules directly. |
| 10/2 | 2026/08/13 10:03 | tool | read-file |
0mArgs:{
"File": "drivers/net/wireless/mediatek/mt76/mt7925/Makefile",
"FirstLine": 1,
"LineCount": 20
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: BSD-3-Clause-Clear\n 2:\t\n 3:\tobj-$(CONFIG_MT7925_COMMON) += mt7925-common.o\n 4:\tobj-$(CONFIG_MT7925E) += mt7925e.o\n 5:\tobj-$(CONFIG_MT7925U) += mt7925u.o\n 6:\t\n 7:\tmt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o\n 8:\tmt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o\n 9:\tmt7925e-y := pci.o pci_mac.o pci_mcu.o\n 10:\tmt7925u-y := usb.o\n"
} |
| 11/2 | 2026/08/13 10:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 5529 output: 49 thoughts: 152 Thoughts: **Kernel Configuration Dependencies for MT7925 Test Mode** Okay, so I'm digging into the kernel configuration here. I see that `mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o`. Alright, so the `testmode.o` module specifically relies on `NL80211_TESTMODE`. That makes sense. Also, I'm thinking about the `MT7925U` which I know is relevant. But, what about the more generic aspects, like `MAC80211` and `USB`? Those are pretty much expected as base dependencies, I don't need to worry about those, it's the more specialized dependencies I'm interested in here. Hold on, I see that `MT7925_COMMON` is actually selected by `MT7925U`. That simplifies things. So, ultimately, to get this `testmode.o` module and therefore test mode working for this MT7925U wireless driver, the core dependencies are `MT7925U` and `NL80211_TESTMODE`. That's what I need to focus on. Now, let me just quickly double-check the implementation and functionality by looking at the `mt7925_testmode_dump` function, to confirm my understanding of the module. Time to check `testmode.c`. |
| 12/2 | 2026/08/13 10:03 | tool | grepper |
0mArgs:{
"Expression": "mt7925_testmode_dump",
"PathPrefix": "drivers/net/wireless/mediatek/mt76/mt7925/"
}Results: {
"Output": "drivers/net/wireless/mediatek/mt76/mt7925/main.c=2716=const struct ieee80211_ops mt7925_ops = {\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-2755-\tCFG80211_TESTMODE_CMD(mt7925_testmode_cmd)\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:2756:\tCFG80211_TESTMODE_DUMP(mt7925_testmode_dump)\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-2757-#ifdef CONFIG_PM\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mt7925.h=411=int mt7925_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,\ndrivers/net/wireless/mediatek/mt76/mt7925/mt7925.h-412-\t\t\tvoid *data, int len);\ndrivers/net/wireless/mediatek/mt76/mt7925/mt7925.h:413:int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,\ndrivers/net/wireless/mediatek/mt76/mt7925/mt7925.h-414-\t\t\t struct netlink_callback *cb, void *data, int len);\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c=124=int mt7925_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-160-\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c:161:int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-162-\t\t\t struct netlink_callback *cb, void *data, int len)\n"
} |
| 13/2 | 2026/08/13 10:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 6680 output: 48 thoughts: 113 Thoughts: **Analyzing the `mt7925_testmode_dump` Mechanism** Okay, so I'm looking at this code and trying to understand this test mode dump functionality for the MT7925. The core of it seems to be centered around `mt7925_testmode_dump`. That's registered as a callback specifically for this purpose. Right, so `mt7925_testmode_dump` is the entry point, the thing that gets called when a test mode dump is triggered. Now I see a modification to `mt7925_tm_query`. And that function is called from `mt7925_testmode_dump`. So, `mt7925_testmode_dump` calls `mt7925_tm_query` and that gets changed. But wait, `mt7925_tm_query` is a `static` function. The static keyword implies scope. Is `mt7925_testmode_dump` the ONLY function that calls it? I'm going to have to make absolutely certain of this. If I'm confident about the scope I'll know where to focus the changes in order to understand how this is working. |
| 14/2 | 2026/08/13 10:03 | tool | grepper |
0mArgs:{
"Expression": "mt7925_tm_query",
"PathPrefix": "drivers/net/wireless/mediatek/mt76/mt7925/"
}Results: {
"Output": "drivers/net/wireless/mediatek/mt76/mt7925/testmode.c=86=static int\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c:87:mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req,\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-88-\t\tchar *evt_resp)\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c=161=int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-197-\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c:198:\t\t\terr = mt7925_tm_query(phy-\u003edev, nla_data(data),\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-199-\t\t\t\t\t evt_resp);\n"
} |
| 15/2 | 2026/08/13 10:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 7427 output: 52 thoughts: 350 Thoughts: **My Analysis of the `mt7925_testmode_dump` Function** Okay, so the initial grep shows that only `mt7925_testmode_dump` calls it. That immediately narrows the focus. Therefore, this is the function I need to examine in detail. Now, let's look at the configuration dependencies. `MT7925U` depends on `MAC80211` and `USB`, and `NL80211_TESTMODE` depends on `CFG80211` and `MAC80211`. So `MT7925U` and `NL80211_TESTMODE` seem like the relevant configurations to keep in mind, although I already knew `MAC80211` would be needed. I need to confirm that `mt7925_testmode_dump` isn't static, and sure enough, it isn't. It's exported in `mt7925.h` and used in `main.c`, which makes it a good symbol to focus on for further analysis. This is good; it's accessible. Ah, but wait... the patch also modifies `mt7925_tm_query`. That's a static function. That's immediately interesting, because static functions often get overlooked. This could be important. The description indicates a fix for an out-of-bounds read/write or an uninitialized memory issue. *That* makes it incredibly interesting, and this is where it gets good. The code itself shows the `memcpy` vulnerability: `memcpy(evt_resp, skb->data, min_t(u32, skb->len, MT7925_EVT_RSP_LEN));` This is an obvious functional change, fixing a genuine bug: specifically, a potential read overflow in `evt_resp` due to the lack of an initialization of `evt_resp` and a possible size mismatch between the source and the destination buffer. This is *absolutely* worth fuzzing. The likelihood of exploitation here seems high with the introduction of the initialization as well. Now let's just make sure I understand `MT7925_EVT_RSP_LEN`. |
| 16/2 | 2026/08/13 10:03 | tool | grepper |
0mArgs:{
"Expression": "MT7925_EVT_RSP_LEN",
"PathPrefix": "drivers/net/wireless/mediatek/mt76/mt7925/"
}Results: {
"Output": "drivers/net/wireless/mediatek/mt76/mt7925/testmode.c-5-\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c:6:#define MT7925_EVT_RSP_LEN 512\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-7-\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c=87=mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req,\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-115-\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c:116:\tmemcpy(evt_resp, skb-\u003edata, min_t(u32, skb-\u003elen, MT7925_EVT_RSP_LEN));\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-117-\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c=161=int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-195-\t\tif (data) {\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c:196:\t\t\tchar evt_resp[MT7925_EVT_RSP_LEN] = {};\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-197-\n"
} |
| 17/2 | 2026/08/13 10:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 8800 output: 125 thoughts: 377 Thoughts: **My Fuzzing Strategy for the MT7925 Driver** Okay, so I'm thinking about how to effectively fuzz this `MT7925` driver vulnerability. I need to focus on the right configuration options. Let's see, the relevant configs are `MT7925U` and `NL80211_TESTMODE`. Wait, `MT7925_COMMON` is automatically included because `MT7925U` and `MT7925E` enable it. Should I bother including `MT7925E`? Nah, that's a PCIe device; PCIe devices are generally much more difficult to fuzz because they are often more complex to emulate. The `MT7925U` is a USB device, and that's *much* easier to target using something like vhci. So, enabling just `MT7925U` should be sufficient, and frankly, better for fuzzing. Now, is `MT7925U` and `NL80211_TESTMODE` enough to get me where I need to be? Let me check to see if `NL80211_TESTMODE` is enough. Yes, `"mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o"`. Okay, let's see which functions are actually modified. It looks like it's just `mt7925_tm_query` and `mt7925_testmode_dump`. And `mt7925_testmode_dump` seems to be the entry point. That's the one I need to be fuzzing. Alright, time to create the fuzzing configuration. I'll call `set-results` and set `WorthFuzzing` to `true`. I'll set the `FocusSymbols` to `["mt7925_testmode_dump"]`, and `EnableConfigs` to `["MT7925U", "NL80211_TESTMODE"]`. The `Reasoning` should be that this patch fixes an out-of-bounds access and uninitialized memory issue in the `mt7925` testmode query handler. The vulnerable code is reachable via `nl80211` testmode commands for the `MT7925` USB wireless driver, and that can be fuzzed using USB emulation. |
| 18/2 | 2026/08/13 10:03 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"MT7925U",
"NL80211_TESTMODE"
],
"FocusSymbols": [
"mt7925_testmode_dump"
],
"Reasoning": "The patch fixes an out-of-bounds access and uninitialized memory issue in the mt7925 testmode query handler. The code is reachable via nl80211 testmode commands for the MT7925 USB wireless driver, which can be fuzzed using USB emulation.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"MT7925U",
"NL80211_TESTMODE"
],
"FocusSymbols": [
"mt7925_testmode_dump"
],
"Reasoning": "The patch fixes an out-of-bounds access and uninitialized memory issue in the mt7925 testmode query handler. The code is reachable via nl80211 testmode commands for the MT7925 USB wireless driver, which can be fuzzed using USB emulation.",
"WorthFuzzing": true
} |
| 19/1 | 2026/08/13 10:03 | action | If |
2mArgs:{
"WorthFuzzing": true
}Results: null |
| 20/2 | 2026/08/13 10:03 | agent | kmsan-evaluator |
2mModel:best-expensive Args: null Results: {
"KMSANReasoning": "The patch fixes a bug in `mt7925_tm_query()` where `memset(pcmd, 0, sizeof(*pcmd))` was used to initialize a stack-allocated `struct mt7925_rftest_cmd cmd`. Because `pcmd` was declared as a `char *`, `sizeof(*pcmd)` evaluated to 1, meaning only the first byte of the structure was zeroed. The remaining 3 bytes of the `padding` field were left uninitialized. This structure was then sent to the MCU via `mt76_mcu_send_and_get_msg()`, resulting in a leak of uninitialized kernel stack memory to the device.\n\nAdditionally, the patch modifies the response handling to allow shorter responses from the MCU. To prevent leaking uninitialized stack memory to user space via `nla_put()` when the response is shorter than `MT7925_EVT_RSP_LEN`, the patch zero-initializes the `evt_resp` buffer (`char evt_resp[MT7925_EVT_RSP_LEN] = {};`).\n\nStandard KASAN detects memory corruption, out-of-bounds accesses, and use-after-free bugs, but it does not track uninitialized memory. KMSAN (KernelMemorySanitizer) is specifically designed to detect reads of uninitialized memory and info-leaks (both to user space and to hardware devices). Since this patch addresses uninitialized stack memory leaks, a dedicated KMSAN fuzzing session is required to verify the fix and ensure no other uninitialized memory issues exist in this code path.",
"NeedsKMSAN": true
}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: For your convenience, here is the diff of the changes:
commit 9e4ea6bbecf451e6dec70021d56db919a459c84f
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Aug 13 10:02:42 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c
index 22a8f1ddc321d..dbf1159effeb9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c
@@ -87,13 +87,14 @@ static int
mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req,
char *evt_resp)
{
+ const unsigned int hdr = offsetof(struct uni_cmd_testmode_ctrl, data);
struct mt7925_rftest_cmd cmd;
- char *pcmd = (char *)&cmd;
+ struct mt7925_rftest_cmd *pcmd = &cmd;
struct sk_buff *skb = NULL;
- int ret = 1;
+ int ret;
memset(pcmd, 0, sizeof(*pcmd));
- memcpy(pcmd + 4, (char *)&req->c, sizeof(struct uni_cmd_testmode_ctrl));
+ memcpy(&pcmd->ctrl, &req->c, sizeof(struct uni_cmd_testmode_ctrl));
if (*((uint16_t *)req->padding) == MCU_UNI_CMD_TESTMODE_CTRL)
ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_QUERY(TESTMODE_CTRL),
@@ -101,16 +102,18 @@ mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req,
else if (*((uint16_t *)req->padding) == MCU_UNI_CMD_TESTMODE_RX_STAT)
ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_QUERY(TESTMODE_RX_STAT),
&cmd, sizeof(cmd), true, &skb);
+ else
+ ret = -EINVAL;
if (ret)
goto out;
- if (skb->len < MT7925_EVT_RSP_LEN + 8) {
+ if (!skb_pull(skb, hdr)) {
ret = -EINVAL;
goto out;
}
- memcpy((char *)evt_resp, (char *)skb->data + 8, MT7925_EVT_RSP_LEN);
+ memcpy(evt_resp, skb->data, min_t(u32, skb->len, MT7925_EVT_RSP_LEN));
out:
dev_kfree_skb(skb);
@@ -190,7 +193,7 @@ int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
data = drv_tb[MT7925_TM_ATTR_QUERY];
if (data) {
- char evt_resp[MT7925_EVT_RSP_LEN];
+ char evt_resp[MT7925_EVT_RSP_LEN] = {};
err = mt7925_tm_query(phy->dev, nla_data(data),
evt_resp);
|
| 21/3 | 2026/08/13 10:03 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 2710 output: 51 thoughts: 1023 Thoughts: **Analyzing the MT7925 Testmode Patch**
Okay, let's break down these changes to `mt7925_tm_query` and `mt7925_testmode_dump` within the MT76 driver. My initial impression is that these are refinements, aiming for increased robustness and potentially addressing a subtle initialization issue.
In `mt7925_tm_query`, the first change is quite clear. Instead of treating the command structure `cmd` as a raw byte array (`char *pcmd`), we're now correctly using a pointer to the structure (`struct mt7925_rftest_cmd *pcmd`). This is cleaner and safer; it avoids potential alignment issues and makes the code more readable. The old `memcpy(pcmd + 4, ...)` using a `char *` and an offset is now `memcpy(&pcmd->ctrl, ...)` which is much preferred. Also, there's a new `else ret = -EINVAL;` which adds a little more error checking in the control flow.
The next change involves how the response data is handled. The old code copied *exactly* `MT7925_EVT_RSP_LEN` bytes from `skb->data + 8` into `evt_resp`. The new code uses `min_t(u32, skb->len, MT7925_EVT_RSP_LEN)` instead. That suggests an attempt to gracefully handle shorter-than-expected responses from the hardware, preventing a potential buffer overflow on the receiving end. The previous `if (skb->len < MT7925_EVT_RSP_LEN + 8)` check is now replaced with `!skb_pull(skb, hdr)` which is a better way to check the length of the sk_buff buffer.
Now, let's consider the implications of these changes in `mt7925_testmode_dump`. Here's where it gets interesting. The old code declared `evt_resp` on the stack without initialization. It then passed this potentially uninitialized buffer to `mt7925_tm_query`. If `mt7925_tm_query` failed for *any* reason, `evt_resp` would contain garbage. The new code remedies this with `char evt_resp[MT7925_EVT_RSP_LEN] = {};`. This is key. By initializing `evt_resp` to zero, any unwritten portions of the buffer after `mt7925_tm_query` returns will be guaranteed to be zero. Coupled with the `min_t` logic, this should result in more predictable behavior, especially if a shorter response is received. The old code risked passing uninitialized data onwards if `mt7925_tm_query` failed or returned a partial response.
I really need to see what `mt7925_testmode_dump` *does* with `evt_resp` after the call to `mt7925_tm_query` to be certain. But from this immediate analysis, it looks like a concerted effort to improve error handling and memory safety, ensuring that the driver is more robust when dealing with unexpected responses from the hardware, or potential failure states. I'll need to double-check the code, but this looks like a good improvement.
|
| 22/3 | 2026/08/13 10:03 | tool | read-file |
0mArgs:{
"File": "drivers/net/wireless/mediatek/mt76/mt7925/testmode.c",
"FirstLine": 180,
"LineCount": 50
}Results: {
"Contents": " 180:\t\t\treturn err;\n 181:\t\n 182:\t\tif (tb[MT76_TM_ATTR_DRV_DATA]) {\n 183:\t\t\tstruct nlattr *drv_tb[NUM_MT7925_TM_ATTRS], *data;\n 184:\t\t\tint ret;\n 185:\t\n 186:\t\t\tdata = tb[MT76_TM_ATTR_DRV_DATA];\n 187:\t\t\tret = nla_parse_nested_deprecated(drv_tb,\n 188:\t\t\t\t\t\t\t MT7925_TM_ATTR_MAX,\n 189:\t\t\t\t\t\t\t data, mt7925_tm_policy,\n 190:\t\t\t\t\t\t\t NULL);\n 191:\t\t\tif (ret)\n 192:\t\t\t\treturn ret;\n 193:\t\n 194:\t\t\tdata = drv_tb[MT7925_TM_ATTR_QUERY];\n 195:\t\t\tif (data) {\n 196:\t\t\t\tchar evt_resp[MT7925_EVT_RSP_LEN] = {};\n 197:\t\n 198:\t\t\t\terr = mt7925_tm_query(phy-\u003edev, nla_data(data),\n 199:\t\t\t\t\t\t evt_resp);\n 200:\t\t\t\tif (err)\n 201:\t\t\t\t\treturn err;\n 202:\t\n 203:\t\t\t\treturn nla_put(msg, MT7925_TM_ATTR_RSP,\n 204:\t\t\t\t\t sizeof(evt_resp), evt_resp);\n 205:\t\t\t}\n 206:\t\t}\n 207:\t\n 208:\t\treturn -EINVAL;\n 209:\t}\n"
} |
| 23/3 | 2026/08/13 10:03 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 4976 output: 26 thoughts: 1563 Thoughts: **Analyzing the Test Mode Response and Potential Uninitialized Memory Issues** Okay, let's break this down. My initial thought is that `evt_resp` gets sent to user space using `nla_put`. Previously, in the older code, if `mt7925_tm_query` was successful, it reliably copied `MT7925_EVT_RSP_LEN` bytes, so `evt_resp` was fully initialized. However, the old code had an important check: if `skb->len` was less than `MT7925_EVT_RSP_LEN + 8`, it returned `-EINVAL`, preventing the `nla_put` call. The old logic also only sent a response if `req->padding` was either `MCU_UNI_CMD_TESTMODE_CTRL` or `MCU_UNI_CMD_TESTMODE_RX_STAT` and initialized `ret` to `1` otherwise, preventing any response. Now, if `mt76_mcu_send_and_get_msg` returned 0 (success) and `skb->len` was large enough, the old code copied `MT7925_EVT_RSP_LEN` bytes starting at an offset of 8, thereby fully initializing `evt_resp`. The critical question is, why did the patch change `evt_resp` to be zero-initialized? It appears that the new code now uses `skb_pull` and then copies a number of bytes determined by `min_t(u32, skb->len, MT7925_EVT_RSP_LEN)`. If `skb->len` is smaller than `MT7925_EVT_RSP_LEN`, it copies fewer bytes. This introduces a potential vulnerability where the uncopied bytes of `evt_resp` would be uninitialized if they weren't zero-initialized, and this uninitialized memory would then be leaked to user space via `nla_put`. This is a *fix* to a potential uninitialized memory issue that was *introduced* by the code change! However, the old code explicitly checked if `skb->len` was at least `MT7925_EVT_RSP_LEN + 8` before copying. So, the original code guaranteed full initialization under the normal flow and error conditions. The rationale behind the change may be that the `skb` length *can* be less than `MT7925_EVT_RSP_LEN + 8`, and they *want* to support shorter responses now. The introduction of `skb_pull(skb, hdr)` suggests that they are specifically accommodating this. Since `skb->len` can be smaller than `MT7925_EVT_RSP_LEN` after pulling the header, zero-initialization of `evt_resp` is necessary to prevent the uninitialized memory leak. So, the patch *prevents* the uninitialized memory issue which it *introduced* by this logic change. Wait, is there *another* uninitialized memory issue? Let's check `mt7925_tm_query`. The old code was using `memcpy(pcmd + 4, ...)` where `pcmd` was a `char *`. This offset by four bytes, but we need to check the actual struct layout. |
| 24/3 | 2026/08/13 10:03 | tool | grepper |
0mArgs:{
"Expression": "struct mt7925_rftest_cmd \\{"
}Results: {
"Output": "drivers/net/wireless/mediatek/mt76/mt7925/mcu.h=662=struct uni_cmd_testmode_ctrl {\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.h-669-\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.h:670:struct mt7925_rftest_cmd {\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.h-671-\tu8 padding[4];\n"
} |
| 25/3 | 2026/08/13 10:04 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 7456 output: 51 thoughts: 43 Thoughts: Here's my take on those thoughts: **Analyzing the mt7925_rftest_cmd Structure** Okay, so I'm digging into the MT7925 driver again, specifically checking out the `struct mt7925_rftest_cmd` definition. My immediate thought is, "Right, rftest, so radio frequency testing command." I know this is a critical structure for controlling the radio's operation for diagnostics and calibration. I need to refresh my memory on the specifics of how this command is structured. I need to understand its fields, how it's packed, and how it interacts with the MT76 MCU. Is this something where I need to check the hardware registers that correspond? What parameters are configurable? What are the implications of changing a certain value? Does this relate to the power amplifier in any way? I need to analyze how this command is constructed and used within the broader context of the driver and the MT76 firmware. Let's start with the header file and go from there. |
| 26/3 | 2026/08/13 10:04 | tool | read-file |
0mArgs:{
"File": "drivers/net/wireless/mediatek/mt76/mt7925/mcu.h",
"FirstLine": 660,
"LineCount": 20
}Results: {
"Contents": " 660:\t};\n 661:\t\n 662:\tstruct uni_cmd_testmode_ctrl {\n 663:\t\tu16 tag;\n 664:\t\tu16 length;\n 665:\t\tu8 action;\n 666:\t\tu8 reserved[3];\n 667:\t\tunion testmode_data data;\n 668:\t} __packed;\n 669:\t\n 670:\tstruct mt7925_rftest_cmd {\n 671:\t\tu8 padding[4];\n 672:\t\tstruct uni_cmd_testmode_ctrl ctrl;\n 673:\t} __packed;\n 674:\t\n 675:\tstatic inline enum connac3_mcu_cipher_type\n 676:\tmt7925_mcu_get_cipher(int cipher)\n 677:\t{\n 678:\t\tswitch (cipher) {\n 679:\t\tcase WLAN_CIPHER_SUITE_WEP40:\n"
} |
| 27/3 | 2026/08/13 10:04 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 8044 output: 25 thoughts: 201 Thoughts: **Refactoring Testmode Command Handling** Okay, so I'm looking at this patch and immediately, I see a section dealing with `struct mt7925_rftest_cmd`. It has some padding, `u8 padding[4]`, and then `struct uni_cmd_testmode_ctrl ctrl`. The older code does a `memcpy` to write into the structure, but offsets the destination pointer by 4 bytes: `memcpy(pcmd + 4, (char *)&req->c, sizeof(struct uni_cmd_testmode_ctrl));`. I recognize this immediately. The patch's change, `memcpy(&pcmd->ctrl, &req->c, sizeof(struct uni_cmd_testmode_ctrl));`, is clearly a more direct and cleaner way to accomplish the same thing. It is functionally identical to the original; the offset in the original just takes care of the padding. This part of the patch is essentially just cleanup – improving readability and avoiding the manual byte offset calculation. Nothing revolutionary here, just solid, maintainable code. But what about `req`? I should examine that more closely. The context of this whole thing is that `req` is a `struct mt7925_tm_cmd *req`, so let's delve into that structure to understand its role. |
| 28/3 | 2026/08/13 10:04 | tool | grepper |
0mArgs:{
"Expression": "struct mt7925_tm_cmd \\{"
}Results: {
"Output": "drivers/net/wireless/mediatek/mt76/mt7925/testmode.c=8=enum mt7925_testmode_attr {\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-18-\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c:19:struct mt7925_tm_cmd {\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-20-\tu8 padding[4];\n"
} |
| 29/3 | 2026/08/13 10:04 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 8705 output: 50 thoughts: 16 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into `struct mt7925_tm_cmd`** Okay, let's dissect this `struct mt7925_tm_cmd`. Immediately, I want to understand its purpose within the broader MT7925 architecture. Is this command-specific, configuration-oriented, or data-transfer related? The naming convention suggests it's a command structure, but that's just a starting point. I'm looking for the fields. I want to see how this translates to hardware interactions. Are we talking registers, DMA descriptors, or something else entirely? My brain's instantly going to the typical suspects: a command opcode, potentially sub-opcodes for finer granularity, some form of addressing or identifier to target the appropriate hardware entity (a specific MAC, a specific queue, a specific channel), length fields to indicate data sizes, and maybe a status/response field to track command completion or errors. I'd be looking for alignment considerations immediately – are there any potential cache line implications with how the hardware accesses this? Padding is always a giveaway. Then, of course, the actual data payloads, if any. What kind of data is being passed with this command? Is this for rate control, traffic shaping, power management parameters, or maybe even firmware control? That will influence my expectations on the data structures within, and the expected hardware implications. Let's see... looking at the code... aha... |
| 30/3 | 2026/08/13 10:04 | tool | read-file |
0mArgs:{
"File": "drivers/net/wireless/mediatek/mt76/mt7925/testmode.c",
"FirstLine": 15,
"LineCount": 20
}Results: {
"Contents": " 15:\t\tNUM_MT7925_TM_ATTRS,\n 16:\t\tMT7925_TM_ATTR_MAX = NUM_MT7925_TM_ATTRS - 1,\n 17:\t};\n 18:\t\n 19:\tstruct mt7925_tm_cmd {\n 20:\t\tu8 padding[4];\n 21:\t\tstruct uni_cmd_testmode_ctrl c;\n 22:\t} __packed;\n 23:\t\n 24:\tstruct mt7925_tm_evt {\n 25:\t\tu32 param0;\n 26:\t\tu32 param1;\n 27:\t} __packed;\n 28:\t\n 29:\tstatic const struct nla_policy mt7925_tm_policy[NUM_MT7925_TM_ATTRS] = {\n 30:\t\t[MT7925_TM_ATTR_SET] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7925_tm_cmd)),\n 31:\t\t[MT7925_TM_ATTR_QUERY] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7925_tm_cmd)),\n 32:\t};\n 33:\t\n 34:\tstatic int\n"
} |
| 31/3 | 2026/08/13 10:04 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9430 output: 24 thoughts: 1705 Thoughts: **Analyzing Potential Uninitialized Memory Reads in the Testmode Code** Okay, let's break this down. First, `req` is a `struct mt7925_tm_cmd`. The code checks `*((uint16_t *)req->padding)`. `req->padding` is a `u8 padding[4]`, so this reads the first two bytes of that padding field. `req` comes from user space via `nla_data(data)`, where `data` comes from `drv_tb[MT7925_TM_ATTR_QUERY]`. The policy enforces `NLA_POLICY_EXACT_LEN(sizeof(struct mt7925_tm_cmd))`, meaning the size of `req` is exactly what the struct dictates. Since this data originates from userspace, the netlink layer should handle the initialization, so there shouldn't be any uninitialized memory read here. Now, what about `evt_resp`? This is where it gets interesting. `evt_resp` is used in `mt7925_testmode_dump` and is eventually sent to user space via `nla_put`. Previously, if `mt7925_tm_query` returned 0, it would have copied exactly `MT7925_EVT_RSP_LEN` bytes into `evt_resp`. But there might be other conditions to consider. Let's revisit the old `mt7925_tm_query` function. If `req->padding` doesn't match `MCU_UNI_CMD_TESTMODE_CTRL` or `MCU_UNI_CMD_TESTMODE_RX_STAT`, `ret` becomes 1, and the function immediately returns 1. In this case, `mt7925_testmode_dump` will return 1, and crucially, it *won't* call `nla_put`. Therefore, no uninitialized memory gets leaked in that scenario. What if `mt76_mcu_send_and_get_msg` returns 0 but `skb` is NULL? Well, the function sets `skb` if it returns 0. If `skb->len` is too short, it returns `-EINVAL`. If `skb->len` is sufficient, the code copies `MT7925_EVT_RSP_LEN` bytes into `evt_resp`. So, the old code seems safe; `evt_resp` *should* be fully initialized in the successful cases. The question then becomes, is there any path where `mt7925_tm_query` could return 0, but `evt_resp` is not fully initialized? No. In the old code, if the function returns 0, it *must* have copied the data via `memcpy`, meaning it is initialized. What about a size mismatch? What if `MT7925_EVT_RSP_LEN` is greater than `skb->data + 8` contains? The check `if (skb->len < MT7925_EVT_RSP_LEN + 8)` prevents that. Okay, what if `skb->len` is large enough, but the actual data from the MCU is garbage? That isn't an uninitialized *kernel* memory issue. It's a problem with the MCU providing bad data, which KMSAN wouldn't catch. Now, looking at the patch, let's see how things are different. The check `if (skb->len < MT7925_EVT_RSP_LEN + 8)` is replaced with `if (!skb_pull(skb, hdr))`. So `hdr` is `offsetof(struct uni_cmd_testmode_ctrl, data)`. Let's confirm what `hdr` actually is. After that, The `memcpy` is changed to `memcpy(evt_resp, skb->data, min_t(u32, skb->len, MT7925_EVT_RSP_LEN));`. So, if `skb->len` is smaller than what is expected, a smaller amount of bytes will be copied from `skb`. |
| 32/3 | 2026/08/13 10:04 | tool | grepper |
0mArgs:{
"Expression": "struct uni_cmd_testmode_ctrl \\{"
}Results: {
"Output": "drivers/net/wireless/mediatek/mt76/mt7925/mcu.h=656=union testmode_evt {\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.h-661-\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.h:662:struct uni_cmd_testmode_ctrl {\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.h-663-\tu16 tag;\n"
} |
| 33/3 | 2026/08/13 10:04 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12223 output: 51 thoughts: 15 Thoughts: Here's a summary of my thought process, as requested: **Diving into `uni_cmd_testmode_ctrl`** Okay, time to get my hands dirty. I need to take a look at the `struct uni_cmd_testmode_ctrl`. This is where the magic happens, or at least, where a specific piece of it is controlled. I need to understand its structure, what it's intended to do, and how it interacts with the broader system. I suspect this struct likely contains the control parameters for whatever testing mode is implemented. This is a critical point as there's a good chance this will be where any edge cases or unexpected behaviour is located. I'll need to confirm what the possible values it can take are, as well as the expected range and if there are any default values that need to be considered. Then, it's just a matter of tracing the implementation to see exactly how these control parameters are used. |
| 34/3 | 2026/08/13 10:04 | tool | read-file |
0mArgs:{
"File": "drivers/net/wireless/mediatek/mt76/mt7925/mcu.h",
"FirstLine": 662,
"LineCount": 10
}Results: {
"Contents": " 662:\tstruct uni_cmd_testmode_ctrl {\n 663:\t\tu16 tag;\n 664:\t\tu16 length;\n 665:\t\tu8 action;\n 666:\t\tu8 reserved[3];\n 667:\t\tunion testmode_data data;\n 668:\t} __packed;\n 669:\t\n 670:\tstruct mt7925_rftest_cmd {\n 671:\t\tu8 padding[4];\n"
} |
| 35/3 | 2026/08/13 10:04 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12631 output: 50 thoughts: 935 Thoughts: **Analyzing the Code and Potential Issues**
Okay, let's break this down. First, I see that `offsetof(struct uni_cmd_testmode_ctrl, data)` evaluates to 8. This confirms that the `hdr` value is 8. The original code was using `skb->data + 8`, and the new code utilizes `skb_pull(skb, hdr)`. `skb_pull` shifts `skb->data` forward by 8 bytes and decrements `skb->len` by 8. Then it copies bytes using `min_t(u32, skb->len, MT7925_EVT_RSP_LEN)`.
My initial thought was that if `skb->len` was smaller than `MT7925_EVT_RSP_LEN` (after the pull), the copy would write fewer bytes than allocated in `evt_resp`. Since `evt_resp` is passed to userspace via `sizeof(evt_resp)` (equal to `MT7925_EVT_RSP_LEN`), uninitialized memory could be leaked. However, the patch zero-initializes `evt_resp` with `{}`.
So, this patch actually *introduces* a case where the copy might be shorter, and *fixes* the possible uninitialized memory issue. But did the original code have a leak? No, because, with the old code, if `skb->len` was smaller than `MT7925_EVT_RSP_LEN + 8`, the function returned `-EINVAL`. This would then propagate to `mt7925_testmode_dump` which also would return `-EINVAL` and would not call `nla_put`. This indicates no uninitialized memory was leaked in the older code.
Next, I need to consider if there are other uninitialized memory issues. What about `req->padding`? Let's check:
```c
if (*((uint16_t *)req->padding) == MCU_UNI_CMD_TESTMODE_CTRL)
```
Here, `req` is a `struct mt7925_tm_cmd *req`, which comes from `nla_data(data)`. `data` is `drv_tb[MT7925_TM_ATTR_QUERY]`. The policy is `NLA_POLICY_EXACT_LEN(sizeof(struct mt7925_tm_cmd))`. This means `req` is exactly the size of `struct mt7925_tm_cmd`. Is `req->padding` uninitialized? No, because it is copied from user space by the netlink layer. KMSAN tracks kernel memory. Therefore, if the user does not initialize it, it's not a kernel uninitialized memory issue.
Now let's check `cmd`:
```c
struct mt7925_rftest_cmd cmd;
- char *pcmd = (char *)&cmd;
+ struct mt7925_rftest_cmd *pcmd = &cmd;
struct sk_buff *skb = NULL;
- int ret = 1;
+ int ret;
memset(pcmd, 0, sizeof(*pcmd));
- memcpy(pcmd + 4, (char *)&req->c, sizeof(struct uni_cmd_testmode_ctrl));
+ memcpy(&pcmd->ctrl, &req->c, sizeof(struct uni_cmd_testmode_ctrl));
```
In the old code, `pcmd` was a `char *`. Then `sizeof(*pcmd)` would equal `sizeof(char)` or `1`. This means `memset(pcmd, 0, sizeof(*pcmd));` only zeroed one byte of `cmd`! That's a definite bug. This is definitely something that needs to be fixed.
|
| 36/3 | 2026/08/13 10:04 | tool | read-file |
0mArgs:{
"File": "drivers/net/wireless/mediatek/mt76/mt7925/testmode.c",
"FirstLine": 85,
"LineCount": 20
}Results: {
"Contents": " 85:\t\n 86:\tstatic int\n 87:\tmt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req,\n 88:\t\t\tchar *evt_resp)\n 89:\t{\n 90:\t\tconst unsigned int hdr = offsetof(struct uni_cmd_testmode_ctrl, data);\n 91:\t\tstruct mt7925_rftest_cmd cmd;\n 92:\t\tstruct mt7925_rftest_cmd *pcmd = \u0026cmd;\n 93:\t\tstruct sk_buff *skb = NULL;\n 94:\t\tint ret;\n 95:\t\n 96:\t\tmemset(pcmd, 0, sizeof(*pcmd));\n 97:\t\tmemcpy(\u0026pcmd-\u003ectrl, \u0026req-\u003ec, sizeof(struct uni_cmd_testmode_ctrl));\n 98:\t\n 99:\t\tif (*((uint16_t *)req-\u003epadding) == MCU_UNI_CMD_TESTMODE_CTRL)\n 100:\t\t\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_UNI_QUERY(TESTMODE_CTRL),\n 101:\t\t\t\t\t\t\t\u0026cmd, sizeof(cmd), true, \u0026skb);\n 102:\t\telse if (*((uint16_t *)req-\u003epadding) == MCU_UNI_CMD_TESTMODE_RX_STAT)\n 103:\t\t\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_UNI_QUERY(TESTMODE_RX_STAT),\n 104:\t\t\t\t\t\t\t\u0026cmd, sizeof(cmd), true, \u0026skb);\n"
} |
| 37/3 | 2026/08/13 10:04 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14904 output: 28 thoughts: 467 Thoughts: **Analyzing a Potential Information Leak in Legacy Code** Okay, so I'm digging into this older C code, and immediately I see a red flag. They're using `memset(pcmd, 0, sizeof(*pcmd))` where `pcmd` is cast to a `char *` and is pointing to `cmd`, which is a `struct mt7925_rftest_cmd`. Since `sizeof(char)` is 1, they're only zeroing the *first byte* of `cmd`. That's a classic rookie mistake! Then, they're using `memcpy` to copy data to `cmd + 4`, which effectively overwrites only part of the structure. The bytes at offsets 1, 2, and 3 are left completely uninitialized. These uninitialized bytes become part of the `cmd` struct that's sent to the MCU, which is concerning. Looking deeper, `cmd` is indeed a `struct mt7925_rftest_cmd`, which contains padding and a control structure. So, that initial `memset` only zeros out `padding[0]`, leaving the rest of the padding potentially holding garbage data. My immediate question is: Is sending uninitialized memory to an external device, in this case, the MCU, an information leak? The answer is a resounding *yes*. Sending uninitialized kernel memory to a potentially untrusted external device is a direct info-leak. This is a classic vulnerability! Now, does KMSAN catch this? Thankfully, KMSAN is designed to detect exactly this kind of scenario: passing uninitialized memory to DMA or external devices, if the driver uses standard DMA APIs or if the memory is copied to a buffer sent to the device. So, the chances are high that KMSAN would flag this. Time to double-check `mt76_mcu_send_and_get_msg` to see what it's doing with that memory. |
| 38/3 | 2026/08/13 10:05 | tool | grepper |
0mArgs:{
"Expression": "mt76_mcu_send_and_get_msg"
}Results: {
"Output": "drivers/net/wireless/mediatek/mt76/mcu.c=54=EXPORT_SYMBOL_GPL(mt76_mcu_rx_event);\ndrivers/net/wireless/mediatek/mt76/mcu.c-55-\ndrivers/net/wireless/mediatek/mt76/mcu.c:56:int mt76_mcu_send_and_get_msg(struct mt76_dev *dev, int cmd, const void *data,\ndrivers/net/wireless/mediatek/mt76/mcu.c-57-\t\t\t int len, bool wait_resp, struct sk_buff **ret_skb)\n--\ndrivers/net/wireless/mediatek/mt76/mcu.c-69-}\ndrivers/net/wireless/mediatek/mt76/mcu.c:70:EXPORT_SYMBOL_GPL(mt76_mcu_send_and_get_msg);\ndrivers/net/wireless/mediatek/mt76/mcu.c-71-\n--\ndrivers/net/wireless/mediatek/mt76/mt76.h=1923=struct sk_buff *mt76_mcu_get_response(struct mt76_dev *dev,\ndrivers/net/wireless/mediatek/mt76/mt76.h-1924-\t\t\t\t unsigned long expires);\ndrivers/net/wireless/mediatek/mt76/mt76.h:1925:int mt76_mcu_send_and_get_msg(struct mt76_dev *dev, int cmd, const void *data,\ndrivers/net/wireless/mediatek/mt76/mt76.h-1926-\t\t\t int len, bool wait_resp, struct sk_buff **ret);\n--\ndrivers/net/wireless/mediatek/mt76/mt76.h=1941=mt76_mcu_send_msg(struct mt76_dev *dev, int cmd, const void *data, int len,\n--\ndrivers/net/wireless/mediatek/mt76/mt76.h-1943-{\ndrivers/net/wireless/mediatek/mt76/mt76.h:1944:\treturn mt76_mcu_send_and_get_msg(dev, cmd, data, len, wait_resp, NULL);\ndrivers/net/wireless/mediatek/mt76/mt76.h-1945-}\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c=2232=int mt7915_mcu_muru_debug_get(struct mt7915_phy *phy)\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-2246-\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c:2247:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_EXT_CMD(MURU_CTRL),\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-2248-\t\t\t\t\t\u0026req, sizeof(req), true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c=2920=int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset, u8 *read_buf)\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-2930-\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c:2931:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76,\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-2932-\t\t\t\t\tMCU_EXT_QUERY(EFUSE_ACCESS),\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c=2954=int mt7915_mcu_get_eeprom_free_block(struct mt7915_dev *dev, u8 *block_num)\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-2966-\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c:2967:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76,\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-2968-\t\t\t\t\tMCU_EXT_QUERY(EFUSE_FREE_BLOCK),\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c=3178=int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch)\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-3217-\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c:3218:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_EXT_CMD(GET_MIB_INFO),\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-3219-\t\t\t\t\treq, len * sizeof(req[0]), true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c=3543=int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len,\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-3560-\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c:3561:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76,\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-3562-\t\t\t\t\tMCU_EXT_CMD(TX_POWER_FEATURE_CTRL),\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c=3895=int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif,\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-3916-\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c:3917:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_EXT_CMD(PHY_STAT_INFO),\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-3918-\t\t\t\t\t\u0026req, sizeof(req), true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c=4136=int mt7915_mcu_wed_wa_tx_stats(struct mt7915_dev *dev, u16 wlan_idx)\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-4169-\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c:4170:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_WA_PARAM_CMD(QUERY),\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-4171-\t\t\t\t\t\u0026req, len, true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c=4204=int mt7915_mcu_rf_regval(struct mt7915_dev *dev, u32 regidx, u32 *val, bool set)\n--\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-4221-\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c:4222:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_EXT_QUERY(RF_REG_ACCESS),\ndrivers/net/wireless/mediatek/mt76/mt7915/mcu.c-4223-\t\t\t\t\t\u0026req, sizeof(req), true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7921/mcu.c=75=static int mt7921_mcu_read_eeprom(struct mt792x_dev *dev, u32 offset, u8 *val)\n--\ndrivers/net/wireless/mediatek/mt76/mt7921/mcu.c-83-\ndrivers/net/wireless/mediatek/mt76/mt7921/mcu.c:84:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_EXT_QUERY(EFUSE_ACCESS),\ndrivers/net/wireless/mediatek/mt76/mt7921/mcu.c-85-\t\t\t\t\t\u0026req, sizeof(req), true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7921/mcu.c=576=static int mt7921_mcu_get_nic_capability(struct mt792x_phy *mphy)\n--\ndrivers/net/wireless/mediatek/mt76/mt7921/mcu.c-585-\ndrivers/net/wireless/mediatek/mt76/mt7921/mcu.c:586:\tret = mt76_mcu_send_and_get_msg(phy-\u003edev, MCU_CE_CMD(GET_NIC_CAPAB),\ndrivers/net/wireless/mediatek/mt76/mt7921/mcu.c-587-\t\t\t\t\tNULL, 0, true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7921/mcu.c=1128=int mt7921_get_txpwr_info(struct mt792x_dev *dev, struct mt7921_txpwr *txpwr)\n--\ndrivers/net/wireless/mediatek/mt76/mt7921/mcu.c-1136-\ndrivers/net/wireless/mediatek/mt76/mt7921/mcu.c:1137:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_CE_CMD(GET_TXPWR),\ndrivers/net/wireless/mediatek/mt76/mt7921/mcu.c-1138-\t\t\t\t\t\u0026req, sizeof(req), true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7921/regd.c=177=mt7921_regd_query_regdb(struct mt792x_phy *phy, char *alpha2)\n--\ndrivers/net/wireless/mediatek/mt76/mt7921/regd.c-231-\ndrivers/net/wireless/mediatek/mt76/mt7921/regd.c:232:\t\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76,\ndrivers/net/wireless/mediatek/mt76/mt7921/regd.c-233-\t\t\t\t\t\tMCU_CE_CMD(SET_REGD_CH),\n--\ndrivers/net/wireless/mediatek/mt76/mt7921/testmode.c=85=mt7921_tm_query(struct mt792x_dev *dev, struct mt7921_tm_cmd *req,\n--\ndrivers/net/wireless/mediatek/mt76/mt7921/testmode.c-96-\ndrivers/net/wireless/mediatek/mt76/mt7921/testmode.c:97:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_CE_CMD(TEST_CTRL),\ndrivers/net/wireless/mediatek/mt76/mt7921/testmode.c-98-\t\t\t\t\t\u0026cmd, sizeof(cmd), true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c=77=int mt7925_mcu_regval(struct mt792x_dev *dev, u32 regidx, u32 *val, bool set)\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-120-\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c:121:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76,\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-122-\t\t\t\t\tMCU_WM_UNI_CMD_QUERY(REG_ACCESS),\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c=826=static int mt7925_mcu_read_eeprom(struct mt792x_dev *dev, u32 offset, u8 *val)\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-860-\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c:861:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL),\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-862-\t\t\t\t\t\u0026req, sizeof(req), true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c=964=int mt7925_mcu_fw_log_2_host(struct mt792x_dev *dev, u8 ctrl)\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-980-\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c:981:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_UNI_CMD(WSYS_CONFIG),\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-982-\t\t\t\t\t\u0026req, sizeof(req), true, NULL);\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c=986=int mt7925_mcu_get_temperature(struct mt792x_phy *phy)\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-1005-\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c:1006:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76,\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-1007-\t\t\t\t\tMCU_WM_UNI_CMD_QUERY(THERMAL),\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c=1070=mt7925_mcu_get_nic_capability(struct mt792x_dev *dev)\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-1088-\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c:1089:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_UNI_CMD(CHIP_CONFIG),\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-1090-\t\t\t\t\t\u0026req, sizeof(req), true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c=1611=int mt7925_mcu_set_eeprom(struct mt792x_dev *dev)\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-1628-\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c:1629:\treturn mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_UNI_CMD(EFUSE_CTRL),\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-1630-\t\t\t\t\t \u0026req, sizeof(req), true, NULL);\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c=2239=int mt7925_get_txpwr_info(struct mt792x_dev *dev, u8 band_idx, struct mt7925_txpwr *txpwr)\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-2252-\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c:2253:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_UNI_CMD(TXPOWER),\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-2254-\t\t\t\t\t\u0026req, sizeof(req), true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c=4079=int mt7925_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy)\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-4100-\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c:4101:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76,\ndrivers/net/wireless/mediatek/mt76/mt7925/mcu.c-4102-\t\t\t\t\tMCU_UNI_CMD(RADIO_STATUS),\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/regd.c=223=mt7925_regd_query_regdb(struct mt792x_phy *phy, char *alpha2)\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/regd.c-279-\ndrivers/net/wireless/mediatek/mt76/mt7925/regd.c:280:\t\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76,\ndrivers/net/wireless/mediatek/mt76/mt7925/regd.c-281-\t\t\t\t\t\tMCU_UNI_CMD(SET_POWER_LIMIT),\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c=87=mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req,\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-99-\tif (*((uint16_t *)req-\u003epadding) == MCU_UNI_CMD_TESTMODE_CTRL)\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c:100:\t\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_UNI_QUERY(TESTMODE_CTRL),\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-101-\t\t\t\t\t\t\u0026cmd, sizeof(cmd), true, \u0026skb);\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-102-\telse if (*((uint16_t *)req-\u003epadding) == MCU_UNI_CMD_TESTMODE_RX_STAT)\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c:103:\t\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_UNI_QUERY(TESTMODE_RX_STAT),\ndrivers/net/wireless/mediatek/mt76/mt7925/testmode.c-104-\t\t\t\t\t\t\u0026cmd, sizeof(cmd), true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c=4363=int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len,\n--\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c-4390-\treq.info.addr = cpu_to_le32(addr);\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c:4391:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, cmd, \u0026req, sizeof(req),\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c-4392-\t\t\t\t\ttrue, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c=4439=int mt7996_mcu_get_efuse_free_block(struct mt7996_dev *dev, u8 *block_num)\n--\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c-4457-\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c:4458:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL), \u0026req,\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c-4459-\t\t\t\t\tsizeof(req), true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c=4469=int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap)\n--\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c-4484-\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c:4485:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76,\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c-4486-\t\t\t\t\tMCU_WM_UNI_CMD_QUERY(CHIP_CONFIG), \u0026req,\n--\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c=4524=int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch)\n--\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c-4564-\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c:4565:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76, MCU_WM_UNI_CMD_QUERY(GET_MIB_INFO),\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c-4566-\t\t\t\t\t\u0026req, sizeof(req), true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c=4603=int mt7996_mcu_get_temperature(struct mt7996_phy *phy)\n--\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c-4635-\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c:4636:\tret = mt76_mcu_send_and_get_msg(\u0026phy-\u003edev-\u003emt76, MCU_WM_UNI_CMD(THERMAL),\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c-4637-\t\t\t\t\t\u0026req, sizeof(req), true, \u0026skb);\n--\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c=5282=int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set)\n--\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c-5307-\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c:5308:\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76,\ndrivers/net/wireless/mediatek/mt76/mt7996/mcu.c-5309-\t\t\t\t\tMCU_WM_UNI_CMD_QUERY(REG_ACCESS),\n"
} |
| 39/3 | 2026/08/13 10:05 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 22493 output: 367 thoughts: 2625 Thoughts: **Uninitialized Memory Vulnerabilities in mt7925 Driver**
Okay, so I've been digging into this patch. The key issue here revolves around uninitialized memory, specifically within the `mt7925_tm_query` function. My initial thought process went like this: `mt76_mcu_send_and_get_msg` eventually copies data to a DMA-mapped buffer, and KMSAN would absolutely catch a problem if uninitialized stack memory gets sent to the MCU.
The code defines `struct mt7925_rftest_cmd cmd` on the stack, which is fine, but the old code used `memset(pcmd, 0, sizeof(*pcmd))`. Here's where the problem lies. Because `pcmd` was a `char *`, `sizeof(*pcmd)` evaluated to 1, zeroing only the *first* byte of the `cmd` structure. This left the `cmd.padding` field, specifically 3 bytes, uninitialized. Subsequent `memcpy` operations then wrote to other parts of `cmd`, but those 3 uninitialized bytes were still sent to the MCU, a clear info-leak. KMSAN is tailor-made to catch this, and that's precisely what's happening.
The patch corrects this by changing `pcmd` to `struct mt7925_rftest_cmd *`, ensuring `memset` zeroes the *entire* structure. This is a very clear KMSAN-specific fix. I then double-checked the logic in `mt76_mcu_send_and_get_msg`. Yes, the function calls `mt76_mcu_send_msg`, which allocates an skb, *copies* the data, and sends it out to the device through DMA or USB. This copying process will propagate the uninitialized bits and KMSAN will flag it at the skb copy or at the send to the device.
Now, I moved on to a second potential issue. In `mt7925_testmode_dump`, the original code used `char evt_resp[MT7925_EVT_RSP_LEN]`. A new code path in the patch allows shorter responses from the MCU. If the response from `mt7925_tm_query` was shorter than `MT7925_EVT_RSP_LEN`, the remaining bytes of `evt_resp` would be uninitialized. These bytes were then copied to user space using `nla_put`, leading to another info-leak. The patch cleverly prevents this by zero-initializing `evt_resp` with `char evt_resp[MT7925_EVT_RSP_LEN] = {};`.
I went back and checked the old code. Actually, the old code's logic of `mt7925_tm_query` always requested at least `MT7925_EVT_RSP_LEN` bytes from the device. This meant that the full buffer was always initialized. The original patch *introduced* the potential for this second info-leak that it fixes via the zero initialization.
Crucially, standard KASAN doesn't detect uninitialized memory. KMSAN is *specifically* designed to catch uninitialized memory reads. This is an area where KASAN is fundamentally blind. The patch is now two-fold: addressing the `memset` bug, which leads to the info-leak to the device, and fixing a new code path that could lead to an info-leak to user space. It is a textbook KMSAN fix and requires dedicated testing with KMSAN. I've confirmed that the patch fixes the `memset` bug and addresses the changes to response handling.
I'm now satisfied that `NeedsKMSAN=true` and that a thorough KMSAN fuzzing session is necessary to confirm the fix and search for any other lingering uninitialized memory bugs in this area.
|
| 40/3 | 2026/08/13 10:05 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch fixes a bug in `mt7925_tm_query()` where `memset(pcmd, 0, sizeof(*pcmd))` was used to initialize a stack-allocated `struct mt7925_rftest_cmd cmd`. Because `pcmd` was declared as a `char *`, `sizeof(*pcmd)` evaluated to 1, meaning only the first byte of the structure was zeroed. The remaining 3 bytes of the `padding` field were left uninitialized. This structure was then sent to the MCU via `mt76_mcu_send_and_get_msg()`, resulting in a leak of uninitialized kernel stack memory to the device.\n\nAdditionally, the patch modifies the response handling to allow shorter responses from the MCU. To prevent leaking uninitialized stack memory to user space via `nla_put()` when the response is shorter than `MT7925_EVT_RSP_LEN`, the patch zero-initializes the `evt_resp` buffer (`char evt_resp[MT7925_EVT_RSP_LEN] = {};`).\n\nStandard KASAN detects memory corruption, out-of-bounds accesses, and use-after-free bugs, but it does not track uninitialized memory. KMSAN (KernelMemorySanitizer) is specifically designed to detect reads of uninitialized memory and info-leaks (both to user space and to hardware devices). Since this patch addresses uninitialized stack memory leaks, a dedicated KMSAN fuzzing session is required to verify the fix and ensure no other uninitialized memory issues exist in this code path.",
"NeedsKMSAN": true
}Results: {
"KMSANReasoning": "The patch fixes a bug in `mt7925_tm_query()` where `memset(pcmd, 0, sizeof(*pcmd))` was used to initialize a stack-allocated `struct mt7925_rftest_cmd cmd`. Because `pcmd` was declared as a `char *`, `sizeof(*pcmd)` evaluated to 1, meaning only the first byte of the structure was zeroed. The remaining 3 bytes of the `padding` field were left uninitialized. This structure was then sent to the MCU via `mt76_mcu_send_and_get_msg()`, resulting in a leak of uninitialized kernel stack memory to the device.\n\nAdditionally, the patch modifies the response handling to allow shorter responses from the MCU. To prevent leaking uninitialized stack memory to user space via `nla_put()` when the response is shorter than `MT7925_EVT_RSP_LEN`, the patch zero-initializes the `evt_resp` buffer (`char evt_resp[MT7925_EVT_RSP_LEN] = {};`).\n\nStandard KASAN detects memory corruption, out-of-bounds accesses, and use-after-free bugs, but it does not track uninitialized memory. KMSAN (KernelMemorySanitizer) is specifically designed to detect reads of uninitialized memory and info-leaks (both to user space and to hardware devices). Since this patch addresses uninitialized stack memory leaks, a dedicated KMSAN fuzzing session is required to verify the fix and ensure no other uninitialized memory issues exist in this code path.",
"NeedsKMSAN": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|