The received length of a DL MUX block is supplied by the device and can
be shorter than the header its signature advertises. ipc_mux_dl_decode()
reads the four byte signature and then lets the decoders index their
header structures without checking skb->len, so a short block makes the
driver decode past the received data.
Check the signature before reading it, and check the header size of the
ADGH, FCTH and CMDH branches before dispatching to their decoders. The
ADB branch already validates its own headers, and the ACBH branch is
covered by the bounds checks of its command chain walk. A block too
short to carry its signature is dropped on the way to the single free.
Fixes: 9413491e20e1 ("net: iosm: encode or decode datagram")
Cc: <redacted>
Reviewed-by: Guanglei Zhu <redacted>
Signed-off-by: Fang Xieyan <redacted>
---
drivers/net/wwan/iosm/iosm_ipc_mux_codec.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c
index 0bbd41263cc2..5c33171ca701 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c
@@ -696,6 +696,11 @@ void ipc_mux_dl_decode(struct iosm_mux *ipc_mux, struct sk_buff *skb)
if (!skb->data)
return;
+ if (skb->len < sizeof(signature)) {
+ dev_err(ipc_mux->dev, "invalid MUX block length %u", skb->len);
+ goto free_skb;
+ }
+
/* Decode the MUX header type. */
signature = le32_to_cpup((__le32 *)skb->data);
@@ -704,15 +709,24 @@ void ipc_mux_dl_decode(struct iosm_mux *ipc_mux, struct sk_buff *skb)
mux_dl_adb_decode(ipc_mux, skb);
break;
case IOSM_AGGR_MUX_SIG_ADGH:
+ if (skb->len < sizeof(struct mux_adgh))
+ break;
+
ipc_mux_dl_adgh_decode(ipc_mux, skb);
break;
case MUX_SIG_FCTH:
+ if (skb->len < sizeof(struct ipc_mem_lite_gen_tbl))
+ break;
+
ipc_mux_dl_fcth_decode(ipc_mux, skb->data);
break;
case IOSM_AGGR_MUX_SIG_ACBH: /* Aggregated Command Block Header */
ipc_mux_dl_acb_decode(ipc_mux, skb);
break;
case MUX_SIG_CMDH:
+ if (skb->len < offsetof(struct mux_lite_cmdh, param))
+ break;
+
ipc_mux_dl_cmd_decode(ipc_mux, skb);
break;
@@ -720,6 +734,7 @@ void ipc_mux_dl_decode(struct iosm_mux *ipc_mux, struct sk_buff *skb)
dev_err(ipc_mux->dev, "invalid ABH signature");
}
+free_skb:
ipc_pcie_kfree_skb(ipc_mux->pcie, skb);
}
--
2.50.1