2 * virtio-net Fuzzing Target
4 * Copyright Red Hat Inc., 2019
7 * Alexander Bulekov <alxndr@bu.edu>
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
13 #include "qemu/osdep.h"
15 #include "standard-headers/linux/virtio_config.h"
16 #include "tests/qtest/libqtest.h"
17 #include "tests/qtest/libqos/virtio-net.h"
22 #define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000)
23 #define QVIRTIO_RX_VQ 0
24 #define QVIRTIO_TX_VQ 1
25 #define QVIRTIO_CTRL_VQ 2
27 static int sockfds
[2];
28 static bool sockfds_initialized
;
30 static void virtio_net_fuzz_multi(QTestState
*s
,
31 const unsigned char *Data
, size_t Size
, bool check_used
)
33 typedef struct vq_action
{
41 uint32_t free_head
= 0;
43 QGuestAllocator
*t_alloc
= fuzz_qos_alloc
;
45 QVirtioNet
*net_if
= fuzz_qos_obj
;
46 QVirtioDevice
*dev
= net_if
->vdev
;
49 while (Size
>= sizeof(vqa
)) {
50 memcpy(&vqa
, Data
, sizeof(vqa
));
54 q
= net_if
->queues
[vqa
.queue
% 3];
56 vqa
.length
= vqa
.length
>= Size
? Size
: vqa
.length
;
59 * Only attempt to write incoming packets, when using the socket
60 * backend. Otherwise, always place the input on a virtqueue.
62 if (vqa
.rx
&& sockfds_initialized
) {
63 int ignored
= write(sockfds
[0], Data
, vqa
.length
);
67 uint64_t req_addr
= guest_alloc(t_alloc
, vqa
.length
);
69 * If checking used ring, ensure that the fuzzer doesn't trigger
70 * trivial asserion failure on zero-zied buffer
72 qtest_memwrite(s
, req_addr
, Data
, vqa
.length
);
75 free_head
= qvirtqueue_add(s
, q
, req_addr
, vqa
.length
,
77 qvirtqueue_add(s
, q
, req_addr
, vqa
.length
, vqa
.write
, vqa
.next
);
78 qvirtqueue_kick(s
, dev
, q
, free_head
);
81 /* Run the main loop */
82 qtest_clock_step(s
, 100);
85 /* Wait on used descriptors */
86 if (check_used
&& !vqa
.rx
) {
87 gint64 start_time
= g_get_monotonic_time();
89 * normally, we could just use qvirtio_wait_used_elem, but since we
90 * must manually run the main-loop for all the bhs to run, we use
91 * this hack with flush_events(), to run the main_loop
93 while (!vqa
.rx
&& q
!= net_if
->queues
[QVIRTIO_RX_VQ
]) {
94 uint32_t got_desc_idx
;
95 /* Input led to a virtio_error */
96 if (dev
->bus
->get_status(dev
) & VIRTIO_CONFIG_S_NEEDS_RESET
) {
99 if (dev
->bus
->get_queue_isr_status(dev
, q
) &&
100 qvirtqueue_get_buf(s
, q
, &got_desc_idx
, NULL
)) {
101 g_assert_cmpint(got_desc_idx
, ==, free_head
);
104 g_assert(g_get_monotonic_time() - start_time
105 <= QVIRTIO_NET_TIMEOUT_US
);
107 /* Run the main loop */
108 qtest_clock_step(s
, 100);
118 static void virtio_net_fuzz_check_used(QTestState
*s
,
119 const unsigned char *Data
, size_t Size
)
121 virtio_net_fuzz_multi(s
, Data
, Size
, true);
126 static void virtio_net_pre_fuzz(QTestState
*s
)
131 static void *virtio_net_test_setup_socket(GString
*cmd_line
, void *arg
)
133 int ret
= socketpair(PF_UNIX
, SOCK_STREAM
, 0, sockfds
);
134 g_assert_cmpint(ret
, !=, -1);
135 g_unix_set_fd_nonblocking(sockfds
[0], true, NULL
);
136 sockfds_initialized
= true;
137 g_string_append_printf(cmd_line
, " -netdev socket,fd=%d,id=hs0 ",
142 static void register_virtio_net_fuzz_targets(void)
145 fuzz_add_qos_target(&(FuzzTarget
){
146 .name
= "virtio-net-socket-check-used",
147 .description
= "Fuzz the virtio-net virtual queues. Wait for the "
148 "descriptors to be used. Timeout may indicate improperly handled "
150 .pre_fuzz
= &virtio_net_pre_fuzz
,
151 .fuzz
= virtio_net_fuzz_check_used
,},
153 &(QOSGraphTestOptions
){.before
= virtio_net_test_setup_socket
}
157 fuzz_target_init(register_virtio_net_fuzz_targets
);