diff --git a/bes2600/bes2600_sdio.c b/bes2600/bes2600_sdio.c index b943882..e306748 100644 --- a/bes2600/bes2600_sdio.c +++ b/bes2600/bes2600_sdio.c @@ -99,6 +99,7 @@ struct sbus_priv { struct work_struct tx_work; struct scatterlist tx_sg[BES_SDIO_TX_MULTIPLE_NUM + 1]; struct scatterlist tx_sg_nosignal[BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL + 1]; + u8 *tx_bounce; u32 tx_data_cnt; u32 tx_xfer_cnt; u32 tx_proc_cnt; @@ -1151,7 +1152,26 @@ static void sdio_tx_work(struct work_struct *work) } } - sg_set_buf(&sg[scatters], tx_buffer->buf, align); + /* + * The transfer length is rounded up to the SDIO block + * size, but tx_buffer->buf is only tx_buffer->len bytes + * long (it usually aliases into an skb linear head). + * Copy into a driver-owned bounce buffer and zero-pad + * to the aligned size; otherwise DMA reads past the + * skb and leaks adjacent kernel memory on the wire -- + * observed as KFENCE OOB reads from + * bes_sdio_memcpy_to_io_helper via dma_map_sg. + */ + if (WARN_ON_ONCE(total_len + align > MAX_SDIO_TRANSFER_LEN)) + goto flush_previous; + memcpy(self->tx_bounce + total_len, + tx_buffer->buf, tx_buffer->len); + if (align > tx_buffer->len) + memset(self->tx_bounce + total_len + + tx_buffer->len, 0, + align - tx_buffer->len); + sg_set_buf(&sg[scatters], + self->tx_bounce + total_len, align); total_len += align; ++scatters; /*del_node:*/ @@ -1936,6 +1956,17 @@ static int bes2600_sdio_probe(struct sdio_func *func, if (!self->single_gathered_buffer) return -ENOMEM; #endif +#ifdef BES_SDIO_TX_MULTIPLE_ENABLE + self->tx_bounce = (u8 *)__get_dma_pages(GFP_KERNEL, + get_order(MAX_SDIO_TRANSFER_LEN)); + if (!self->tx_bounce) { +#ifndef SDIO_HOST_ADMA_SUPPORT + free_pages((unsigned long)self->single_gathered_buffer, + get_order(MAX_SDIO_TRANSFER_LEN)); +#endif + return -ENOMEM; + } +#endif #ifdef BES_SDIO_RXTX_TOGGLE self->fw_started = false; #endif @@ -2063,6 +2094,12 @@ static void bes2600_sdio_remove(struct sdio_func *func) if (self->single_gathered_buffer) { free_pages((unsigned long)self->single_gathered_buffer, get_order(MAX_SDIO_TRANSFER_LEN)); } +#endif +#ifdef BES_SDIO_TX_MULTIPLE_ENABLE + if (self->tx_bounce) { + free_pages((unsigned long)self->tx_bounce, + get_order(MAX_SDIO_TRANSFER_LEN)); + } #endif kfree(self); }