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) + 1, 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_get_hw_spec() checks that the response is longer than its fixed part before subtracting, and nxpwifi_update_vs_ie() rejects a token 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 --- 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 96540914f3cf6..7e05dd5ccaa81 100644 --- a/drivers/net/wireless/nxp/nxpwifi/11ax.c +++ b/drivers/net/wireless/nxp/nxpwifi/11ax.c @@ -204,20 +204,36 @@ 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; u8 ext_id; struct nxpwifi_11ax_he_cap_cfg *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) { ext_id = *((u8 *)header + sizeof(*header) + 1); 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, base-commit: 4056b3c9d3c92ea45955641dbafeb9b960e146ef -- 2.43.0