summary refs log tree commit diff
path: root/fuzz/block_fuzzer.rs
blob: 6dd3cf23f9b83bfae9f2b9f03533ece29b24def7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#![no_main]

use std::fs::File;
use std::io::{Cursor, Read, Seek, SeekFrom};
use std::mem::size_of;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::panic;
use std::process;
use std::slice;
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;

use devices::virtio::{Block, Queue, VirtioDevice};
use sys_util::{EventFd, GuestAddress, GuestMemory, SharedMemory};

const MEM_SIZE: u64 = 256 * 1024 * 1024;
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
const QUEUE_SIZE: u16 = 16; // Max entries in the queue.
const CMD_SIZE: usize = 16; // Bytes in the command.

// Take the first 64 bits of data as an address and the next 64 bits as data to
// store there. The rest of the data is used as a qcow image.
#[export_name = "LLVMFuzzerTestOneInput"]
pub fn test_one_input(data: *const u8, size: usize) -> i32 {
    // We cannot unwind past ffi boundaries.
    panic::catch_unwind(|| {
        // Safe because the libfuzzer runtime will guarantee that `data` is at least
        // `size` bytes long and that it will be valid for the lifetime of this
        // function.
        let bytes = unsafe { slice::from_raw_parts(data, size) };
        let size_u64 = size_of::<u64>();
        let mem = GuestMemory::new(&[(GuestAddress(0), MEM_SIZE)]).unwrap();

        // The fuzz data is interpreted as:
        // starting index 8 bytes
        // command location 8 bytes
        // command 16 bytes
        // descriptors circular buffer 16 bytes * 3
        if bytes.len() < 4 * size_u64 {
            // Need an index to start.
            return;
        }

        let mut data_image = Cursor::new(bytes);

        let first_index = read_u64(&mut data_image);
        if first_index > MEM_SIZE / DESC_SIZE {
            return;
        }
        let first_offset = first_index * DESC_SIZE;
        if first_offset as usize + size_u64 > bytes.len() {
            return;
        }

        let command_addr = read_u64(&mut data_image);
        if command_addr > MEM_SIZE - CMD_SIZE as u64 {
            return;
        }
        if mem
            .write_all_at_addr(
                &bytes[2 * size_u64..(2 * size_u64) + CMD_SIZE],
                GuestAddress(command_addr as u64),
            )
            .is_err()
        {
            return;
        }

        data_image.seek(SeekFrom::Start(first_offset)).unwrap();
        let desc_table = read_u64(&mut data_image);

        if mem
            .write_all_at_addr(&bytes[32..], GuestAddress(desc_table as u64))
            .is_err()
        {
            return;
        }

        let mut q = Queue::new(QUEUE_SIZE);
        q.ready = true;
        q.size = QUEUE_SIZE / 2;
        q.max_size = QUEUE_SIZE;

        let queue_evts: Vec<EventFd> = vec![EventFd::new().unwrap()];
        let queue_fd = queue_evts[0].as_raw_fd();
        let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(queue_fd)) };

        let shm = SharedMemory::anon().unwrap();
        let disk_file: File = shm.into();
        let mut block = Block::new(Box::new(disk_file), false, None).unwrap();

        block.activate(
            mem,
            EventFd::new().unwrap(),
            EventFd::new().unwrap(),
            Arc::new(AtomicUsize::new(0)),
            vec![q],
            queue_evts,
        );

        queue_evt.write(77).unwrap(); // Rings the doorbell, any byte will do.
    })
    .err()
    .map(|_| process::abort());

    0
}

fn read_u64<T: Read>(readable: &mut T) -> u64 {
    let mut buf = [0u8; size_of::<u64>()];
    readable.read_exact(&mut buf[..]).unwrap();
    u64::from_le_bytes(buf)
}