wifi: brcmfmac: validate msgbuf flowring IDs before use

Firmware messages carry flow_ring_id values which brcmfmac converts
to an internal flowid by subtracting
BRCMF_H2D_MSGRING_FLOWRING_IDSTART. The resulting value is used as
a bit index in txstatus_done_map and as an array index into
msgbuf->flowrings and the flowring state.

Validate the firmware supplied flow_ring_id before using it. This
prevents flow_ring_id values below BRCMF_H2D_MSGRING_FLOWRING_IDSTART
from underflowing and rejects values outside msgbuf->max_flowrings.

In the tx status path, complete the packet with an error after
removing a valid packet id so the skb is not leaked when the flow
ring id is invalid.

Signed-off-by: Can Peng <pengcan@kylinos.cn>
Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com>
Link: https://patch.msgid.link/20260723055618.550834-1-pengcan@kylinos.cn
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Can Peng
2026-07-23 13:56:17 +08:00
committed by Johannes Berg
parent 3a006d09a2
commit 32856f39fe

View File

@@ -560,6 +560,28 @@ brcmf_msgbuf_remove_flowring(struct brcmf_msgbuf *msgbuf, u16 flowid)
brcmf_flowring_delete(msgbuf->flow, flowid);
}
static bool brcmf_msgbuf_get_flowid(struct brcmf_msgbuf *msgbuf,
u16 flow_ring_id, u16 *flowid)
{
u32 id = flow_ring_id;
if (id < BRCMF_H2D_MSGRING_FLOWRING_IDSTART) {
bphy_err(msgbuf->drvr, "invalid flowring id %u\n",
flow_ring_id);
return false;
}
id -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART;
if (id >= msgbuf->max_flowrings) {
bphy_err(msgbuf->drvr, "invalid flowring id %u\n",
flow_ring_id);
return false;
}
*flowid = id;
return true;
}
static struct brcmf_msgbuf_work_item *
brcmf_msgbuf_dequeue_work(struct brcmf_msgbuf *msgbuf)
@@ -880,17 +902,23 @@ brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf)
struct msgbuf_tx_status *tx_status;
u32 idx;
struct sk_buff *skb;
u16 flow_ring_id;
u16 flowid;
tx_status = (struct msgbuf_tx_status *)buf;
idx = le32_to_cpu(tx_status->msg.request_id) - 1;
flowid = le16_to_cpu(tx_status->compl_hdr.flow_ring_id);
flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART;
flow_ring_id = le16_to_cpu(tx_status->compl_hdr.flow_ring_id);
skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
msgbuf->tx_pktids, idx);
if (!skb)
return;
if (!brcmf_msgbuf_get_flowid(msgbuf, flow_ring_id, &flowid)) {
brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx),
skb, false);
return;
}
set_bit(flowid, msgbuf->txstatus_done_map);
commonring = msgbuf->flowrings[flowid];
atomic_dec(&commonring->outstanding_tx);
@@ -1237,13 +1265,16 @@ brcmf_msgbuf_process_flow_ring_create_response(struct brcmf_msgbuf *msgbuf,
struct msgbuf_flowring_create_resp *flowring_create_resp;
u16 status;
u16 flowid;
u16 flow_ring_id;
flowring_create_resp = (struct msgbuf_flowring_create_resp *)buf;
flowid = le16_to_cpu(flowring_create_resp->compl_hdr.flow_ring_id);
flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART;
flow_ring_id = le16_to_cpu(flowring_create_resp->compl_hdr.flow_ring_id);
status = le16_to_cpu(flowring_create_resp->compl_hdr.status);
if (!brcmf_msgbuf_get_flowid(msgbuf, flow_ring_id, &flowid))
return;
if (status) {
bphy_err(drvr, "Flowring creation failed, code %d\n", status);
brcmf_msgbuf_remove_flowring(msgbuf, flowid);
@@ -1266,13 +1297,16 @@ brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf,
struct msgbuf_flowring_delete_resp *flowring_delete_resp;
u16 status;
u16 flowid;
u16 flow_ring_id;
flowring_delete_resp = (struct msgbuf_flowring_delete_resp *)buf;
flowid = le16_to_cpu(flowring_delete_resp->compl_hdr.flow_ring_id);
flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART;
flow_ring_id = le16_to_cpu(flowring_delete_resp->compl_hdr.flow_ring_id);
status = le16_to_cpu(flowring_delete_resp->compl_hdr.status);
if (!brcmf_msgbuf_get_flowid(msgbuf, flow_ring_id, &flowid))
return;
if (status) {
bphy_err(drvr, "Flowring deletion failed, code %d\n", status);
brcmf_flowring_delete(msgbuf->flow, flowid);