| From de594e47659029316bbf9391efb79da0a1a08e08 Mon Sep 17 00:00:00 2001 |
| From: Paolo Bonzini <pbonzini@redhat.com> |
| Date: Wed, 14 Aug 2019 17:35:21 +0530 |
| Subject: [PATCH] scsi: lsi: exit infinite loop while executing script |
| (CVE-2019-12068) |
| |
| When executing script in lsi_execute_script(), the LSI scsi adapter |
| emulator advances 's->dsp' index to read next opcode. This can lead |
| to an infinite loop if the next opcode is empty. Move the existing |
| loop exit after 10k iterations so that it covers no-op opcodes as |
| well. |
| |
| Upstream-Status: Backport [https://git.qemu.org/?p=qemu.git;a=commit;h=de594e47659029316bbf9391efb79da0a1a08e08] |
| CVE: CVE-2019-12068 |
| |
| Reported-by: Bugs SysSec <bugs-syssec@rub.de> |
| Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> |
| Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org> |
| Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> |
| |
| Signed-off-by: Changqing Li <changqing.li@windriver.com> |
| --- |
| hw/scsi/lsi53c895a.c | 41 +++++++++++++++++++++++++++-------------- |
| 1 file changed, 27 insertions(+), 14 deletions(-) |
| |
| diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c |
| index 222a286..ec53b14 100644 |
| --- a/hw/scsi/lsi53c895a.c |
| +++ b/hw/scsi/lsi53c895a.c |
| @@ -186,6 +186,9 @@ static const char *names[] = { |
| /* Flag set if this is a tagged command. */ |
| #define LSI_TAG_VALID (1 << 16) |
| |
| +/* Maximum instructions to process. */ |
| +#define LSI_MAX_INSN 10000 |
| + |
| typedef struct lsi_request { |
| SCSIRequest *req; |
| uint32_t tag; |
| @@ -1133,7 +1136,21 @@ static void lsi_execute_script(LSIState *s) |
| |
| s->istat1 |= LSI_ISTAT1_SRUN; |
| again: |
| - insn_processed++; |
| + if (++insn_processed > LSI_MAX_INSN) { |
| + /* Some windows drivers make the device spin waiting for a memory |
| + location to change. If we have been executed a lot of code then |
| + assume this is the case and force an unexpected device disconnect. |
| + This is apparently sufficient to beat the drivers into submission. |
| + */ |
| + if (!(s->sien0 & LSI_SIST0_UDC)) { |
| + qemu_log_mask(LOG_GUEST_ERROR, |
| + "lsi_scsi: inf. loop with UDC masked"); |
| + } |
| + lsi_script_scsi_interrupt(s, LSI_SIST0_UDC, 0); |
| + lsi_disconnect(s); |
| + trace_lsi_execute_script_stop(); |
| + return; |
| + } |
| insn = read_dword(s, s->dsp); |
| if (!insn) { |
| /* If we receive an empty opcode increment the DSP by 4 bytes |
| @@ -1570,19 +1587,7 @@ again: |
| } |
| } |
| } |
| - if (insn_processed > 10000 && s->waiting == LSI_NOWAIT) { |
| - /* Some windows drivers make the device spin waiting for a memory |
| - location to change. If we have been executed a lot of code then |
| - assume this is the case and force an unexpected device disconnect. |
| - This is apparently sufficient to beat the drivers into submission. |
| - */ |
| - if (!(s->sien0 & LSI_SIST0_UDC)) { |
| - qemu_log_mask(LOG_GUEST_ERROR, |
| - "lsi_scsi: inf. loop with UDC masked"); |
| - } |
| - lsi_script_scsi_interrupt(s, LSI_SIST0_UDC, 0); |
| - lsi_disconnect(s); |
| - } else if (s->istat1 & LSI_ISTAT1_SRUN && s->waiting == LSI_NOWAIT) { |
| + if (s->istat1 & LSI_ISTAT1_SRUN && s->waiting == LSI_NOWAIT) { |
| if (s->dcntl & LSI_DCNTL_SSM) { |
| lsi_script_dma_interrupt(s, LSI_DSTAT_SSI); |
| } else { |
| @@ -1970,6 +1975,10 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) |
| case 0x2f: /* DSP[24:31] */ |
| s->dsp &= 0x00ffffff; |
| s->dsp |= val << 24; |
| + /* |
| + * FIXME: if s->waiting != LSI_NOWAIT, this will only execute one |
| + * instruction. Is this correct? |
| + */ |
| if ((s->dmode & LSI_DMODE_MAN) == 0 |
| && (s->istat1 & LSI_ISTAT1_SRUN) == 0) |
| lsi_execute_script(s); |
| @@ -1988,6 +1997,10 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) |
| break; |
| case 0x3b: /* DCNTL */ |
| s->dcntl = val & ~(LSI_DCNTL_PFF | LSI_DCNTL_STD); |
| + /* |
| + * FIXME: if s->waiting != LSI_NOWAIT, this will only execute one |
| + * instruction. Is this correct? |
| + */ |
| if ((val & LSI_DCNTL_STD) && (s->istat1 & LSI_ISTAT1_SRUN) == 0) |
| lsi_execute_script(s); |
| break; |
| -- |
| 2.7.4 |
| |