Skip to content
Open
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
51 changes: 43 additions & 8 deletions src/rawposix/src/fs_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,17 @@ pub extern "C" fn openat_syscall(
Ok(p) => p,
Err(_) => return syscall_error(Errno::EINVAL, "openat", "invalid path"),
};

// Reject absolute paths — the host kernel ignores dirfd for absolute paths,
// which would allow a guest to escape the cage sandbox.
if raw_path.as_bytes().starts_with(b"/") {
return syscall_error(
Errno::EACCES,
"openat",
"absolute path bypasses dirfd sandbox",
);
}

let c_path = match CString::new(raw_path) {
Ok(c) => c,
Err(_) => return syscall_error(Errno::EINVAL, "openat", "invalid path"),
Expand Down Expand Up @@ -2108,16 +2119,19 @@ pub extern "C" fn readlinkat_syscall(

let raw_path = match get_cstr(path_arg) {
Ok(p) => p,
Err(_) => {
return syscall_error(
Errno::EINVAL,
"readlinkat",
"invalid
path",
)
}
Err(_) => return syscall_error(Errno::EINVAL, "readlinkat", "invalid path"),
};

// Reject absolute paths — the host kernel ignores dirfd for absolute paths,
// which would allow a guest to read arbitrary host filesystem paths.
if raw_path.as_bytes().starts_with(b"/") {
return syscall_error(
Errno::EACCES,
"readlinkat",
"absolute path bypasses dirfd sandbox",
);
}

unsafe { libc::readlinkat(kernel_fd, raw_path.as_ptr() as *const c_char, buf, buflen) }
};

Expand Down Expand Up @@ -2328,6 +2342,17 @@ pub extern "C" fn unlinkat_syscall(
// For this case, we pass the provided pathname directly.
let pathname = pathname_arg;
let tmp_cstr = get_cstr(pathname).unwrap();

// Reject absolute paths — the host kernel ignores dirfd for absolute paths,
// which would allow a guest to escape the cage sandbox.
if tmp_cstr.as_bytes().starts_with(b"/") {
return syscall_error(
Errno::EACCES,
"unlinkat",
"absolute path bypasses dirfd sandbox",
);
}

c_path = CString::new(tmp_cstr).unwrap();
vfd.underfd as i32
};
Expand Down Expand Up @@ -4791,11 +4816,21 @@ pub extern "C" fn symlinkat_syscall(
// Use the raw (untranslated) linkpath here intentionally — sc_convert_path_to_host
// resolves relative to CWD, but symlinkat interprets linkpath relative to dirfd.
// The kernel handles that resolution, so we pass the original guest pointer.
// However, we must reject absolute paths because the host kernel ignores dirfd
// for absolute paths, which would allow a guest to escape the cage sandbox.
let raw_linkpath = match get_cstr(linkpath_arg) {
Ok(p) => p,
Err(_) => return syscall_error(Errno::EINVAL, "symlinkat", "invalid linkpath"),
};

if raw_linkpath.as_bytes().starts_with(b"/") {
return syscall_error(
Errno::EACCES,
"symlinkat",
"absolute path bypasses dirfd sandbox",
);
}

unsafe {
libc::symlinkat(
target.as_ptr() as *const libc::c_char,
Expand Down