Files
KF-OnlineJudge/judge/crates/sandbox/src/launcher/entry.rs
T

80 lines
2.9 KiB
Rust

//! Runs after a fresh exec of the trusted binary, without a Tokio runtime.
//! No custom pre_exec closure, fork, user program, or permission change occurs.
use super::protocol::{ChildMessage, ControlChannel, ExpectedPeer, ParentMessage};
use crate::outcome::{ErrorCode, ExecutionStage};
use std::io;
use std::os::unix::net::UnixStream;
use std::path::Path;
use std::time::{Duration, Instant};
pub const STARTUP_TIMEOUT: Duration = Duration::from_secs(5);
/// Parent must create a listener in a fresh private directory under its trusted
/// state root before spawning this entry. Socket path is trusted deployment data.
/// The kernel peer credentials, rather than that path, authenticate the parent.
pub fn serve(socket_path: &Path) -> io::Result<()> {
let deadline = Instant::now() + STARTUP_TIMEOUT;
ensure_single_threaded()?;
let parent = rustix::process::getppid()
.filter(|pid| *pid != rustix::process::Pid::INIT)
.ok_or_else(|| {
io::Error::new(io::ErrorKind::PermissionDenied, "missing launcher parent")
})?;
// A full/stalled listener queue must fail, rather than block before the
// channel deadline is installed. OwnedFd closes on every failure path.
let socket = rustix::net::socket_with(
rustix::net::AddressFamily::UNIX,
rustix::net::SocketType::STREAM,
rustix::net::SocketFlags::CLOEXEC | rustix::net::SocketFlags::NONBLOCK,
None,
)?;
let address = rustix::net::SocketAddrUnix::new(socket_path)?;
rustix::net::connect(&socket, &address)?;
let stream = UnixStream::from(socket);
let mut channel = ControlChannel::new(
stream,
ExpectedPeer {
pid: parent.as_raw_pid() as u32,
uid: rustix::process::geteuid().as_raw(),
gid: rustix::process::getegid().as_raw(),
},
deadline,
)?;
channel.send(&ChildMessage::Hello {
pid: std::process::id(),
threads: 1,
})?;
match channel.receive::<ParentMessage>()? {
ParentMessage::Initialize { run_id } => {
// A transport handshake is never an isolation or exec permit.
channel.send(&ChildMessage::InitializationRefused {
run_id,
stage: ExecutionStage::Preparation,
code: ErrorCode::CapabilityUnavailable,
})?;
}
ParentMessage::Cancel {} => channel.send(&ChildMessage::Cancelled {})?,
}
Ok(())
}
/// Must be repeated immediately before future privileged/fork operations.
pub fn ensure_single_threaded() -> io::Result<()> {
let mut count = 0;
for entry in std::fs::read_dir("/proc/self/task")? {
entry?;
count += 1;
if count > 1 {
break;
}
}
if count != 1 {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"launcher requires one thread",
));
}
Ok(())
}