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/libqos/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 int ignored
= write(sockfds
[0], Data
, vqa
.length
);
68 uint64_t req_addr
= guest_alloc(t_alloc
, vqa
.length
);
70 * If checking used ring, ensure that the fuzzer doesn't trigger
71 * trivial asserion failure on zero-zied buffer
73 qtest_memwrite(s
, req_addr
, Data
, vqa
.length
);
76 free_head
= qvirtqueue_add(s
, q
, req_addr
, vqa
.length
,
78 qvirtqueue_add(s
, q
, req_addr
, vqa
.length
, vqa
.write
, vqa
.next
);
79 qvirtqueue_kick(s
, dev
, q
, free_head
);
82 /* Run the main loop */
83 qtest_clock_step(s
, 100);
86 /* Wait on used descriptors */
87 if (check_used
&& !vqa
.rx
) {
88 gint64 start_time
= g_get_monotonic_time();
90 * normally, we could just use qvirtio_wait_used_elem, but since we
91 * must manually run the main-loop for all the bhs to run, we use
92 * this hack with flush_events(), to run the main_loop
94 while (!vqa
.rx
&& q
!= net_if
->queues
[QVIRTIO_RX_VQ
]) {
95 uint32_t got_desc_idx
;
96 /* Input led to a virtio_error */
97 if (dev
->bus
->get_status(dev
) & VIRTIO_CONFIG_S_NEEDS_RESET
) {
100 if (dev
->bus
->get_queue_isr_status(dev
, q
) &&
101 qvirtqueue_get_buf(s
, q
, &got_desc_idx
, NULL
)) {
102 g_assert_cmpint(got_desc_idx
, ==, free_head
);
105 g_assert(g_get_monotonic_time() - start_time
106 <= QVIRTIO_NET_TIMEOUT_US
);
108 /* Run the main loop */
109 qtest_clock_step(s
, 100);
118 static void virtio_net_fork_fuzz(QTestState
*s
,
119 const unsigned char *Data
, size_t Size
)
122 virtio_net_fuzz_multi(s
, Data
, Size
, false);
131 static void virtio_net_fork_fuzz_check_used(QTestState
*s
,
132 const unsigned char *Data
, size_t Size
)
135 virtio_net_fuzz_multi(s
, Data
, Size
, true);
144 static void virtio_net_pre_fuzz(QTestState
*s
)
150 static void *virtio_net_test_setup_socket(GString
*cmd_line
, void *arg
)
152 int ret
= socketpair(PF_UNIX
, SOCK_STREAM
, 0, sockfds
);
153 g_assert_cmpint(ret
, !=, -1);
154 fcntl(sockfds
[0], F_SETFL
, O_NONBLOCK
);
155 sockfds_initialized
= true;
156 g_string_append_printf(cmd_line
, " -netdev socket,fd=%d,id=hs0 ",
161 static void *virtio_net_test_setup_user(GString
*cmd_line
, void *arg
)
163 g_string_append_printf(cmd_line
, " -netdev user,id=hs0 ");
167 static void register_virtio_net_fuzz_targets(void)
169 fuzz_add_qos_target(&(FuzzTarget
){
170 .name
= "virtio-net-socket",
171 .description
= "Fuzz the virtio-net virtual queues. Fuzz incoming "
172 "traffic using the socket backend",
173 .pre_fuzz
= &virtio_net_pre_fuzz
,
174 .fuzz
= virtio_net_fork_fuzz
,},
176 &(QOSGraphTestOptions
){.before
= virtio_net_test_setup_socket
}
179 fuzz_add_qos_target(&(FuzzTarget
){
180 .name
= "virtio-net-socket-check-used",
181 .description
= "Fuzz the virtio-net virtual queues. Wait for the "
182 "descriptors to be used. Timeout may indicate improperly handled "
184 .pre_fuzz
= &virtio_net_pre_fuzz
,
185 .fuzz
= virtio_net_fork_fuzz_check_used
,},
187 &(QOSGraphTestOptions
){.before
= virtio_net_test_setup_socket
}
189 fuzz_add_qos_target(&(FuzzTarget
){
190 .name
= "virtio-net-slirp",
191 .description
= "Fuzz the virtio-net virtual queues with the slirp "
192 " backend. Warning: May result in network traffic emitted from the "
193 " process. Run in an isolated network environment.",
194 .pre_fuzz
= &virtio_net_pre_fuzz
,
195 .fuzz
= virtio_net_fork_fuzz
,},
197 &(QOSGraphTestOptions
){.before
= virtio_net_test_setup_user
}
201 fuzz_target_init(register_virtio_net_fuzz_targets
);