Establish bounded descriptor budgets for sandbox domains
This commit is contained in:
parent
42e197510e
commit
503f090121
1 changed files with 191 additions and 12 deletions
|
|
@ -17,8 +17,9 @@ use lazaret_core::identity::{
|
|||
};
|
||||
use lazaret_core::records::{Domain, Session, SessionPhase};
|
||||
use lazaret_linux::filesystem::try_exclusive_flock;
|
||||
use lazaret_runtime_supervisor::sandbox::default_sandbox_layout;
|
||||
use lazaret_runtime_supervisor::sandbox::{DOMAIN_OPEN_FILE_LIMIT, default_sandbox_layout};
|
||||
use rustix::fs::{Mode, OFlags, openat};
|
||||
use rustix::process::{Resource, Rlimit, getrlimit, setrlimit};
|
||||
|
||||
use crate::bootstrap::{CoordinatorLifecycle, serve_control_connection_with_escalation};
|
||||
use crate::cold_start::{BootstrapOwnerGuard, ColdStart, ColdStartError};
|
||||
|
|
@ -73,6 +74,7 @@ pub fn dispatch_private_client_bootstrap(
|
|||
|
||||
fn run_private_client_bootstrap(arguments: &[OsString]) -> Result<(), PrivateClientBootstrapError> {
|
||||
let parsed = parse_arguments(arguments).inspect_err(|_| signal(b'E'))?;
|
||||
ensure_domain_nofile_limit().inspect_err(|_| signal(b'E'))?;
|
||||
let owner = BootstrapOwnerGuard::from_stdin()
|
||||
.map_err(failure)
|
||||
.inspect_err(|_| signal(b'E'))?;
|
||||
|
|
@ -158,6 +160,51 @@ fn run_private_client_bootstrap(arguments: &[OsString]) -> Result<(), PrivateCli
|
|||
.map_err(failure)
|
||||
}
|
||||
|
||||
/// Establishes the descriptor budget inherited by one Domain runtime.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns an error when the hard limit is insufficient or the soft limit cannot be raised.
|
||||
pub fn ensure_domain_nofile_limit() -> Result<(), PrivateClientBootstrapError> {
|
||||
let inherited = getrlimit(Resource::Nofile);
|
||||
let Some(required) = required_nofile_limit(inherited)? else {
|
||||
return Ok(());
|
||||
};
|
||||
setrlimit(Resource::Nofile, required).map_err(|error| {
|
||||
failure(format!(
|
||||
"raising Host Coordinator RLIMIT_NOFILE from {} to {} failed: {error}",
|
||||
display_limit(inherited.current),
|
||||
DOMAIN_OPEN_FILE_LIMIT
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn required_nofile_limit(inherited: Rlimit) -> Result<Option<Rlimit>, PrivateClientBootstrapError> {
|
||||
if inherited
|
||||
.maximum
|
||||
.is_some_and(|maximum| maximum < DOMAIN_OPEN_FILE_LIMIT)
|
||||
{
|
||||
return Err(failure(format!(
|
||||
"Host Coordinator requires RLIMIT_NOFILE {DOMAIN_OPEN_FILE_LIMIT}, but inherited soft/hard limits are {}/{}",
|
||||
display_limit(inherited.current),
|
||||
display_limit(inherited.maximum)
|
||||
)));
|
||||
}
|
||||
if inherited
|
||||
.current
|
||||
.is_none_or(|current| current >= DOMAIN_OPEN_FILE_LIMIT)
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(Some(Rlimit {
|
||||
current: Some(DOMAIN_OPEN_FILE_LIMIT),
|
||||
maximum: inherited.maximum,
|
||||
}))
|
||||
}
|
||||
|
||||
fn display_limit(limit: Option<u64>) -> String {
|
||||
limit.map_or_else(|| "unlimited".to_owned(), |limit| limit.to_string())
|
||||
}
|
||||
|
||||
fn serve_published(
|
||||
lifecycle: &Mutex<CoordinatorLifecycle<'_, '_>>,
|
||||
waiters: &RefCell<BootstrapWaiterFace>,
|
||||
|
|
@ -495,6 +542,57 @@ mod tests {
|
|||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn host_coordinator_plans_a_bounded_raise_from_a_low_soft_nofile_limit() {
|
||||
assert_eq!(
|
||||
required_nofile_limit(Rlimit {
|
||||
current: Some(1_024),
|
||||
maximum: Some(DOMAIN_OPEN_FILE_LIMIT),
|
||||
})
|
||||
.unwrap(),
|
||||
Some(Rlimit {
|
||||
current: Some(DOMAIN_OPEN_FILE_LIMIT),
|
||||
maximum: Some(DOMAIN_OPEN_FILE_LIMIT),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn host_coordinator_refuses_an_insufficient_hard_nofile_limit() {
|
||||
let error = required_nofile_limit(Rlimit {
|
||||
current: Some(1_024),
|
||||
maximum: Some(DOMAIN_OPEN_FILE_LIMIT - 1),
|
||||
})
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
error.to_string(),
|
||||
format!(
|
||||
"Host Coordinator requires RLIMIT_NOFILE {DOMAIN_OPEN_FILE_LIMIT}, but inherited soft/hard limits are 1024/{}",
|
||||
DOMAIN_OPEN_FILE_LIMIT - 1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn host_coordinator_preserves_sufficient_and_unlimited_nofile_limits() {
|
||||
for limit in [
|
||||
Rlimit {
|
||||
current: Some(DOMAIN_OPEN_FILE_LIMIT),
|
||||
maximum: Some(DOMAIN_OPEN_FILE_LIMIT),
|
||||
},
|
||||
Rlimit {
|
||||
current: Some(DOMAIN_OPEN_FILE_LIMIT + 1),
|
||||
maximum: None,
|
||||
},
|
||||
Rlimit {
|
||||
current: None,
|
||||
maximum: None,
|
||||
},
|
||||
] {
|
||||
assert_eq!(required_nofile_limit(limit).unwrap(), None);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn private_bootstrap_arguments_are_exact_and_public() {
|
||||
let arguments = [
|
||||
|
|
|
|||
Loading…
Reference in a new issue