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"
19 #include "fork_fuzz.h"
23 #define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000)
24 #define QVIRTIO_RX_VQ 0
25 #define QVIRTIO_TX_VQ 1
26 #define QVIRTIO_CTRL_VQ 2
28 static int sockfds
[2];
29 static bool sockfds_initialized
;
31 static void virtio_net_fuzz_multi(QTestState
*s
,
32 const unsigned char *Data
, size_t Size
, bool check_used
)
34 typedef struct vq_action
{
42 uint32_t free_head
= 0;
44 QGuestAllocator
*t_alloc
= fuzz_qos_alloc
;
46 QVirtioNet
*net_if
= fuzz_qos_obj
;
47 QVirtioDevice
*dev
= net_if
->vdev
;
50 while (Size
>= sizeof(vqa
)) {
51 memcpy(&vqa
, Data
, sizeof(vqa
));
55 q
= net_if
->queues
[vqa
.queue
% 3];
57 vqa
.length
= vqa
.length
>= Size
? Size
: vqa
.length
;
60 * Only attempt to write incoming packets, when using the socket
61 * backend. Otherwise, always place the input on a virtqueue.
63 if (vqa
.rx
&& sockfds_initialized
) {
64 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);
117 static void virtio_net_fork_fuzz(QTestState
*s
,
118 const unsigned char *Data
, size_t Size
)
121 virtio_net_fuzz_multi(s
, Data
, Size
, false);
130 static void virtio_net_fork_fuzz_check_used(QTestState
*s
,
131 const unsigned char *Data
, size_t Size
)
134 virtio_net_fuzz_multi(s
, Data
, Size
, true);
143 static void virtio_net_pre_fuzz(QTestState
*s
)
149 static void *virtio_net_test_setup_socket(GString
*cmd_line
, void *arg
)
151 int ret
= socketpair(PF_UNIX
, SOCK_STREAM
, 0, sockfds
);
152 g_assert_cmpint(ret
, !=, -1);
153 fcntl(sockfds
[0], F_SETFL
, O_NONBLOCK
);
154 sockfds_initialized
= true;
155 g_string_append_printf(cmd_line
, " -netdev socket,fd=%d,id=hs0 ",
160 static void *virtio_net_test_setup_user(GString
*cmd_line
, void *arg
)
162 g_string_append_printf(cmd_line
, " -netdev user,id=hs0 ");
166 static void register_virtio_net_fuzz_targets(void)
168 fuzz_add_qos_target(&(FuzzTarget
){
169 .name
= "virtio-net-socket",
170 .description
= "Fuzz the virtio-net virtual queues. Fuzz incoming "
171 "traffic using the socket backend",
172 .pre_fuzz
= &virtio_net_pre_fuzz
,
173 .fuzz
= virtio_net_fork_fuzz
,},
175 &(QOSGraphTestOptions
){.before
= virtio_net_test_setup_socket
}
178 fuzz_add_qos_target(&(FuzzTarget
){
179 .name
= "virtio-net-socket-check-used",
180 .description
= "Fuzz the virtio-net virtual queues. Wait for the "
181 "descriptors to be used. Timeout may indicate improperly handled "
183 .pre_fuzz
= &virtio_net_pre_fuzz
,
184 .fuzz
= virtio_net_fork_fuzz_check_used
,},
186 &(QOSGraphTestOptions
){.before
= virtio_net_test_setup_socket
}
188 fuzz_add_qos_target(&(FuzzTarget
){
189 .name
= "virtio-net-slirp",
190 .description
= "Fuzz the virtio-net virtual queues with the slirp "
191 " backend. Warning: May result in network traffic emitted from the "
192 " process. Run in an isolated network environment.",
193 .pre_fuzz
= &virtio_net_pre_fuzz
,
194 .fuzz
= virtio_net_fork_fuzz
,},
196 &(QOSGraphTestOptions
){.before
= virtio_net_test_setup_user
}
200 fuzz_target_init(register_virtio_net_fuzz_targets
);