[PATCH v4 3/3] wifi: nxpwifi: bound the TLV lengths in the 11AX_CFG response
From: Yehyeong Lee
Date: Mon Oct 19 2026 - 20:52:27 EST
nxpwifi_ret_11ax_cfg() walks the TLVs the firmware returns and copies an
HE capability into fixed-size buffers without bounding any length. A
response shorter than the fixed part wraps the u16 walk length, and
header->len is used as a memcpy length without being checked against the
destination.
Reject a response too short for the fixed part, reject a TLV longer than
the bytes left, and reject an HE capability that does not fit its
destination. Select the destination buffer first, by band, then validate
and copy that one.
The lengths come from device firmware, so this is not a demonstrated
out-of-bounds write. The driver has not appeared in a released kernel,
so there is 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 | 49 +++++++++++++++++--------
1 file changed, 34 insertions(+), 15 deletions(-)
diff --git a/drivers/net/wireless/nxp/nxpwifi/11ax.c b/drivers/net/wireless/nxp/nxpwifi/11ax.c
index 16bda167fa66b..be38a034be325 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,34 +212,53 @@ 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) {
+ u8 *user_cap, *user_cap_len;
+ size_t user_cap_size;
+
+ if (he_cfg->band_config & BIT(1)) {
+ user_cap = priv->user_he_cap;
+ user_cap_len = &priv->user_he_cap_len;
+ user_cap_size =
+ sizeof(priv->user_he_cap);
+ } else {
+ user_cap = priv->user_2g_he_cap;
+ user_cap_len =
+ &priv->user_2g_he_cap_len;
+ user_cap_size =
+ sizeof(priv->user_2g_he_cap);
+ }
+
+ if (tlv_len > sizeof(*he_cap) -
+ offsetof(struct nxpwifi_11ax_he_cap_cfg,
+ ext_id) ||
+ sizeof(*header) + tlv_len > user_cap_size)
+ return -EINVAL;
+
he_cap->id = tlv_type;
he_cap->len = tlv_len;
memcpy((u8 *)&he_cap->ext_id,
(u8 *)header + sizeof(*header),
tlv_len);
- if (he_cfg->band_config & BIT(1)) {
- memcpy(priv->user_he_cap,
- (u8 *)header,
- sizeof(*header) + tlv_len);
- priv->user_he_cap_len =
- sizeof(*header) + tlv_len;
- } else {
- memcpy(priv->user_2g_he_cap,
- (u8 *)header,
- sizeof(*header) + tlv_len);
- priv->user_2g_he_cap_len =
- sizeof(*header) + tlv_len;
- }
+ memcpy(user_cap, (u8 *)header,
+ sizeof(*header) + tlv_len);
+ *user_cap_len = sizeof(*header) + tlv_len;
}
}
--
2.43.0