diff options
| author | Nicolas Boichat <drinkcat@chromium.org> | 2015-12-27 05:17:06 -0500 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2015-12-30 12:26:40 -0500 |
| commit | de327e4966cdbad2b7053c84a6f591fbdc54f7cb (patch) | |
| tree | 4d84876c8a528509fa6c9fa412a401934f80f56d | |
| parent | 8005c49d9aea74d382f474ce11afbbc7d7130bec (diff) | |
spi: mediatek: Prevent overflows in FIFO transfers
In the case where transfer length is not a multiple of 4, KASAN
reports 2 out-of-bounds memory accesses:
- mtk_spi_interrupt: ioread32_rep writes past the end of
trans->rx_buf.
- mtk_spi_fifo_transfer: iowrite32_rep reads past the end of
xfer->tx_buf.
Fix this by using memcpy on the remainder of the bytes.
Signed-off-by: Nicolas Boichat <drinkcat@chromium.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
| -rw-r--r-- | drivers/spi/spi-mt65xx.c | 28 |
1 files changed, 18 insertions, 10 deletions
diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 563954a61424..375d412dbf05 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c | |||
| @@ -323,7 +323,8 @@ static int mtk_spi_fifo_transfer(struct spi_master *master, | |||
| 323 | struct spi_device *spi, | 323 | struct spi_device *spi, |
| 324 | struct spi_transfer *xfer) | 324 | struct spi_transfer *xfer) |
| 325 | { | 325 | { |
| 326 | int cnt; | 326 | int cnt, remainder; |
| 327 | u32 reg_val; | ||
| 327 | struct mtk_spi *mdata = spi_master_get_devdata(master); | 328 | struct mtk_spi *mdata = spi_master_get_devdata(master); |
| 328 | 329 | ||
| 329 | mdata->cur_transfer = xfer; | 330 | mdata->cur_transfer = xfer; |
| @@ -331,12 +332,16 @@ static int mtk_spi_fifo_transfer(struct spi_master *master, | |||
| 331 | mtk_spi_prepare_transfer(master, xfer); | 332 | mtk_spi_prepare_transfer(master, xfer); |
| 332 | mtk_spi_setup_packet(master); | 333 | mtk_spi_setup_packet(master); |
| 333 | 334 | ||
| 334 | if (xfer->len % 4) | 335 | cnt = xfer->len / 4; |
| 335 | cnt = xfer->len / 4 + 1; | ||
| 336 | else | ||
| 337 | cnt = xfer->len / 4; | ||
| 338 | iowrite32_rep(mdata->base + SPI_TX_DATA_REG, xfer->tx_buf, cnt); | 336 | iowrite32_rep(mdata->base + SPI_TX_DATA_REG, xfer->tx_buf, cnt); |
| 339 | 337 | ||
| 338 | remainder = xfer->len % 4; | ||
| 339 | if (remainder > 0) { | ||
| 340 | reg_val = 0; | ||
| 341 | memcpy(®_val, xfer->tx_buf + (cnt * 4), remainder); | ||
| 342 | writel(reg_val, mdata->base + SPI_TX_DATA_REG); | ||
| 343 | } | ||
| 344 | |||
| 340 | mtk_spi_enable_transfer(master); | 345 | mtk_spi_enable_transfer(master); |
| 341 | 346 | ||
| 342 | return 1; | 347 | return 1; |
| @@ -418,7 +423,7 @@ static int mtk_spi_setup(struct spi_device *spi) | |||
| 418 | 423 | ||
| 419 | static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id) | 424 | static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id) |
| 420 | { | 425 | { |
| 421 | u32 cmd, reg_val, cnt; | 426 | u32 cmd, reg_val, cnt, remainder; |
| 422 | struct spi_master *master = dev_id; | 427 | struct spi_master *master = dev_id; |
| 423 | struct mtk_spi *mdata = spi_master_get_devdata(master); | 428 | struct mtk_spi *mdata = spi_master_get_devdata(master); |
| 424 | struct spi_transfer *trans = mdata->cur_transfer; | 429 | struct spi_transfer *trans = mdata->cur_transfer; |
| @@ -431,12 +436,15 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id) | |||
| 431 | 436 | ||
| 432 | if (!master->can_dma(master, master->cur_msg->spi, trans)) { | 437 | if (!master->can_dma(master, master->cur_msg->spi, trans)) { |
| 433 | if (trans->rx_buf) { | 438 | if (trans->rx_buf) { |
| 434 | if (mdata->xfer_len % 4) | 439 | cnt = mdata->xfer_len / 4; |
| 435 | cnt = mdata->xfer_len / 4 + 1; | ||
| 436 | else | ||
| 437 | cnt = mdata->xfer_len / 4; | ||
| 438 | ioread32_rep(mdata->base + SPI_RX_DATA_REG, | 440 | ioread32_rep(mdata->base + SPI_RX_DATA_REG, |
| 439 | trans->rx_buf, cnt); | 441 | trans->rx_buf, cnt); |
| 442 | remainder = mdata->xfer_len % 4; | ||
| 443 | if (remainder > 0) { | ||
| 444 | reg_val = readl(mdata->base + SPI_RX_DATA_REG); | ||
| 445 | memcpy(trans->rx_buf + (cnt * 4), | ||
| 446 | ®_val, remainder); | ||
| 447 | } | ||
| 440 | } | 448 | } |
| 441 | spi_finalize_current_transfer(master); | 449 | spi_finalize_current_transfer(master); |
| 442 | return IRQ_HANDLED; | 450 | return IRQ_HANDLED; |
