Re: [PATCH v8 19/21] fuzz: add virtio-net fuzz target
On 200205 1357, Darren Kenny wrote: > On Wed, Jan 29, 2020 at 05:34:27AM +, Bulekov, Alexander wrote: > > The virtio-net fuzz target feeds inputs to all three virtio-net > > virtqueues, and uses forking to avoid leaking state between fuzz runs. > > > > Signed-off-by: Alexander Bulekov > > --- > > tests/qtest/fuzz/Makefile.include | 1 + > > tests/qtest/fuzz/virtio_net_fuzz.c | 195 + > > 2 files changed, 196 insertions(+) > > create mode 100644 tests/qtest/fuzz/virtio_net_fuzz.c > > > > diff --git a/tests/qtest/fuzz/Makefile.include > > b/tests/qtest/fuzz/Makefile.include > > index 38b8cdd9f1..77385777ef 100644 > > --- a/tests/qtest/fuzz/Makefile.include > > +++ b/tests/qtest/fuzz/Makefile.include > > @@ -8,6 +8,7 @@ fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o > > > > # Targets > > fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o > > +fuzz-obj-y += tests/qtest/fuzz/virtio_net_fuzz.o > > > > FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest > > > > diff --git a/tests/qtest/fuzz/virtio_net_fuzz.c > > b/tests/qtest/fuzz/virtio_net_fuzz.c > > new file mode 100644 > > index 00..e4e3e50865 > > --- /dev/null > > +++ b/tests/qtest/fuzz/virtio_net_fuzz.c > > @@ -0,0 +1,195 @@ > > +/* > > + * virtio-net Fuzzing Target > > + * > > + * Copyright Red Hat Inc., 2019 > > + * > > + * Authors: > > + * Alexander Bulekov > > + * > > + * This work is licensed under the terms of the GNU GPL, version 2 or > > later. > > + * See the COPYING file in the top-level directory. > > + */ > > + > > +#include "qemu/osdep.h" > > + > > +#include "standard-headers/linux/virtio_config.h" > > +#include "tests/qtest/libqtest.h" > > +#include "tests/qtest/libqos/virtio-net.h" > > +#include "fuzz.h" > > +#include "fork_fuzz.h" > > +#include "qos_fuzz.h" > > + > > + > > +#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000) > > +#define QVIRTIO_RX_VQ 0 > > +#define QVIRTIO_TX_VQ 1 > > +#define QVIRTIO_CTRL_VQ 2 > > + > > +static int sockfds[2]; > > +static bool sockfds_initialized; > > + > > +static void virtio_net_fuzz_multi(QTestState *s, > > +const unsigned char *Data, size_t Size, bool check_used) > > +{ > > +typedef struct vq_action { > > +uint8_t queue; > > +uint8_t length; > > +uint8_t write; > > +uint8_t next; > > +uint8_t rx; > > +} vq_action; > > + > > +uint32_t free_head = 0; > > + > > +QGuestAllocator *t_alloc = fuzz_qos_alloc; > > + > > +QVirtioNet *net_if = fuzz_qos_obj; > > +QVirtioDevice *dev = net_if->vdev; > > +QVirtQueue *q; > > +vq_action vqa; > > +while (Size >= sizeof(vqa)) { > > +memcpy(&vqa, Data, sizeof(vqa)); > > +Data += sizeof(vqa); > > +Size -= sizeof(vqa); > > + > > +q = net_if->queues[vqa.queue % 3]; > > + > > +vqa.length = vqa.length >= Size ? Size : vqa.length; > > + > > +/* > > + * Only attempt to write incoming packets, when using the socket > > + * backend. Otherwise, always place the input on a virtqueue. > > + */ > > +if (vqa.rx && sockfds_initialized) { > > +write(sockfds[0], Data, vqa.length); > > +} else { > > +vqa.rx = 0; > > +uint64_t req_addr = guest_alloc(t_alloc, vqa.length); > > +/* > > + * If checking used ring, ensure that the fuzzer doesn't > > trigger > > + * trivial asserion failure on zero-zied buffer > > + */ > > +qtest_memwrite(s, req_addr, Data, vqa.length); > > + > > + > > +free_head = qvirtqueue_add(s, q, req_addr, vqa.length, > > +vqa.write, vqa.next); > > +qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , > > vqa.next); > > +qvirtqueue_kick(s, dev, q, free_head); > > +} > > + > > +/* > > + * normally, we could just use qvirtio_wait_used_elem, but since we > > + * must manually run the main-loop for all the bhs to run, we use > > + * this hack with flush_events(), to run the main_loop > > + */ > > +gint64 start_time = g_get_monotonic_time(); > > NIT: It's a little unclear using a for(;;) when in reality, for most > use-cases, other than virtio_net_fork_fuzz_check_used() it will > never really loop around. > > Maybe it would be clearer if the parts that never repeat were > outside the loop? e.g. > >/* Run the main loop */ >qtest_clock_step(s, 100); >flush_events(s); > >/* Poll the used vring only if we added to the TX or CTRL vq */ >while (check_used && !vqa.rx && q != net_if->queues[QVIRTIO_RX_VQ]) { >uint32_t got_desc_idx; >/* Input led to a virtio_error */ >if (dev->bus->get_status(dev) & VIRTIO_CONFIG_S_NEEDS_RESET) { >break; >} >if (dev->bus->get_queue_isr_status(dev, q) && >qvirtqueue_get_buf(s, q, &got_desc_idx, NULL)) { >g_assert_cmpint(got_des
Re: [PATCH v8 19/21] fuzz: add virtio-net fuzz target
On Wed, Jan 29, 2020 at 05:34:27AM +, Bulekov, Alexander wrote: The virtio-net fuzz target feeds inputs to all three virtio-net virtqueues, and uses forking to avoid leaking state between fuzz runs. Signed-off-by: Alexander Bulekov --- tests/qtest/fuzz/Makefile.include | 1 + tests/qtest/fuzz/virtio_net_fuzz.c | 195 + 2 files changed, 196 insertions(+) create mode 100644 tests/qtest/fuzz/virtio_net_fuzz.c diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include index 38b8cdd9f1..77385777ef 100644 --- a/tests/qtest/fuzz/Makefile.include +++ b/tests/qtest/fuzz/Makefile.include @@ -8,6 +8,7 @@ fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o # Targets fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o +fuzz-obj-y += tests/qtest/fuzz/virtio_net_fuzz.o FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest diff --git a/tests/qtest/fuzz/virtio_net_fuzz.c b/tests/qtest/fuzz/virtio_net_fuzz.c new file mode 100644 index 00..e4e3e50865 --- /dev/null +++ b/tests/qtest/fuzz/virtio_net_fuzz.c @@ -0,0 +1,195 @@ +/* + * virtio-net Fuzzing Target + * + * Copyright Red Hat Inc., 2019 + * + * Authors: + * Alexander Bulekov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "standard-headers/linux/virtio_config.h" +#include "tests/qtest/libqtest.h" +#include "tests/qtest/libqos/virtio-net.h" +#include "fuzz.h" +#include "fork_fuzz.h" +#include "qos_fuzz.h" + + +#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000) +#define QVIRTIO_RX_VQ 0 +#define QVIRTIO_TX_VQ 1 +#define QVIRTIO_CTRL_VQ 2 + +static int sockfds[2]; +static bool sockfds_initialized; + +static void virtio_net_fuzz_multi(QTestState *s, +const unsigned char *Data, size_t Size, bool check_used) +{ +typedef struct vq_action { +uint8_t queue; +uint8_t length; +uint8_t write; +uint8_t next; +uint8_t rx; +} vq_action; + +uint32_t free_head = 0; + +QGuestAllocator *t_alloc = fuzz_qos_alloc; + +QVirtioNet *net_if = fuzz_qos_obj; +QVirtioDevice *dev = net_if->vdev; +QVirtQueue *q; +vq_action vqa; +while (Size >= sizeof(vqa)) { +memcpy(&vqa, Data, sizeof(vqa)); +Data += sizeof(vqa); +Size -= sizeof(vqa); + +q = net_if->queues[vqa.queue % 3]; + +vqa.length = vqa.length >= Size ? Size : vqa.length; + +/* + * Only attempt to write incoming packets, when using the socket + * backend. Otherwise, always place the input on a virtqueue. + */ +if (vqa.rx && sockfds_initialized) { +write(sockfds[0], Data, vqa.length); +} else { +vqa.rx = 0; +uint64_t req_addr = guest_alloc(t_alloc, vqa.length); +/* + * If checking used ring, ensure that the fuzzer doesn't trigger + * trivial asserion failure on zero-zied buffer + */ +qtest_memwrite(s, req_addr, Data, vqa.length); + + +free_head = qvirtqueue_add(s, q, req_addr, vqa.length, +vqa.write, vqa.next); +qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next); +qvirtqueue_kick(s, dev, q, free_head); +} + +/* + * normally, we could just use qvirtio_wait_used_elem, but since we + * must manually run the main-loop for all the bhs to run, we use + * this hack with flush_events(), to run the main_loop + */ +gint64 start_time = g_get_monotonic_time(); NIT: It's a little unclear using a for(;;) when in reality, for most use-cases, other than virtio_net_fork_fuzz_check_used() it will never really loop around. Maybe it would be clearer if the parts that never repeat were outside the loop? e.g. /* Run the main loop */ qtest_clock_step(s, 100); flush_events(s); /* Poll the used vring only if we added to the TX or CTRL vq */ while (check_used && !vqa.rx && q != net_if->queues[QVIRTIO_RX_VQ]) { uint32_t got_desc_idx; /* Input led to a virtio_error */ if (dev->bus->get_status(dev) & VIRTIO_CONFIG_S_NEEDS_RESET) { break; } if (dev->bus->get_queue_isr_status(dev, q) && qvirtqueue_get_buf(s, q, &got_desc_idx, NULL)) { g_assert_cmpint(got_desc_idx, ==, free_head); break; } g_assert(g_get_monotonic_time() - start_time <= QVIRTIO_NET_TIMEOUT_US); /* Run the main loop */ qtest_clock_step(s, 100); flush_events(s); } Unless I'm reading it incorrectly that should do the same thing. Thanks, Darren. +for (;;) { +/* Run the main loop */ +qtest_clock_step(s, 100); +flush_events(s); +/* Poll the used vring only if we added to the TX or CTRL vq */ +if (check_used &
Re: [PATCH v8 19/21] fuzz: add virtio-net fuzz target
On Wed, Jan 29, 2020 at 05:34:27AM +, Bulekov, Alexander wrote: > The virtio-net fuzz target feeds inputs to all three virtio-net > virtqueues, and uses forking to avoid leaking state between fuzz runs. > > Signed-off-by: Alexander Bulekov > --- > tests/qtest/fuzz/Makefile.include | 1 + > tests/qtest/fuzz/virtio_net_fuzz.c | 195 + > 2 files changed, 196 insertions(+) > create mode 100644 tests/qtest/fuzz/virtio_net_fuzz.c Reviewed-by: Stefan Hajnoczi signature.asc Description: PGP signature
[PATCH v8 19/21] fuzz: add virtio-net fuzz target
The virtio-net fuzz target feeds inputs to all three virtio-net virtqueues, and uses forking to avoid leaking state between fuzz runs. Signed-off-by: Alexander Bulekov --- tests/qtest/fuzz/Makefile.include | 1 + tests/qtest/fuzz/virtio_net_fuzz.c | 195 + 2 files changed, 196 insertions(+) create mode 100644 tests/qtest/fuzz/virtio_net_fuzz.c diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include index 38b8cdd9f1..77385777ef 100644 --- a/tests/qtest/fuzz/Makefile.include +++ b/tests/qtest/fuzz/Makefile.include @@ -8,6 +8,7 @@ fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o # Targets fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o +fuzz-obj-y += tests/qtest/fuzz/virtio_net_fuzz.o FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest diff --git a/tests/qtest/fuzz/virtio_net_fuzz.c b/tests/qtest/fuzz/virtio_net_fuzz.c new file mode 100644 index 00..e4e3e50865 --- /dev/null +++ b/tests/qtest/fuzz/virtio_net_fuzz.c @@ -0,0 +1,195 @@ +/* + * virtio-net Fuzzing Target + * + * Copyright Red Hat Inc., 2019 + * + * Authors: + * Alexander Bulekov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "standard-headers/linux/virtio_config.h" +#include "tests/qtest/libqtest.h" +#include "tests/qtest/libqos/virtio-net.h" +#include "fuzz.h" +#include "fork_fuzz.h" +#include "qos_fuzz.h" + + +#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000) +#define QVIRTIO_RX_VQ 0 +#define QVIRTIO_TX_VQ 1 +#define QVIRTIO_CTRL_VQ 2 + +static int sockfds[2]; +static bool sockfds_initialized; + +static void virtio_net_fuzz_multi(QTestState *s, +const unsigned char *Data, size_t Size, bool check_used) +{ +typedef struct vq_action { +uint8_t queue; +uint8_t length; +uint8_t write; +uint8_t next; +uint8_t rx; +} vq_action; + +uint32_t free_head = 0; + +QGuestAllocator *t_alloc = fuzz_qos_alloc; + +QVirtioNet *net_if = fuzz_qos_obj; +QVirtioDevice *dev = net_if->vdev; +QVirtQueue *q; +vq_action vqa; +while (Size >= sizeof(vqa)) { +memcpy(&vqa, Data, sizeof(vqa)); +Data += sizeof(vqa); +Size -= sizeof(vqa); + +q = net_if->queues[vqa.queue % 3]; + +vqa.length = vqa.length >= Size ? Size : vqa.length; + +/* + * Only attempt to write incoming packets, when using the socket + * backend. Otherwise, always place the input on a virtqueue. + */ +if (vqa.rx && sockfds_initialized) { +write(sockfds[0], Data, vqa.length); +} else { +vqa.rx = 0; +uint64_t req_addr = guest_alloc(t_alloc, vqa.length); +/* + * If checking used ring, ensure that the fuzzer doesn't trigger + * trivial asserion failure on zero-zied buffer + */ +qtest_memwrite(s, req_addr, Data, vqa.length); + + +free_head = qvirtqueue_add(s, q, req_addr, vqa.length, +vqa.write, vqa.next); +qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next); +qvirtqueue_kick(s, dev, q, free_head); +} + +/* + * normally, we could just use qvirtio_wait_used_elem, but since we + * must manually run the main-loop for all the bhs to run, we use + * this hack with flush_events(), to run the main_loop + */ +gint64 start_time = g_get_monotonic_time(); +for (;;) { +/* Run the main loop */ +qtest_clock_step(s, 100); +flush_events(s); +/* Poll the used vring only if we added to the TX or CTRL vq */ +if (check_used && !vqa.rx && q != net_if->queues[QVIRTIO_RX_VQ]) { +uint32_t got_desc_idx; +/* Input led to a virtio_error */ +if (dev->bus->get_status(dev) & VIRTIO_CONFIG_S_NEEDS_RESET) { +break; +} +if (dev->bus->get_queue_isr_status(dev, q) && +qvirtqueue_get_buf(s, q, &got_desc_idx, NULL)) { +g_assert_cmpint(got_desc_idx, ==, free_head); +break; +} +g_assert(g_get_monotonic_time() - start_time + <= QVIRTIO_NET_TIMEOUT_US); +} else { +break; +} +} +Data += vqa.length; +Size -= vqa.length; +} +} + +static void virtio_net_fork_fuzz(QTestState *s, +const unsigned char *Data, size_t Size) +{ +if (fork() == 0) { +virtio_net_fuzz_multi(s, Data, Size, false); +flush_events(s); +_Exit(0); +} else { +wait(NULL); +} +} + +static void virtio_net_fork_fuzz_check_used(QTestState *s, +const unsigned char *Data, size_t Size) +{ +if (