Message ID | 20210407195801.685-6-mark.cave-ayland@ilande.co.uk |
---|---|
State | New |
Headers | show |
Series | esp: fix asserts/segfaults discovered by fuzzer | expand |
On 4/7/21 9:57 PM, Mark Cave-Ayland wrote: > The const pointer returned by fifo8_pop_buf() lies directly within the array used > to model the FIFO. Building with address sanitizers enabled shows that if the > caller expects a minimum number of bytes present then if the FIFO is nearly full, > the caller may unexpectedly access past the end of the array. > > Introduce esp_fifo_pop_buf() which takes a destination buffer and performs a > memcpy() in it to guarantee that the caller cannot overwrite the FIFO array and > update all callers to use it. Similarly add underflow protection similar to > esp_fifo_push() and esp_fifo_pop() so that instead of triggering an assert() > the operation becomes a no-op. > > Buglink: https://bugs.launchpad.net/qemu/+bug/1909247 > Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> > Tested-by: Alexander Bulekov <alxndr@bu.edu> > --- > hw/scsi/esp.c | 40 ++++++++++++++++++++++++++++------------ > 1 file changed, 28 insertions(+), 12 deletions(-) Way cleaner/safer. Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
On Wed, 7 Apr 2021 at 21:02, Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> wrote: > > The const pointer returned by fifo8_pop_buf() lies directly within the array used > to model the FIFO. Building with address sanitizers enabled shows that if the > caller expects a minimum number of bytes present then if the FIFO is nearly full, > the caller may unexpectedly access past the end of the array. > > Introduce esp_fifo_pop_buf() which takes a destination buffer and performs a > memcpy() in it to guarantee that the caller cannot overwrite the FIFO array and > update all callers to use it. Similarly add underflow protection similar to > esp_fifo_push() and esp_fifo_pop() so that instead of triggering an assert() > the operation becomes a no-op. > > Buglink: https://bugs.launchpad.net/qemu/+bug/1909247 > Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> > Tested-by: Alexander Bulekov <alxndr@bu.edu> > --- > hw/scsi/esp.c | 40 ++++++++++++++++++++++++++++------------ > 1 file changed, 28 insertions(+), 12 deletions(-) > > diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c > index ff8fa73de9..1aa2caf57d 100644 > --- a/hw/scsi/esp.c > +++ b/hw/scsi/esp.c > @@ -117,6 +117,23 @@ static uint8_t esp_fifo_pop(Fifo8 *fifo) > return fifo8_pop(fifo); > } > > +static uint32_t esp_fifo_pop_buf(Fifo8 *fifo, uint8_t *dest, int maxlen) > +{ > + const uint8_t *buf; > + uint32_t n; > + > + if (maxlen == 0) { > + return 0; > + } > + > + buf = fifo8_pop_buf(fifo, maxlen, &n); > + if (dest) { > + memcpy(dest, buf, n); > + } > + > + return n; > +} > + > static uint32_t esp_get_tc(ESPState *s) > { > uint32_t dmalen; > @@ -241,11 +258,11 @@ static uint32_t get_cmd(ESPState *s, uint32_t maxlen) > if (dmalen == 0) { > return 0; > } > - memcpy(buf, fifo8_pop_buf(&s->fifo, dmalen, &n), dmalen); > - if (dmalen >= 3) { > + n = esp_fifo_pop_buf(&s->fifo, buf, dmalen); > + if (n >= 3) { > buf[0] = buf[2] >> 5; > } > - fifo8_push_all(&s->cmdfifo, buf, dmalen); > + fifo8_push_all(&s->cmdfifo, buf, n); > } > trace_esp_get_cmd(dmalen, target); This bit could be tidied up further -- it currently sets dmalen = MIN(fifo8_num_used(&s->fifo), maxlen); in order not to pull more bytes out of the FIFO than it has in it; but now we are making that check in esp_fifo_pop_buf() I think you could just do dmalen = esp_fifo_pop_buf(&s->fifo, buf, maxlen); and drop the dmalen = MIN(fifo8_num_used(&s->fifo), maxlen); if (dmalen == 0) { return 0; } part and the use of 'n' entirely. But this code isn't wrong, so we can do that later to avoid having to respin here. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> thanks -- PMM
diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index ff8fa73de9..1aa2caf57d 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -117,6 +117,23 @@ static uint8_t esp_fifo_pop(Fifo8 *fifo) return fifo8_pop(fifo); } +static uint32_t esp_fifo_pop_buf(Fifo8 *fifo, uint8_t *dest, int maxlen) +{ + const uint8_t *buf; + uint32_t n; + + if (maxlen == 0) { + return 0; + } + + buf = fifo8_pop_buf(fifo, maxlen, &n); + if (dest) { + memcpy(dest, buf, n); + } + + return n; +} + static uint32_t esp_get_tc(ESPState *s) { uint32_t dmalen; @@ -241,11 +258,11 @@ static uint32_t get_cmd(ESPState *s, uint32_t maxlen) if (dmalen == 0) { return 0; } - memcpy(buf, fifo8_pop_buf(&s->fifo, dmalen, &n), dmalen); - if (dmalen >= 3) { + n = esp_fifo_pop_buf(&s->fifo, buf, dmalen); + if (n >= 3) { buf[0] = buf[2] >> 5; } - fifo8_push_all(&s->cmdfifo, buf, dmalen); + fifo8_push_all(&s->cmdfifo, buf, n); } trace_esp_get_cmd(dmalen, target); @@ -258,16 +275,16 @@ static uint32_t get_cmd(ESPState *s, uint32_t maxlen) static void do_busid_cmd(ESPState *s, uint8_t busid) { - uint32_t n, cmdlen; + uint32_t cmdlen; int32_t datalen; int lun; SCSIDevice *current_lun; - uint8_t *buf; + uint8_t buf[ESP_CMDFIFO_SZ]; trace_esp_do_busid_cmd(busid); lun = busid & 7; cmdlen = fifo8_num_used(&s->cmdfifo); - buf = (uint8_t *)fifo8_pop_buf(&s->cmdfifo, cmdlen, &n); + esp_fifo_pop_buf(&s->cmdfifo, buf, cmdlen); current_lun = scsi_device_find(&s->bus, 0, s->current_dev->id, lun); s->current_req = scsi_req_new(current_lun, 0, lun, buf, s); @@ -300,13 +317,12 @@ static void do_busid_cmd(ESPState *s, uint8_t busid) static void do_cmd(ESPState *s) { uint8_t busid = fifo8_pop(&s->cmdfifo); - uint32_t n; s->cmdfifo_cdb_offset--; /* Ignore extended messages for now */ if (s->cmdfifo_cdb_offset) { - fifo8_pop_buf(&s->cmdfifo, s->cmdfifo_cdb_offset, &n); + esp_fifo_pop_buf(&s->cmdfifo, NULL, s->cmdfifo_cdb_offset); s->cmdfifo_cdb_offset = 0; } @@ -484,7 +500,7 @@ static void do_dma_pdma_cb(ESPState *s) /* Copy FIFO data to device */ len = MIN(s->async_len, ESP_FIFO_SZ); len = MIN(len, fifo8_num_used(&s->fifo)); - memcpy(s->async_buf, fifo8_pop_buf(&s->fifo, len, &n), len); + n = esp_fifo_pop_buf(&s->fifo, s->async_buf, len); s->async_buf += n; s->async_len -= n; s->ti_size += n; @@ -492,7 +508,7 @@ static void do_dma_pdma_cb(ESPState *s) if (n < len) { /* Unaligned accesses can cause FIFO wraparound */ len = len - n; - memcpy(s->async_buf, fifo8_pop_buf(&s->fifo, len, &n), len); + n = esp_fifo_pop_buf(&s->fifo, s->async_buf, len); s->async_buf += n; s->async_len -= n; s->ti_size += n; @@ -668,7 +684,7 @@ static void esp_do_dma(ESPState *s) static void esp_do_nodma(ESPState *s) { int to_device = ((s->rregs[ESP_RSTAT] & 7) == STAT_DO); - uint32_t cmdlen, n; + uint32_t cmdlen; int len; if (s->do_cmd) { @@ -709,7 +725,7 @@ static void esp_do_nodma(ESPState *s) if (to_device) { len = MIN(fifo8_num_used(&s->fifo), ESP_FIFO_SZ); - memcpy(s->async_buf, fifo8_pop_buf(&s->fifo, len, &n), len); + esp_fifo_pop_buf(&s->fifo, s->async_buf, len); s->async_buf += len; s->async_len -= len; s->ti_size += len;