Skip to content

Commit 1cb72de

Browse files
brandonpikeroypat
authored andcommitted
Change IoVecBuffer[Mut] len to u32
This commit changes the iovec len primitive to match descriptor chain's (u32). This removes some ugly casting and potential overflow problems, and allows us to upcast when needed in a non-lossy manor. Signed-off-by: Brandon Pike <[email protected]>
1 parent 0f39350 commit 1cb72de

File tree

6 files changed

+60
-45
lines changed

6 files changed

+60
-45
lines changed

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

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ pub enum IoVecError {
1818
WriteOnlyDescriptor,
1919
/// Tried to create an 'IoVecMut` from a read-only descriptor chain
2020
ReadOnlyDescriptor,
21+
/// Tried to create an `IoVec` or `IoVecMut` from a descriptor chain that was too large
22+
OverflowedDescriptor,
2123
/// Guest memory error: {0}
2224
GuestMemory(#[from] GuestMemoryError),
2325
}
@@ -40,14 +42,14 @@ pub struct IoVecBuffer {
4042
// container of the memory regions included in this IO vector
4143
vecs: IoVecVec,
4244
// Total length of the IoVecBuffer
43-
len: usize,
45+
len: u32,
4446
}
4547

4648
impl IoVecBuffer {
4749
/// Create an `IoVecBuffer` from a `DescriptorChain`
4850
pub fn from_descriptor_chain(head: DescriptorChain) -> Result<Self, IoVecError> {
4951
let mut vecs = IoVecVec::new();
50-
let mut len = 0usize;
52+
let mut len = 0u32;
5153

5254
let mut next_descriptor = Some(head);
5355
while let Some(desc) = next_descriptor {
@@ -68,7 +70,9 @@ impl IoVecBuffer {
6870
iov_base,
6971
iov_len: desc.len as size_t,
7072
});
71-
len += desc.len as usize;
73+
len = len
74+
.checked_add(desc.len)
75+
.ok_or(IoVecError::OverflowedDescriptor)?;
7276

7377
next_descriptor = desc.next_descriptor();
7478
}
@@ -77,7 +81,7 @@ impl IoVecBuffer {
7781
}
7882

7983
/// Get the total length of the memory regions covered by this `IoVecBuffer`
80-
pub(crate) fn len(&self) -> usize {
84+
pub(crate) fn len(&self) -> u32 {
8185
self.len
8286
}
8387

@@ -106,7 +110,7 @@ impl IoVecBuffer {
106110
mut buf: &mut [u8],
107111
offset: usize,
108112
) -> Result<(), VolatileMemoryError> {
109-
if offset < self.len() {
113+
if offset < self.len() as usize {
110114
let expected = buf.len();
111115
let bytes_read = self.read_volatile_at(&mut buf, offset, expected)?;
112116

@@ -188,14 +192,14 @@ pub struct IoVecBufferMut {
188192
// container of the memory regions included in this IO vector
189193
vecs: IoVecVec,
190194
// Total length of the IoVecBufferMut
191-
len: usize,
195+
len: u32,
192196
}
193197

194198
impl IoVecBufferMut {
195199
/// Create an `IoVecBufferMut` from a `DescriptorChain`
196200
pub fn from_descriptor_chain(head: DescriptorChain) -> Result<Self, IoVecError> {
197201
let mut vecs = IoVecVec::new();
198-
let mut len = 0usize;
202+
let mut len = 0u32;
199203

200204
for desc in head {
201205
if !desc.is_write_only() {
@@ -217,14 +221,16 @@ impl IoVecBufferMut {
217221
iov_base,
218222
iov_len: desc.len as size_t,
219223
});
220-
len += desc.len as usize;
224+
len = len
225+
.checked_add(desc.len)
226+
.ok_or(IoVecError::OverflowedDescriptor)?;
221227
}
222228

223229
Ok(Self { vecs, len })
224230
}
225231

226232
/// Get the total length of the memory regions covered by this `IoVecBuffer`
227-
pub(crate) fn len(&self) -> usize {
233+
pub(crate) fn len(&self) -> u32 {
228234
self.len
229235
}
230236

@@ -244,7 +250,7 @@ impl IoVecBufferMut {
244250
mut buf: &[u8],
245251
offset: usize,
246252
) -> Result<(), VolatileMemoryError> {
247-
if offset < self.len() {
253+
if offset < self.len() as usize {
248254
let expected = buf.len();
249255
let bytes_written = self.write_volatile_at(&mut buf, offset, expected)?;
250256

@@ -335,18 +341,18 @@ mod tests {
335341
iov_len: buf.len(),
336342
}]
337343
.into(),
338-
len: buf.len(),
344+
len: buf.len().try_into().unwrap(),
339345
}
340346
}
341347
}
342348

343349
impl<'a> From<Vec<&'a [u8]>> for IoVecBuffer {
344350
fn from(buffer: Vec<&'a [u8]>) -> Self {
345-
let mut len = 0;
351+
let mut len = 0_u32;
346352
let vecs = buffer
347353
.into_iter()
348354
.map(|slice| {
349-
len += slice.len();
355+
len += TryInto::<u32>::try_into(slice.len()).unwrap();
350356
iovec {
351357
iov_base: slice.as_ptr() as *mut c_void,
352358
iov_len: slice.len(),
@@ -366,7 +372,7 @@ mod tests {
366372
iov_len: buf.len(),
367373
}]
368374
.into(),
369-
len: buf.len(),
375+
len: buf.len().try_into().unwrap(),
370376
}
371377
}
372378
}
@@ -607,7 +613,6 @@ mod verification {
607613

608614
use libc::{c_void, iovec};
609615
use vm_memory::bitmap::BitmapSlice;
610-
use vm_memory::volatile_memory::Error;
611616
use vm_memory::VolatileSlice;
612617

613618
use super::{IoVecBuffer, IoVecBufferMut, IoVecVec};
@@ -622,10 +627,10 @@ mod verification {
622627
// >= 1.
623628
const MAX_DESC_LENGTH: usize = 4;
624629

625-
fn create_iovecs(mem: *mut u8, size: usize) -> (IoVecVec, usize) {
630+
fn create_iovecs(mem: *mut u8, size: usize) -> (IoVecVec, u32) {
626631
let nr_descs: usize = kani::any_where(|&n| n <= MAX_DESC_LENGTH);
627632
let mut vecs: Vec<iovec> = Vec::with_capacity(nr_descs);
628-
let mut len = 0usize;
633+
let mut len = 0u32;
629634
for _ in 0..nr_descs {
630635
// The `IoVecBuffer(Mut)` constructors ensure that the memory region described by every
631636
// `Descriptor` in the chain is a valid, i.e. it is memory with then guest's memory
@@ -637,7 +642,7 @@ mod verification {
637642
let iov_base = unsafe { mem.offset(addr.try_into().unwrap()) } as *mut c_void;
638643

639644
vecs.push(iovec { iov_base, iov_len });
640-
len += iov_len;
645+
len += u32::try_from(iov_len).unwrap();
641646
}
642647

643648
(vecs, len)
@@ -712,7 +717,7 @@ mod verification {
712717
let iov: IoVecBuffer = kani::any();
713718

714719
let mut buf = vec![0; GUEST_MEMORY_SIZE];
715-
let offset: usize = kani::any();
720+
let offset: u32 = kani::any();
716721

717722
// We can't really check the contents that the operation here writes into `buf`, because
718723
// our `IoVecBuffer` being completely arbitrary can contain overlapping memory regions, so
@@ -724,9 +729,13 @@ mod verification {
724729
// Furthermore, we know our Read-/WriteVolatile implementation above is infallible, so
725730
// provided that the logic inside read_volatile_at is correct, we should always get Ok(...)
726731
assert_eq!(
727-
iov.read_volatile_at(&mut KaniBuffer(&mut buf), offset, GUEST_MEMORY_SIZE)
728-
.unwrap(),
729-
buf.len().min(iov.len().saturating_sub(offset))
732+
iov.read_volatile_at(
733+
&mut KaniBuffer(&mut buf),
734+
offset as usize,
735+
GUEST_MEMORY_SIZE
736+
)
737+
.unwrap(),
738+
buf.len().min(iov.len().saturating_sub(offset) as usize)
730739
);
731740
}
732741

@@ -737,7 +746,7 @@ mod verification {
737746
let mut iov_mut: IoVecBufferMut = kani::any();
738747

739748
let mut buf = kani::vec::any_vec::<u8, GUEST_MEMORY_SIZE>();
740-
let offset: usize = kani::any();
749+
let offset: u32 = kani::any();
741750

742751
// We can't really check the contents that the operation here writes into `IoVecBufferMut`,
743752
// because our `IoVecBufferMut` being completely arbitrary can contain overlapping memory
@@ -750,9 +759,13 @@ mod verification {
750759
// provided that the logic inside write_volatile_at is correct, we should always get Ok(...)
751760
assert_eq!(
752761
iov_mut
753-
.write_volatile_at(&mut KaniBuffer(&mut buf), offset, GUEST_MEMORY_SIZE)
762+
.write_volatile_at(
763+
&mut KaniBuffer(&mut buf),
764+
offset as usize,
765+
GUEST_MEMORY_SIZE
766+
)
754767
.unwrap(),
755-
buf.len().min(iov_mut.len().saturating_sub(offset))
768+
buf.len().min(iov_mut.len().saturating_sub(offset) as usize)
756769
);
757770
}
758771
}

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ impl Net {
462462

463463
if let Some(ns) = mmds_ns {
464464
if ns.is_mmds_frame(headers) {
465-
let mut frame = vec![0u8; frame_iovec.len() - vnet_hdr_len()];
465+
let mut frame = vec![0u8; frame_iovec.len() as usize - vnet_hdr_len()];
466466
// Ok to unwrap here, because we are passing a buffer that has the exact size
467467
// of the `IoVecBuffer` minus the VNET headers.
468468
frame_iovec
@@ -472,7 +472,7 @@ impl Net {
472472
METRICS.mmds.rx_accepted.inc();
473473

474474
// MMDS frames are not accounted by the rate limiter.
475-
Self::rate_limiter_replenish_op(rate_limiter, frame_iovec.len() as u64);
475+
Self::rate_limiter_replenish_op(rate_limiter, u64::from(frame_iovec.len()));
476476

477477
// MMDS consumed the frame.
478478
return Ok(true);
@@ -493,7 +493,7 @@ impl Net {
493493
let _metric = net_metrics.tap_write_agg.record_latency_metrics();
494494
match Self::write_tap(tap, frame_iovec) {
495495
Ok(_) => {
496-
let len = frame_iovec.len() as u64;
496+
let len = u64::from(frame_iovec.len());
497497
net_metrics.tx_bytes_count.add(len);
498498
net_metrics.tx_packets_count.inc();
499499
net_metrics.tx_count.inc();
@@ -609,7 +609,7 @@ impl Net {
609609
};
610610

611611
// We only handle frames that are up to MAX_BUFFER_SIZE
612-
if buffer.len() > MAX_BUFFER_SIZE {
612+
if buffer.len() as usize > MAX_BUFFER_SIZE {
613613
error!("net: received too big frame from driver");
614614
self.metrics.tx_malformed_frames.inc();
615615
tx_queue
@@ -618,7 +618,7 @@ impl Net {
618618
continue;
619619
}
620620

621-
if !Self::rate_limiter_consume_op(&mut self.tx_rate_limiter, buffer.len() as u64) {
621+
if !Self::rate_limiter_consume_op(&mut self.tx_rate_limiter, u64::from(buffer.len())) {
622622
tx_queue.undo_pop();
623623
self.metrics.tx_rate_limiter_throttled.inc();
624624
break;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ pub mod tests {
363363

364364
tap.write_iovec(&scattered).unwrap();
365365

366-
let mut read_buf = vec![0u8; scattered.len()];
366+
let mut read_buf = vec![0u8; scattered.len() as usize];
367367
assert!(tap_traffic_simulator.pop_rx_packet(&mut read_buf));
368368
assert_eq!(
369369
&read_buf[..PAYLOAD_SIZE - VNET_HDR_SIZE],

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,15 +112,15 @@ impl Entropy {
112112
return Ok(0);
113113
}
114114

115-
let mut rand_bytes = vec![0; iovec.len()];
115+
let mut rand_bytes = vec![0; iovec.len() as usize];
116116
rand::fill(&mut rand_bytes).map_err(|err| {
117117
METRICS.host_rng_fails.inc();
118118
err
119119
})?;
120120

121121
// It is ok to unwrap here. We are writing `iovec.len()` bytes at offset 0.
122122
iovec.write_all_volatile_at(&rand_bytes, 0).unwrap();
123-
Ok(iovec.len().try_into().unwrap())
123+
Ok(iovec.len())
124124
}
125125

126126
fn process_entropy_queue(&mut self) {
@@ -142,7 +142,7 @@ impl Entropy {
142142
// Check for available rate limiting budget.
143143
// If not enough budget is available, leave the request descriptor in the queue
144144
// to handle once we do have budget.
145-
if !Self::rate_limit_request(&mut self.rate_limiter, iovec.len() as u64) {
145+
if !Self::rate_limit_request(&mut self.rate_limiter, u64::from(iovec.len())) {
146146
debug!("entropy: throttling entropy queue");
147147
METRICS.entropy_rate_limiter_throttled.inc();
148148
self.queues[RNG_QUEUE].undo_pop();

src/vmm/src/devices/virtio/vsock/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ mod defs {
110110
#[rustfmt::skip]
111111
pub enum VsockError {
112112
/** The total length of the descriptor chain ({0}) is too short to hold a packet of length {1} + header */
113-
DescChainTooShortForPacket(usize, u32),
113+
DescChainTooShortForPacket(u32, u32),
114114
/// Empty queue
115115
EmptyQueue,
116116
/// EventFd error: {0}
@@ -122,6 +122,8 @@ pub enum VsockError {
122122
/** The total length of the descriptor chain ({0}) is less than the number of bytes required\
123123
to hold a vsock packet header.*/
124124
DescChainTooShortForHeader(usize),
125+
/// The descriptor chain length was greater than the max ([u32::MAX])
126+
DescChainOverflow,
125127
/// The vsock header `len` field holds an invalid value: {0}
126128
InvalidPktLen(u32),
127129
/// A data fetch was attempted when no data was available.
@@ -144,6 +146,7 @@ impl From<IoVecError> for VsockError {
144146
IoVecError::WriteOnlyDescriptor => VsockError::UnreadableDescriptor,
145147
IoVecError::ReadOnlyDescriptor => VsockError::UnwritableDescriptor,
146148
IoVecError::GuestMemory(err) => VsockError::GuestMemoryMmap(err),
149+
IoVecError::OverflowedDescriptor => VsockError::DescChainOverflow,
147150
}
148151
}
149152
}

src/vmm/src/devices/virtio/vsock/packet.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ impl VsockPacket {
139139
return Err(VsockError::InvalidPktLen(hdr.len));
140140
}
141141

142-
if (hdr.len as usize) > buffer.len() - VSOCK_PKT_HDR_SIZE as usize {
142+
if hdr.len > buffer.len() - VSOCK_PKT_HDR_SIZE {
143143
return Err(VsockError::DescChainTooShortForPacket(
144144
buffer.len(),
145145
hdr.len,
@@ -160,8 +160,8 @@ impl VsockPacket {
160160
pub fn from_rx_virtq_head(chain: DescriptorChain) -> Result<Self, VsockError> {
161161
let buffer = IoVecBufferMut::from_descriptor_chain(chain)?;
162162

163-
if buffer.len() < VSOCK_PKT_HDR_SIZE as usize {
164-
return Err(VsockError::DescChainTooShortForHeader(buffer.len()));
163+
if buffer.len() < VSOCK_PKT_HDR_SIZE {
164+
return Err(VsockError::DescChainTooShortForHeader(buffer.len() as usize));
165165
}
166166

167167
Ok(Self {
@@ -212,7 +212,7 @@ impl VsockPacket {
212212
VsockPacketBuffer::Tx(ref iovec_buf) => iovec_buf.len(),
213213
VsockPacketBuffer::Rx(ref iovec_buf) => iovec_buf.len(),
214214
};
215-
chain_length - VSOCK_PKT_HDR_SIZE as usize
215+
(chain_length - VSOCK_PKT_HDR_SIZE) as usize
216216
}
217217

218218
pub fn read_at_offset_from<T: ReadVolatile + Debug>(
@@ -225,8 +225,7 @@ impl VsockPacket {
225225
VsockPacketBuffer::Tx(_) => Err(VsockError::UnwritableDescriptor),
226226
VsockPacketBuffer::Rx(ref mut buffer) => {
227227
if count
228-
> buffer
229-
.len()
228+
> (buffer.len() as usize)
230229
.saturating_sub(VSOCK_PKT_HDR_SIZE as usize)
231230
.saturating_sub(offset)
232231
{
@@ -249,8 +248,7 @@ impl VsockPacket {
249248
match self.buffer {
250249
VsockPacketBuffer::Tx(ref buffer) => {
251250
if count
252-
> buffer
253-
.len()
251+
> (buffer.len() as usize)
254252
.saturating_sub(VSOCK_PKT_HDR_SIZE as usize)
255253
.saturating_sub(offset)
256254
{
@@ -427,9 +425,10 @@ mod tests {
427425
.unwrap(),
428426
)
429427
.unwrap();
428+
430429
assert_eq!(
431-
pkt.buf_size(),
432-
handler_ctx.guest_txvq.dtable[1].len.get() as usize
430+
TryInto::<u32>::try_into(pkt.buf_size()).unwrap(),
431+
handler_ctx.guest_txvq.dtable[1].len.get()
433432
);
434433
}
435434

0 commit comments

Comments
 (0)