in src/devices/src/virtio/block/request.rs [802:958]
fn do_random_request_parse(
sparsity: u64,
data_len: u32,
sector: u64,
request_type: RequestType,
virtio_request_id: u32,
coins_arr: &[bool],
) -> (Result<Request, Error>, GuestMemoryMmap, Queue) {
let coins = &mut coins_arr.iter();
// Randomize descriptor addresses. Assumed page size as max buffer len.
let base_addr = sparsity & 0x0000_FFFF_FFFF_F000; // 48 bit base, page aligned.
let max_desc_len = 0x1000;
// First addr starts at page base + 1.
let req_type_addr = GuestAddress(base_addr).checked_add(0x1000).unwrap();
// Use first 4 bits of randomness to shift the gap size between this descriptor
// and the next one.
let mut next_desc_dist = max_desc_len + (0x1000 << (sparsity & 0xF));
let data_addr = req_type_addr.checked_add(next_desc_dist).unwrap();
// Use next 4 bits of randomness to shift gap size between this descriptor
// and the next one.
next_desc_dist = max_desc_len + (0x1000 << ((sparsity & 0xF0) >> 4));
let status_addr = data_addr.checked_add(next_desc_dist).unwrap();
let mem_end = status_addr.checked_add(max_desc_len).unwrap();
let mem: GuestMemoryMmap = create_anon_guest_memory(
&[(
GuestAddress(base_addr),
(mem_end.0 - base_addr).try_into().unwrap(),
)],
false,
)
.unwrap();
let mut rq = RequestVirtQueue::new(GuestAddress(base_addr), &mem);
let q = rq.vq.create_queue();
// Make sure that data_len is a multiple of 512
// and that 512 <= data_len <= (4096 + 512).
let valid_data_len = ((data_len & 4096) | (SECTOR_SIZE as u32 - 1)) + 1;
let sectors_len = u64::from(valid_data_len) / SECTOR_SIZE;
// Craft a random request with the randomized parameters.
let mut request = Request {
r#type: request_type,
data_len: valid_data_len,
status_addr,
sector: sector & (NUM_DISK_SECTORS - sectors_len),
data_addr,
};
let request_header = RequestHeader::new(virtio_request_id, request.sector);
rq.set_hdr_desc(
req_type_addr.0,
max_desc_len as u32,
VIRTQ_DESC_F_NEXT,
request_header,
);
// Flush requests have no data desc.
if request.r#type == RequestType::Flush {
request.data_addr = GuestAddress(0);
request.data_len = 0;
rq.mut_hdr_desc()
.next
.set(RequestVirtQueue::STATUS_DESC as u16);
} else {
rq.set_data_desc(
request.data_addr.0,
request.data_len,
request_type_flags(request.r#type),
)
}
rq.set_status_desc(request.status_addr.0, 1, VIRTQ_DESC_F_WRITE);
// Flip a coin - should we generate a valid request or an error.
if *coins.next().unwrap() {
return (Ok(request), mem, q);
}
// This is the initial correct value.
let data_desc_flags = &rq.mut_data_desc().flags;
// Flip coin - corrupt the status desc len.
if *coins.next().unwrap() {
rq.mut_status_desc().len.set(0);
return (Err(Error::DescriptorLengthTooSmall), mem, q);
}
// Flip coin - corrupt data desc next flag.
// Exception: flush requests do not have data desc.
if *coins.next().unwrap() && request.r#type != RequestType::Flush {
data_desc_flags.set(data_desc_flags.get() & !VIRTQ_DESC_F_NEXT);
return (Err(Error::DescriptorChainTooShort), mem, q);
}
// Flip coin - req type desc is write only.
if *coins.next().unwrap() {
let hdr_desc_flags = &rq.mut_hdr_desc().flags;
hdr_desc_flags.set(hdr_desc_flags.get() | VIRTQ_DESC_F_WRITE);
return (Err(Error::UnexpectedWriteOnlyDescriptor), mem, q);
}
// Corrupt data desc accessibility
if *coins.next().unwrap() {
match request.r#type {
// Readonly buffer is writable.
RequestType::Out => {
data_desc_flags.set(data_desc_flags.get() | VIRTQ_DESC_F_WRITE);
return (Err(Error::UnexpectedWriteOnlyDescriptor), mem, q);
}
// Writeable buffer is readonly.
RequestType::In | RequestType::GetDeviceID => {
data_desc_flags.set(data_desc_flags.get() & !VIRTQ_DESC_F_WRITE);
return (Err(Error::UnexpectedReadOnlyDescriptor), mem, q);
}
_ => {}
};
}
// Flip coin - Corrupt data_len
if *coins.next().unwrap() {
match request.r#type {
RequestType::In | RequestType::Out => {
// data_len is not a multiple of 512
rq.mut_data_desc()
.len
.set(valid_data_len + (data_len % 511) + 1);
return (Err(Error::InvalidDataLength), mem, q);
}
RequestType::GetDeviceID => {
// data_len is < VIRTIO_BLK_ID_BYTES
rq.mut_data_desc()
.len
.set(data_len & (VIRTIO_BLK_ID_BYTES - 1));
return (Err(Error::InvalidDataLength), mem, q);
}
_ => {}
};
}
// Flip coin - Corrupt sector
if *coins.next().unwrap() {
match request.r#type {
RequestType::In | RequestType::Out => {
rq.mut_hdr().sector = (sector | NUM_DISK_SECTORS) + 1;
return (Err(Error::InvalidOffset), mem, q);
}
_ => {}
};
}
// Simulate no status descriptor.
rq.mut_hdr_desc().flags.set(0);
(Err(Error::DescriptorChainTooShort), mem, q)
}