Skip to content

Commit c136b5a

Browse files
committed
fix(jailer): Make "init" in new PID NS a session leader
https://man7.org/linux/man-pages/man7/pid_namespaces.7.html > A process in an ancestor namespace can send signals to the "init" > process of a child PID namespace only if the "init" process has > established a handelr for that signal. Firecracker (i.e. the "init" process of the new PID namespace) sets up handlers for some signals including SIGHUP and jailer exits soon after spawning firecracker into the new PID namespace. If the jailer process is a session leader and its exit happens after firecracker configures the signal handlers, SIGHUP will be sent to firecracker and be caught by the handler unexpectedly. In order to avoid the above issue, if jailer is a session leader, creates a new session and makes the child process (i.e. firecracker) become the leader of the new session to not get SIGHUP on the exit of jailer. Note that this is the case only if `--daemonize` is not passed. This is because we use the double fork method to daemonize, making itself not a session leader. Signed-off-by: Takahiro Itazuri <[email protected]>
1 parent 2167247 commit c136b5a

File tree

3 files changed

+48
-1
lines changed

3 files changed

+48
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ and this project adheres to
5454
Firecracker's `--parent-cpu-time-us` values, which caused development builds
5555
of Firecracker to crash (but production builds were unaffected as underflows
5656
do not panic in release mode).
57+
- [#5045](https://github.com/firecracker-microvm/firecracker/pull/5045): Fixed
58+
an issue where firecracker intermittent receives SIGHUP when using jailer with
59+
`--new-pid-ns` but without `--daemonize`.
5760

5861
## [1.10.1]
5962

src/jailer/src/env.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,12 +330,52 @@ impl Env {
330330
}
331331

332332
fn exec_into_new_pid_ns(&mut self, chroot_exec_file: PathBuf) -> Result<(), JailerError> {
333+
// https://man7.org/linux/man-pages/man7/pid_namespaces.7.html
334+
// > a process in an ancestor namespace can send signals to the "init" process of a child
335+
// > PID namespace only if the "init" process has established a handler for that signal.
336+
//
337+
// Firecracker (i.e. the "init" process of the new PID namespace) sets up handlers for some
338+
// signals including SIGHUP and jailer exits soon after spawning firecracker into a new PID
339+
// namespace. If the jailer process is a session leader and its exit happens after
340+
// firecracker configures the signal handlers, SIGHUP will be sent to firecracker and be
341+
// caught by the handler unexpectedly.
342+
//
343+
// In order to avoid the above issue, if jailer is a session leader, creates a new session
344+
// and makes the child process (i.e. firecracker) become the leader of the new session to
345+
// not get SIGHUP on the exit of jailer.
346+
347+
// Check whether jailer is a session leader or not before clone().
348+
// Note that, if `--daemonize` is passed, jailer is always not a session leader. This is
349+
// because we use the double fork method, making itself not a session leader.
350+
let is_session_leader = match self.daemonize {
351+
true => false,
352+
false => {
353+
// SAFETY: Safe because it doesn't take any input parameters.
354+
let sid = SyscallReturnCode(unsafe { libc::getsid(0) })
355+
.into_result()
356+
.map_err(JailerError::GetSid)?;
357+
// SAFETY: Safe because it doesn't take any input parameters.
358+
let ppid = SyscallReturnCode(unsafe { libc::getpid() })
359+
.into_result()
360+
.map_err(JailerError::GetPid)?;
361+
sid == ppid
362+
}
363+
};
364+
333365
// Duplicate the current process. The child process will belong to the previously created
334366
// PID namespace. The current process will not be moved into the newly created namespace,
335367
// but its first child will assume the role of init(1) in the new namespace.
336368
let pid = clone(std::ptr::null_mut(), libc::CLONE_NEWPID)?;
337369
match pid {
338-
0 => Err(JailerError::Exec(self.exec_command(chroot_exec_file))),
370+
0 => {
371+
if is_session_leader {
372+
// SAFETY: Safe bacause it doesn't take any input parameters.
373+
SyscallReturnCode(unsafe { libc::setsid() })
374+
.into_empty_result()
375+
.map_err(JailerError::SetSid)?;
376+
}
377+
Err(JailerError::Exec(self.exec_command(chroot_exec_file)))
378+
}
339379
child_pid => {
340380
// Save the PID of the process running the exec file provided
341381
// inside <chroot_exec_file>.pid file.

src/jailer/src/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ pub enum JailerError {
8585
FromBytesWithNul(std::ffi::FromBytesWithNulError),
8686
#[error("Failed to get flags from fd: {0}")]
8787
GetOldFdFlags(io::Error),
88+
#[error("Failed to get PID (getpid): {0}")]
89+
GetPid(io::Error),
90+
#[error("Failed to get SID (getsid): {0}")]
91+
GetSid(io::Error),
8892
#[error("Invalid gid: {0}")]
8993
Gid(String),
9094
#[error("Invalid instance ID: {0}")]

0 commit comments

Comments
 (0)