Skip to content

Commit f815852

Browse files
committed
refactor(isolation/filemap): Improve API
This shifts usages of `CStr` and `OsStr` onto API consumers. All places where guest paths are used now use `&str`, and all places where host paths are used now use `&Path` (or the owned variants, respectively).
1 parent 02d971d commit f815852

File tree

4 files changed

+51
-56
lines changed

4 files changed

+51
-56
lines changed

src/hypercall.rs

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -259,19 +259,35 @@ fn translate_last_errno() -> Option<i32> {
259259
None
260260
}
261261

262+
/// Decodes a guest path given at address `path_addr` in `mem`.
263+
///
264+
/// # Safety
265+
///
266+
/// The calling convention of hypercalls ensures that the given address doesn't alias with anything mutable.
267+
/// The return value is only valid for the duration of the hypercall.
268+
unsafe fn decode_guest_path(mem: &MmapMemory, path_addr: GuestPhysAddr) -> Option<&str> {
269+
let requested_path_ptr = mem.host_address(path_addr).unwrap() as *const i8;
270+
unsafe { CStr::from_ptr(requested_path_ptr) }.to_str().ok()
271+
}
272+
262273
/// unlink deletes a name from the filesystem. This is used to handle `unlink` syscalls from the guest.
263274
///
264-
/// Note for when using Landlock: Unlinking files results in them being veiled. If a text
275+
/// Note for when using Landlock: Unlinking files results in them being veiled. If a
265276
/// file (that existed during initialization) called `log.txt` is unlinked, attempting to
266277
/// open `log.txt` again will result in an error.
267278
fn unlink(mem: &MmapMemory, sysunlink: &mut UnlinkParams, file_map: &mut UhyveFileMap) {
268-
let requested_path_ptr = mem.host_address(sysunlink.name).unwrap() as *const i8;
269-
let guest_path = unsafe { CStr::from_ptr(requested_path_ptr) };
279+
let guest_path = if let Some(guest_path) = unsafe { decode_guest_path(mem, sysunlink.name) } {
280+
guest_path
281+
} else {
282+
error!("The kernel requested to unlink() an non-UTF8 path: Rejecting...");
283+
sysunlink.ret = -EINVAL;
284+
return;
285+
};
270286
sysunlink.ret = match file_map.unlink(guest_path) {
271287
Ok(Some(host_path)) => {
272288
// We can safely unwrap here, as host_path.as_bytes will never contain internal \0 bytes
273289
// As host_path_c_string is a valid CString, this implementation is presumed to be safe.
274-
let host_path_c_string = CString::new(host_path.as_bytes()).unwrap();
290+
let host_path_c_string = CString::new(host_path.as_os_str().as_bytes()).unwrap();
275291
if unsafe { libc::unlink(host_path_c_string.as_c_str().as_ptr()) } < 0 {
276292
-translate_last_errno().unwrap_or(1)
277293
} else {
@@ -293,9 +309,14 @@ fn unlink(mem: &MmapMemory, sysunlink: &mut UnlinkParams, file_map: &mut UhyveFi
293309

294310
/// Handles an open syscall by opening a file on the host.
295311
fn open(mem: &MmapMemory, sysopen: &mut OpenParams, file_map: &mut UhyveFileMap) {
296-
let requested_path_ptr = mem.host_address(sysopen.name).unwrap() as *const i8;
312+
let guest_path = if let Some(guest_path) = unsafe { decode_guest_path(mem, sysopen.name) } {
313+
guest_path
314+
} else {
315+
error!("The kernel requested to open() an non-UTF8 path: Rejecting...");
316+
sysopen.ret = -EINVAL;
317+
return;
318+
};
297319
let mut flags = sysopen.flags & ALLOWED_OPEN_FLAGS;
298-
let guest_path = unsafe { CStr::from_ptr(requested_path_ptr) };
299320
// See: https://lwn.net/Articles/926782/
300321
// See: https://github.com/hermit-os/kernel/commit/71bc629
301322
if (flags & (O_DIRECTORY | O_CREAT)) == (O_DIRECTORY | O_CREAT) {
@@ -378,10 +399,7 @@ fn open(mem: &MmapMemory, sysopen: &mut OpenParams, file_map: &mut UhyveFileMap)
378399

379400
/// Handles an close syscall by closing the file on the host.
380401
fn close(sysclose: &mut CloseParams, file_map: &mut UhyveFileMap) {
381-
sysclose.ret = if file_map
382-
.fdmap
383-
.is_fd_present(GuestFd(sysclose.fd.into_raw_fd()))
384-
{
402+
sysclose.ret = if file_map.fdmap.is_fd_present(GuestFd(sysclose.fd)) {
385403
match file_map.fdmap.remove(GuestFd(sysclose.fd)) {
386404
Some(FdData::Raw(fd)) => {
387405
if unsafe { libc::close(fd) } < 0 {

src/isolation/filemap/mod.rs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
use std::{
2-
ffi::{CStr, CString, OsString},
3-
os::unix::ffi::OsStrExt,
4-
path::PathBuf,
5-
};
1+
use std::{ffi::CString, os::unix::ffi::OsStrExt, path::PathBuf};
62

73
#[cfg(target_os = "linux")]
84
use libc::{O_DIRECT, O_SYNC};
@@ -99,17 +95,17 @@ impl UhyveFileMap {
9995
/// Returns the host_path on the host filesystem given a requested guest_path, if it exists.
10096
///
10197
/// * `guest_path` - The guest path that is to be looked up in the map.
102-
pub fn get_host_path(&self, guest_path: &CStr) -> Option<UhyveMapLeaf> {
103-
tree::resolve_guest_path(&self.root, guest_path.to_bytes())
98+
pub fn get_host_path(&self, guest_path: &str) -> Option<UhyveMapLeaf> {
99+
tree::resolve_guest_path(&self.root, guest_path.as_bytes())
104100
}
105101

106102
/// Returns an array of all host paths (for Landlock).
107103
#[cfg(target_os = "linux")]
108-
pub(crate) fn get_all_host_paths(&self) -> impl Iterator<Item = &std::ffi::OsStr> {
109-
tree::get_all_host_paths(&self.root).map(|i| i.as_os_str())
104+
pub(crate) fn get_all_host_paths(&self) -> impl Iterator<Item = &std::path::Path> {
105+
tree::get_all_host_paths(&self.root)
110106
}
111107

112-
/// Returns an iterator (non-unique) over all mountable guest directories.
108+
/// Returns an iterator over all mountable guest directories.
113109
pub(crate) fn get_all_guest_dirs(&self) -> impl Iterator<Item = String> {
114110
tree::get_all_guest_dirs(&self.root)
115111
}
@@ -139,15 +135,15 @@ impl UhyveFileMap {
139135
///
140136
/// Note that this is also used for the entire setup of the uhyve file tree,
141137
/// and this also called for the entire initial mapping.
142-
pub fn create_leaf(&mut self, guest_path: &CStr, file: UhyveMapLeaf) -> bool {
143-
tree::create_leaf(&mut self.root, guest_path.to_bytes(), file)
138+
pub fn create_leaf(&mut self, guest_path: &str, file: UhyveMapLeaf) -> bool {
139+
tree::create_leaf(&mut self.root, guest_path.as_bytes(), file)
144140
}
145141

146142
/// Inserts an opened temporary file into the file map. Returns a CString so that
147143
/// the file can be directly used by [crate::hypercall::open].
148144
///
149145
/// * `guest_path` - The requested guest path.
150-
pub fn create_temporary_file(&mut self, guest_path: &CStr) -> Option<CString> {
146+
pub fn create_temporary_file(&mut self, guest_path: &str) -> Option<CString> {
151147
let host_path = self.tempdir.path().join(Uuid::new_v4().to_string());
152148
trace!("create_temporary_file (host_path): {host_path:#?}");
153149
let ret = CString::new(host_path.as_os_str().as_bytes()).unwrap();
@@ -159,7 +155,7 @@ impl UhyveFileMap {
159155
}
160156

161157
/// Attempt to remove a file. Note that this will fail on non-empty directories.
162-
pub fn unlink(&mut self, guest_path: &CStr) -> Result<Option<OsString>, ()> {
163-
tree::unlink(&mut self.root, guest_path.to_bytes()).map(|i| i.map(|j| j.into_os_string()))
158+
pub fn unlink(&mut self, guest_path: &str) -> Result<Option<PathBuf>, ()> {
159+
tree::unlink(&mut self.root, guest_path.as_bytes())
164160
}
165161
}

src/isolation/filemap/tests.rs

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -43,39 +43,37 @@ fn test_uhyvefilemap() {
4343
);
4444

4545
assert_eq!(
46-
map.get_host_path(c"readme_file.md")
46+
map.get_host_path("readme_file.md")
4747
.unwrap()
4848
.unwrap_on_host(),
4949
OsString::from(&map_results[0])
5050
);
5151
assert_eq!(
52-
map.get_host_path(c"guest_folder").unwrap().unwrap_on_host(),
52+
map.get_host_path("guest_folder").unwrap().unwrap_on_host(),
5353
OsString::from(&map_results[1])
5454
);
5555
assert_eq!(
56-
map.get_host_path(c"guest_symlink")
57-
.unwrap()
58-
.unwrap_on_host(),
56+
map.get_host_path("guest_symlink").unwrap().unwrap_on_host(),
5957
OsString::from(&map_results[2])
6058
);
6159
assert_eq!(
62-
map.get_host_path(c"guest_dangling_symlink")
60+
map.get_host_path("guest_dangling_symlink")
6361
.unwrap()
6462
.unwrap_on_host(),
6563
OsString::from(&map_results[3])
6664
);
6765
assert_eq!(
68-
map.get_host_path(c"guest_file").unwrap().unwrap_on_host(),
66+
map.get_host_path("guest_file").unwrap().unwrap_on_host(),
6967
OsString::from(&map_results[4])
7068
);
7169
assert_eq!(
72-
map.get_host_path(c"guest_file_symlink")
70+
map.get_host_path("guest_file_symlink")
7371
.unwrap()
7472
.unwrap_on_host(),
7573
OsString::from(&map_results[5])
7674
);
7775

78-
assert!(map.get_host_path(c"this_file_is_not_mapped").is_none());
76+
assert!(map.get_host_path("this_file_is_not_mapped").is_none());
7977
}
8078

8179
#[test]
@@ -110,12 +108,7 @@ fn test_uhyvefilemap_directory() {
110108
},
111109
);
112110

113-
let mut found_host_path = map.get_host_path(
114-
CString::new(target_guest_path.as_os_str().as_bytes())
115-
.unwrap()
116-
.as_c_str(),
117-
);
118-
111+
let mut found_host_path = map.get_host_path(target_guest_path.to_str().unwrap());
119112
assert_eq!(found_host_path.unwrap().unwrap_on_host(), target_host_path);
120113

121114
// Tests successful directory traversal of the child directory.
@@ -124,11 +117,7 @@ fn test_uhyvefilemap_directory() {
124117
target_host_path.pop();
125118
target_guest_path.pop();
126119

127-
found_host_path = map.get_host_path(
128-
CString::new(target_guest_path.as_os_str().as_bytes())
129-
.unwrap()
130-
.as_c_str(),
131-
);
120+
found_host_path = map.get_host_path(target_guest_path.to_str().unwrap());
132121
assert_eq!(found_host_path.unwrap().unwrap_on_host(), target_host_path);
133122

134123
// Tests directory traversal leading to valid symbolic link with an
@@ -154,11 +143,7 @@ fn test_uhyvefilemap_directory() {
154143
target_guest_path = PathBuf::from("/root/this_symlink_leads_to_a_file");
155144
target_host_path = fixture_path.clone();
156145
target_host_path.push("this_folder_exists/file_in_folder.txt");
157-
found_host_path = map.get_host_path(
158-
CString::new(target_guest_path.as_os_str().as_bytes())
159-
.unwrap()
160-
.as_c_str(),
161-
);
146+
found_host_path = map.get_host_path(target_guest_path.to_str().unwrap());
162147
assert_eq!(found_host_path.unwrap().unwrap_on_host(), target_host_path);
163148

164149
// Tests directory traversal with no maps
@@ -172,11 +157,7 @@ fn test_uhyvefilemap_directory() {
172157
sync: false,
173158
},
174159
);
175-
found_host_path = map.get_host_path(
176-
CString::new(target_guest_path.as_os_str().as_bytes())
177-
.unwrap()
178-
.as_c_str(),
179-
);
160+
found_host_path = map.get_host_path(target_guest_path.to_str().unwrap());
180161
assert!(found_host_path.is_none());
181162
}
182163

src/vm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ impl<VirtBackend: VirtualizationBackend> UhyveVm<VirtBackend> {
353353
file_sandbox_mode,
354354
kernel_path.into(),
355355
output,
356-
host_paths,
356+
host_paths.map(|i| i.as_os_str()),
357357
temp_dir,
358358
#[cfg(feature = "instrument")]
359359
trace,

0 commit comments

Comments
 (0)