Skip to content

Commit a2a0bfd

Browse files
georgepisaltualindima
authored andcommitted
block: fix off-by-one error in addr validation
The block device validated addresses from virtio descriptors before trying to execute requests. In this validation, the `checked_offset` function of guest memory was used to determine if the slice defined by the sum of the address and length of the virtio descriptor was within the guest memory bounds. However, this sum is greater than the last valid offset, `addr + len - 1`, by 1. This made the block device mark descriptors with slices at the very end of a region as invalid, making the last byte of a memory region unusable by the block device. Changed the request parsing method to subtract one from the sum. Also adde dregression tests for this case. Signed-off-by: George Pisaltu <[email protected]> Signed-off-by: alindima <[email protected]>
1 parent 3125f9c commit a2a0bfd

File tree

2 files changed

+71
-5
lines changed

2 files changed

+71
-5
lines changed

src/devices/src/virtio/block/device.rs

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,10 @@ impl Block {
230230
e.status()
231231
}
232232
};
233-
// We use unwrap because the request parsing process already checked that the
234-
// status_addr was valid.
235-
mem.write_obj(status, request.status_addr).unwrap();
233+
234+
if let Err(e) = mem.write_obj(status, request.status_addr) {
235+
error!("Failed to write virtio block status: {:?}", e)
236+
}
236237
}
237238
Err(e) => {
238239
error!("Failed to parse available descriptor chain: {:?}", e);
@@ -584,6 +585,39 @@ pub(crate) mod tests {
584585
assert_eq!(vq.used.ring[0].get().len, 0);
585586
}
586587

588+
#[test]
589+
fn test_addr_out_of_bounds() {
590+
let mut block = default_block();
591+
// Default mem size is 0x10000
592+
let mem = default_mem();
593+
let vq = VirtQueue::new(GuestAddress(0), &mem, 16);
594+
block.set_queue(0, vq.create_queue());
595+
block.activate(mem.clone()).unwrap();
596+
initialize_virtqueue(&vq);
597+
vq.dtable[1].set(0xff00, 0x1000, VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE, 2);
598+
599+
let request_type_addr = GuestAddress(vq.dtable[0].addr.get());
600+
601+
// Mark the next available descriptor.
602+
vq.avail.idx.set(1);
603+
// Read.
604+
{
605+
vq.used.idx.set(0);
606+
607+
mem.write_obj::<u32>(VIRTIO_BLK_T_IN, request_type_addr)
608+
.unwrap();
609+
vq.dtable[1]
610+
.flags
611+
.set(VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE);
612+
613+
check_metric_after_block!(
614+
&METRICS.block.execute_fails,
615+
1,
616+
invoke_handler_for_queue_event(&mut block)
617+
);
618+
}
619+
}
620+
587621
#[test]
588622
fn test_request_execute_failures() {
589623
let mut block = default_block();
@@ -671,6 +705,38 @@ pub(crate) mod tests {
671705
VIRTIO_BLK_S_UNSUPP
672706
);
673707
}
708+
#[test]
709+
fn test_end_of_region() {
710+
let mut block = default_block();
711+
let mem = default_mem();
712+
let vq = VirtQueue::new(GuestAddress(0), &mem, 16);
713+
block.set_queue(0, vq.create_queue());
714+
block.activate(mem.clone()).unwrap();
715+
initialize_virtqueue(&vq);
716+
vq.dtable[1].set(0xf000, 0x1000, VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE, 2);
717+
718+
let request_type_addr = GuestAddress(vq.dtable[0].addr.get());
719+
let status_addr = GuestAddress(vq.dtable[2].addr.get());
720+
721+
vq.used.idx.set(0);
722+
723+
mem.write_obj::<u32>(VIRTIO_BLK_T_IN, request_type_addr)
724+
.unwrap();
725+
vq.dtable[1]
726+
.flags
727+
.set(VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE);
728+
729+
check_metric_after_block!(
730+
&METRICS.block.read_count,
731+
1,
732+
invoke_handler_for_queue_event(&mut block)
733+
);
734+
735+
assert_eq!(vq.used.idx.get(), 1);
736+
assert_eq!(vq.used.ring[0].get().id, 0);
737+
assert_eq!(vq.used.ring[0].get().len, vq.dtable[1].len.get());
738+
assert_eq!(mem.read_obj::<u32>(status_addr).unwrap(), VIRTIO_BLK_S_OK);
739+
}
674740

675741
#[test]
676742
fn test_read_write() {

src/devices/src/virtio/block/request.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ impl Request {
160160

161161
// Check that the address of the data descriptor is valid in guest memory.
162162
let _ = mem
163-
.checked_offset(data_desc.addr, data_desc.len as usize)
163+
.checked_offset(data_desc.addr, (data_desc.len - 1) as usize)
164164
.ok_or(Error::GuestMemory(GuestMemoryError::InvalidGuestAddress(
165165
data_desc.addr,
166166
)))?;
@@ -181,7 +181,7 @@ impl Request {
181181
// Check that the address of the status descriptor is valid in guest memory.
182182
// We will write an u32 status here after executing the request.
183183
let _ = mem
184-
.checked_offset(status_desc.addr, mem::size_of::<u32>())
184+
.checked_offset(status_desc.addr, (mem::size_of::<u32>() - 1) as usize)
185185
.ok_or(Error::GuestMemory(GuestMemoryError::InvalidGuestAddress(
186186
status_desc.addr,
187187
)))?;

0 commit comments

Comments
 (0)