Re: [PATCH net v2] net: wwan: iosm: bound device offsets in the MUX downlink decoder

From: Loic Poulain

Date: Mon Jun 22 2026 - 05:34:21 EST


On Sat, Jun 20, 2026 at 3:13 PM Maoyi Xie <maoyixie.tju@xxxxxxxxx> wrote:
>
> mux_dl_adb_decode() walks a chain of aggregated datagram tables using
> offsets and lengths taken from the modem. first_table_index,
> next_table_index, table_length, datagram_index and datagram_length are
> all device supplied le values. Only first_table_index was checked, and
> only for being non zero. The decoder then formed adth = block +
> adth_index and read the table header and the datagram entries with no
> bound against the received skb. A modem that reports an index or a
> length past the downlink buffer makes the decoder read out of bounds.
>
> The buffer is IPC_MEM_MAX_DL_MUX_LITE_BUF_SIZE and skb->len is at most
> that, so skb->len is the real limit, but none of these in band offsets
> were checked against it.
>
> Validate every device offset and length against skb->len before use.
> The block header must fit. Each table header, on entry and after every
> next_table_index, must lie inside the skb. The datagram table must fit.
> Each datagram index and length must stay inside the skb. The header
> padding must not exceed the datagram length so the receive length does
> not wrap.
>
> This was reproduced under KASAN as a slab out of bounds read on a normal
> downlink receive once the iosm net device is up.
>
> Fixes: 1f52d7b62285 ("net: wwan: iosm: Enable M.2 7360 WWAN card support")
> Suggested-by: Loic Poulain <loic.poulain@xxxxxxxxxxxxxxxx>
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Maoyi Xie <maoyixie.tju@xxxxxxxxx>

Reviewed-by: Loic Poulain <loic.poulain@xxxxxxxxxxxxxxxx>


> ---
> Changes in v2:
> - mux_dl_process_dg now uses intermediate native endian locals dg_index
> and dg_len so the bound checks read cleaner and avoid the repeated
> le32_to_cpu conversions, per Loic Poulain's review. No functional
> change.
>
> Link to v1: https://lore.kernel.org/all/178185979029.4044562.9993615975949055530@xxxxxxxxxxxx/
>
> drivers/net/wwan/iosm/iosm_ipc_mux_codec.c | 33 ++++++++++++++++------
> 1 file changed, 24 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c
> index bff46f7ca59f..ff9a4bc52f29 100644
> --- a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c
> +++ b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c
> @@ -553,19 +553,21 @@ static int mux_dl_process_dg(struct iosm_mux *ipc_mux, struct mux_adbh *adbh,
> u32 packet_offset, i, rc, dg_len;
>
> for (i = 0; i < nr_of_dg; i++, dg++) {
> - if (le32_to_cpu(dg->datagram_index)
> - < sizeof(struct mux_adbh))
> + u32 dg_index = le32_to_cpu(dg->datagram_index);
> +
> + dg_len = le16_to_cpu(dg->datagram_length);
> +
> + if (dg_index < sizeof(struct mux_adbh))
> goto dg_error;
>
> - /* Is the packet inside of the ADB */
> - if (le32_to_cpu(dg->datagram_index) >=
> - le32_to_cpu(adbh->block_length)) {
> + /* Is the packet inside of the ADB and the received skb ? */
> + if (dg_index >= le32_to_cpu(adbh->block_length) ||
> + dg_index >= skb->len ||
> + dg_len > skb->len - dg_index ||
> + dl_head_pad_len >= dg_len) {
> goto dg_error;
> } else {
> - packet_offset =
> - le32_to_cpu(dg->datagram_index) +
> - dl_head_pad_len;
> - dg_len = le16_to_cpu(dg->datagram_length);
> + packet_offset = dg_index + dl_head_pad_len;
> /* Pass the packet to the netif layer. */
> rc = ipc_mux_net_receive(ipc_mux, if_id, ipc_mux->wwan,
> packet_offset,
> @@ -595,6 +597,10 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux,
> block = skb->data;
> adbh = (struct mux_adbh *)block;
>
> + /* The block header itself must fit in the received skb. */
> + if (skb->len < sizeof(struct mux_adbh))
> + goto adb_decode_err;
> +
> /* Process the aggregated datagram tables. */
> adth_index = le32_to_cpu(adbh->first_table_index);
>
> @@ -606,6 +612,11 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux,
>
> /* Loop through mixed session tables. */
> while (adth_index) {
> + /* The table header must lie within the received skb. */
> + if (adth_index < sizeof(struct mux_adbh) ||
> + adth_index > skb->len - sizeof(struct mux_adth))
> + goto adb_decode_err;
> +
> /* Get the reference to the table header. */
> adth = (struct mux_adth *)(block + adth_index);
>
> @@ -629,6 +640,10 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux,
> if (le16_to_cpu(adth->table_length) < sizeof(struct mux_adth))
> goto adb_decode_err;
>
> + /* The whole datagram table must fit in the received skb. */
> + if (le16_to_cpu(adth->table_length) > skb->len - adth_index)
> + goto adb_decode_err;
> +
> /* Calculate the number of datagrams. */
> nr_of_dg = (le16_to_cpu(adth->table_length) -
> sizeof(struct mux_adth)) /
> --
> 2.34.1
>