use std::ffi::{CStr, CString, c_char, c_int, c_void};
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
use std::rc::Rc;
use std::time::Instant;
use super::super::SupervisorWireError;
use super::super::auth_adapter::broker_report::{BROKER_RESUME_BYTE, encode_broker_trace_report};
use super::super::auth_adapter::{
AuthAdapterError, AuthWorkerPipeFailure, AuthWorkerPool, AuthWorkerResultPoll,
DedicatedChildWaitDomain, ExactAuthWorkerAuthority, FreshAuthJobId,
};
use super::{
ActiveBrokerGate, ActiveBrokerProcess, BrokerEntryError, BrokerGateExit, EAGAIN, EINTR,
F_GETFL, F_SETFL, O_NONBLOCK, ensure_deadline_live, fcntl,
finish_trace_report_before_authority, last_errno, read_resume_commit,
require_resume_commit_eof, set_nonblocking, write_control_while_dormant,
};
use crate::backend::macos::bootstrap::{TaskAuditIdentity, capture_task_audit_identity};
use crate::backend::macos::supervisor::deployer_helper_path;
use crate::backend::macos::supervisor::spawn_primitives::{
SpawnAttributes, SpawnFileActions, spawn,
};
const SIGKILL: c_int = 9;
const SIGSTOP: c_int = 17;
const SIGTRAP: c_int = 5;
const PT_CONTINUE: c_int = 7;
const PT_KILL: c_int = 8;
const WNOHANG: c_int = 1;
const WUNTRACED: c_int = 2;
const ESRCH: c_int = 3;
const ECHILD: c_int = 10;
const POLLIN: i16 = 0x0001;
const POLLOUT: i16 = 0x0004;
const EPIPE: c_int = 32;
pub(in crate::backend::macos::supervisor) const INSTALLED_LAUNCHER_MODE: &str =
"--supervisor-launcher";
pub(in crate::backend::macos::supervisor) const INSTALLED_LAUNCHER_DEATH_ARGUMENT: &str =
"--broker-death-fd=3";
pub(in crate::backend::macos::supervisor) const INSTALLED_LAUNCHER_PLAN_ARGUMENT: &str =
"--plan-fd=4";
const CANONICAL_PATH: &str = "PATH=/usr/bin:/bin";
const CANONICAL_LANG: &str = "LANG=C";
const CANONICAL_LOCALE: &str = "LC_ALL=C";
const NULL_DEVICE: &str = "/dev/null";
pub(in crate::backend::macos::supervisor) const LAUNCHER_DEATH_FD: c_int = 3;
pub(in crate::backend::macos::supervisor) const LAUNCHER_PLAN_FD: c_int = 4;
const LAUNCHER_STDIO_FDS: [c_int; 3] = [0, 1, 2];
const STABLE_FD_MINIMUM: c_int = 10;
const F_DUPFD_CLOEXEC: c_int = 67;
const F_SETNOSIGPIPE: c_int = 73;
const O_RDWR: c_int = 2;
const TASK_BOOTSTRAP_PORT: c_int = 4;
const MACH_PORT_DEAD: u32 = !0;
#[repr(C)]
struct PollFd {
fd: c_int,
events: i16,
revents: i16,
}
unsafe extern "C" {
fn getegid() -> u32;
fn geteuid() -> u32;
fn getgid() -> u32;
fn getuid() -> u32;
fn kill(pid: c_int, signal: c_int) -> c_int;
fn pipe(descriptors: *mut c_int) -> c_int;
fn poll(descriptors: *mut PollFd, count: u32, timeout_ms: c_int) -> c_int;
fn ptrace(request: c_int, pid: c_int, address: *mut c_void, data: c_int) -> c_int;
fn read(fd: c_int, buffer: *mut u8, count: usize) -> isize;
fn waitpid(pid: c_int, status: *mut c_int, options: c_int) -> c_int;
fn write(fd: c_int, buffer: *const u8, count: usize) -> isize;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum LauncherSpawnFailure {
InvalidFixedImage,
Plan(SupervisorWireError),
DeadlineExpired,
ServiceGone,
InvalidGate,
Pipe(c_int),
InvalidWaitDomain,
Descriptor(c_int),
FileActions(c_int),
Attributes(c_int),
Spawn(c_int),
}
#[must_use = "a failed launcher spawn retains exact broker authority"]
pub(super) struct LauncherSpawnError {
active: ActiveBrokerProcess,
failure: LauncherSpawnFailure,
}
impl LauncherSpawnError {
pub(super) fn into_parts(self) -> (ActiveBrokerProcess, LauncherSpawnFailure) {
(self.active, self.failure)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum LauncherWaitError {
InvalidPid,
ServiceGone,
InvalidGate,
DeadlineExpired,
UnexpectedStatus,
IdentityTransition,
Native(c_int),
}
#[derive(Debug)]
pub(super) enum LauncherSignatureError<WorkerFailure> {
Launcher(LauncherWaitError),
Pipe(AuthWorkerPipeFailure),
Auth(AuthAdapterError<WorkerFailure>),
BindingMismatch,
}
pub(super) struct InstalledLauncherImage {
path: CString,
mode: CString,
death_argument: CString,
plan_argument: CString,
environment_path: CString,
environment_lang: CString,
environment_locale: CString,
null_device: CString,
}
impl InstalledLauncherImage {
pub(super) unsafe fn from_verified_installation(
path: &CStr,
) -> Result<Self, LauncherSpawnFailure> {
Ok(Self {
path: deployer_helper_path(path).ok_or(LauncherSpawnFailure::InvalidFixedImage)?,
mode: fixed_launcher_cstring(INSTALLED_LAUNCHER_MODE)?,
death_argument: fixed_launcher_cstring(INSTALLED_LAUNCHER_DEATH_ARGUMENT)?,
plan_argument: fixed_launcher_cstring(INSTALLED_LAUNCHER_PLAN_ARGUMENT)?,
environment_path: fixed_launcher_cstring(CANONICAL_PATH)?,
environment_lang: fixed_launcher_cstring(CANONICAL_LANG)?,
environment_locale: fixed_launcher_cstring(CANONICAL_LOCALE)?,
null_device: fixed_launcher_cstring(NULL_DEVICE)?,
})
}
fn argv(&self) -> [*mut c_char; 5] {
[
self.path.as_ptr().cast_mut(),
self.mode.as_ptr().cast_mut(),
self.death_argument.as_ptr().cast_mut(),
self.plan_argument.as_ptr().cast_mut(),
std::ptr::null_mut(),
]
}
fn environment(&self) -> [*mut c_char; 4] {
[
self.environment_path.as_ptr().cast_mut(),
self.environment_lang.as_ptr().cast_mut(),
self.environment_locale.as_ptr().cast_mut(),
std::ptr::null_mut(),
]
}
fn fixed_identity(&self) -> FixedLauncherIdentity {
unsafe {
FixedLauncherIdentity {
real_uid: getuid(),
effective_uid: geteuid(),
real_gid: getgid(),
effective_gid: getegid(),
executable: self.path.as_bytes().to_vec(),
}
}
}
}
fn fixed_launcher_cstring(value: &'static str) -> Result<CString, LauncherSpawnFailure> {
CString::new(value).map_err(|_| LauncherSpawnFailure::InvalidFixedImage)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ExactPhase {
AwaitingInitialStop,
UnprovenInitialStop,
AwaitingExecTrap,
ObservedTracedStop,
ExecTrapHeld,
RunningTarget,
Reaped,
}
struct ExactLauncher {
pid: c_int,
phase: ExactPhase,
active: ActiveBrokerProcess,
expected_launcher: FixedLauncherIdentity,
channels: Option<RetainedLauncherChannels>,
_thread_confined: std::marker::PhantomData<Rc<()>>,
}
pub(super) struct RetainedLauncherChannels {
plan: Option<LauncherPlanDelivery>,
death_writer: OwnedFd,
}
struct LauncherPlanDelivery {
writer: OwnedFd,
frame: Vec<u8>,
}
impl RetainedLauncherChannels {
#[cfg(test)]
pub(super) fn for_test(plan_writer: OwnedFd, death_writer: OwnedFd, frame: Vec<u8>) -> Self {
Self {
plan: Some(LauncherPlanDelivery {
writer: plan_writer,
frame,
}),
death_writer,
}
}
}
pub(super) struct FixedLauncherIdentity {
real_uid: u32,
effective_uid: u32,
real_gid: u32,
effective_gid: u32,
executable: Vec<u8>,
}
impl FixedLauncherIdentity {
#[cfg(test)]
fn for_test(
real_uid: u32,
effective_uid: u32,
real_gid: u32,
effective_gid: u32,
executable: Vec<u8>,
) -> Self {
Self {
real_uid,
effective_uid,
real_gid,
effective_gid,
executable,
}
}
}
#[must_use = "the exact launcher must reach exec trap or be exact-cleaned"]
pub(super) struct SpawnedLauncher {
inner: Option<ExactLauncher>,
}
pub(super) fn spawn_fixed_launcher(
active: ActiveBrokerProcess,
image: &InstalledLauncherImage,
wait_domain: &mut DedicatedChildWaitDomain,
) -> Result<SpawnedLauncher, Box<LauncherSpawnError>> {
PreparedLauncherSpawn::prepare(active, image, wait_domain)?.spawn_and_arm(wait_domain)
}
struct PreparedLauncherSpawn<'image> {
image: &'image InstalledLauncherImage,
active: ActiveBrokerProcess,
resources: LauncherSpawnResources,
}
struct LauncherSpawnResources {
actions: SpawnFileActions,
attributes: SpawnAttributes,
death_reader: OwnedFd,
plan_reader: OwnedFd,
channels: RetainedLauncherChannels,
expected_launcher: FixedLauncherIdentity,
}
impl<'image> PreparedLauncherSpawn<'image> {
fn prepare(
active: ActiveBrokerProcess,
image: &'image InstalledLauncherImage,
wait_domain: &mut DedicatedChildWaitDomain,
) -> Result<Self, Box<LauncherSpawnError>> {
match LauncherSpawnResources::acquire(&active, image, wait_domain) {
Ok(resources) => Ok(Self {
image,
active,
resources,
}),
Err(failure) => Err(Box::new(LauncherSpawnError { active, failure })),
}
}
}
impl LauncherSpawnResources {
fn acquire(
active: &ActiveBrokerProcess,
image: &InstalledLauncherImage,
wait_domain: &mut DedicatedChildWaitDomain,
) -> Result<Self, LauncherSpawnFailure> {
let frame = active
.plan
.launcher_frame()
.map_err(LauncherSpawnFailure::Plan)?;
let expected_launcher = image.fixed_identity();
let (death_reader, death_writer) = create_launcher_pipe(wait_domain)?;
let (plan_reader, plan_writer) = create_launcher_pipe(wait_domain)?;
set_no_sigpipe(death_writer.as_raw_fd())?;
set_no_sigpipe(plan_writer.as_raw_fd())?;
set_writer_nonblocking(plan_writer.as_raw_fd())?;
let mut actions = SpawnFileActions::new().map_err(LauncherSpawnFailure::FileActions)?;
for fd in LAUNCHER_STDIO_FDS {
actions
.add_open(fd, image.null_device.as_c_str(), O_RDWR, 0)
.map_err(LauncherSpawnFailure::FileActions)?;
}
actions
.add_dup2(death_reader.as_raw_fd(), LAUNCHER_DEATH_FD)
.map_err(LauncherSpawnFailure::FileActions)?;
actions
.add_dup2(plan_reader.as_raw_fd(), LAUNCHER_PLAN_FD)
.map_err(LauncherSpawnFailure::FileActions)?;
for fd in [
death_reader.as_raw_fd(),
death_writer.as_raw_fd(),
plan_reader.as_raw_fd(),
plan_writer.as_raw_fd(),
] {
actions
.add_close(fd)
.map_err(LauncherSpawnFailure::FileActions)?;
}
let mut attributes = SpawnAttributes::new().map_err(LauncherSpawnFailure::Attributes)?;
attributes
.configure_canonical_signals()
.map_err(LauncherSpawnFailure::Attributes)?;
attributes
.set_special_port(MACH_PORT_DEAD, TASK_BOOTSTRAP_PORT)
.map_err(LauncherSpawnFailure::Attributes)?;
Ok(Self {
actions,
attributes,
death_reader,
plan_reader,
channels: RetainedLauncherChannels {
plan: Some(LauncherPlanDelivery {
writer: plan_writer,
frame,
}),
death_writer,
},
expected_launcher,
})
}
}
impl PreparedLauncherSpawn<'_> {
fn spawn_and_arm(
self,
wait_domain: &mut DedicatedChildWaitDomain,
) -> Result<SpawnedLauncher, Box<LauncherSpawnError>> {
let Self {
image,
active,
resources:
LauncherSpawnResources {
actions,
attributes,
death_reader,
plan_reader,
channels,
expected_launcher,
},
} = self;
if let Err(failure) = ensure_spawn_admissible(&active) {
return Err(Box::new(LauncherSpawnError { active, failure }));
}
if wait_domain.verify_single_threaded_spawn().is_err() {
return Err(Box::new(LauncherSpawnError {
active,
failure: LauncherSpawnFailure::InvalidWaitDomain,
}));
}
let argv = image.argv();
let environment = image.environment();
let pid = match unsafe {
spawn(
image.path.as_c_str(),
&actions,
&attributes,
&argv,
&environment,
)
} {
Ok(pid) => pid,
Err(error) => {
return Err(Box::new(LauncherSpawnError {
active,
failure: LauncherSpawnFailure::Spawn(error),
}));
}
};
if pid <= 0 {
std::process::abort();
}
let launcher = unsafe { SpawnedLauncher::arm(pid, active, expected_launcher, channels) };
drop(death_reader);
drop(plan_reader);
drop(actions);
drop(attributes);
Ok(launcher)
}
}
fn ensure_spawn_admissible(active: &ActiveBrokerProcess) -> Result<(), LauncherSpawnFailure> {
set_gate_nonblocking(&active.gate).map_err(spawn_gate_failure)?;
let verdict =
probe_gate(&active.gate).and_then(|()| ensure_deadline(active.plan.deadline().local()));
let restored = set_gate_blocking(&active.gate);
verdict.map_err(spawn_gate_failure)?;
restored
}
fn set_gate_blocking(gate: &ActiveBrokerGate) -> Result<(), LauncherSpawnFailure> {
set_nonblocking(gate.reader.as_raw_fd(), false).map_err(|error| match error {
BrokerEntryError::Descriptor(error) => LauncherSpawnFailure::Descriptor(error),
_ => std::process::abort(),
})
}
fn spawn_gate_failure(error: LauncherWaitError) -> LauncherSpawnFailure {
match error {
LauncherWaitError::ServiceGone => LauncherSpawnFailure::ServiceGone,
LauncherWaitError::InvalidGate => LauncherSpawnFailure::InvalidGate,
LauncherWaitError::DeadlineExpired => LauncherSpawnFailure::DeadlineExpired,
LauncherWaitError::Native(error) => LauncherSpawnFailure::Descriptor(error),
LauncherWaitError::InvalidPid
| LauncherWaitError::UnexpectedStatus
| LauncherWaitError::IdentityTransition => std::process::abort(),
}
}
fn create_launcher_pipe(
wait_domain: &mut DedicatedChildWaitDomain,
) -> Result<(OwnedFd, OwnedFd), LauncherSpawnFailure> {
wait_domain
.verify_single_threaded_spawn()
.map_err(|_| LauncherSpawnFailure::InvalidWaitDomain)?;
let mut descriptors = [-1; 2];
if unsafe { pipe(descriptors.as_mut_ptr()) } != 0 {
return Err(LauncherSpawnFailure::Pipe(last_errno()));
}
let reader = unsafe { OwnedFd::from_raw_fd(descriptors[0]) };
let writer = unsafe { OwnedFd::from_raw_fd(descriptors[1]) };
let reader = duplicate_cloexec(reader.as_raw_fd())?;
let writer = duplicate_cloexec(writer.as_raw_fd())?;
Ok((reader, writer))
}
fn duplicate_cloexec(fd: c_int) -> Result<OwnedFd, LauncherSpawnFailure> {
let duplicate = unsafe { fcntl(fd, F_DUPFD_CLOEXEC, STABLE_FD_MINIMUM) };
if duplicate < 0 {
return Err(LauncherSpawnFailure::Descriptor(last_errno()));
}
Ok(unsafe { OwnedFd::from_raw_fd(duplicate) })
}
fn set_no_sigpipe(fd: c_int) -> Result<(), LauncherSpawnFailure> {
if unsafe { fcntl(fd, F_SETNOSIGPIPE, 1) } == 0 {
Ok(())
} else {
Err(LauncherSpawnFailure::Descriptor(last_errno()))
}
}
fn set_writer_nonblocking(fd: c_int) -> Result<(), LauncherSpawnFailure> {
let flags = unsafe { fcntl(fd, F_GETFL) };
if flags < 0 {
return Err(LauncherSpawnFailure::Descriptor(last_errno()));
}
if unsafe { fcntl(fd, F_SETFL, flags | O_NONBLOCK) } == 0 {
Ok(())
} else {
Err(LauncherSpawnFailure::Descriptor(last_errno()))
}
}
#[must_use = "the observed initial stop must prove ptrace or exact-clean"]
pub(super) struct InitialStopObserved {
inner: Option<ExactLauncher>,
before_exec: TaskAuditIdentity,
}
#[must_use = "the running traced launcher must reach exec trap or exact-clean"]
pub(super) struct AwaitingExecTrap {
inner: Option<ExactLauncher>,
before_exec: TaskAuditIdentity,
}
#[must_use = "the exec-trap-held launcher must report, resume, or exact-clean"]
pub(super) struct ExecTrapHeld {
inner: Option<ExactLauncher>,
_after_exec: TaskAuditIdentity,
}
#[must_use = "the signature-verified target must report, resume, or exact-clean"]
pub(super) struct SignatureVerifiedExecTrap {
inner: Option<ExactLauncher>,
}
#[must_use = "the reported target must receive Ready-bound resume or exact-clean"]
pub(super) struct ReportedExecTrapHeld {
inner: Option<ExactLauncher>,
}
#[must_use = "the committed target must resume exactly once or exact-clean"]
pub(super) struct ReadyCommittedExecTrap {
inner: Option<ExactLauncher>,
}
#[must_use = "the running target must retain broker cleanup authority"]
pub(super) struct ResumedTarget {
inner: Option<ExactLauncher>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum ExactTargetExit {
Exited(u8),
Signaled(c_int),
}
impl SpawnedLauncher {
unsafe fn arm(
pid: c_int,
active: ActiveBrokerProcess,
expected_launcher: FixedLauncherIdentity,
channels: RetainedLauncherChannels,
) -> Self {
if pid <= 0 {
std::process::abort();
}
Self {
inner: Some(ExactLauncher {
pid,
phase: ExactPhase::AwaitingInitialStop,
active,
expected_launcher,
channels: Some(channels),
_thread_confined: std::marker::PhantomData,
}),
}
}
#[cfg(test)]
pub(super) unsafe fn from_positive_spawn(
pid: c_int,
active: ActiveBrokerProcess,
expected_launcher: FixedLauncherIdentity,
channels: RetainedLauncherChannels,
) -> Result<Self, LauncherWaitError> {
if pid <= 0 {
return Err(LauncherWaitError::InvalidPid);
}
Ok(unsafe { Self::arm(pid, active, expected_launcher, channels) })
}
pub(super) fn wait_initial_stop(mut self) -> Result<InitialStopObserved, LauncherWaitError> {
let mut inner = self.inner.take().unwrap_or_else(|| std::process::abort());
set_gate_nonblocking(inner.gate())?;
wait_for_exact_stop(&mut inner, SIGSTOP)?;
probe_gate(inner.gate())?;
ensure_deadline(inner.deadline())?;
let before_exec = capture_task_audit_identity(inner.pid)
.map_err(|_| LauncherWaitError::IdentityTransition)?;
probe_gate(inner.gate())?;
ensure_deadline(inner.deadline())?;
if !before_exec.proves_exact_process_image(
inner.pid,
inner.expected_launcher.real_uid,
inner.expected_launcher.effective_uid,
inner.expected_launcher.real_gid,
inner.expected_launcher.effective_gid,
&inner.expected_launcher.executable,
) {
return Err(LauncherWaitError::IdentityTransition);
}
Ok(InitialStopObserved {
inner: Some(inner),
before_exec,
})
}
}
impl InitialStopObserved {
pub(super) fn prove_trace_and_continue_to_exec(
mut self,
) -> Result<AwaitingExecTrap, LauncherWaitError> {
let mut inner = self.inner.take().unwrap_or_else(|| std::process::abort());
probe_gate(inner.gate())?;
ensure_deadline(inner.deadline())?;
if unsafe {
ptrace(
PT_CONTINUE,
inner.pid,
std::ptr::without_provenance_mut::<c_void>(1),
0,
)
} != 0
{
return Err(LauncherWaitError::Native(last_errno()));
}
inner.phase = ExactPhase::AwaitingExecTrap;
probe_gate(inner.gate())?;
ensure_deadline(inner.deadline())?;
Ok(AwaitingExecTrap {
inner: Some(inner),
before_exec: self.before_exec,
})
}
}
impl AwaitingExecTrap {
pub(super) fn deliver_plan(&mut self) -> Result<(), LauncherWaitError> {
let inner = self.inner.as_mut().unwrap_or_else(|| std::process::abort());
let Some(LauncherPlanDelivery { writer, frame }) =
inner.channels.as_mut().and_then(|held| held.plan.take())
else {
std::process::abort();
};
let deadline = inner.deadline();
let mut written = 0_usize;
while written < frame.len() {
probe_gate(&inner.active.gate)?;
ensure_deadline(deadline)?;
let remaining = &frame[written..];
let result = unsafe { write(writer.as_raw_fd(), remaining.as_ptr(), remaining.len()) };
if result > 0 {
written += usize::try_from(result).unwrap_or_else(|_| std::process::abort());
continue;
}
if result == 0 {
return Err(LauncherWaitError::UnexpectedStatus);
}
match last_errno() {
EINTR => {}
EAGAIN => poll_plan_slice(&inner.active.gate, writer.as_raw_fd())?,
EPIPE => return Err(LauncherWaitError::UnexpectedStatus),
error => return Err(LauncherWaitError::Native(error)),
}
}
drop(writer);
probe_gate(&inner.active.gate)?;
ensure_deadline(deadline)
}
pub(super) fn wait_exec_trap(mut self) -> Result<ExecTrapHeld, LauncherWaitError> {
let mut inner = self.inner.take().unwrap_or_else(|| std::process::abort());
wait_for_exact_stop(&mut inner, SIGTRAP)?;
inner.phase = ExactPhase::ExecTrapHeld;
probe_gate(inner.gate())?;
ensure_deadline(inner.deadline())?;
let after_exec = capture_task_audit_identity(inner.pid)
.map_err(|_| LauncherWaitError::IdentityTransition)?;
if !after_exec.proves_exec_transition_from(
&self.before_exec,
inner.pid,
inner.expected_euid(),
inner.expected_egid(),
inner.expected_executable(),
) {
return Err(LauncherWaitError::IdentityTransition);
}
probe_gate(inner.gate())?;
ensure_deadline(inner.deadline())?;
Ok(ExecTrapHeld {
inner: Some(inner),
_after_exec: after_exec,
})
}
}
impl ExecTrapHeld {
pub(super) fn verify_signature<Authority: ExactAuthWorkerAuthority>(
mut self,
pool: &mut AuthWorkerPool<Authority>,
job_id: FreshAuthJobId,
) -> Result<SignatureVerifiedExecTrap, LauncherSignatureError<Authority::Failure>> {
let inner = self.inner.as_ref().unwrap_or_else(|| std::process::abort());
let audit_identity = self._after_exec.audit_identity();
let effective_uid = inner.expected_euid();
let effective_gid = inner.expected_egid();
let frame_digest = inner.active.plan.plan_digest();
let expected_code_identity = inner.active.plan.target_identity();
let wire_deadline = inner.active.plan.deadline().wire();
let deadline = inner.deadline();
let dispatched = pool
.dispatch_exec_trap(
audit_identity,
effective_uid,
effective_gid,
frame_digest,
expected_code_identity,
job_id,
wire_deadline,
)
.map_err(LauncherSignatureError::Auth)?;
let worker = dispatched.worker();
let mut receipt = match dispatched.submit() {
Ok(receipt) => receipt,
Err(error) => {
return Err(cancel_signature_worker(
pool,
worker,
LauncherSignatureError::Pipe(error),
));
}
};
let received = loop {
if let Err(error) = probe_gate(inner.gate()).and_then(|()| ensure_deadline(deadline)) {
return Err(cancel_signature_worker(
pool,
worker,
LauncherSignatureError::Launcher(error),
));
}
match receipt.poll() {
Ok(AuthWorkerResultPoll::Complete(received)) => break received,
Ok(AuthWorkerResultPoll::Pending(next)) => {
let result_fd = next.result_fd();
receipt = next;
if let Err(error) = poll_signature_slice(inner.gate(), result_fd) {
return Err(cancel_signature_worker(
pool,
worker,
LauncherSignatureError::Launcher(error),
));
}
}
Err(error) => {
return Err(cancel_signature_worker(
pool,
worker,
LauncherSignatureError::Pipe(error),
));
}
}
};
let mut completion = pool.complete_exec_trap(received);
let authenticated = loop {
match completion {
Ok(authenticated) => break authenticated,
Err(AuthAdapterError::WorkerRetirementPending(pending)) if pending == worker => {
if let Err(error) =
probe_gate(inner.gate()).and_then(|()| ensure_deadline(deadline))
{
return Err(cancel_signature_worker(
pool,
worker,
LauncherSignatureError::Launcher(error),
));
}
if let Err(error) = poll_gate_slice(inner.gate()) {
return Err(cancel_signature_worker(
pool,
worker,
LauncherSignatureError::Launcher(error),
));
}
completion = pool.poll_completed_exec_trap(worker);
}
Err(error) => return Err(LauncherSignatureError::Auth(error)),
}
};
if authenticated.audit_identity() != audit_identity
|| authenticated.effective_uid() != effective_uid
|| authenticated.effective_gid() != effective_gid
|| authenticated.frame_digest() != frame_digest
|| authenticated.code_identity() != expected_code_identity
|| authenticated.deadline() != wire_deadline
{
return Err(LauncherSignatureError::BindingMismatch);
}
let inner = self.inner.take().unwrap_or_else(|| std::process::abort());
Ok(SignatureVerifiedExecTrap { inner: Some(inner) })
}
#[cfg(test)]
unsafe fn assume_signature_verified_for_test(mut self) -> SignatureVerifiedExecTrap {
SignatureVerifiedExecTrap {
inner: self.inner.take(),
}
}
#[cfg(test)]
fn exact_pid_for_test(&self) -> c_int {
self.inner
.as_ref()
.unwrap_or_else(|| std::process::abort())
.pid
}
#[cfg(test)]
fn wait_for_gate_eof_for_test(&self) {
let inner = self.inner.as_ref().unwrap_or_else(|| std::process::abort());
loop {
match probe_gate(inner.gate()) {
Err(LauncherWaitError::ServiceGone) => return,
Ok(()) => poll_gate_slice(inner.gate()).unwrap(),
Err(error) => panic!("unexpected gate probe failure: {error:?}"),
}
}
}
}
impl SignatureVerifiedExecTrap {
pub(super) fn report_trace_stops(
mut self,
) -> Result<Result<ReportedExecTrapHeld, BrokerGateExit>, BrokerEntryError> {
let mut inner = self.inner.take().unwrap_or_else(|| std::process::abort());
let deadline = inner.deadline();
ensure_deadline_live(Some(deadline))?;
let bytes = encode_broker_trace_report(inner.active.plan.trace_report_binding())
.map_err(|error| BrokerEntryError::Plan(error.into()))?;
let gate_fd = inner.active.gate.reader.as_raw_fd();
set_nonblocking(gate_fd, true)?;
if write_control_while_dormant(&mut inner.active.trace, gate_fd, &bytes, deadline)?
.is_some()
{
return Ok(Err(BrokerGateExit::ServiceGone));
}
if let Some(exit) = finish_trace_report_before_authority(&inner.active.trace, gate_fd)? {
return Ok(Err(exit));
}
Ok(Ok(ReportedExecTrapHeld { inner: Some(inner) }))
}
#[cfg(test)]
fn exact_pid_for_test(&self) -> c_int {
self.inner
.as_ref()
.unwrap_or_else(|| std::process::abort())
.pid
}
#[cfg(test)]
fn wait_for_gate_eof_for_test(&self) {
let inner = self.inner.as_ref().unwrap_or_else(|| std::process::abort());
loop {
match probe_gate(inner.gate()) {
Err(LauncherWaitError::ServiceGone) => return,
Ok(()) => poll_gate_slice(inner.gate()).unwrap(),
Err(error) => panic!("unexpected gate probe failure: {error:?}"),
}
}
}
}
impl ReportedExecTrapHeld {
pub(super) fn wait_for_ready_commit(
mut self,
) -> Result<Result<ReadyCommittedExecTrap, BrokerGateExit>, BrokerEntryError> {
let mut inner = self.inner.take().unwrap_or_else(|| std::process::abort());
let gate_fd = inner.active.gate.reader.as_raw_fd();
let mut resume = [0_u8; 1];
if read_resume_commit(&mut inner.active.trace, gate_fd, &mut resume)?.is_some() {
return Ok(Err(BrokerGateExit::ServiceGone));
}
if resume != BROKER_RESUME_BYTE {
return Err(BrokerEntryError::Plan(SupervisorWireError::Malformed));
}
if require_resume_commit_eof(&mut inner.active.trace, gate_fd)?.is_some() {
return Ok(Err(BrokerGateExit::ServiceGone));
}
Ok(Ok(ReadyCommittedExecTrap { inner: Some(inner) }))
}
}
impl ReadyCommittedExecTrap {
pub(super) fn resume_target(mut self) -> Result<ResumedTarget, LauncherWaitError> {
let mut inner = self.inner.take().unwrap_or_else(|| std::process::abort());
probe_gate(inner.gate())?;
if unsafe {
ptrace(
PT_CONTINUE,
inner.pid,
std::ptr::without_provenance_mut::<c_void>(1),
0,
)
} != 0
{
return Err(LauncherWaitError::Native(last_errno()));
}
inner.phase = ExactPhase::RunningTarget;
Ok(ResumedTarget { inner: Some(inner) })
}
#[cfg(test)]
fn wait_for_gate_eof_for_test(&self) {
let inner = self.inner.as_ref().unwrap_or_else(|| std::process::abort());
loop {
match probe_gate(inner.gate()) {
Err(LauncherWaitError::ServiceGone) => return,
Ok(()) => poll_gate_slice(inner.gate()).unwrap(),
Err(error) => panic!("unexpected gate probe failure: {error:?}"),
}
}
}
}
impl ResumedTarget {
pub(super) fn wait_for_exit(self) -> Result<ExactTargetExit, LauncherWaitError> {
self.wait_for_exit_with_post_wait(|_| {})
}
fn wait_for_exit_with_post_wait<Barrier>(
mut self,
barrier: Barrier,
) -> Result<ExactTargetExit, LauncherWaitError>
where
Barrier: FnOnce(&ActiveBrokerGate),
{
let mut inner = self.inner.take().unwrap_or_else(|| std::process::abort());
let mut barrier = Some(barrier);
loop {
probe_gate(inner.gate())?;
let mut status = 0;
let result = unsafe { waitpid(inner.pid, &raw mut status, WNOHANG | WUNTRACED) };
if result == inner.pid {
if traced_stop_signal(status).is_some() {
inner.phase = ExactPhase::ObservedTracedStop;
barrier.take().unwrap_or_else(|| std::process::abort())(inner.gate());
probe_gate(inner.gate())?;
return Err(LauncherWaitError::UnexpectedStatus);
}
inner.phase = ExactPhase::Reaped;
drain_exact_child(inner.pid);
barrier.take().unwrap_or_else(|| std::process::abort())(inner.gate());
probe_gate(inner.gate())?;
return exact_target_exit(status).ok_or(LauncherWaitError::UnexpectedStatus);
}
if result < 0 {
let error = last_errno();
if error == EINTR {
continue;
}
if error == ECHILD {
std::process::abort();
}
return Err(LauncherWaitError::Native(error));
}
if result > 0 {
std::process::abort();
}
poll_gate_slice(inner.gate())?;
}
}
#[cfg(test)]
fn wait_for_exit_with_post_wait_for_test<Barrier>(
self,
barrier: Barrier,
) -> Result<ExactTargetExit, LauncherWaitError>
where
Barrier: FnOnce(&ActiveBrokerGate),
{
self.wait_for_exit_with_post_wait(barrier)
}
#[cfg(test)]
fn exact_pid_for_test(&self) -> c_int {
self.inner
.as_ref()
.unwrap_or_else(|| std::process::abort())
.pid
}
#[cfg(test)]
fn wait_for_gate_eof_for_test(&self) {
let inner = self.inner.as_ref().unwrap_or_else(|| std::process::abort());
loop {
match probe_gate(inner.gate()) {
Err(LauncherWaitError::ServiceGone) => return,
Ok(()) => poll_gate_slice(inner.gate()).unwrap(),
Err(error) => panic!("unexpected gate probe failure: {error:?}"),
}
}
}
}
impl ExactLauncher {
fn gate(&self) -> &ActiveBrokerGate {
&self.active.gate
}
fn deadline(&self) -> Instant {
self.active.plan.deadline().local()
}
fn expected_euid(&self) -> u32 {
self.active.plan.effective_uid()
}
fn expected_egid(&self) -> u32 {
self.active.plan.effective_gid()
}
fn expected_executable(&self) -> &[u8] {
self.active.plan.installed_executable()
}
}
fn wait_for_exact_stop(
inner: &mut ExactLauncher,
expected_signal: c_int,
) -> Result<(), LauncherWaitError> {
loop {
probe_gate(inner.gate())?;
ensure_deadline(inner.deadline())?;
let mut status = 0;
let result = unsafe { waitpid(inner.pid, &mut status, WNOHANG | WUNTRACED) };
if result == inner.pid {
match traced_stop_signal(status) {
Some(signal) => {
inner.phase = match inner.phase {
ExactPhase::AwaitingInitialStop => ExactPhase::UnprovenInitialStop,
ExactPhase::AwaitingExecTrap => ExactPhase::ObservedTracedStop,
_ => std::process::abort(),
};
if signal == expected_signal {
return Ok(());
}
return Err(LauncherWaitError::UnexpectedStatus);
}
None => {
inner.phase = ExactPhase::Reaped;
drain_exact_child(inner.pid);
return Err(LauncherWaitError::UnexpectedStatus);
}
}
}
if result < 0 {
let error = last_errno();
if error == EINTR {
continue;
}
if error == ECHILD {
std::process::abort();
}
return Err(LauncherWaitError::Native(error));
}
poll_gate_slice(inner.gate())?;
}
}
fn set_gate_nonblocking(gate: &ActiveBrokerGate) -> Result<(), LauncherWaitError> {
let fd = gate.reader.as_raw_fd();
let flags = unsafe { fcntl(fd, F_GETFL) };
if flags < 0 || unsafe { fcntl(fd, F_SETFL, flags | O_NONBLOCK) } != 0 {
return Err(LauncherWaitError::Native(last_errno()));
}
Ok(())
}
fn probe_gate(gate: &ActiveBrokerGate) -> Result<(), LauncherWaitError> {
let mut byte = 0_u8;
loop {
let result = unsafe { read(gate.reader.as_raw_fd(), &mut byte, 1) };
if result == 0 {
return Err(LauncherWaitError::ServiceGone);
}
if result == 1 {
return Err(LauncherWaitError::InvalidGate);
}
let error = last_errno();
if error == EINTR {
continue;
}
if error == EAGAIN {
return Ok(());
}
return Err(LauncherWaitError::Native(error));
}
}
fn poll_plan_slice(gate: &ActiveBrokerGate, writer: c_int) -> Result<(), LauncherWaitError> {
let mut descriptors = [
PollFd {
fd: gate.reader.as_raw_fd(),
events: POLLIN,
revents: 0,
},
PollFd {
fd: writer,
events: POLLOUT,
revents: 0,
},
];
let result = unsafe { poll(descriptors.as_mut_ptr(), 2, 1) };
if result < 0 {
let error = last_errno();
if error != EINTR {
return Err(LauncherWaitError::Native(error));
}
}
Ok(())
}
fn poll_signature_slice(gate: &ActiveBrokerGate, result: c_int) -> Result<(), LauncherWaitError> {
let mut descriptors = [
PollFd {
fd: gate.reader.as_raw_fd(),
events: POLLIN,
revents: 0,
},
PollFd {
fd: result,
events: POLLIN,
revents: 0,
},
];
let polled = unsafe { poll(descriptors.as_mut_ptr(), 2, 1) };
if polled < 0 {
let error = last_errno();
if error != EINTR {
return Err(LauncherWaitError::Native(error));
}
}
Ok(())
}
fn cancel_signature_worker<Authority: ExactAuthWorkerAuthority>(
pool: &mut AuthWorkerPool<Authority>,
worker: super::super::auth_adapter::AuthWorkerIdentity,
original: LauncherSignatureError<Authority::Failure>,
) -> LauncherSignatureError<Authority::Failure> {
match pool.cancel(worker) {
Ok(()) => original,
Err(error) => LauncherSignatureError::Auth(error),
}
}
fn poll_gate_slice(gate: &ActiveBrokerGate) -> Result<(), LauncherWaitError> {
let mut descriptor = PollFd {
fd: gate.reader.as_raw_fd(),
events: POLLIN,
revents: 0,
};
let result = unsafe { poll(&raw mut descriptor, 1, 1) };
if result < 0 {
let error = last_errno();
if error != EINTR {
return Err(LauncherWaitError::Native(error));
}
}
Ok(())
}
fn ensure_deadline(deadline: Instant) -> Result<(), LauncherWaitError> {
if Instant::now() >= deadline {
Err(LauncherWaitError::DeadlineExpired)
} else {
Ok(())
}
}
fn traced_stop_signal(status: c_int) -> Option<c_int> {
(status & 0xff == 0x7f).then_some((status >> 8) & 0xff)
}
fn exact_target_exit(status: c_int) -> Option<ExactTargetExit> {
let terminal = status & 0x7f;
if terminal == 0 {
Some(ExactTargetExit::Exited(((status >> 8) & 0xff) as u8))
} else if terminal != 0x7f {
Some(ExactTargetExit::Signaled(terminal))
} else {
None
}
}
impl Drop for ExactLauncher {
fn drop(&mut self) {
drop(self.channels.take());
if self.phase == ExactPhase::Reaped {
return;
}
match self.phase {
ExactPhase::AwaitingInitialStop => exact_signal(self.pid, SIGKILL),
ExactPhase::UnprovenInitialStop => exact_unproven_stop_kill(self.pid),
ExactPhase::AwaitingExecTrap | ExactPhase::RunningTarget => {
exact_signal(self.pid, SIGSTOP)
}
ExactPhase::ObservedTracedStop | ExactPhase::ExecTrapHeld => {
exact_ptrace_kill(self.pid)
}
ExactPhase::Reaped => return,
}
drain_exact_child(self.pid);
self.phase = ExactPhase::Reaped;
}
}
fn drain_exact_child(pid: c_int) {
loop {
let mut status = 0;
let result = unsafe { waitpid(pid, &raw mut status, WUNTRACED) };
if result == pid {
if traced_stop_signal(status).is_some() {
exact_ptrace_kill(pid);
}
continue;
}
if result < 0 {
let error = last_errno();
if error == EINTR {
continue;
}
if error == ECHILD {
return;
}
}
std::process::abort();
}
}
fn exact_signal(pid: c_int, signal: c_int) {
if unsafe { kill(pid, signal) } != 0 && last_errno() != ESRCH {
std::process::abort();
}
}
fn exact_ptrace_kill(pid: c_int) {
if unsafe { ptrace(PT_KILL, pid, std::ptr::null_mut(), 0) } != 0 && last_errno() != ESRCH {
std::process::abort();
}
}
fn exact_unproven_stop_kill(pid: c_int) {
if unsafe { ptrace(PT_KILL, pid, std::ptr::null_mut(), 0) } == 0 {
return;
}
exact_signal(pid, SIGKILL);
}
#[cfg(test)]
#[path = "supervisor_broker_launcher_test.rs"]
mod tests;