diff options
author | Florian Achleitner <achleitner.florian@fronius.com> | 2015-11-18 03:04:12 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-12-13 22:59:48 -0500 |
commit | ed7a85045d0a0688a51bdab9e1a1d6ee79cb33b6 (patch) | |
tree | 19c03da356744ef5f10ed6612fa9cbfe6192bfc9 /drivers/tty | |
parent | 63d8cb3f19dabb409a09b4f2b8827934ab9365a3 (diff) |
sc16is7xx: Fix TX buffer overrun caused by wrong tx fifo level read-out
We found that our sc16is7xx on spi reported a TX fifo free space value
(TXLVL_REG) of 255 ocassionally, which is obviously wrong, with a
64 byte fifo and caused a buffer overrun and a kernel crash.
To trigger this, a large write to the tty is sufficient. The fifo fills,
TXLVL_REG reads zero, but the handle_tx function does a zero-data-length
write to the TX fifo anyways through sc16is7xx_fifo_write. The next
TXLVL_REG read then yields 255, for unknown reasons. A subsequent read
is ok.
Prevent zero-data-length writes if the TX fifo is full, because they are
pointless, and because they trigger wrong TXLVL read-outs.
Furthermore, prevent a TX buffer overrun if the peripheral reports values
larger than the buffer size and thus, don't allow the peripheral to crash
the kernel.
Signed-off-by: Florian Achleitner <achleitner.florian@fronius.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r-- | drivers/tty/serial/sc16is7xx.c | 13 |
1 files changed, 13 insertions, 0 deletions
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index edb5305b9d4d..5815bcbc55b2 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c | |||
@@ -389,6 +389,13 @@ static void sc16is7xx_fifo_write(struct uart_port *port, u8 to_send) | |||
389 | const u8 line = sc16is7xx_line(port); | 389 | const u8 line = sc16is7xx_line(port); |
390 | u8 addr = (SC16IS7XX_THR_REG << SC16IS7XX_REG_SHIFT) | line; | 390 | u8 addr = (SC16IS7XX_THR_REG << SC16IS7XX_REG_SHIFT) | line; |
391 | 391 | ||
392 | /* | ||
393 | * Don't send zero-length data, at least on SPI it confuses the chip | ||
394 | * delivering wrong TXLVL data. | ||
395 | */ | ||
396 | if (unlikely(!to_send)) | ||
397 | return; | ||
398 | |||
392 | regcache_cache_bypass(s->regmap, true); | 399 | regcache_cache_bypass(s->regmap, true); |
393 | regmap_raw_write(s->regmap, addr, s->buf, to_send); | 400 | regmap_raw_write(s->regmap, addr, s->buf, to_send); |
394 | regcache_cache_bypass(s->regmap, false); | 401 | regcache_cache_bypass(s->regmap, false); |
@@ -630,6 +637,12 @@ static void sc16is7xx_handle_tx(struct uart_port *port) | |||
630 | if (likely(to_send)) { | 637 | if (likely(to_send)) { |
631 | /* Limit to size of TX FIFO */ | 638 | /* Limit to size of TX FIFO */ |
632 | txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); | 639 | txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); |
640 | if (txlen > SC16IS7XX_FIFO_SIZE) { | ||
641 | dev_err_ratelimited(port->dev, | ||
642 | "chip reports %d free bytes in TX fifo, but it only has %d", | ||
643 | txlen, SC16IS7XX_FIFO_SIZE); | ||
644 | txlen = 0; | ||
645 | } | ||
633 | to_send = (to_send > txlen) ? txlen : to_send; | 646 | to_send = (to_send > txlen) ? txlen : to_send; |
634 | 647 | ||
635 | /* Add data to send */ | 648 | /* Add data to send */ |