Skip to content

Commit 6363dec

Browse files
committed
refactor(queue): update DescriptorChain implementation
Store pointer to the descriptor table in the `DescriptorChain` for accessing descriptors. This means we no longer store reference to guest memory in the `DescriptorChain`, so we need to pass reference to guest memory into some methods which relied on it's presence in the `DescriptorChain` Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent 3bb8167 commit 6363dec

File tree

9 files changed

+96
-101
lines changed

9 files changed

+96
-101
lines changed

src/vmm/src/devices/virtio/iovec.rs

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ use std::io::ErrorKind;
55

66
use libc::{c_void, iovec, size_t};
77
use smallvec::SmallVec;
8+
use vm_memory::bitmap::Bitmap;
89
use vm_memory::{
9-
GuestMemoryError, ReadVolatile, VolatileMemoryError, VolatileSlice, WriteVolatile,
10+
GuestMemory, GuestMemoryError, ReadVolatile, VolatileMemoryError, VolatileSlice, WriteVolatile,
1011
};
1112

1213
use crate::devices::virtio::queue::DescriptorChain;
13-
use crate::vstate::memory::{Bitmap, GuestMemory};
14+
use crate::vstate::memory::GuestMemoryMmap;
1415

1516
#[derive(Debug, thiserror::Error, displaydoc::Display)]
1617
pub enum IoVecError {
@@ -57,6 +58,7 @@ impl IoVecBuffer {
5758
/// The descriptor chain cannot be referencing the same memory location as another chain
5859
pub unsafe fn load_descriptor_chain(
5960
&mut self,
61+
mem: &GuestMemoryMmap,
6062
head: DescriptorChain,
6163
) -> Result<(), IoVecError> {
6264
self.clear();
@@ -70,8 +72,7 @@ impl IoVecBuffer {
7072
// We use get_slice instead of `get_host_address` here in order to have the whole
7173
// range of the descriptor chain checked, i.e. [addr, addr + len) is a valid memory
7274
// region in the GuestMemoryMmap.
73-
let iov_base = desc
74-
.mem
75+
let iov_base = mem
7576
.get_slice(desc.addr, desc.len as usize)?
7677
.ptr_guard_mut()
7778
.as_ptr()
@@ -96,9 +97,12 @@ impl IoVecBuffer {
9697
/// # Safety
9798
///
9899
/// The descriptor chain cannot be referencing the same memory location as another chain
99-
pub unsafe fn from_descriptor_chain(head: DescriptorChain) -> Result<Self, IoVecError> {
100+
pub unsafe fn from_descriptor_chain(
101+
mem: &GuestMemoryMmap,
102+
head: DescriptorChain,
103+
) -> Result<Self, IoVecError> {
100104
let mut new_buffer = Self::default();
101-
new_buffer.load_descriptor_chain(head)?;
105+
new_buffer.load_descriptor_chain(mem, head)?;
102106
Ok(new_buffer)
103107
}
104108

@@ -231,6 +235,7 @@ impl IoVecBufferMut {
231235
/// The descriptor chain cannot be referencing the same memory location as another chain
232236
pub unsafe fn load_descriptor_chain(
233237
&mut self,
238+
mem: &GuestMemoryMmap,
234239
head: DescriptorChain,
235240
) -> Result<(), IoVecError> {
236241
self.clear();
@@ -244,7 +249,7 @@ impl IoVecBufferMut {
244249
// We use get_slice instead of `get_host_address` here in order to have the whole
245250
// range of the descriptor chain checked, i.e. [addr, addr + len) is a valid memory
246251
// region in the GuestMemoryMmap.
247-
let slice = desc.mem.get_slice(desc.addr, desc.len as usize)?;
252+
let slice = mem.get_slice(desc.addr, desc.len as usize)?;
248253

249254
// We need to mark the area of guest memory that will be mutated through this
250255
// IoVecBufferMut as dirty ahead of time, as we loose access to all
@@ -272,9 +277,12 @@ impl IoVecBufferMut {
272277
/// # Safety
273278
///
274279
/// The descriptor chain cannot be referencing the same memory location as another chain
275-
pub unsafe fn from_descriptor_chain(head: DescriptorChain) -> Result<Self, IoVecError> {
280+
pub unsafe fn from_descriptor_chain(
281+
mem: &GuestMemoryMmap,
282+
head: DescriptorChain,
283+
) -> Result<Self, IoVecError> {
276284
let mut new_buffer = Self::default();
277-
new_buffer.load_descriptor_chain(head)?;
285+
new_buffer.load_descriptor_chain(mem, head)?;
278286
Ok(new_buffer)
279287
}
280288

@@ -484,22 +492,22 @@ mod tests {
484492
let (mut q, _) = read_only_chain(&mem);
485493
let head = q.pop(&mem).unwrap();
486494
// SAFETY: This descriptor chain is only loaded into one buffer
487-
unsafe { IoVecBuffer::from_descriptor_chain(head).unwrap() };
495+
unsafe { IoVecBuffer::from_descriptor_chain(&mem, head).unwrap() };
488496

489497
let (mut q, _) = write_only_chain(&mem);
490498
let head = q.pop(&mem).unwrap();
491499
// SAFETY: This descriptor chain is only loaded into one buffer
492-
unsafe { IoVecBuffer::from_descriptor_chain(head).unwrap_err() };
500+
unsafe { IoVecBuffer::from_descriptor_chain(&mem, head).unwrap_err() };
493501

494502
let (mut q, _) = read_only_chain(&mem);
495503
let head = q.pop(&mem).unwrap();
496504
// SAFETY: This descriptor chain is only loaded into one buffer
497-
unsafe { IoVecBufferMut::from_descriptor_chain(head).unwrap_err() };
505+
unsafe { IoVecBufferMut::from_descriptor_chain(&mem, head).unwrap_err() };
498506

499507
let (mut q, _) = write_only_chain(&mem);
500508
let head = q.pop(&mem).unwrap();
501509
// SAFETY: This descriptor chain is only loaded into one buffer
502-
unsafe { IoVecBufferMut::from_descriptor_chain(head).unwrap() };
510+
unsafe { IoVecBufferMut::from_descriptor_chain(&mem, head).unwrap() };
503511
}
504512

505513
#[test]
@@ -509,7 +517,7 @@ mod tests {
509517
let head = q.pop(&mem).unwrap();
510518

511519
// SAFETY: This descriptor chain is only loaded once in this test
512-
let iovec = unsafe { IoVecBuffer::from_descriptor_chain(head).unwrap() };
520+
let iovec = unsafe { IoVecBuffer::from_descriptor_chain(&mem, head).unwrap() };
513521
assert_eq!(iovec.len(), 4 * 64);
514522
}
515523

@@ -520,7 +528,7 @@ mod tests {
520528
let head = q.pop(&mem).unwrap();
521529

522530
// SAFETY: This descriptor chain is only loaded once in this test
523-
let iovec = unsafe { IoVecBufferMut::from_descriptor_chain(head).unwrap() };
531+
let iovec = unsafe { IoVecBufferMut::from_descriptor_chain(&mem, head).unwrap() };
524532
assert_eq!(iovec.len(), 4 * 64);
525533
}
526534

@@ -531,7 +539,7 @@ mod tests {
531539
let head = q.pop(&mem).unwrap();
532540

533541
// SAFETY: This descriptor chain is only loaded once in this test
534-
let iovec = unsafe { IoVecBuffer::from_descriptor_chain(head).unwrap() };
542+
let iovec = unsafe { IoVecBuffer::from_descriptor_chain(&mem, head).unwrap() };
535543

536544
let mut buf = vec![0u8; 257];
537545
assert_eq!(
@@ -586,7 +594,7 @@ mod tests {
586594
let head = q.pop(&mem).unwrap();
587595

588596
// SAFETY: This descriptor chain is only loaded into one buffer
589-
let mut iovec = unsafe { IoVecBufferMut::from_descriptor_chain(head).unwrap() };
597+
let mut iovec = unsafe { IoVecBufferMut::from_descriptor_chain(&mem, head).unwrap() };
590598
let buf = vec![0u8, 1, 2, 3, 4];
591599

592600
// One test vector for each part of the chain

src/vmm/src/devices/virtio/net/device.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ impl Net {
603603
// SAFETY: This descriptor chain is only loaded once
604604
// virtio requests are handled sequentially so no two IoVecBuffers
605605
// are live at the same time, meaning this has exclusive ownership over the memory
606-
if unsafe { self.tx_buffer.load_descriptor_chain(head).is_err() } {
606+
if unsafe { self.tx_buffer.load_descriptor_chain(mem, head).is_err() } {
607607
self.metrics.tx_fails.inc();
608608
tx_queue
609609
.add_used(head_index, 0)

src/vmm/src/devices/virtio/queue.rs

Lines changed: 39 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,8 @@ use std::cmp::min;
99
use std::num::Wrapping;
1010
use std::sync::atomic::{fence, Ordering};
1111

12-
use utils::usize_to_u64;
13-
1412
use crate::logger::error;
15-
use crate::vstate::memory::{
16-
Address, Bitmap, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap,
17-
};
13+
use crate::vstate::memory::{Address, Bitmap, ByteValued, GuestAddress, GuestMemory};
1814

1915
pub const VIRTQ_DESC_F_NEXT: u16 = 0x1;
2016
pub const VIRTQ_DESC_F_WRITE: u16 = 0x2;
@@ -72,14 +68,12 @@ unsafe impl ByteValued for UsedElement {}
7268

7369
/// A virtio descriptor chain.
7470
#[derive(Debug, Copy, Clone)]
75-
pub struct DescriptorChain<'a, M: GuestMemory = GuestMemoryMmap> {
76-
desc_table: GuestAddress,
71+
pub struct DescriptorChain {
72+
desc_table_ptr: *const Descriptor,
73+
7774
queue_size: u16,
7875
ttl: u16, // used to prevent infinite chain cycles
7976

80-
/// Reference to guest memory
81-
pub mem: &'a M,
82-
8377
/// Index into the descriptor table
8478
pub index: u16,
8579

@@ -97,38 +91,20 @@ pub struct DescriptorChain<'a, M: GuestMemory = GuestMemoryMmap> {
9791
pub next: u16,
9892
}
9993

100-
impl<'a, M: GuestMemory> DescriptorChain<'a, M> {
94+
impl DescriptorChain {
10195
/// Creates a new `DescriptorChain` from the given memory and descriptor table.
10296
///
10397
/// Note that the desc_table and queue_size are assumed to be validated by the caller.
104-
fn checked_new(
105-
mem: &'a M,
106-
desc_table: GuestAddress,
107-
queue_size: u16,
108-
index: u16,
109-
) -> Option<Self> {
110-
if index >= queue_size {
98+
fn checked_new(desc_table_ptr: *const Descriptor, queue_size: u16, index: u16) -> Option<Self> {
99+
if queue_size <= index {
111100
return None;
112101
}
113102

114-
// There's no need for checking as we already validated the descriptor table and index
115-
// bounds.
116-
let desc_head = desc_table.unchecked_add(u64::from(index) * 16);
117-
118-
// These reads can't fail unless Guest memory is hopelessly broken.
119-
let desc = match mem.read_obj::<Descriptor>(desc_head) {
120-
Ok(ret) => ret,
121-
Err(err) => {
122-
error!(
123-
"Failed to read virtio descriptor from memory at address {:#x}: {}",
124-
desc_head.0, err
125-
);
126-
return None;
127-
}
128-
};
103+
// SAFETY:
104+
// index is in 0..queue_size bounds
105+
let desc = unsafe { desc_table_ptr.add(usize::from(index)).read_volatile() };
129106
let chain = DescriptorChain {
130-
mem,
131-
desc_table,
107+
desc_table_ptr,
132108
queue_size,
133109
ttl: queue_size,
134110
index,
@@ -168,7 +144,7 @@ impl<'a, M: GuestMemory> DescriptorChain<'a, M> {
168144
/// the head of the next _available_ descriptor chain.
169145
pub fn next_descriptor(&self) -> Option<Self> {
170146
if self.has_next() {
171-
DescriptorChain::checked_new(self.mem, self.desc_table, self.queue_size, self.next).map(
147+
DescriptorChain::checked_new(self.desc_table_ptr, self.queue_size, self.next).map(
172148
|mut c| {
173149
c.ttl = self.ttl - 1;
174150
c
@@ -181,19 +157,19 @@ impl<'a, M: GuestMemory> DescriptorChain<'a, M> {
181157
}
182158

183159
#[derive(Debug)]
184-
pub struct DescriptorIterator<'a>(Option<DescriptorChain<'a>>);
160+
pub struct DescriptorIterator(Option<DescriptorChain>);
185161

186-
impl<'a> IntoIterator for DescriptorChain<'a> {
187-
type Item = DescriptorChain<'a>;
188-
type IntoIter = DescriptorIterator<'a>;
162+
impl IntoIterator for DescriptorChain {
163+
type Item = DescriptorChain;
164+
type IntoIter = DescriptorIterator;
189165

190166
fn into_iter(self) -> Self::IntoIter {
191167
DescriptorIterator(Some(self))
192168
}
193169
}
194170

195-
impl<'a> Iterator for DescriptorIterator<'a> {
196-
type Item = DescriptorChain<'a>;
171+
impl Iterator for DescriptorIterator {
172+
type Item = DescriptorChain;
197173

198174
fn next(&mut self) -> Option<Self::Item> {
199175
self.0.take().map(|desc| {
@@ -524,7 +500,7 @@ impl Queue {
524500
}
525501

526502
/// Pop the first available descriptor chain from the avail ring.
527-
pub fn pop<'b, M: GuestMemory>(&mut self, mem: &'b M) -> Option<DescriptorChain<'b, M>> {
503+
pub fn pop<M: GuestMemory>(&mut self, mem: &M) -> Option<DescriptorChain> {
528504
debug_assert!(self.is_valid(mem));
529505

530506
let len = self.len();
@@ -548,15 +524,15 @@ impl Queue {
548524
return None;
549525
}
550526

551-
self.do_pop_unchecked(mem)
527+
self.do_pop_unchecked()
552528
}
553529

554530
/// Try to pop the first available descriptor chain from the avail ring.
555531
/// If no descriptor is available, enable notifications.
556-
pub fn pop_or_enable_notification<'b, M: GuestMemory>(
532+
pub fn pop_or_enable_notification<M: GuestMemory>(
557533
&mut self,
558-
mem: &'b M,
559-
) -> Option<DescriptorChain<'b, M>> {
534+
mem: &M,
535+
) -> Option<DescriptorChain> {
560536
if !self.uses_notif_suppression {
561537
return self.pop(mem);
562538
}
@@ -565,18 +541,15 @@ impl Queue {
565541
return None;
566542
}
567543

568-
self.do_pop_unchecked(mem)
544+
self.do_pop_unchecked()
569545
}
570546

571547
/// Pop the first available descriptor chain from the avail ring.
572548
///
573549
/// # Important
574550
/// This is an internal method that ASSUMES THAT THERE ARE AVAILABLE DESCRIPTORS. Otherwise it
575551
/// will retrieve a descriptor that contains garbage data (obsolete/empty).
576-
fn do_pop_unchecked<'b, M: GuestMemory>(
577-
&mut self,
578-
mem: &'b M,
579-
) -> Option<DescriptorChain<'b, M>> {
552+
fn do_pop_unchecked(&mut self) -> Option<DescriptorChain> {
580553
// This fence ensures all subsequent reads see the updated driver writes.
581554
fence(Ordering::Acquire);
582555

@@ -592,11 +565,12 @@ impl Queue {
592565
// index is bound by the queue size
593566
let desc_index = unsafe { self.avail_ring_ring_get(usize::from(idx)) };
594567

595-
DescriptorChain::checked_new(mem, self.desc_table_address, self.actual_size(), desc_index)
596-
.map(|dc| {
568+
DescriptorChain::checked_new(self.desc_table_ptr, self.actual_size(), desc_index).map(
569+
|dc| {
597570
self.next_avail += Wrapping(1);
598571
dc
599-
})
572+
},
573+
)
600574
}
601575

602576
/// Undo the effects of the last `self.pop()` call.
@@ -1175,12 +1149,8 @@ mod verification {
11751149
let ProofContext(queue, mem) = ProofContext::bounded_queue();
11761150

11771151
let index = kani::any();
1178-
let maybe_chain = DescriptorChain::checked_new(
1179-
&mem,
1180-
queue.desc_table_address,
1181-
queue.actual_size(),
1182-
index,
1183-
);
1152+
let maybe_chain =
1153+
DescriptorChain::checked_new(queue.desc_table_ptr, queue.actual_size(), index);
11841154

11851155
if index >= queue.actual_size() {
11861156
assert!(maybe_chain.is_none())
@@ -1210,6 +1180,8 @@ mod verification {
12101180
#[cfg(test)]
12111181
mod tests {
12121182

1183+
use vm_memory::Bytes;
1184+
12131185
pub use super::*;
12141186
use crate::devices::virtio::queue::QueueError::DescIndexOutOfBounds;
12151187
use crate::devices::virtio::test_utils::{default_mem, VirtQueue};
@@ -1230,14 +1202,13 @@ mod tests {
12301202
fn test_checked_new_descriptor_chain() {
12311203
let m = &multi_region_mem(&[(GuestAddress(0), 0x10000), (GuestAddress(0x20000), 0x2000)]);
12321204
let vq = VirtQueue::new(GuestAddress(0), m, 16);
1205+
let mut q = vq.create_queue();
1206+
q.initialize(m).unwrap();
12331207

12341208
assert!(vq.end().0 < 0x1000);
12351209

12361210
// index >= queue_size
1237-
assert!(DescriptorChain::checked_new(m, vq.dtable_start(), 16, 16).is_none());
1238-
1239-
// desc_table address is way off
1240-
assert!(DescriptorChain::checked_new(m, GuestAddress(0x00ff_ffff_ffff), 16, 0).is_none());
1211+
assert!(DescriptorChain::checked_new(q.desc_table_ptr, 16, 16).is_none());
12411212

12421213
// Let's create an invalid chain.
12431214
{
@@ -1248,18 +1219,17 @@ mod tests {
12481219
// .. but the index of the next descriptor is too large
12491220
vq.dtable[0].next.set(16);
12501221

1251-
assert!(DescriptorChain::checked_new(m, vq.dtable_start(), 16, 0).is_none());
1222+
assert!(DescriptorChain::checked_new(q.desc_table_ptr, 16, 0).is_none());
12521223
}
12531224

12541225
// Finally, let's test an ok chain.
12551226
{
12561227
vq.dtable[0].next.set(1);
12571228
vq.dtable[1].set(0x2000, 0x1000, 0, 0);
12581229

1259-
let c = DescriptorChain::checked_new(m, vq.dtable_start(), 16, 0).unwrap();
1230+
let c = DescriptorChain::checked_new(q.desc_table_ptr, 16, 0).unwrap();
12601231

1261-
assert_eq!(c.mem as *const GuestMemoryMmap, m as *const GuestMemoryMmap);
1262-
assert_eq!(c.desc_table, vq.dtable_start());
1232+
assert_eq!(c.desc_table_ptr, q.desc_table_ptr);
12631233
assert_eq!(c.queue_size, 16);
12641234
assert_eq!(c.ttl, c.queue_size);
12651235
assert_eq!(c.index, 0);

0 commit comments

Comments
 (0)