vmm: Avoid zombie sigwinch_listener processes

When a guest running on a terminal reboots, the sigwinch_listener
subprocess exits and a new one restarts. The parent never wait()s
for children, so the old subprocess remains as a zombie. With further
reboots, more and more zombies build up.

As there are no other children for which we want the exit status,
the easiest fix is to take advantage of the implicit reaping specified
by POSIX when we set the disposition of SIGCHLD to SIG_IGN.

For this to work, we also need to set the correct default exit signal
of SIGCHLD when using clone3() CLONE_CLEAR_SIGHAND. Unlike the fallback
fork() path, clone_args::default() initialises the exit signal to zero,
which results in a child with non-standard reaping behaviour.

Signed-off-by: Chris Webb <chris@arachsys.com>
This commit is contained in:
Chris Webb 2024-02-19 16:27:12 +00:00 committed by Rob Bradford
parent 6362b711c6
commit 09f3658999
2 changed files with 10 additions and 2 deletions

View File

@ -561,6 +561,11 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
.ok(); .ok();
} }
// SAFETY: Trivially safe.
unsafe {
libc::signal(libc::SIGCHLD, libc::SIG_IGN);
}
// Before we start any threads, mask the signals we'll be // Before we start any threads, mask the signals we'll be
// installing handlers for, to make sure they only ever run on the // installing handlers for, to make sure they only ever run on the
// dedicated signal handling thread we'll start in a bit. // dedicated signal handling thread we'll start in a bit.

View File

@ -6,7 +6,7 @@ use arch::_NSIG;
use libc::{ use libc::{
c_int, c_void, close, fork, getpgrp, ioctl, pipe2, poll, pollfd, setsid, sigemptyset, c_int, c_void, close, fork, getpgrp, ioctl, pipe2, poll, pollfd, setsid, sigemptyset,
siginfo_t, signal, sigprocmask, syscall, tcgetpgrp, tcsetpgrp, SYS_close_range, EINVAL, ENOSYS, siginfo_t, signal, sigprocmask, syscall, tcgetpgrp, tcsetpgrp, SYS_close_range, EINVAL, ENOSYS,
ENOTTY, O_CLOEXEC, POLLERR, SIGWINCH, SIG_DFL, SIG_SETMASK, STDERR_FILENO, TIOCSCTTY, ENOTTY, O_CLOEXEC, POLLERR, SIGCHLD, SIGWINCH, SIG_DFL, SIG_SETMASK, STDERR_FILENO, TIOCSCTTY,
}; };
use seccompiler::{apply_filter, BpfProgram}; use seccompiler::{apply_filter, BpfProgram};
use std::cell::RefCell; use std::cell::RefCell;
@ -204,7 +204,10 @@ fn sigwinch_listener_main(seccomp_filter: BpfProgram, tx: File, tty: File) -> !
/// ///
/// Same as [`fork`]. /// Same as [`fork`].
unsafe fn clone_clear_sighand() -> io::Result<u64> { unsafe fn clone_clear_sighand() -> io::Result<u64> {
let mut args = clone_args::default(); let mut args = clone_args {
exit_signal: SIGCHLD as u64,
..Default::default()
};
args.flags |= CLONE_CLEAR_SIGHAND; args.flags |= CLONE_CLEAR_SIGHAND;
let r = clone3(&mut args, size_of::<clone_args>()); let r = clone3(&mut args, size_of::<clone_args>());
if r != -1 { if r != -1 {