Skip to content

Commit 72ecab4

Browse files
fogtijounathaen
authored andcommitted
feat(hypercall): Provide unit test for read hypercall on Virtual fds
This is used to prevent accidental reintroduction of the bug fixed in PR #1447.
1 parent 6486ef9 commit 72ecab4

1 file changed

Lines changed: 63 additions & 37 deletions

File tree

‎src/hypercall/by_fd.rs‎

Lines changed: 63 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use core::cmp;
1+
use core::{cmp, mem::MaybeUninit};
22
use std::{io, os::fd::IntoRawFd};
33

44
use uhyve_interface::{
@@ -76,42 +76,12 @@ pub(super) fn read(
7676
) {
7777
sysread.ret = if let Some(fdata) = file_map.fdmap.get_mut(GuestFd(sysread.fd.into_raw_fd())) {
7878
if let Ok(host_address) = mem.host_address(sysread.buf) {
79-
match fdata {
80-
FdData::Raw(rfd) => {
81-
let bytes_read = unsafe {
82-
libc::read(
83-
*rfd,
84-
host_address as *mut libc::c_void,
85-
sysread.len as usize,
86-
)
87-
};
88-
if bytes_read < 0 {
89-
-translate_last_errno().unwrap_or(1) as i64
90-
} else {
91-
bytes_read as i64
92-
}
93-
}
94-
FdData::Virtual { data, offset } => {
95-
let remaining = {
96-
let pos = cmp::min(*offset, data.len() as u64);
97-
&data[pos as usize..]
98-
};
99-
let amt = cmp::min(remaining.len() as u64, sysread.len) as usize;
100-
assert!(amt <= isize::MAX as usize);
101-
102-
// SAFETY: the input slices can't overlap, as `host_address` is owned by the guest
103-
// and `data` is owned by the host.
104-
unsafe {
105-
core::ptr::copy_nonoverlapping(
106-
remaining.as_ptr(),
107-
host_address as *mut u8,
108-
amt,
109-
)
110-
};
111-
*offset += amt as u64;
112-
amt as i64
113-
}
114-
FdData::MappedDirectory { .. } => -EBADF as i64,
79+
if let Ok(len) = sysread.len.try_into() {
80+
read_internal(fdata, unsafe {
81+
core::slice::from_raw_parts_mut(host_address as *mut MaybeUninit<u8>, len)
82+
})
83+
} else {
84+
-EINVAL as i64
11585
}
11686
} else {
11787
warn!("Unable to get host address for read buffer");
@@ -122,6 +92,41 @@ pub(super) fn read(
12292
};
12393
}
12494

95+
fn read_internal(fdata: &mut FdData, out_bytes: &mut [MaybeUninit<u8>]) -> i64 {
96+
match fdata {
97+
FdData::Raw(rfd) => {
98+
let bytes_read = unsafe {
99+
libc::read(
100+
*rfd,
101+
out_bytes.as_mut_ptr().cast::<libc::c_void>(),
102+
out_bytes.len(),
103+
)
104+
};
105+
if bytes_read < 0 {
106+
-translate_last_errno().unwrap_or(1) as i64
107+
} else {
108+
bytes_read as i64
109+
}
110+
}
111+
FdData::Virtual { data, offset } => {
112+
let remaining = {
113+
let pos = cmp::min(*offset, data.len() as u64);
114+
&data[pos as usize..]
115+
};
116+
let amt = out_bytes
117+
.iter_mut()
118+
.zip(remaining.iter())
119+
.map(|(o, i)| {
120+
o.write(*i);
121+
})
122+
.count();
123+
*offset += amt as u64;
124+
amt as i64
125+
}
126+
FdData::MappedDirectory { .. } => -EBADF as i64,
127+
}
128+
}
129+
125130
/// Handles a v1 write hypercall (for which a guest-provided guest virtual address must be
126131
/// converted to a guest physical address by the host).
127132
pub(super) fn write_v1<N: NetworkBackend>(
@@ -288,3 +293,24 @@ pub(super) fn lseek(syslseek: &mut LseekParams, file_map: &mut UhyveFileMap) {
288293
}
289294
};
290295
}
296+
297+
#[cfg(test)]
298+
mod tests {
299+
use std::sync::Arc;
300+
301+
use super::{FdData, MaybeUninit, read_internal};
302+
303+
#[test]
304+
fn test_read_internal_on_virtual00() {
305+
let data: Arc<[u8]> = Arc::from(b"Hello!".to_vec().into_boxed_slice());
306+
let len = data.len() as u64;
307+
let mut fdata = FdData::Virtual { data, offset: 0 };
308+
let mut out = [MaybeUninit::new(0u8); 20];
309+
310+
assert_eq!(read_internal(&mut fdata, &mut out), len as i64);
311+
let FdData::Virtual { offset, .. } = &fdata else {
312+
unreachable!();
313+
};
314+
assert_eq!(*offset, len);
315+
}
316+
}

0 commit comments

Comments
 (0)