Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 76 additions & 27 deletions src/hypercall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
HypervisorResult,
isolation::{
fd::{FdData, GuestFd, UhyveFileDescriptorLayer},
filemap::UhyveFileMap,
filemap::{UhyveFileMap, UhyveMapLeaf},
},
mem::MmapMemory,
params::EnvVars,
Expand Down Expand Up @@ -246,7 +246,7 @@ fn translate_last_errno() -> Option<i32> {
($($x:ident),*) => {{[ $((libc::$x, hermit_abi::errno::$x)),* ]}}
}
for (e_host, e_guest) in error_pairs!(
EBADF, EEXIST, EFAULT, EINVAL, EIO, EOVERFLOW, EPERM, ENOENT, EROFS
EBADF, EEXIST, EFAULT, EINVAL, EIO, EISDIR, EOVERFLOW, EPERM, ENOENT, EROFS
) {
if errno == e_host {
return Some(e_guest);
Expand All @@ -259,34 +259,64 @@ fn translate_last_errno() -> Option<i32> {
None
}

/// Decodes a guest path given at address `path_addr` in `mem`.
///
/// # Safety
///
/// The calling convention of hypercalls ensures that the given address doesn't alias with anything mutable.
/// The return value is only valid for the duration of the hypercall.
unsafe fn decode_guest_path(mem: &MmapMemory, path_addr: GuestPhysAddr) -> Option<&str> {
let requested_path_ptr = mem.host_address(path_addr).unwrap() as *const i8;
unsafe { CStr::from_ptr(requested_path_ptr) }.to_str().ok()
}

/// unlink deletes a name from the filesystem. This is used to handle `unlink` syscalls from the guest.
///
/// Note for when using Landlock: Unlinking files results in them being veiled. If a text
/// Note for when using Landlock: Unlinking files results in them being veiled. If a
/// file (that existed during initialization) called `log.txt` is unlinked, attempting to
/// open `log.txt` again will result in an error.
fn unlink(mem: &MmapMemory, sysunlink: &mut UnlinkParams, file_map: &mut UhyveFileMap) {
let requested_path_ptr = mem.host_address(sysunlink.name).unwrap() as *const i8;
let guest_path = unsafe { CStr::from_ptr(requested_path_ptr) };
sysunlink.ret = if let Some(host_path) = file_map.get_host_path(guest_path) {
// We can safely unwrap here, as host_path.as_bytes will never contain internal \0 bytes
// As host_path_c_string is a valid CString, this implementation is presumed to be safe.
let host_path_c_string = CString::new(host_path.as_bytes()).unwrap();
if unsafe { libc::unlink(host_path_c_string.as_c_str().as_ptr()) } < 0 {
-translate_last_errno().unwrap_or(1)
} else {
let guest_path = if let Some(guest_path) = unsafe { decode_guest_path(mem, sysunlink.name) } {
guest_path
} else {
error!("The kernel requested to unlink() an non-UTF8 path: Rejecting...");
sysunlink.ret = -EINVAL;
return;
};
sysunlink.ret = match file_map.unlink(guest_path) {
Ok(Some(host_path)) => {
// We can safely unwrap here, as host_path.as_bytes will never contain internal \0 bytes
// As host_path_c_string is a valid CString, this implementation is presumed to be safe.
let host_path_c_string = CString::new(host_path.as_os_str().as_bytes()).unwrap();
if unsafe { libc::unlink(host_path_c_string.as_c_str().as_ptr()) } < 0 {
-translate_last_errno().unwrap_or(1)
} else {
0
}
}
Ok(None) => {
// Removed virtual entry
0
}
} else {
error!("The kernel requested to unlink() an unknown path ({guest_path:?}): Rejecting...");
-ENOENT
Err(()) => {
error!(
"The kernel requested to unlink() an unknown path ({guest_path:?}): Rejecting..."
);
-ENOENT
}
};
}

/// Handles an open syscall by opening a file on the host.
fn open(mem: &MmapMemory, sysopen: &mut OpenParams, file_map: &mut UhyveFileMap) {
let requested_path_ptr = mem.host_address(sysopen.name).unwrap() as *const i8;
let guest_path = if let Some(guest_path) = unsafe { decode_guest_path(mem, sysopen.name) } {
guest_path
} else {
error!("The kernel requested to open() an non-UTF8 path: Rejecting...");
sysopen.ret = -EINVAL;
return;
};
let mut flags = sysopen.flags & ALLOWED_OPEN_FLAGS;
let guest_path = unsafe { CStr::from_ptr(requested_path_ptr) };
// See: https://lwn.net/Articles/926782/
// See: https://github.com/hermit-os/kernel/commit/71bc629
if (flags & (O_DIRECTORY | O_CREAT)) == (O_DIRECTORY | O_CREAT) {
Expand Down Expand Up @@ -319,10 +349,25 @@ fn open(mem: &MmapMemory, sysopen: &mut OpenParams, file_map: &mut UhyveFileMap)

sysopen.ret = if let Some(host_path) = file_map.get_host_path(guest_path) {
debug!("{guest_path:#?} found in file map.");
// We can safely unwrap here, as host_path.as_bytes will never contain internal \0 bytes
// As host_path_c_string is a valid CString, this implementation is presumed to be safe.
let host_path_c_string = CString::new(host_path.as_bytes()).unwrap();
do_open(&mut file_map.fdmap, host_path_c_string, flags, sysopen.mode)
match host_path {
UhyveMapLeaf::OnHost(host_path) => {
// We can safely unwrap here, as host_path.as_bytes will never contain internal \0 bytes
// As host_path_c_string is a valid CString, this implementation is presumed to be safe.
let host_path_c_string = CString::new(host_path.as_os_str().as_bytes()).unwrap();
do_open(&mut file_map.fdmap, host_path_c_string, flags, sysopen.mode)
}
UhyveMapLeaf::Virtual(data) => {
if let Some(guest_fd) = file_map.fdmap.insert(FdData::Virtual {
// The following only clones a pointer, and increases an `Arc` refcount.
data: data.clone(),
offset: 0,
}) {
guest_fd.0
} else {
-ENOENT
}
}
}
} else {
debug!("{guest_path:#?} not found in file map.");
if (flags & O_CREAT) == O_CREAT {
Expand All @@ -336,8 +381,15 @@ fn open(mem: &MmapMemory, sysopen: &mut OpenParams, file_map: &mut UhyveFileMap)
flags |= file_map.get_io_mode_flags();
}

let host_path_c_string = file_map.create_temporary_file(guest_path);
do_open(&mut file_map.fdmap, host_path_c_string, flags, sysopen.mode)
match file_map.create_temporary_file(guest_path) {
Some(host_path_c_string) => {
do_open(&mut file_map.fdmap, host_path_c_string, flags, sysopen.mode)
}
None => {
debug!("Returning -EINVAL for {guest_path:#?}");
-EINVAL
}
}
} else {
debug!("Returning -ENOENT for {guest_path:#?}");
-ENOENT
Expand All @@ -347,10 +399,7 @@ fn open(mem: &MmapMemory, sysopen: &mut OpenParams, file_map: &mut UhyveFileMap)

/// Handles an close syscall by closing the file on the host.
fn close(sysclose: &mut CloseParams, file_map: &mut UhyveFileMap) {
sysclose.ret = if file_map
.fdmap
.is_fd_present(GuestFd(sysclose.fd.into_raw_fd()))
{
sysclose.ret = if file_map.fdmap.is_fd_present(GuestFd(sysclose.fd)) {
match file_map.fdmap.remove(GuestFd(sysclose.fd)) {
Some(FdData::Raw(fd)) => {
if unsafe { libc::close(fd) } < 0 {
Expand Down
3 changes: 1 addition & 2 deletions src/isolation/fd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ pub enum FdData {
/// A host file descriptor
Raw(RawFd),

#[allow(dead_code)]
/// An in-memory slice (possibly mmap-ed)
/// An in-memory slice.
///
/// SAFETY: It is not allowed for `data` to point into guest memory.
Virtual { data: Arc<[u8]>, offset: u64 },
Expand Down
Loading