Skip to content

Commit

Permalink
Use the close_range syscall for sanitizing the jailer process
Browse files Browse the repository at this point in the history
Closes all open FDs in a single syscall. In case close_range is not
available then fallback to the old approach. In case the fallback fails
then discover open FDs by reading from /proc/self/fds.

Fixes a bug where firecracker would end up spending minutes starting
which arises when running on systems with OPEN_MAX set to a very high
number.

Signed-off-by: Grzegorz Uriasz <gorbak25@gmail.com>
  • Loading branch information
gorbak25 committed Mar 20, 2023
1 parent b18c3ab commit 108c764
Showing 1 changed file with 40 additions and 8 deletions.
48 changes: 40 additions & 8 deletions src/jailer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,19 +235,51 @@ pub fn readln_special<T: AsRef<Path>>(file_path: &T) -> Result<String> {
Ok(line)
}

fn sanitize_process() {
// First thing to do is make sure we don't keep any inherited FDs
// other that IN, OUT and ERR.
// Closes all FDs other than 0 (STDIN), 1 (STDOUT) and 2 (STDERR)
fn close_inherited_fds() {
// First try using the close_range syscall to close all open FDs in the range of 4..UINT_MAX
// SAFETY: if the syscall is not available then ENOSYS will be returned
if unsafe {
libc::syscall(libc::SYS_close_range, 3, libc::c_uint::MAX, libc::CLOSE_RANGE_UNSHARE)
} == 0 {
return
}

// Probably the kernel does not support close_range, kernel < 5.9
// SAFETY: Always safe.
let fd_limit = i32::try_from(unsafe { libc::sysconf(libc::_SC_OPEN_MAX) }).unwrap();
// Close all file descriptors excluding 0 (STDIN), 1 (STDOUT) and 2 (STDERR).
for fd in 3..fd_limit {
// SAFETY: Safe because close() cannot fail when passed a valid parameter.
unsafe {
libc::close(fd);

// Check if the fd_limit is small enough to iterate through
if fd_limit <= 2_i32.pow(20) {
for fd in 3..fd_limit {
// SAFETY: Safe because close() cannot fail when passed a valid parameter.
unsafe {
libc::close(fd);
}
}
return
}

// If both methods failed then fallback to reading /proc
// This means that we are on kernel < 5.9 and OPEN_MAX is set to a pathological size
if let Ok(mut paths) = fs::read_dir("/proc/self/fd") {
while let Some(Ok(path)) = paths.next() {
let file_name = path.file_name();
let fd_str = file_name.to_str().unwrap_or("0");
let fd = fd_str.parse::<i32>().unwrap_or(0);

if fd > 2 {
// SAFETY: Safe because close() cannot fail when passed a valid parameter.
unsafe { libc::close(fd) };
}
}
}
}

fn sanitize_process() {
// First thing to do is make sure we don't keep any inherited FDs
// other that IN, OUT and ERR.
close_inherited_fds();
// Cleanup environment variables
clean_env_vars();
}
Expand Down

0 comments on commit 108c764

Please sign in to comment.