/src/crosvm/fuzz/fuzz_targets/virtqueue_fuzzer.rs
Line | Count | Source |
1 | | // Copyright 2019 The ChromiumOS Authors |
2 | | // Use of this source code is governed by a BSD-style license that can be |
3 | | // found in the LICENSE file. |
4 | | |
5 | | #![cfg(not(test))] |
6 | | #![no_main] |
7 | | |
8 | | use std::io::Read; |
9 | | use std::io::Write; |
10 | | use std::mem::size_of; |
11 | | |
12 | | use base::Event; |
13 | | use crosvm_fuzz::fuzz_target; |
14 | | use crosvm_fuzz::rand::FuzzRng; |
15 | | use devices::virtio::Interrupt; |
16 | | use devices::virtio::QueueConfig; |
17 | | use devices::IrqLevelEvent; |
18 | | use rand::Rng; |
19 | | use vm_memory::GuestAddress; |
20 | | use vm_memory::GuestMemory; |
21 | | |
22 | | const MAX_QUEUE_SIZE: u16 = 256; |
23 | | const MEM_SIZE: u64 = 1024 * 1024; |
24 | | |
25 | | thread_local! { |
26 | | static GUEST_MEM: GuestMemory = GuestMemory::new(&[(GuestAddress(0), MEM_SIZE)]).unwrap(); |
27 | | } |
28 | | |
29 | | // These are taken from the virtio spec and can be used as a reference for the size calculations in |
30 | | // the fuzzer. |
31 | | #[repr(C, packed)] |
32 | | struct virtq_desc { |
33 | | addr: u64, |
34 | | len: u32, |
35 | | flags: u16, |
36 | | next: u16, |
37 | | } |
38 | | |
39 | | #[repr(C, packed)] |
40 | | struct virtq_avail { |
41 | | flags: u16, |
42 | | idx: u16, |
43 | | ring: [u16; MAX_QUEUE_SIZE as usize], |
44 | | used_event: u16, |
45 | | } |
46 | | |
47 | | #[repr(C, packed)] |
48 | | struct virtq_used_elem { |
49 | | id: u32, |
50 | | len: u32, |
51 | | } |
52 | | |
53 | | #[repr(C, packed)] |
54 | | struct virtq_used { |
55 | | flags: u16, |
56 | | idx: u16, |
57 | | ring: [virtq_used_elem; MAX_QUEUE_SIZE as usize], |
58 | | avail_event: u16, |
59 | | } |
60 | | |
61 | | fuzz_target!(|data: &[u8]| { |
62 | | let interrupt = Interrupt::new( |
63 | | IrqLevelEvent::new().unwrap(), |
64 | | None, // msix_config |
65 | | 0xFFFF, // VIRTIO_MSI_NO_VECTOR |
66 | | #[cfg(target_arch = "x86_64")] |
67 | | None, |
68 | | ); |
69 | | |
70 | | let mut q = QueueConfig::new(MAX_QUEUE_SIZE, 0); |
71 | | let mut rng = FuzzRng::new(data); |
72 | | q.set_size(rng.random()); |
73 | | |
74 | | // For each of {desc_table,avail_ring,used_ring} generate a random address that includes enough |
75 | | // space to hold the relevant struct with the largest possible queue size. |
76 | | let max_table_size = MAX_QUEUE_SIZE as u64 * size_of::<virtq_desc>() as u64; |
77 | | q.set_desc_table(GuestAddress(rng.random_range(0..MEM_SIZE - max_table_size))); |
78 | | q.set_avail_ring(GuestAddress( |
79 | | rng.random_range(0..MEM_SIZE - size_of::<virtq_avail>() as u64), |
80 | | )); |
81 | | q.set_used_ring(GuestAddress( |
82 | | rng.random_range(0..MEM_SIZE - size_of::<virtq_used>() as u64), |
83 | | )); |
84 | | q.set_ready(true); |
85 | | |
86 | 0 | GUEST_MEM.with(|mem| { |
87 | 0 | let mut q = if let Ok(q) = q.activate(mem, Event::new().unwrap(), interrupt) { |
88 | 0 | q |
89 | | } else { |
90 | 0 | return; |
91 | | }; |
92 | | |
93 | | // First zero out all of the memory. |
94 | 0 | let vs = mem |
95 | 0 | .get_slice_at_addr(GuestAddress(0), MEM_SIZE as usize) |
96 | 0 | .unwrap(); |
97 | 0 | vs.write_bytes(0); |
98 | | |
99 | | // Fill in the descriptor table. |
100 | 0 | let queue_size = q.size() as usize; |
101 | 0 | let mut buf = vec![0u8; queue_size * size_of::<virtq_desc>()]; |
102 | | |
103 | 0 | rng.fill(&mut buf[..]); |
104 | 0 | mem.write_all_at_addr(&buf[..], q.desc_table()).unwrap(); |
105 | | |
106 | | // Fill in the available ring. See the definition of virtq_avail above for the source of |
107 | | // these numbers. |
108 | 0 | let avail_size = 4 + (queue_size * 2) + 2; |
109 | 0 | buf.resize(avail_size, 0); |
110 | 0 | rng.fill(&mut buf[..]); |
111 | 0 | mem.write_all_at_addr(&buf[..], q.avail_ring()).unwrap(); |
112 | | |
113 | | // Fill in the used ring. See the definition of virtq_used above for the source of |
114 | | // these numbers. |
115 | 0 | let used_size = 4 + (queue_size * size_of::<virtq_used_elem>()) + 2; |
116 | 0 | buf.resize(used_size, 0); |
117 | 0 | rng.fill(&mut buf[..]); |
118 | 0 | mem.write_all_at_addr(&buf[..], q.used_ring()).unwrap(); |
119 | | |
120 | 0 | while let Some(mut avail_desc) = q.pop() { |
121 | 0 | // Read the entire readable portion of the buffer. |
122 | 0 | let mut read_buf = vec![0u8; avail_desc.reader.available_bytes()]; |
123 | 0 | avail_desc.reader.read_exact(&mut read_buf).unwrap(); |
124 | 0 |
|
125 | 0 | // Write the entire writable portion of the buffer. |
126 | 0 | let write_buf = vec![0u8; avail_desc.writer.available_bytes()]; |
127 | 0 | avail_desc.writer.write_all(&write_buf).unwrap(); |
128 | 0 |
|
129 | 0 | q.add_used(avail_desc); |
130 | 0 | } |
131 | 0 | }); |
132 | | }); |