hw/scsi/lsi53c895a.c | 7 ++++++- tests/qtest/fuzz-lsi53c895a-test.c | 33 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-)
We cannot use the generic reentrancy guard in the LSI code, so
we have to manually prevent endless reentrancy here. The problematic
lsi_execute_script() function has already a way to detect whether
too many instructions have been executed - we just have to slightly
change the logic here that it also takes into account if the function
has been called too often in a reentrant way.
The code in fuzz-lsi53c895a-test.c has been taken from an earlier
patch by Mauro Matteo Cascella.
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1563
Signed-off-by: Thomas Huth <thuth@redhat.com>
---
hw/scsi/lsi53c895a.c | 7 ++++++-
tests/qtest/fuzz-lsi53c895a-test.c | 33 ++++++++++++++++++++++++++++++
2 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c
index 048436352b..8b34199ab8 100644
--- a/hw/scsi/lsi53c895a.c
+++ b/hw/scsi/lsi53c895a.c
@@ -1134,10 +1134,13 @@ static void lsi_execute_script(LSIState *s)
uint32_t addr, addr_high;
int opcode;
int insn_processed = 0;
+ static int reentrancy_level;
+
+ reentrancy_level++;
s->istat1 |= LSI_ISTAT1_SRUN;
again:
- if (++insn_processed > LSI_MAX_INSN) {
+ if (++insn_processed > LSI_MAX_INSN || reentrancy_level > 8) {
/* 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.
@@ -1596,6 +1599,8 @@ again:
}
}
trace_lsi_execute_script_stop();
+
+ reentrancy_level--;
}
static uint8_t lsi_reg_readb(LSIState *s, int offset)
diff --git a/tests/qtest/fuzz-lsi53c895a-test.c b/tests/qtest/fuzz-lsi53c895a-test.c
index 2012bd54b7..1b55928b9f 100644
--- a/tests/qtest/fuzz-lsi53c895a-test.c
+++ b/tests/qtest/fuzz-lsi53c895a-test.c
@@ -8,6 +8,36 @@
#include "qemu/osdep.h"
#include "libqtest.h"
+/*
+ * This used to trigger a DMA reentrancy issue
+ * leading to memory corruption bugs like stack
+ * overflow or use-after-free
+ * https://gitlab.com/qemu-project/qemu/-/issues/1563
+ */
+static void test_lsi_dma_reentrancy(void)
+{
+ QTestState *s;
+
+ s = qtest_init("-M q35 -m 512M -nodefaults "
+ "-blockdev driver=null-co,node-name=null0 "
+ "-device lsi53c810 -device scsi-cd,drive=null0");
+
+ qtest_outl(s, 0xcf8, 0x80000804); /* PCI Command Register */
+ qtest_outw(s, 0xcfc, 0x7); /* Enables accesses */
+ qtest_outl(s, 0xcf8, 0x80000814); /* Memory Bar 1 */
+ qtest_outl(s, 0xcfc, 0xff100000); /* Set MMIO Address*/
+ qtest_outl(s, 0xcf8, 0x80000818); /* Memory Bar 2 */
+ qtest_outl(s, 0xcfc, 0xff000000); /* Set RAM Address*/
+ qtest_writel(s, 0xff000000, 0xc0000024);
+ qtest_writel(s, 0xff000114, 0x00000080);
+ qtest_writel(s, 0xff00012c, 0xff000000);
+ qtest_writel(s, 0xff000004, 0xff000114);
+ qtest_writel(s, 0xff000008, 0xff100014);
+ qtest_writel(s, 0xff10002f, 0x000000ff);
+
+ qtest_quit(s);
+}
+
/*
* This used to trigger a UAF in lsi_do_msgout()
* https://gitlab.com/qemu-project/qemu/-/issues/972
@@ -124,5 +154,8 @@ int main(int argc, char **argv)
qtest_add_func("fuzz/lsi53c895a/lsi_do_msgout_cancel_req",
test_lsi_do_msgout_cancel_req);
+ qtest_add_func("fuzz/lsi53c895a/lsi_dma_reentrancy",
+ test_lsi_dma_reentrancy);
+
return g_test_run();
}
--
2.31.1
On Tue, 16 May 2023 at 06:20, Thomas Huth <thuth@redhat.com> wrote: > > We cannot use the generic reentrancy guard in the LSI code, so > we have to manually prevent endless reentrancy here. The problematic > lsi_execute_script() function has already a way to detect whether > too many instructions have been executed - we just have to slightly > change the logic here that it also takes into account if the function > has been called too often in a reentrant way. > > The code in fuzz-lsi53c895a-test.c has been taken from an earlier > patch by Mauro Matteo Cascella. > > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1563 > Signed-off-by: Thomas Huth <thuth@redhat.com> > --- > hw/scsi/lsi53c895a.c | 7 ++++++- > tests/qtest/fuzz-lsi53c895a-test.c | 33 ++++++++++++++++++++++++++++++ > 2 files changed, 39 insertions(+), 1 deletion(-) > > diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c > index 048436352b..8b34199ab8 100644 > --- a/hw/scsi/lsi53c895a.c > +++ b/hw/scsi/lsi53c895a.c > @@ -1134,10 +1134,13 @@ static void lsi_execute_script(LSIState *s) > uint32_t addr, addr_high; > int opcode; > int insn_processed = 0; > + static int reentrancy_level; > + > + reentrancy_level++; > > s->istat1 |= LSI_ISTAT1_SRUN; > again: > - if (++insn_processed > LSI_MAX_INSN) { > + if (++insn_processed > LSI_MAX_INSN || reentrancy_level > 8) { Why 8 and not some other number? > /* 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. > @@ -1596,6 +1599,8 @@ again: > } > } > trace_lsi_execute_script_stop(); > + > + reentrancy_level--; > } > > static uint8_t lsi_reg_readb(LSIState *s, int offset) > diff --git a/tests/qtest/fuzz-lsi53c895a-test.c b/tests/qtest/fuzz-lsi53c895a-test.c > index 2012bd54b7..1b55928b9f 100644 > --- a/tests/qtest/fuzz-lsi53c895a-test.c > +++ b/tests/qtest/fuzz-lsi53c895a-test.c > @@ -8,6 +8,36 @@ > #include "qemu/osdep.h" > #include "libqtest.h" > > +/* > + * This used to trigger a DMA reentrancy issue > + * leading to memory corruption bugs like stack > + * overflow or use-after-free > + * https://gitlab.com/qemu-project/qemu/-/issues/1563 > + */ > +static void test_lsi_dma_reentrancy(void) > +{ > + QTestState *s; > + > + s = qtest_init("-M q35 -m 512M -nodefaults " > + "-blockdev driver=null-co,node-name=null0 " > + "-device lsi53c810 -device scsi-cd,drive=null0"); > + > + qtest_outl(s, 0xcf8, 0x80000804); /* PCI Command Register */ > + qtest_outw(s, 0xcfc, 0x7); /* Enables accesses */ > + qtest_outl(s, 0xcf8, 0x80000814); /* Memory Bar 1 */ > + qtest_outl(s, 0xcfc, 0xff100000); /* Set MMIO Address*/ > + qtest_outl(s, 0xcf8, 0x80000818); /* Memory Bar 2 */ > + qtest_outl(s, 0xcfc, 0xff000000); /* Set RAM Address*/ > + qtest_writel(s, 0xff000000, 0xc0000024); > + qtest_writel(s, 0xff000114, 0x00000080); > + qtest_writel(s, 0xff00012c, 0xff000000); > + qtest_writel(s, 0xff000004, 0xff000114); > + qtest_writel(s, 0xff000008, 0xff100014); > + qtest_writel(s, 0xff10002f, 0x000000ff); > + > + qtest_quit(s); > +} > + > /* > * This used to trigger a UAF in lsi_do_msgout() > * https://gitlab.com/qemu-project/qemu/-/issues/972 > @@ -124,5 +154,8 @@ int main(int argc, char **argv) > qtest_add_func("fuzz/lsi53c895a/lsi_do_msgout_cancel_req", > test_lsi_do_msgout_cancel_req); > > + qtest_add_func("fuzz/lsi53c895a/lsi_dma_reentrancy", > + test_lsi_dma_reentrancy); > + > return g_test_run(); > } > -- > 2.31.1 > >
On 16/05/2023 15.40, Stefan Hajnoczi wrote: > On Tue, 16 May 2023 at 06:20, Thomas Huth <thuth@redhat.com> wrote: >> >> We cannot use the generic reentrancy guard in the LSI code, so >> we have to manually prevent endless reentrancy here. The problematic >> lsi_execute_script() function has already a way to detect whether >> too many instructions have been executed - we just have to slightly >> change the logic here that it also takes into account if the function >> has been called too often in a reentrant way. >> >> The code in fuzz-lsi53c895a-test.c has been taken from an earlier >> patch by Mauro Matteo Cascella. >> >> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1563 >> Signed-off-by: Thomas Huth <thuth@redhat.com> >> --- >> hw/scsi/lsi53c895a.c | 7 ++++++- >> tests/qtest/fuzz-lsi53c895a-test.c | 33 ++++++++++++++++++++++++++++++ >> 2 files changed, 39 insertions(+), 1 deletion(-) >> >> diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c >> index 048436352b..8b34199ab8 100644 >> --- a/hw/scsi/lsi53c895a.c >> +++ b/hw/scsi/lsi53c895a.c >> @@ -1134,10 +1134,13 @@ static void lsi_execute_script(LSIState *s) >> uint32_t addr, addr_high; >> int opcode; >> int insn_processed = 0; >> + static int reentrancy_level; >> + >> + reentrancy_level++; >> >> s->istat1 |= LSI_ISTAT1_SRUN; >> again: >> - if (++insn_processed > LSI_MAX_INSN) { >> + if (++insn_processed > LSI_MAX_INSN || reentrancy_level > 8) { > > Why 8 and not some other number? It's just a random number which seemed to be a good compromise to me. We need at least 2 to be able to boot Linux. And if I add some debug printf and use it with the reproducer from the bug ticket, QEMU crashes after it reached level 1569. Anything in between should be OK at a first glance, but I'd take at least 3 or 4 to be one the safe side for some exotic use cases. 8 should really cover all real life cases, I guess. Or what would you suggest? Thomas
On Tue, 16 May 2023 at 10:50, Thomas Huth <thuth@redhat.com> wrote: > > On 16/05/2023 15.40, Stefan Hajnoczi wrote: > > On Tue, 16 May 2023 at 06:20, Thomas Huth <thuth@redhat.com> wrote: > >> > >> We cannot use the generic reentrancy guard in the LSI code, so > >> we have to manually prevent endless reentrancy here. The problematic > >> lsi_execute_script() function has already a way to detect whether > >> too many instructions have been executed - we just have to slightly > >> change the logic here that it also takes into account if the function > >> has been called too often in a reentrant way. > >> > >> The code in fuzz-lsi53c895a-test.c has been taken from an earlier > >> patch by Mauro Matteo Cascella. > >> > >> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1563 > >> Signed-off-by: Thomas Huth <thuth@redhat.com> > >> --- > >> hw/scsi/lsi53c895a.c | 7 ++++++- > >> tests/qtest/fuzz-lsi53c895a-test.c | 33 ++++++++++++++++++++++++++++++ > >> 2 files changed, 39 insertions(+), 1 deletion(-) > >> > >> diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c > >> index 048436352b..8b34199ab8 100644 > >> --- a/hw/scsi/lsi53c895a.c > >> +++ b/hw/scsi/lsi53c895a.c > >> @@ -1134,10 +1134,13 @@ static void lsi_execute_script(LSIState *s) > >> uint32_t addr, addr_high; > >> int opcode; > >> int insn_processed = 0; > >> + static int reentrancy_level; > >> + > >> + reentrancy_level++; > >> > >> s->istat1 |= LSI_ISTAT1_SRUN; > >> again: > >> - if (++insn_processed > LSI_MAX_INSN) { > >> + if (++insn_processed > LSI_MAX_INSN || reentrancy_level > 8) { > > > > Why 8 and not some other number? > > It's just a random number which seemed to be a good compromise to me. We > need at least 2 to be able to boot Linux. And if I add some debug printf and > use it with the reproducer from the bug ticket, QEMU crashes after it > reached level 1569. Anything in between should be OK at a first glance, but > I'd take at least 3 or 4 to be one the safe side for some exotic use cases. > 8 should really cover all real life cases, I guess. Or what would you suggest? Please add a comment explaining that 8 is an arbitrary number that should be sufficient for all legitimate guest drivers. Thanks, Stefan
© 2016 - 2024 Red Hat, Inc.