mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
virtio-devices: block: reject duplicate in-flight head_index
A malicious or buggy guest can violate virtio by making the same
descriptor head available twice before the first chain has been placed
on the used ring. The submit path pushed both chains onto the
VecDeque-backed inflight_requests keyed by head_index, and on completion
find_inflight_request() returned the first linear match. That Request's
complete_async() freed its bounce buffer while the other chain's
io_uring op was still targeting it, producing a use-after-free the
kernel could then scribble into.
Signed-off-by: Dylan Reid <dgreid@fb.com>
(cherry picked from commit 544fa4aa76)
This commit is contained in:
@@ -211,6 +211,10 @@ impl BlockEpollHandler {
|
||||
Setting device status to 'NEEDS_RESET' and stopping processing queues until reset."
|
||||
);
|
||||
|
||||
self.set_needs_reset();
|
||||
}
|
||||
|
||||
fn set_needs_reset(&mut self) {
|
||||
self.device_status
|
||||
.fetch_or(crate::DEVICE_NEEDS_RESET as u8, Ordering::SeqCst);
|
||||
|
||||
@@ -220,6 +224,17 @@ Setting device status to 'NEEDS_RESET' and stopping processing queues until rese
|
||||
}
|
||||
}
|
||||
|
||||
// A spec-compliant driver never reuses a virtqueue head_index while the
|
||||
// corresponding chain is still available (virtio 1.x §2.7.13.4).
|
||||
// Double check the guest driver is behaving.
|
||||
fn is_head_in_flight(
|
||||
inflight: &VecDeque<(u16, Request)>,
|
||||
batch: &[(u16, Request)],
|
||||
head: u16,
|
||||
) -> bool {
|
||||
batch.iter().any(|(h, _)| *h == head) || inflight.iter().any(|(h, _)| *h == head)
|
||||
}
|
||||
|
||||
fn process_queue_submit(&mut self) -> Result<()> {
|
||||
if self.needs_reset() {
|
||||
return Ok(());
|
||||
@@ -239,6 +254,14 @@ Setting device status to 'NEEDS_RESET' and stopping processing queues until rese
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let head = desc_chain.head_index();
|
||||
if Self::is_head_in_flight(&self.inflight_requests, &batch_inflight_requests, head) {
|
||||
warn!("Guest reused virtio-blk head_index {head} while the chain was used");
|
||||
self.set_needs_reset();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut request = Request::parse(&mut desc_chain, self.access_platform.as_deref())
|
||||
.map_err(Error::RequestParsing)?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user