esp: introduce esp_fifo_pop_buf() and use it instead of fifo8_pop_buf()

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>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-Id: <20210407195801.685-6-mark.cave-ayland@ilande.co.uk>
This commit is contained in:
Mark Cave-Ayland 2021-04-07 20:57:54 +01:00
parent c5fef9112b
commit 7b320a8e67

View File

@ -117,6 +117,23 @@ static uint8_t esp_fifo_pop(Fifo8 *fifo)
return fifo8_pop(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) static uint32_t esp_get_tc(ESPState *s)
{ {
uint32_t dmalen; uint32_t dmalen;
@ -241,11 +258,11 @@ static uint32_t get_cmd(ESPState *s, uint32_t maxlen)
if (dmalen == 0) { if (dmalen == 0) {
return 0; return 0;
} }
memcpy(buf, fifo8_pop_buf(&s->fifo, dmalen, &n), dmalen); n = esp_fifo_pop_buf(&s->fifo, buf, dmalen);
if (dmalen >= 3) { if (n >= 3) {
buf[0] = buf[2] >> 5; 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); 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) static void do_busid_cmd(ESPState *s, uint8_t busid)
{ {
uint32_t n, cmdlen; uint32_t cmdlen;
int32_t datalen; int32_t datalen;
int lun; int lun;
SCSIDevice *current_lun; SCSIDevice *current_lun;
uint8_t *buf; uint8_t buf[ESP_CMDFIFO_SZ];
trace_esp_do_busid_cmd(busid); trace_esp_do_busid_cmd(busid);
lun = busid & 7; lun = busid & 7;
cmdlen = fifo8_num_used(&s->cmdfifo); 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); 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); 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) static void do_cmd(ESPState *s)
{ {
uint8_t busid = fifo8_pop(&s->cmdfifo); uint8_t busid = fifo8_pop(&s->cmdfifo);
uint32_t n;
s->cmdfifo_cdb_offset--; s->cmdfifo_cdb_offset--;
/* Ignore extended messages for now */ /* Ignore extended messages for now */
if (s->cmdfifo_cdb_offset) { 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; s->cmdfifo_cdb_offset = 0;
} }
@ -484,7 +500,7 @@ static void do_dma_pdma_cb(ESPState *s)
/* Copy FIFO data to device */ /* Copy FIFO data to device */
len = MIN(s->async_len, ESP_FIFO_SZ); len = MIN(s->async_len, ESP_FIFO_SZ);
len = MIN(len, fifo8_num_used(&s->fifo)); 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_buf += n;
s->async_len -= n; s->async_len -= n;
s->ti_size += n; s->ti_size += n;
@ -492,7 +508,7 @@ static void do_dma_pdma_cb(ESPState *s)
if (n < len) { if (n < len) {
/* Unaligned accesses can cause FIFO wraparound */ /* Unaligned accesses can cause FIFO wraparound */
len = len - n; 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_buf += n;
s->async_len -= n; s->async_len -= n;
s->ti_size += n; s->ti_size += n;
@ -668,7 +684,7 @@ static void esp_do_dma(ESPState *s)
static void esp_do_nodma(ESPState *s) static void esp_do_nodma(ESPState *s)
{ {
int to_device = ((s->rregs[ESP_RSTAT] & 7) == STAT_DO); int to_device = ((s->rregs[ESP_RSTAT] & 7) == STAT_DO);
uint32_t cmdlen, n; uint32_t cmdlen;
int len; int len;
if (s->do_cmd) { if (s->do_cmd) {
@ -709,7 +725,7 @@ static void esp_do_nodma(ESPState *s)
if (to_device) { if (to_device) {
len = MIN(fifo8_num_used(&s->fifo), ESP_FIFO_SZ); 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_buf += len;
s->async_len -= len; s->async_len -= len;
s->ti_size += len; s->ti_size += len;