Skip to content

Commit da92bf6

Browse files
Sudan Landgewearyzen
Sudan Landge
authored andcommitted
fix: use double fork mechanism for daemonize
Use the double fork method to daemonize jailer as suggested in https://0xjet.github.io/3OHA/2022/04/11/post.html 1st fork to make sure setsid doesn't fail and 2nd fork is to make sure the daemon code cannot reacquire a controlling terminal. Signed-off-by: Sudan Landge <[email protected]>
1 parent fcd5269 commit da92bf6

File tree

3 files changed

+37
-6
lines changed

3 files changed

+37
-6
lines changed

docs/jailer.md

-4
Original file line numberDiff line numberDiff line change
@@ -271,10 +271,6 @@ Note: default value for `<api-sock>` is `/run/firecracker.socket`.
271271
binary file in a new PID namespace, in order to become a pseudo-init process.
272272
Alternatively, the user can spawn the jailer in a new PID namespace via a
273273
combination of `clone()` with the `CLONE_NEWPID` flag and `exec()`.
274-
- When running with `--daemonize`, the jailer will fail to start if it's a
275-
process group leader, because `setsid()` returns an error in this case.
276-
Spawning the jailer via `clone()` and `exec()` also ensures it cannot be a
277-
process group leader.
278274
- We run the jailer as the `root` user; it actually requires a more restricted
279275
set of capabilities, but that's to be determined as features stabilize.
280276
- The jailer can only log messages to stdout/err for now, which is why the

src/jailer/src/env.rs

+35-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::os::unix::fs::PermissionsExt;
88
use std::os::unix::io::AsRawFd;
99
use std::os::unix::process::CommandExt;
1010
use std::path::{Component, Path, PathBuf};
11-
use std::process::{Command, Stdio};
11+
use std::process::{exit, Command, Stdio};
1212
use std::{fmt, io};
1313

1414
use utils::arg_parser::Error::MissingValue;
@@ -674,12 +674,45 @@ impl Env {
674674

675675
// Daemonize before exec, if so required (when the dev_null variable != None).
676676
if let Some(dev_null) = dev_null {
677-
// Call setsid().
677+
// We follow the double fork method to daemonize the jailer referring to
678+
// https://0xjet.github.io/3OHA/2022/04/11/post.html
679+
// setsid() will fail if the calling process is a process group leader.
680+
// By calling fork(), we guarantee that the newly created process inherits
681+
// the PGID from its parent and, therefore, is not a process group leader.
682+
// SAFETY: Safe because it's a library function.
683+
let child_pid = unsafe { libc::fork() };
684+
if child_pid < 0 {
685+
return Err(JailerError::Daemonize(io::Error::last_os_error()));
686+
}
687+
688+
if child_pid != 0 {
689+
// parent exiting
690+
exit(0);
691+
}
692+
693+
// Call setsid() in child
678694
// SAFETY: Safe because it's a library function.
679695
SyscallReturnCode(unsafe { libc::setsid() })
680696
.into_empty_result()
681697
.map_err(JailerError::SetSid)?;
682698

699+
// Daemons should not have controlling terminals.
700+
// If a daemon has a controlling terminal, it can receive signals
701+
// from it that might cause it to halt or exit unexpectedly.
702+
// The second fork() ensures that grandchild is not a session,
703+
// leader and thus cannot reacquire a controlling terminal.
704+
// SAFETY: Safe because it's a library function.
705+
let grandchild_pid = unsafe { libc::fork() };
706+
if grandchild_pid < 0 {
707+
return Err(JailerError::Daemonize(io::Error::last_os_error()));
708+
}
709+
710+
if grandchild_pid != 0 {
711+
// child exiting
712+
exit(0);
713+
}
714+
715+
// grandchild is the daemon
683716
// Replace the stdio file descriptors with the /dev/null fd.
684717
dup2(dev_null.as_raw_fd(), STDIN_FILENO)?;
685718
dup2(dev_null.as_raw_fd(), STDOUT_FILENO)?;

src/jailer/src/main.rs

+2
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ pub enum JailerError {
6464
CreateDir(PathBuf, io::Error),
6565
#[error("Encountered interior \\0 while parsing a string")]
6666
CStringParsing(NulError),
67+
#[error("Failed to daemonize: {0}")]
68+
Daemonize(io::Error),
6769
#[error("Failed to open directory {0}: {1}")]
6870
DirOpen(String, String),
6971
#[error("Failed to duplicate fd: {0}")]

0 commit comments

Comments
 (0)