From a8fa2af64b316383f24a6f27ac213a17c7a1365b Mon Sep 17 00:00:00 2001 From: Praveen K Paladugu Date: Thu, 16 May 2024 20:41:35 +0000 Subject: [PATCH] vmm: dup serial fds to preserve them across reboots During vm_shutdown or vm_snapshot, all the console devices will be closed. When this happens stdout (FD #2) will also be closed as the console device using these FD is closed. If the VM were to be started later, FD#2 can be assigned to a different file. But pre_create_console_devices looks for FD#2 while opening tty device, which could point to any file. To avoid this problem, the STDOUT FD is duplicated when being assigned to a console device. Even if the console devices were to be closed, the duplicated FD will be closed and FD#2 will continue to point to STDOUT. Signed-off-by: Praveen K Paladugu --- vmm/src/console_devices.rs | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/vmm/src/console_devices.rs b/vmm/src/console_devices.rs index 1e4ff22b5..aec30c1be 100644 --- a/vmm/src/console_devices.rs +++ b/vmm/src/console_devices.rs @@ -237,9 +237,37 @@ pub(crate) fn pre_create_console_devices(vmm: &mut Vmm) -> ConsoleDeviceResult { - let out = stdout(); - console_info.serial_main_fd = Some(out.as_raw_fd()); - set_raw_mode(&out, vmm.original_termios_opt.clone())?; + // During vm_shutdown, when serial device is closed, FD#2(STDOUT) + // will be closed and FD#2 could be reused in a future boot of the + // guest by a different file. + // + // To ensure FD#2 always points to STANDARD OUT, a `dup` of STDOUT + // is passed to serial device. Doing so, even if the serial device + // were to be closed, FD#2 will continue to point to STANDARD OUT. + + // SAFETY: FFI call to dup. Trivially safe. + let stdout = unsafe { libc::dup(libc::STDOUT_FILENO) }; + if stdout == -1 { + return vmm_sys_util::errno::errno_result().map_err(ConsoleDeviceError::DupFd); + } + // SAFETY: stdout is valid and owned solely by us. + let stdout = unsafe { File::from_raw_fd(stdout) }; + + // SAFETY: FFI call. Trivially safe. + if unsafe { libc::isatty(libc::STDOUT_FILENO) } == 1 { + vmm.console_resize_pipe = Some( + listen_for_sigwinch_on_tty( + stdout.try_clone().unwrap(), + &vmm.seccomp_action, + vmm.hypervisor.hypervisor_type(), + ) + .map_err(ConsoleDeviceError::StartSigwinchListener)?, + ); + } + + // Make sure stdout is in raw mode, if it's a terminal. + set_raw_mode(&stdout, vmm.original_termios_opt.clone())?; + console_info.serial_main_fd = Some(stdout.into_raw_fd()); } ConsoleOutputMode::Socket => { let listener = UnixListener::bind(vmconfig.serial.socket.as_ref().unwrap())