Skip to content

Commit e28a1e0

Browse files
authored
Fix soundness issues with MMIO and shared memory (#18)
* First cut at sound volatile MMIO. * Use new volatile module for blk too. * Backwards compatibility for old rustc in example. * Document volatile module. * Use volatile module for remaining devices. * UsedRing is only read by the driver. * No need to volatile for queue. The queue is just shared memory, not MMIO memory, so there is no need to use volatile reads and writes. * Add fence before reading used ring and after writing available ring.
1 parent 62f3e4f commit e28a1e0

File tree

10 files changed

+394
-213
lines changed

10 files changed

+394
-213
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,5 @@ description = "VirtIO guest drivers."
88
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
99

1010
[dependencies]
11-
volatile = "0.3"
1211
log = "0.4"
1312
bitflags = "1.3"

src/blk.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
use super::*;
22
use crate::queue::VirtQueue;
33
use crate::transport::Transport;
4+
use crate::volatile::{volread, Volatile};
45
use bitflags::*;
56
use core::hint::spin_loop;
67
use log::*;
7-
use volatile::Volatile;
88

99
/// The virtio block device is a simple virtual block device (ie. disk).
1010
///
1111
/// Read and write requests (and other exotic requests) are placed in the queue,
1212
/// and serviced (probably out of order) by the device except where noted.
13-
pub struct VirtIOBlk<'a, H: Hal, T: Transport> {
13+
pub struct VirtIOBlk<H: Hal, T: Transport> {
1414
transport: T,
15-
queue: VirtQueue<'a, H>,
15+
queue: VirtQueue<H>,
1616
capacity: usize,
1717
}
1818

19-
impl<H: Hal, T: Transport> VirtIOBlk<'_, H, T> {
19+
impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
2020
/// Create a new VirtIO-Blk driver.
2121
pub fn new(mut transport: T) -> Result<Self> {
2222
transport.begin_init(|features| {
@@ -28,21 +28,19 @@ impl<H: Hal, T: Transport> VirtIOBlk<'_, H, T> {
2828
});
2929

3030
// read configuration space
31-
let config_space = transport.config_space().cast::<BlkConfig>();
32-
let config = unsafe { config_space.as_ref() };
31+
let config = transport.config_space().cast::<BlkConfig>();
3332
info!("config: {:?}", config);
34-
info!(
35-
"found a block device of size {}KB",
36-
config.capacity.read() / 2
37-
);
33+
// Safe because config is a valid pointer to the device configuration space.
34+
let capacity = unsafe { volread!(config, capacity) };
35+
info!("found a block device of size {}KB", capacity / 2);
3836

3937
let queue = VirtQueue::new(&mut transport, 0, 16)?;
4038
transport.finish_init();
4139

4240
Ok(VirtIOBlk {
4341
transport,
4442
queue,
45-
capacity: config.capacity.read() as usize,
43+
capacity: capacity as usize,
4644
})
4745
}
4846

src/console.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use super::*;
22
use crate::queue::VirtQueue;
33
use crate::transport::Transport;
4+
use crate::volatile::{ReadOnly, WriteOnly};
45
use bitflags::*;
56
use core::{fmt, hint::spin_loop};
67
use log::*;
7-
use volatile::{ReadOnly, WriteOnly};
88

99
const QUEUE_RECEIVEQ_PORT_0: usize = 0;
1010
const QUEUE_TRANSMITQ_PORT_0: usize = 1;
@@ -14,8 +14,8 @@ const QUEUE_SIZE: u16 = 2;
1414
/// Emergency and cols/rows unimplemented.
1515
pub struct VirtIOConsole<'a, H: Hal, T: Transport> {
1616
transport: T,
17-
receiveq: VirtQueue<'a, H>,
18-
transmitq: VirtQueue<'a, H>,
17+
receiveq: VirtQueue<H>,
18+
transmitq: VirtQueue<H>,
1919
queue_buf_dma: DMA<H>,
2020
queue_buf_rx: &'a mut [u8],
2121
cursor: usize,
@@ -161,7 +161,7 @@ mod tests {
161161
cols: ReadOnly::new(0),
162162
rows: ReadOnly::new(0),
163163
max_nr_ports: ReadOnly::new(0),
164-
emerg_wr: WriteOnly::new(0),
164+
emerg_wr: WriteOnly::default(),
165165
};
166166
let state = Arc::new(Mutex::new(State {
167167
status: DeviceStatus::empty(),

src/gpu.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use super::*;
22
use crate::queue::VirtQueue;
33
use crate::transport::Transport;
4+
use crate::volatile::{ReadOnly, Volatile, WriteOnly};
45
use bitflags::*;
56
use core::{fmt, hint::spin_loop};
67
use log::*;
7-
use volatile::{ReadOnly, Volatile, WriteOnly};
88

99
/// A virtio based graphics adapter.
1010
///
@@ -21,9 +21,9 @@ pub struct VirtIOGpu<'a, H: Hal, T: Transport> {
2121
/// DMA area of cursor image buffer.
2222
cursor_buffer_dma: Option<DMA<H>>,
2323
/// Queue for sending control commands.
24-
control_queue: VirtQueue<'a, H>,
24+
control_queue: VirtQueue<H>,
2525
/// Queue for sending cursor commands.
26-
cursor_queue: VirtQueue<'a, H>,
26+
cursor_queue: VirtQueue<H>,
2727
/// Queue buffer DMA
2828
queue_buf_dma: DMA<H>,
2929
/// Send buffer for queue.

src/input.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
use super::*;
22
use crate::transport::Transport;
3+
use crate::volatile::{volread, volwrite, ReadOnly, WriteOnly};
34
use alloc::boxed::Box;
45
use bitflags::*;
56
use log::*;
6-
use volatile::{ReadOnly, WriteOnly};
77

88
/// Virtual human interface devices such as keyboards, mice and tablets.
99
///
1010
/// An instance of the virtio device represents one such input device.
1111
/// Device behavior mirrors that of the evdev layer in Linux,
1212
/// making pass-through implementations on top of evdev easy.
13-
pub struct VirtIOInput<'a, H: Hal, T: Transport> {
13+
pub struct VirtIOInput<H: Hal, T: Transport> {
1414
transport: T,
15-
event_queue: VirtQueue<'a, H>,
16-
status_queue: VirtQueue<'a, H>,
15+
event_queue: VirtQueue<H>,
16+
status_queue: VirtQueue<H>,
1717
event_buf: Box<[InputEvent; 32]>,
1818
}
1919

20-
impl<H: Hal, T: Transport> VirtIOInput<'_, H, T> {
20+
impl<H: Hal, T: Transport> VirtIOInput<H, T> {
2121
/// Create a new VirtIO-Input driver.
2222
pub fn new(mut transport: T) -> Result<Self> {
2323
let mut event_buf = Box::new([InputEvent::default(); QUEUE_SIZE]);
@@ -75,12 +75,16 @@ impl<H: Hal, T: Transport> VirtIOInput<'_, H, T> {
7575
subsel: u8,
7676
out: &mut [u8],
7777
) -> u8 {
78-
let mut config_space = self.transport.config_space().cast::<Config>();
79-
let config = unsafe { config_space.as_mut() };
80-
config.select.write(select as u8);
81-
config.subsel.write(subsel);
82-
let size = config.size.read();
83-
let data = config.data.read();
78+
let config = self.transport.config_space().cast::<Config>();
79+
let size;
80+
let data;
81+
// Safe because config points to a valid MMIO region for the config space.
82+
unsafe {
83+
volwrite!(config, select, select as u8);
84+
volwrite!(config, subsel, subsel);
85+
size = volread!(config, size);
86+
data = volread!(config, data);
87+
}
8488
out[..size as usize].copy_from_slice(&data[..size as usize]);
8589
size
8690
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod input;
1515
mod net;
1616
mod queue;
1717
mod transport;
18+
mod volatile;
1819

1920
pub use self::blk::{BlkResp, RespStatus, VirtIOBlk};
2021
pub use self::console::VirtIOConsole;

src/net.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ use core::mem::{size_of, MaybeUninit};
22

33
use super::*;
44
use crate::transport::Transport;
5+
use crate::volatile::{volread, ReadOnly, Volatile};
56
use bitflags::*;
67
use core::hint::spin_loop;
78
use log::*;
8-
use volatile::{ReadOnly, Volatile};
99

1010
/// The virtio network device is a virtual ethernet card.
1111
///
@@ -14,14 +14,14 @@ use volatile::{ReadOnly, Volatile};
1414
/// Empty buffers are placed in one virtqueue for receiving packets, and
1515
/// outgoing packets are enqueued into another for transmission in that order.
1616
/// A third command queue is used to control advanced filtering features.
17-
pub struct VirtIONet<'a, H: Hal, T: Transport> {
17+
pub struct VirtIONet<H: Hal, T: Transport> {
1818
transport: T,
1919
mac: EthernetAddress,
20-
recv_queue: VirtQueue<'a, H>,
21-
send_queue: VirtQueue<'a, H>,
20+
recv_queue: VirtQueue<H>,
21+
send_queue: VirtQueue<H>,
2222
}
2323

24-
impl<H: Hal, T: Transport> VirtIONet<'_, H, T> {
24+
impl<H: Hal, T: Transport> VirtIONet<H, T> {
2525
/// Create a new VirtIO-Net driver.
2626
pub fn new(mut transport: T) -> Result<Self> {
2727
transport.begin_init(|features| {
@@ -31,10 +31,13 @@ impl<H: Hal, T: Transport> VirtIONet<'_, H, T> {
3131
(features & supported_features).bits()
3232
});
3333
// read configuration space
34-
let config_space = transport.config_space().cast::<Config>();
35-
let config = unsafe { config_space.as_ref() };
36-
let mac = config.mac.read();
37-
debug!("Got MAC={:?}, status={:?}", mac, config.status.read());
34+
let config = transport.config_space().cast::<Config>();
35+
let mac;
36+
// Safe because config points to a valid MMIO region for the config space.
37+
unsafe {
38+
mac = volread!(config, mac);
39+
debug!("Got MAC={:?}, status={:?}", mac, volread!(config, status));
40+
}
3841

3942
let queue_num = 2; // for simplicity
4043
let recv_queue = VirtQueue::new(&mut transport, QUEUE_RECEIVE, queue_num)?;

0 commit comments

Comments
 (0)