[PATCH v3 3/3] wifi: nxpwifi: bound the TLV lengths in the 11AX_CFG response
From: Yehyeong Lee
Date: Fri Oct 16 2026 - 00:29:18 EST
nxpwifi_ret_11ax_cfg() walks the TLVs the firmware returns for
HOST_CMD_11AX_CFG and copies one of them into two fixed-size
destinations. Nothing bounds any of the three lengths involved.
The walk length comes straight out of the response:
left_len = le16_to_cpu(resp->size) - sizeof(*he_cfg) - S_DS_GEN;
left_len is a u16 and the two sizes add up to 11, so a response claiming
fewer than 11 bytes makes the subtraction wrap instead of ending the
walk, and the loop condition is only "left_len > sizeof(*header)".
Inside the loop, header->len is used as a memcpy length twice without
being compared to what is left or to what the destinations hold:
memcpy((u8 *)&he_cap->ext_id,
(u8 *)header + sizeof(*header),
tlv_len);
memcpy(priv->user_he_cap, (u8 *)header, sizeof(*header) + tlv_len);
The first destination runs from ext_id to the end of struct
nxpwifi_11ax_he_cap_cfg, which is 50 bytes; the second is
priv->user_he_cap[HE_CAP_MAX_SIZE], which is 54. header->len is 16 bits.
nxpwifi_set_11ax_status() declares the struct nxpwifi_11ax_he_cfg this
writes into on the stack and issues the command synchronously, so the
frame is still live when the response is parsed.
Do what the driver already does elsewhere. nxpwifi_ret_sta_get_hw_spec()
checks that the response is longer than its fixed part before
subtracting, and nxpwifi_uap_parse_tail_ies() rejects an element longer
than the bytes remaining. Add the matching three checks here: refuse a
response that cannot hold the fixed part, refuse a TLV that claims more
than the remaining length, and refuse an HE-capability TLV that does not
fit either destination. A TLV whose length is within those bounds is
copied exactly as before, including one that fills both destinations
completely.
This is not a claim of a reachable out-of-bounds write. The lengths come
from the device firmware, and whether it can emit these values is not
something the tree answers. The arithmetic reaches past both
destinations, the idiom is already in the driver, and the checks are
cheap.
The driver has not appeared in a released kernel, so there is nothing to
backport to and no stable tag.
Fixes: 73b01e57ed3e ("wifi: nxp: add nxpwifi driver for IW61x")
Signed-off-by: Yehyeong Lee <yhlee@xxxxxxxxxxxxxxxxxx>
---
drivers/net/wireless/nxp/nxpwifi/11ax.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/nxp/nxpwifi/11ax.c b/drivers/net/wireless/nxp/nxpwifi/11ax.c
index a5278375329a4..d59bd213f3e45 100644
--- a/drivers/net/wireless/nxp/nxpwifi/11ax.c
+++ b/drivers/net/wireless/nxp/nxpwifi/11ax.c
@@ -204,7 +204,7 @@ int nxpwifi_ret_11ax_cfg(struct nxpwifi_private *priv,
{
struct host_cmd_11ax_cfg *he_cfg = &resp->params.ax_cfg;
struct nxpwifi_ie_types_header *header;
- u16 left_len, tlv_type, tlv_len;
+ u16 resp_size, left_len, tlv_type, tlv_len;
struct nxpwifi_11ax_he_cap_cfg *he_cap;
u8 ext_id;
@@ -212,16 +212,32 @@ int nxpwifi_ret_11ax_cfg(struct nxpwifi_private *priv,
return 0;
he_cap = &ax_cfg->he_cap_cfg;
- left_len = le16_to_cpu(resp->size) - sizeof(*he_cfg) - S_DS_GEN;
+ resp_size = le16_to_cpu(resp->size);
+ if (resp_size < S_DS_GEN + sizeof(*he_cfg))
+ return -EINVAL;
+
+ left_len = resp_size - S_DS_GEN - sizeof(*he_cfg);
header = (struct nxpwifi_ie_types_header *)he_cfg->tlv;
while (left_len > sizeof(*header)) {
tlv_type = le16_to_cpu(header->type);
tlv_len = le16_to_cpu(header->len);
+ if (sizeof(*header) + tlv_len > left_len)
+ return -EINVAL;
+
if (tlv_type == TLV_TYPE_EXTENSION_ID && tlv_len >= 1) {
ext_id = *((u8 *)header + sizeof(*header));
if (ext_id == WLAN_EID_EXT_HE_CAPABILITY) {
+ if (tlv_len > sizeof(*he_cap) -
+ offsetof(struct nxpwifi_11ax_he_cap_cfg,
+ ext_id) ||
+ sizeof(*header) + tlv_len >
+ sizeof(priv->user_he_cap) ||
+ sizeof(*header) + tlv_len >
+ sizeof(priv->user_2g_he_cap))
+ return -EINVAL;
+
he_cap->id = tlv_type;
he_cap->len = tlv_len;
memcpy((u8 *)&he_cap->ext_id,
--
2.43.0