diff --git a/crates/sandlock-core/src/checkpoint/capture.rs b/crates/sandlock-core/src/checkpoint/capture.rs index 9b7b445a..bb76c311 100644 --- a/crates/sandlock-core/src/checkpoint/capture.rs +++ b/crates/sandlock-core/src/checkpoint/capture.rs @@ -9,14 +9,14 @@ use crate::error::{SandlockError, SandboxRuntimeError}; pub(crate) fn ptrace_seize(pid: i32) -> io::Result<()> { let ret = unsafe { - libc::ptrace(libc::PTRACE_SEIZE as libc::c_uint, pid, 0, 0) + libc::ptrace(libc::PTRACE_SEIZE, pid, 0, 0) }; if ret < 0 { return Err(io::Error::last_os_error()); } // PTRACE_INTERRUPT stops the tracee without SIGSTOP side effects let ret = unsafe { - libc::ptrace(libc::PTRACE_INTERRUPT as libc::c_uint, pid, 0, 0) + libc::ptrace(libc::PTRACE_INTERRUPT, pid, 0, 0) }; if ret < 0 { return Err(io::Error::last_os_error()); diff --git a/crates/sandlock-core/src/chroot/dispatch.rs b/crates/sandlock-core/src/chroot/dispatch.rs index 9d76b78d..7b7ee140 100644 --- a/crates/sandlock-core/src/chroot/dispatch.rs +++ b/crates/sandlock-core/src/chroot/dispatch.rs @@ -1024,7 +1024,7 @@ pub(crate) async fn handle_chroot_exec( let child_interp_fd = unsafe { libc::ioctl( notif_fd, - SECCOMP_IOCTL_NOTIF_ADDFD as libc::c_ulong, + SECCOMP_IOCTL_NOTIF_ADDFD as libc::Ioctl, &addfd_interp as *const _, ) }; @@ -1073,7 +1073,7 @@ pub(crate) async fn handle_chroot_exec( let child_fd = unsafe { libc::ioctl( notif_fd, - SECCOMP_IOCTL_NOTIF_ADDFD as libc::c_ulong, + SECCOMP_IOCTL_NOTIF_ADDFD as libc::Ioctl, &addfd as *const _, ) }; diff --git a/crates/sandlock-core/src/cow/dispatch.rs b/crates/sandlock-core/src/cow/dispatch.rs index 2555e85e..e845ac99 100644 --- a/crates/sandlock-core/src/cow/dispatch.rs +++ b/crates/sandlock-core/src/cow/dispatch.rs @@ -1095,7 +1095,7 @@ pub(crate) async fn handle_cow_exec( let child_fd = unsafe { libc::ioctl( notif_fd, - crate::sys::structs::SECCOMP_IOCTL_NOTIF_ADDFD as libc::c_ulong, + crate::sys::structs::SECCOMP_IOCTL_NOTIF_ADDFD as libc::Ioctl, &addfd as *const _, ) }; @@ -1380,7 +1380,7 @@ pub(crate) async fn handle_cow_chdir( let child_fd = unsafe { libc::ioctl( notif_fd, - crate::sys::structs::SECCOMP_IOCTL_NOTIF_ADDFD as libc::c_ulong, + crate::sys::structs::SECCOMP_IOCTL_NOTIF_ADDFD as libc::Ioctl, &addfd as *const _, ) }; diff --git a/crates/sandlock-core/src/freeze.rs b/crates/sandlock-core/src/freeze.rs index 40bdb7a5..d1d57649 100644 --- a/crates/sandlock-core/src/freeze.rs +++ b/crates/sandlock-core/src/freeze.rs @@ -119,7 +119,7 @@ fn seize_and_interrupt(tid: i32) -> io::Result { } let ret = unsafe { - libc::ptrace(libc::PTRACE_SEIZE as libc::c_uint, tid, 0, 0) + libc::ptrace(libc::PTRACE_SEIZE, tid, 0, 0) }; if ret < 0 { let err = io::Error::last_os_error(); @@ -132,7 +132,7 @@ fn seize_and_interrupt(tid: i32) -> io::Result { // before returning so we don't leave the task traced-but-running. let ret = unsafe { - libc::ptrace(libc::PTRACE_INTERRUPT as libc::c_uint, tid, 0, 0) + libc::ptrace(libc::PTRACE_INTERRUPT, tid, 0, 0) }; if ret < 0 { let err = io::Error::last_os_error(); diff --git a/crates/sandlock-core/src/landlock.rs b/crates/sandlock-core/src/landlock.rs index 490e32b9..5cef470d 100644 --- a/crates/sandlock-core/src/landlock.rs +++ b/crates/sandlock-core/src/landlock.rs @@ -150,19 +150,21 @@ pub fn abi_version() -> Result { /// Open `path` and add a Landlock path-beneath rule to `ruleset_fd`. fn add_path_rule(ruleset_fd: &OwnedFd, path: &Path, access: u64) -> Result<(), ConfinementError> { - use std::os::unix::fs::OpenOptionsExt; + use std::os::fd::FromRawFd; // Reference the path with O_PATH rather than opening it for I/O: O_PATH does // not block on FIFOs and needs no read permission on the target, so a rule // on a FIFO or a write-only/no-read path neither hangs nor fails here. An // O_PATH fd still supports fstat (the file-type check below) and serves as a - // valid parent_fd for landlock_add_rule. - let file = std::fs::OpenOptions::new() - .read(true) - .custom_flags(libc::O_PATH | libc::O_CLOEXEC) - .open(path) - .map_err(|e| { - ConfinementError::Landlock(format!("open path {:?} failed: {}", path, e)) - })?; + // valid parent_fd for landlock_add_rule. Call open(2) directly: musl folds + // O_PATH into O_ACCMODE, which OpenOptions::custom_flags masks out. + let c_path = std::ffi::CString::new(path.as_os_str().as_encoded_bytes()) + .map_err(|e| ConfinementError::Landlock(format!("open path {:?} failed: {}", path, e)))?; + let raw = unsafe { libc::open(c_path.as_ptr(), libc::O_PATH | libc::O_CLOEXEC) }; + if raw < 0 { + let e = std::io::Error::last_os_error(); + return Err(ConfinementError::Landlock(format!("open path {:?} failed: {}", path, e))); + } + let file = std::fs::File::from(unsafe { OwnedFd::from_raw_fd(raw) }); // Directory-only access rights (READ_DIR, MAKE_*, REMOVE_*, REFER) make // landlock_add_rule fail with EINVAL on a non-directory path. Mask the diff --git a/crates/sandlock-core/src/network/send_engine.rs b/crates/sandlock-core/src/network/send_engine.rs index cafc5236..268ec7ab 100644 --- a/crates/sandlock-core/src/network/send_engine.rs +++ b/crates/sandlock-core/src/network/send_engine.rs @@ -42,7 +42,7 @@ fn send_materialized_at(fd: RawFd, m: &MaterializedMsg, offset: usize, flags: i3 } if let Some(ref c) = m.control { msg.msg_control = c.as_ptr() as *mut libc::c_void; - msg.msg_controllen = c.len(); + msg.msg_controllen = c.len() as _; } } msg.msg_iov = &iov as *const libc::iovec as *mut libc::iovec; diff --git a/crates/sandlock-core/src/resource.rs b/crates/sandlock-core/src/resource.rs index 398d45a2..a4610f77 100644 --- a/crates/sandlock-core/src/resource.rs +++ b/crates/sandlock-core/src/resource.rs @@ -315,7 +315,7 @@ fn process_creation_worker( | libc::PTRACE_O_TRACEVFORK | libc::PTRACE_O_TRACECLONE | libc::PTRACE_O_TRACESYSGOOD) as libc::c_ulong; - let ret = unsafe { libc::ptrace(libc::PTRACE_SEIZE as libc::c_uint, caller_tid, 0, opts) }; + let ret = unsafe { libc::ptrace(libc::PTRACE_SEIZE, caller_tid, 0, opts) }; if ret < 0 { let errno = io::Error::last_os_error().raw_os_error().unwrap_or(libc::EPERM); let _ = attached_tx.send(Err(errno)); @@ -441,13 +441,13 @@ fn run_creation_event_loop(caller_tid: i32, ctx: &Arc) -> io::Res // Some other signal-delivery-stop in the window: forward the pending // signal and keep waiting for the fork event. let inject = if stopsig == libc::SIGTRAP { 0 } else { stopsig as libc::c_ulong }; - ptrace_resume(caller_tid, libc::PTRACE_CONT, inject)?; + ptrace_cont(caller_tid, inject)?; } } -/// `ptrace(request, tid, 0, data)` returning an error on failure. -fn ptrace_resume(tid: i32, request: libc::c_uint, data: libc::c_ulong) -> io::Result<()> { - let ret = unsafe { libc::ptrace(request, tid, 0, data) }; +/// Resume `tid` with `sig` injected (0 for none), failing on error. +fn ptrace_cont(tid: i32, sig: libc::c_ulong) -> io::Result<()> { + let ret = unsafe { libc::ptrace(libc::PTRACE_CONT, tid, 0, sig) }; if ret < 0 { return Err(io::Error::last_os_error()); } @@ -461,7 +461,7 @@ fn handle_fork_event(caller_tid: i32, ctx: &Arc) -> io::Result io::Result { let mut notif: SeccompNotif = unsafe { std::mem::zeroed() }; let ret = unsafe { - libc::ioctl(fd, SECCOMP_IOCTL_NOTIF_RECV as libc::c_ulong, &mut notif as *mut _) + libc::ioctl(fd, SECCOMP_IOCTL_NOTIF_RECV as libc::Ioctl, &mut notif as *mut _) }; if ret < 0 { Err(io::Error::last_os_error()) @@ -945,7 +945,7 @@ fn inject_fd_and_send(fd: RawFd, id: u64, srcfd: RawFd, newfd_flags: u32) -> io: newfd_flags, }; let ret = unsafe { - libc::ioctl(fd, SECCOMP_IOCTL_NOTIF_ADDFD as libc::c_ulong, &addfd as *const _) + libc::ioctl(fd, SECCOMP_IOCTL_NOTIF_ADDFD as libc::Ioctl, &addfd as *const _) }; if ret < 0 { Err(io::Error::last_os_error()) @@ -965,7 +965,7 @@ fn inject_fd(fd: RawFd, id: u64, srcfd: RawFd, targetfd: i32) -> io::Result<()> newfd_flags: 0, }; let ret = unsafe { - libc::ioctl(fd, SECCOMP_IOCTL_NOTIF_ADDFD as libc::c_ulong, &addfd as *const _) + libc::ioctl(fd, SECCOMP_IOCTL_NOTIF_ADDFD as libc::Ioctl, &addfd as *const _) }; if ret < 0 { Err(io::Error::last_os_error()) @@ -977,7 +977,7 @@ fn inject_fd(fd: RawFd, id: u64, srcfd: RawFd, targetfd: i32) -> io::Result<()> /// Raw ioctl to send a notification response. fn send_resp_raw(fd: RawFd, resp: &SeccompNotifResp) -> io::Result<()> { let ret = unsafe { - libc::ioctl(fd, SECCOMP_IOCTL_NOTIF_SEND as libc::c_ulong, resp as *const _) + libc::ioctl(fd, SECCOMP_IOCTL_NOTIF_SEND as libc::Ioctl, resp as *const _) }; if ret < 0 { Err(io::Error::last_os_error()) @@ -990,7 +990,7 @@ fn send_resp_raw(fd: RawFd, resp: &SeccompNotifResp) -> io::Result<()> { /// ioctl(fd, SECCOMP_IOCTL_NOTIF_ID_VALID, &id) pub(crate) fn id_valid(fd: RawFd, id: u64) -> io::Result<()> { let ret = unsafe { - libc::ioctl(fd, SECCOMP_IOCTL_NOTIF_ID_VALID as libc::c_ulong, &id as *const _) + libc::ioctl(fd, SECCOMP_IOCTL_NOTIF_ID_VALID as libc::Ioctl, &id as *const _) }; if ret < 0 { Err(io::Error::last_os_error()) @@ -1003,7 +1003,7 @@ pub(crate) fn id_valid(fd: RawFd, id: u64) -> io::Result<()> { fn try_set_sync_wakeup(fd: RawFd) { let flags: u64 = SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP as u64; unsafe { - libc::ioctl(fd, SECCOMP_IOCTL_NOTIF_SET_FLAGS as libc::c_ulong, &flags as *const _); + libc::ioctl(fd, SECCOMP_IOCTL_NOTIF_SET_FLAGS as libc::Ioctl, &flags as *const _); } } @@ -1256,20 +1256,24 @@ fn read_exec_ptr_array( } } -/// Read exactly `len` bytes starting at `addr`, chunked at page boundaries. -fn read_exec_range( +/// Whether the `len` bytes at `addr` hold no NUL. Checked one page at a time +/// so the scan stops at a string's terminator before touching a later page, +/// which may be unmapped: musl's allocator leaves gaps between chunks. +fn nul_free_run( read: &mut impl FnMut(u64, usize) -> Result, NotifError>, addr: u64, len: usize, -) -> Result, NotifError> { - let mut out = Vec::with_capacity(len); +) -> Result { + let end = addr + len as u64; let mut cur = addr; - while out.len() < len { - let chunk = ((4096 - cur % 4096) as usize).min(len - out.len()); - out.extend_from_slice(&read(cur, chunk)?); + while cur < end { + let chunk = ((4096 - cur % 4096) as usize).min((end - cur) as usize); + if read(cur, chunk)?.contains(&0) { + return Ok(false); + } cur += chunk as u64; } - Ok(out) + Ok(true) } /// Read a NUL-terminated string (NUL excluded) of at most @@ -1358,8 +1362,7 @@ fn plan_exec_rewrite( below.dedup(); let mut nul_free_from = path_ptr; for &p in below.iter().rev() { - let seg = read_exec_range(read, p, (nul_free_from - p) as usize)?; - if seg.contains(&0) { + if !nul_free_run(read, p, (nul_free_from - p) as usize)? { break; } relocate(&mut buf, &mut relocated, read, p)?; diff --git a/crates/sandlock-core/src/sys/fs.rs b/crates/sandlock-core/src/sys/fs.rs index dd610677..e394278c 100644 --- a/crates/sandlock-core/src/sys/fs.rs +++ b/crates/sandlock-core/src/sys/fs.rs @@ -692,6 +692,8 @@ mod tests { #[test] fn statx_confines_symlinked_parent() { + // The libc crate only exposes this constant for glibc targets. + const STATX_BASIC_STATS: u32 = 0x7ff; let tmp = TempDir::new().unwrap(); let root = tmp.path(); std::fs::write(root.join("f"), "data").unwrap(); @@ -699,9 +701,9 @@ mod tests { let mut buf = vec![0u8; 256]; // In-tree file resolves; escaping parent does not. - skip_if_nosys!(statx_in_root(root, "f", 0, libc::STATX_BASIC_STATS, &mut buf)).unwrap(); + skip_if_nosys!(statx_in_root(root, "f", 0, STATX_BASIC_STATS, &mut buf)).unwrap(); assert_eq!( - statx_in_root(root, "dirlink/group", 0, libc::STATX_BASIC_STATS, &mut buf), + statx_in_root(root, "dirlink/group", 0, STATX_BASIC_STATS, &mut buf), Err(libc::ENOENT) ); }