Skip to content

Commit bfb16b1

Browse files
abcxffclaude
andcommitted
feat(macos): port the native runtime + sidecar to macOS
The native stack previously compiled only on Linux. This adds macOS (aarch64/x86_64) support, gated entirely behind cfg(target_os) so the Linux paths are untouched. v8-runtime (per-thread CPU accounting): - timeout.rs: pthread_getcpuclockid has no macOS equivalent; the CpuBudgetGuard watchdog now reads the execution thread's CPU time via the Mach thread port (pthread_mach_thread_np + thread_info(THREAD_BASIC_INFO)), readable cross-thread like the Linux clockid. - bridge.rs: RUSAGE_THREAD is Linux-only; process.cpuUsage/resourceUsage get per-thread CPU time from thread_info, the rest best-effort from RUSAGE_SELF. sidecar (host-mount filesystem confinement): - macos_fs.rs: macOS has no openat2(RESOLVE_BENEATH); resolve-beneath path resolution now goes through cap-std (audited userspace walk; openat2 on Linux, fd-relative O_NOFOLLOW walk elsewhere). F_GETPATH replaces readlink on /proc/self/fd; /dev/fd/N replaces /proc/self/fd/N. - host_dir.rs + filesystem.rs: open_beneath / mapped-runtime helpers resolve via cap-std on macOS; O_PATH -> read-only anchor, O_TMPFILE -> empty. All nine mapped-runtime child ops (mkdir/rmdir/unlink/symlink/rename/readlink/lstat/ lutimes) converted from the Linux /proc/self/fd-join idiom to fd-relative *at calls, since /dev/fd/N cannot have children appended. lstat goes through a platform-neutral HostStat translator; rename uses renameat with a real-path EXDEV fallback. Also fixes a latent dangling-fd bug in the mapped utimes path (the resolved handle is now held across the operation on both platforms). - execution.rs: waitid/WNOWAIT is unavailable in nix on macOS; the child-status poll uses waitpid(WNOHANG) there. - Plus latent macOS type-width fixes (mode_t/dev_t/nlink_t). All sidecar lib tests pass on macOS, including every host-mount escape test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 19f34bd commit bfb16b1

9 files changed

Lines changed: 915 additions & 98 deletions

File tree

Cargo.lock

Lines changed: 90 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/sidecar/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ url = "2"
5656
vbare = { workspace = true }
5757
vfs = { workspace = true }
5858

59+
[target.'cfg(target_os = "macos")'.dependencies]
60+
# macOS has no openat2/RESOLVE_BENEATH; cap-std provides the audited
61+
# resolve-beneath path resolution used in place of openat2 on this platform.
62+
cap-std = "3"
63+
5964
[build-dependencies]
6065
vbare-compiler = { workspace = true }
6166

crates/sidecar/src/execution.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,11 @@ use http::{HeaderMap, HeaderName, HeaderValue, Method, Request, Response, Uri};
6262
use md5::Md5;
6363
use nix::libc;
6464
use nix::sys::signal::{kill as send_signal, Signal};
65-
use nix::sys::wait::{waitid as wait_on_child, Id as WaitId, WaitPidFlag, WaitStatus};
65+
use nix::sys::wait::WaitStatus;
66+
#[cfg(not(target_os = "macos"))]
67+
use nix::sys::wait::{waitid as wait_on_child, Id as WaitId, WaitPidFlag};
68+
#[cfg(target_os = "macos")]
69+
use nix::sys::wait::{waitpid, WaitPidFlag};
6670
use nix::unistd::Pid;
6771
use openssl::bn::{BigNum, BigNumContext};
6872
use openssl::derive::Deriver;
@@ -20991,6 +20995,7 @@ pub(crate) fn runtime_child_is_alive(child_pid: u32) -> Result<bool, SidecarErro
2099120995
Ok(runtime_child_exit_status(child_pid)?.is_none())
2099220996
}
2099320997

20998+
#[cfg(not(target_os = "macos"))]
2099420999
fn runtime_child_exit_status(child_pid: u32) -> Result<Option<i32>, SidecarError> {
2099521000
if child_pid == 0 {
2099621001
return Ok(Some(0));
@@ -21016,6 +21021,30 @@ fn runtime_child_exit_status(child_pid: u32) -> Result<Option<i32>, SidecarError
2101621021
}
2101721022
}
2101821023

21024+
// macOS nix exposes no `waitid`/`WNOWAIT`, so we poll with `waitpid(WNOHANG)`.
21025+
// NOTE: unlike Linux's `waitid(WNOWAIT)`, `waitpid` REAPS an exited child rather
21026+
// than leaving it waitable. That is correct for this poll (the sidecar is the
21027+
// reaping parent), but a second status query after exit returns ECHILD → treated
21028+
// as "exited(0)" below.
21029+
#[cfg(target_os = "macos")]
21030+
fn runtime_child_exit_status(child_pid: u32) -> Result<Option<i32>, SidecarError> {
21031+
if child_pid == 0 {
21032+
return Ok(Some(0));
21033+
}
21034+
21035+
match waitpid(Pid::from_raw(child_pid as i32), Some(WaitPidFlag::WNOHANG)) {
21036+
Ok(WaitStatus::StillAlive)
21037+
| Ok(WaitStatus::Stopped(_, _))
21038+
| Ok(WaitStatus::Continued(_)) => Ok(None),
21039+
Ok(WaitStatus::Exited(_, status)) => Ok(Some(status)),
21040+
Ok(WaitStatus::Signaled(_, signal, _)) => Ok(Some(128 + signal as i32)),
21041+
Err(nix::errno::Errno::ECHILD) => Ok(Some(0)),
21042+
Err(error) => Err(SidecarError::Execution(format!(
21043+
"failed to inspect guest runtime process {child_pid}: {error}"
21044+
))),
21045+
}
21046+
}
21047+
2101921048
pub(crate) fn signal_runtime_process(child_pid: u32, signal: i32) -> Result<(), SidecarError> {
2102021049
if child_pid == 0 {
2102121050
return Ok(());

0 commit comments

Comments
 (0)