Message ID | 20200819061110.1320568-4-alxndr@bu.edu |
---|---|
State | New |
Headers | show |
Series | Add a General Virtual Device Fuzzer | expand |
On Wednesday, 2020-08-19 at 02:10:58 -04, Alexander Bulekov wrote: > This patch compares TYPE_PCI_DEVICE objects against the user-provided > matching pattern. If there is a match, we use some hacks and leverage > QOS to map each possible BAR for that device. Now fuzzed inputs might be > converted to pci_read/write commands which target specific. This means > that we can fuzz a particular device's PCI configuration space, > > Signed-off-by: Alexander Bulekov <alxndr@bu.edu> Reviewed-by: Darren Kenny <darren.kenny@oracle.com> Thanks, Darren. > --- > tests/qtest/fuzz/general_fuzz.c | 83 +++++++++++++++++++++++++++++++++ > 1 file changed, 83 insertions(+) > > diff --git a/tests/qtest/fuzz/general_fuzz.c b/tests/qtest/fuzz/general_fuzz.c > index 01bcb029b1..17b572a439 100644 > --- a/tests/qtest/fuzz/general_fuzz.c > +++ b/tests/qtest/fuzz/general_fuzz.c > @@ -24,6 +24,7 @@ > #include "exec/ramblock.h" > #include "exec/address-spaces.h" > #include "hw/qdev-core.h" > +#include "hw/pci/pci.h" > > /* > * SEPARATOR is used to separate "operations" in the fuzz input > @@ -35,12 +36,17 @@ enum cmds{ > OP_OUT, > OP_READ, > OP_WRITE, > + OP_PCI_READ, > + OP_PCI_WRITE, > OP_CLOCK_STEP, > }; > > #define DEFAULT_TIMEOUT_US 100000 > #define USEC_IN_SEC 100000000 > > +#define PCI_HOST_BRIDGE_CFG 0xcf8 > +#define PCI_HOST_BRIDGE_DATA 0xcfc > + > typedef struct { > size_t addr; > size_t len; /* The number of bytes until the end of the I/O region */ > @@ -52,6 +58,8 @@ static useconds_t timeout = 100000; > * user for fuzzing. > */ > static GPtrArray *fuzzable_memoryregions; > +static GPtrArray *fuzzable_pci_devices; > + > /* > * Here we want to convert a fuzzer-provided [io-region-index, offset] to > * a physical address. To do this, we iterate over all of the matched > @@ -283,6 +291,65 @@ static void op_write(QTestState *s, const unsigned char * data, size_t len) > break; > } > } > +static void op_pci_read(QTestState *s, const unsigned char * data, size_t len) > +{ > + enum Sizes {Byte, Word, Long, end_sizes}; > + struct { > + uint8_t size; > + uint8_t base; > + uint8_t offset; > + } a; > + if (len < sizeof(a) || fuzzable_pci_devices->len == 0) { > + return; > + } > + memcpy(&a, data, sizeof(a)); > + PCIDevice *dev = g_ptr_array_index(fuzzable_pci_devices, > + a.base % fuzzable_pci_devices->len); > + int devfn = dev->devfn; > + qtest_outl(s, PCI_HOST_BRIDGE_CFG, (1U << 31) | (devfn << 8) | a.offset); > + switch (a.size %= end_sizes) { > + case Byte: > + qtest_inb(s, PCI_HOST_BRIDGE_DATA); > + break; > + case Word: > + qtest_inw(s, PCI_HOST_BRIDGE_DATA); > + break; > + case Long: > + qtest_inl(s, PCI_HOST_BRIDGE_DATA); > + break; > + } > +} > + > +static void op_pci_write(QTestState *s, const unsigned char * data, size_t len) > +{ > + enum Sizes {Byte, Word, Long, end_sizes}; > + struct { > + uint8_t size; > + uint8_t base; > + uint8_t offset; > + uint32_t value; > + } a; > + if (len < sizeof(a) || fuzzable_pci_devices->len == 0) { > + return; > + } > + memcpy(&a, data, sizeof(a)); > + PCIDevice *dev = g_ptr_array_index(fuzzable_pci_devices, > + a.base % fuzzable_pci_devices->len); > + int devfn = dev->devfn; > + qtest_outl(s, PCI_HOST_BRIDGE_CFG, (1U << 31) | (devfn << 8) | a.offset); > + switch (a.size %= end_sizes) { > + case Byte: > + qtest_outb(s, PCI_HOST_BRIDGE_DATA, a.value & 0xFF); > + break; > + case Word: > + qtest_outw(s, PCI_HOST_BRIDGE_DATA, a.value & 0xFFFF); > + break; > + case Long: > + qtest_outl(s, PCI_HOST_BRIDGE_DATA, a.value & 0xFFFFFFFF); > + break; > + } > +} > + > static void op_clock_step(QTestState *s, const unsigned char *data, size_t len) > { > qtest_clock_step_next(s); > @@ -327,6 +394,8 @@ static void general_fuzz(QTestState *s, const unsigned char *Data, size_t Size) > [OP_OUT] = op_out, > [OP_READ] = op_read, > [OP_WRITE] = op_write, > + [OP_PCI_READ] = op_pci_read, > + [OP_PCI_WRITE] = op_pci_write, > [OP_CLOCK_STEP] = op_clock_step, > }; > const unsigned char *cmd = Data; > @@ -418,6 +487,19 @@ static int locate_fuzz_objects(Object *child, void *opaque) > if (g_pattern_match_simple(pattern, object_get_typename(child))) { > /* Find and save ptrs to any child MemoryRegions */ > object_child_foreach_recursive(child, locate_fuzz_memory_regions, NULL); > + > + /* > + * We matched an object. If its a PCI device, store a pointer to it so > + * we can map BARs and fuzz its config space. > + */ > + if (object_dynamic_cast(OBJECT(child), TYPE_PCI_DEVICE)) { > + /* > + * Don't want duplicate pointers to the same PCIDevice, so remove > + * copies of the pointer, before adding it. > + */ > + g_ptr_array_remove_fast(fuzzable_pci_devices, PCI_DEVICE(child)); > + g_ptr_array_add(fuzzable_pci_devices, PCI_DEVICE(child)); > + } > } else if (object_dynamic_cast(OBJECT(child), TYPE_MEMORY_REGION)) { > if (g_pattern_match_simple(pattern, > object_get_canonical_path_component(child))) { > @@ -445,6 +527,7 @@ static void general_pre_fuzz(QTestState *s) > } > > fuzzable_memoryregions = g_ptr_array_new(); > + fuzzable_pci_devices = g_ptr_array_new(); > char **result = g_strsplit (getenv("QEMU_FUZZ_OBJECTS"), " ", -1); > for (int i = 0; result[i] != NULL; i++) { > printf("Matching objects by name %s\n", result[i]); > -- > 2.27.0
diff --git a/tests/qtest/fuzz/general_fuzz.c b/tests/qtest/fuzz/general_fuzz.c index 01bcb029b1..17b572a439 100644 --- a/tests/qtest/fuzz/general_fuzz.c +++ b/tests/qtest/fuzz/general_fuzz.c @@ -24,6 +24,7 @@ #include "exec/ramblock.h" #include "exec/address-spaces.h" #include "hw/qdev-core.h" +#include "hw/pci/pci.h" /* * SEPARATOR is used to separate "operations" in the fuzz input @@ -35,12 +36,17 @@ enum cmds{ OP_OUT, OP_READ, OP_WRITE, + OP_PCI_READ, + OP_PCI_WRITE, OP_CLOCK_STEP, }; #define DEFAULT_TIMEOUT_US 100000 #define USEC_IN_SEC 100000000 +#define PCI_HOST_BRIDGE_CFG 0xcf8 +#define PCI_HOST_BRIDGE_DATA 0xcfc + typedef struct { size_t addr; size_t len; /* The number of bytes until the end of the I/O region */ @@ -52,6 +58,8 @@ static useconds_t timeout = 100000; * user for fuzzing. */ static GPtrArray *fuzzable_memoryregions; +static GPtrArray *fuzzable_pci_devices; + /* * Here we want to convert a fuzzer-provided [io-region-index, offset] to * a physical address. To do this, we iterate over all of the matched @@ -283,6 +291,65 @@ static void op_write(QTestState *s, const unsigned char * data, size_t len) break; } } +static void op_pci_read(QTestState *s, const unsigned char * data, size_t len) +{ + enum Sizes {Byte, Word, Long, end_sizes}; + struct { + uint8_t size; + uint8_t base; + uint8_t offset; + } a; + if (len < sizeof(a) || fuzzable_pci_devices->len == 0) { + return; + } + memcpy(&a, data, sizeof(a)); + PCIDevice *dev = g_ptr_array_index(fuzzable_pci_devices, + a.base % fuzzable_pci_devices->len); + int devfn = dev->devfn; + qtest_outl(s, PCI_HOST_BRIDGE_CFG, (1U << 31) | (devfn << 8) | a.offset); + switch (a.size %= end_sizes) { + case Byte: + qtest_inb(s, PCI_HOST_BRIDGE_DATA); + break; + case Word: + qtest_inw(s, PCI_HOST_BRIDGE_DATA); + break; + case Long: + qtest_inl(s, PCI_HOST_BRIDGE_DATA); + break; + } +} + +static void op_pci_write(QTestState *s, const unsigned char * data, size_t len) +{ + enum Sizes {Byte, Word, Long, end_sizes}; + struct { + uint8_t size; + uint8_t base; + uint8_t offset; + uint32_t value; + } a; + if (len < sizeof(a) || fuzzable_pci_devices->len == 0) { + return; + } + memcpy(&a, data, sizeof(a)); + PCIDevice *dev = g_ptr_array_index(fuzzable_pci_devices, + a.base % fuzzable_pci_devices->len); + int devfn = dev->devfn; + qtest_outl(s, PCI_HOST_BRIDGE_CFG, (1U << 31) | (devfn << 8) | a.offset); + switch (a.size %= end_sizes) { + case Byte: + qtest_outb(s, PCI_HOST_BRIDGE_DATA, a.value & 0xFF); + break; + case Word: + qtest_outw(s, PCI_HOST_BRIDGE_DATA, a.value & 0xFFFF); + break; + case Long: + qtest_outl(s, PCI_HOST_BRIDGE_DATA, a.value & 0xFFFFFFFF); + break; + } +} + static void op_clock_step(QTestState *s, const unsigned char *data, size_t len) { qtest_clock_step_next(s); @@ -327,6 +394,8 @@ static void general_fuzz(QTestState *s, const unsigned char *Data, size_t Size) [OP_OUT] = op_out, [OP_READ] = op_read, [OP_WRITE] = op_write, + [OP_PCI_READ] = op_pci_read, + [OP_PCI_WRITE] = op_pci_write, [OP_CLOCK_STEP] = op_clock_step, }; const unsigned char *cmd = Data; @@ -418,6 +487,19 @@ static int locate_fuzz_objects(Object *child, void *opaque) if (g_pattern_match_simple(pattern, object_get_typename(child))) { /* Find and save ptrs to any child MemoryRegions */ object_child_foreach_recursive(child, locate_fuzz_memory_regions, NULL); + + /* + * We matched an object. If its a PCI device, store a pointer to it so + * we can map BARs and fuzz its config space. + */ + if (object_dynamic_cast(OBJECT(child), TYPE_PCI_DEVICE)) { + /* + * Don't want duplicate pointers to the same PCIDevice, so remove + * copies of the pointer, before adding it. + */ + g_ptr_array_remove_fast(fuzzable_pci_devices, PCI_DEVICE(child)); + g_ptr_array_add(fuzzable_pci_devices, PCI_DEVICE(child)); + } } else if (object_dynamic_cast(OBJECT(child), TYPE_MEMORY_REGION)) { if (g_pattern_match_simple(pattern, object_get_canonical_path_component(child))) { @@ -445,6 +527,7 @@ static void general_pre_fuzz(QTestState *s) } fuzzable_memoryregions = g_ptr_array_new(); + fuzzable_pci_devices = g_ptr_array_new(); char **result = g_strsplit (getenv("QEMU_FUZZ_OBJECTS"), " ", -1); for (int i = 0; result[i] != NULL; i++) { printf("Matching objects by name %s\n", result[i]);
This patch compares TYPE_PCI_DEVICE objects against the user-provided matching pattern. If there is a match, we use some hacks and leverage QOS to map each possible BAR for that device. Now fuzzed inputs might be converted to pci_read/write commands which target specific. This means that we can fuzz a particular device's PCI configuration space, Signed-off-by: Alexander Bulekov <alxndr@bu.edu> --- tests/qtest/fuzz/general_fuzz.c | 83 +++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+)