Skip to content

Commit 0945a59

Browse files
author
Alexandra Iordache
authored
Merge branch 'master' into fix-1316
2 parents 6a3ef2a + 8ff3115 commit 0945a59

File tree

3 files changed

+209
-148
lines changed

3 files changed

+209
-148
lines changed

devices/src/virtio/block.rs

Lines changed: 90 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use epoll;
99
use std::cmp;
10+
use std::convert::From;
1011
use std::fs::File;
1112
use std::io::{self, Read, Seek, SeekFrom, Write};
1213
use std::os::linux::fs::MetadataExt;
@@ -17,7 +18,7 @@ use std::sync::mpsc;
1718
use std::sync::Arc;
1819

1920
use logger::{Metric, METRICS};
20-
use memory_model::{GuestAddress, GuestMemory, GuestMemoryError};
21+
use memory_model::{DataInit, GuestAddress, GuestMemory, GuestMemoryError};
2122
use rate_limiter::{RateLimiter, TokenType};
2223
use sys_util::EventFd;
2324
use virtio_gen::virtio_blk::*;
@@ -46,8 +47,6 @@ pub const BLOCK_EVENTS_COUNT: usize = 2;
4647
enum Error {
4748
/// Guest gave us bad memory addresses.
4849
GuestMemory(GuestMemoryError),
49-
/// Guest gave us offsets that would have overflowed a usize.
50-
CheckedOffset(GuestAddress, usize),
5150
/// Guest gave us a write only descriptor that protocol says to read from.
5251
UnexpectedWriteOnlyDescriptor,
5352
/// Guest gave us a read only descriptor that protocol says to write to.
@@ -94,29 +93,18 @@ enum RequestType {
9493
Unsupported(u32),
9594
}
9695

97-
fn request_type(mem: &GuestMemory, desc_addr: GuestAddress) -> result::Result<RequestType, Error> {
98-
let type_ = mem
99-
.read_obj_from_addr(desc_addr)
100-
.map_err(Error::GuestMemory)?;
101-
match type_ {
102-
VIRTIO_BLK_T_IN => Ok(RequestType::In),
103-
VIRTIO_BLK_T_OUT => Ok(RequestType::Out),
104-
VIRTIO_BLK_T_FLUSH => Ok(RequestType::Flush),
105-
VIRTIO_BLK_T_GET_ID => Ok(RequestType::GetDeviceID),
106-
t => Ok(RequestType::Unsupported(t)),
96+
impl From<u32> for RequestType {
97+
fn from(value: u32) -> Self {
98+
match value {
99+
VIRTIO_BLK_T_IN => RequestType::In,
100+
VIRTIO_BLK_T_OUT => RequestType::Out,
101+
VIRTIO_BLK_T_FLUSH => RequestType::Flush,
102+
VIRTIO_BLK_T_GET_ID => RequestType::GetDeviceID,
103+
t => RequestType::Unsupported(t),
104+
}
107105
}
108106
}
109107

110-
fn sector(mem: &GuestMemory, desc_addr: GuestAddress) -> result::Result<u64, Error> {
111-
const SECTOR_OFFSET: usize = 8;
112-
let addr = match mem.checked_offset(desc_addr, SECTOR_OFFSET) {
113-
Some(v) => v,
114-
None => return Err(Error::CheckedOffset(desc_addr, SECTOR_OFFSET)),
115-
};
116-
117-
mem.read_obj_from_addr(addr).map_err(Error::GuestMemory)
118-
}
119-
120108
fn build_device_id(disk_image: &File) -> result::Result<String, Error> {
121109
let blk_metadata = match disk_image.metadata() {
122110
Err(_) => return Err(Error::GetFileMetadata),
@@ -158,16 +146,54 @@ struct Request {
158146
status_addr: GuestAddress,
159147
}
160148

149+
/// The request header represents the mandatory fields of each block device request.
150+
///
151+
/// A request header contains the following fields:
152+
/// * request_type: an u32 value mapping to a read, write or flush operation.
153+
/// * reserved: 32 bits are reserved for future extensions of the Virtio Spec.
154+
/// * sector: an u64 value representing the offset where a read/write is to occur.
155+
///
156+
/// The header simplifies reading the request from memory as all request follow
157+
/// the same memory layout.
158+
#[derive(Copy, Clone)]
159+
#[repr(C)]
160+
struct RequestHeader {
161+
request_type: u32,
162+
_reserved: u32,
163+
sector: u64,
164+
}
165+
166+
// Safe because RequestHeader only contains plain data.
167+
unsafe impl DataInit for RequestHeader {}
168+
169+
impl RequestHeader {
170+
/// Reads the request header from GuestMemory starting at `addr`.
171+
///
172+
/// Virtio 1.0 specifies that the data is transmitted by the driver in little-endian
173+
/// format. Firecracker currently runs only on little endian platforms so we don't
174+
/// need to do an explicit little endian read as all reads are little endian by default.
175+
/// When running on a big endian platform, this code should not compile, and support
176+
/// for explicit little endian reads is required.
177+
#[cfg(target_endian = "little")]
178+
fn read_from(memory: &GuestMemory, addr: GuestAddress) -> result::Result<Self, Error> {
179+
let request_header: RequestHeader = memory
180+
.read_obj_from_addr(addr)
181+
.map_err(Error::GuestMemory)?;
182+
Ok(request_header)
183+
}
184+
}
185+
161186
impl Request {
162187
fn parse(avail_desc: &DescriptorChain, mem: &GuestMemory) -> result::Result<Request, Error> {
163188
// The head contains the request type which MUST be readable.
164189
if avail_desc.is_write_only() {
165190
return Err(Error::UnexpectedWriteOnlyDescriptor);
166191
}
167192

193+
let request_header = RequestHeader::read_from(mem, avail_desc.addr)?;
168194
let mut req = Request {
169-
request_type: request_type(&mem, avail_desc.addr)?,
170-
sector: sector(&mem, avail_desc.addr)?,
195+
request_type: RequestType::from(request_header.request_type),
196+
sector: request_header.sector,
171197
data_addr: GuestAddress(0),
172198
data_len: 0,
173199
status_addr: GuestAddress(0),
@@ -777,57 +803,52 @@ mod tests {
777803
assert_eq!(h.interrupt_evt.read().unwrap(), 2);
778804
}
779805

780-
#[test]
781-
fn test_request_type() {
782-
let m = &GuestMemory::new(&[(GuestAddress(0), 0x1000)]).unwrap();
783-
let a = GuestAddress(0);
784-
785-
// We write values associated with different request type at an address in memory,
786-
// and verify the request type is parsed correctly.
806+
// Writes at address 0x0 the request_type, reserved, sector.
807+
fn write_request_header(mem: &GuestMemory, request_type: u32, sector: u64) {
808+
let addr = GuestAddress(0);
787809

788-
m.write_obj_at_addr::<u32>(VIRTIO_BLK_T_IN, a).unwrap();
789-
assert_eq!(request_type(m, a).unwrap(), RequestType::In);
790-
791-
m.write_obj_at_addr::<u32>(VIRTIO_BLK_T_OUT, a).unwrap();
792-
assert_eq!(request_type(m, a).unwrap(), RequestType::Out);
793-
794-
m.write_obj_at_addr::<u32>(VIRTIO_BLK_T_FLUSH, a).unwrap();
795-
assert_eq!(request_type(m, a).unwrap(), RequestType::Flush);
796-
797-
m.write_obj_at_addr::<u32>(VIRTIO_BLK_T_GET_ID, a).unwrap();
798-
assert_eq!(request_type(m, a).unwrap(), RequestType::GetDeviceID);
799-
800-
// The value written here should be invalid.
801-
m.write_obj_at_addr::<u32>(VIRTIO_BLK_T_FLUSH + 10, a)
810+
mem.write_obj_at_addr::<u32>(request_type, addr).unwrap();
811+
mem.write_obj_at_addr::<u64>(sector, addr.checked_add(8).unwrap())
802812
.unwrap();
803-
assert_eq!(
804-
request_type(m, a).unwrap(),
805-
RequestType::Unsupported(VIRTIO_BLK_T_FLUSH + 10)
806-
);
807-
808-
// The provided address cannot be read, as it's outside the memory space.
809-
let a = GuestAddress(0x1000);
810-
assert!(request_type(m, a).is_err())
811813
}
812814

813815
#[test]
814-
fn test_sector() {
815-
let m = &GuestMemory::new(&[(GuestAddress(0), 0x1000)]).unwrap();
816-
let a = GuestAddress(0);
817-
818-
// Here we test that a sector number is parsed correctly from memory. The actual sector
819-
// number is expected to be found 8 bytes after the address provided as parameter to the
820-
// sector() function.
821-
822-
m.write_obj_at_addr::<u64>(123_454_321, a.checked_add(8).unwrap())
823-
.unwrap();
824-
assert_eq!(sector(m, a).unwrap(), 123_454_321);
816+
fn test_read_request_header() {
817+
let mem = GuestMemory::new(&[(GuestAddress(0), 0x1000)]).unwrap();
818+
let addr = GuestAddress(0);
819+
let sector = 123_454_321;
820+
821+
// Test that all supported request types are read correctly from memory.
822+
let supported_request_types = vec![
823+
VIRTIO_BLK_T_IN,
824+
VIRTIO_BLK_T_OUT,
825+
VIRTIO_BLK_T_FLUSH,
826+
VIRTIO_BLK_T_GET_ID,
827+
];
828+
829+
for request_type in supported_request_types {
830+
write_request_header(&mem, request_type, sector);
831+
832+
let request_header = RequestHeader::read_from(&mem, addr).unwrap();
833+
assert_eq!(request_header.request_type, request_type);
834+
assert_eq!(request_header.sector, sector);
835+
}
825836

826-
// Reading from a slightly different address should not lead a correct result in this case.
827-
assert_ne!(sector(m, a.checked_add(1).unwrap()).unwrap(), 123_454_321);
837+
// Test that trying to read a request header that goes outside of the
838+
// memory boundary fails.
839+
assert!(RequestHeader::read_from(&mem, GuestAddress(0x1000)).is_err());
840+
}
828841

829-
// The provided address is outside the valid memory range.
830-
assert!(sector(m, a.checked_add(0x1000).unwrap()).is_err());
842+
#[test]
843+
fn test_request_type_from() {
844+
assert_eq!(RequestType::from(VIRTIO_BLK_T_IN), RequestType::In);
845+
assert_eq!(RequestType::from(VIRTIO_BLK_T_OUT), RequestType::Out);
846+
assert_eq!(RequestType::from(VIRTIO_BLK_T_FLUSH), RequestType::Flush);
847+
assert_eq!(
848+
RequestType::from(VIRTIO_BLK_T_GET_ID),
849+
RequestType::GetDeviceID
850+
);
851+
assert_eq!(RequestType::from(42), RequestType::Unsupported(42));
831852
}
832853

833854
#[test]

0 commit comments

Comments
 (0)