1 /* 2 * virtio-net Fuzzing Target 3 * 4 * Copyright Red Hat Inc., 2019 5 * 6 * Authors: 7 * Alexander Bulekov <alxndr@bu.edu> 8 * 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. 11 */ 12 13 #include "qemu/osdep.h" 14 15 #include "standard-headers/linux/virtio_config.h" 16 #include "tests/qtest/libqtest.h" 17 #include "tests/qtest/libqos/virtio-net.h" 18 #include "fuzz.h" 19 #include "qos_fuzz.h" 20 21 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 26 27 static int sockfds[2]; 28 static bool sockfds_initialized; 29 30 static void virtio_net_fuzz_multi(QTestState *s, 31 const unsigned char *Data, size_t Size, bool check_used) 32 { 33 typedef struct vq_action { 34 uint8_t queue; 35 uint8_t length; 36 uint8_t write; 37 uint8_t next; 38 uint8_t rx; 39 } vq_action; 40 41 uint32_t free_head = 0; 42 43 QGuestAllocator *t_alloc = fuzz_qos_alloc; 44 45 QVirtioNet *net_if = fuzz_qos_obj; 46 QVirtioDevice *dev = net_if->vdev; 47 QVirtQueue *q; 48 vq_action vqa; 49 while (Size >= sizeof(vqa)) { 50 memcpy(&vqa, Data, sizeof(vqa)); 51 Data += sizeof(vqa); 52 Size -= sizeof(vqa); 53 54 q = net_if->queues[vqa.queue % 3]; 55 56 vqa.length = vqa.length >= Size ? Size : vqa.length; 57 58 /* 59 * Only attempt to write incoming packets, when using the socket 60 * backend. Otherwise, always place the input on a virtqueue. 61 */ 62 if (vqa.rx && sockfds_initialized) { 63 int ignored = write(sockfds[0], Data, vqa.length); 64 (void) ignored; 65 } else { 66 vqa.rx = 0; 67 uint64_t req_addr = guest_alloc(t_alloc, vqa.length); 68 /* 69 * If checking used ring, ensure that the fuzzer doesn't trigger 70 * trivial asserion failure on zero-zied buffer 71 */ 72 qtest_memwrite(s, req_addr, Data, vqa.length); 73 74 75 free_head = qvirtqueue_add(s, q, req_addr, vqa.length, 76 vqa.write, vqa.next); 77 qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next); 78 qvirtqueue_kick(s, dev, q, free_head); 79 } 80 81 /* Run the main loop */ 82 qtest_clock_step(s, 100); 83 flush_events(s); 84 85 /* Wait on used descriptors */ 86 if (check_used && !vqa.rx) { 87 gint64 start_time = g_get_monotonic_time(); 88 /* 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 92 */ 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) { 97 break; 98 } 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); 102 break; 103 } 104 g_assert(g_get_monotonic_time() - start_time 105 <= QVIRTIO_NET_TIMEOUT_US); 106 107 /* Run the main loop */ 108 qtest_clock_step(s, 100); 109 flush_events(s); 110 } 111 } 112 Data += vqa.length; 113 Size -= vqa.length; 114 } 115 } 116 117 118 static void virtio_net_fuzz_check_used(QTestState *s, 119 const unsigned char *Data, size_t Size) 120 { 121 virtio_net_fuzz_multi(s, Data, Size, true); 122 flush_events(s); 123 fuzz_reset(s); 124 } 125 126 static void virtio_net_pre_fuzz(QTestState *s) 127 { 128 qos_init_path(s); 129 } 130 131 static void *virtio_net_test_setup_socket(GString *cmd_line, void *arg) 132 { 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 ", 138 sockfds[1]); 139 return arg; 140 } 141 142 static void register_virtio_net_fuzz_targets(void) 143 { 144 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 " 149 "input", 150 .pre_fuzz = &virtio_net_pre_fuzz, 151 .fuzz = virtio_net_fuzz_check_used,}, 152 "virtio-net", 153 &(QOSGraphTestOptions){.before = virtio_net_test_setup_socket} 154 ); 155 } 156 157 fuzz_target_init(register_virtio_net_fuzz_targets); 158