mirror of
https://github.com/vhaudiquet/ecrou.git
synced 2026-11-12 22:22:44 +00:00
fix(vm): save the tty before the fork and retry waitpid on EINTR
The termios save ran after fork, so a child that reached raw mode first would be captured and later restored as-is. A caught signal interrupted waitpid, failed the run, and left the machine running; EINTR now retries the wait.
This commit is contained in:
1 parent
1e69044198
commit
8ceab3130d
1 file changed
+27
-7
+27
-7
@@ -264,6 +264,14 @@ pub fn run_prepared(rootfs: &PreparedRootfs, options: &RunOptions) -> Result<i32
|
|||||||
.with_context(|| format!("creating {}", console_path.display()))?;
|
.with_context(|| format!("creating {}", console_path.display()))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The parent holds the terminal's mode across an interactive
|
||||||
|
// machine: libkrun restores through the child's exit observers,
|
||||||
|
// which a SIGKILLed machine never runs. The save happens before the
|
||||||
|
// fork because the child can reach raw mode at any moment after it;
|
||||||
|
// a save after the fork could capture libkrun's mode and restore
|
||||||
|
// that. (Only a tty is saved, so a piped -i run is a no-op here.)
|
||||||
|
let tty_restore = TtyRestore::save(interactive);
|
||||||
|
|
||||||
// Fork with no other threads alive: the child only touches libkrun
|
// Fork with no other threads alive: the child only touches libkrun
|
||||||
// configuration before entering the VM loop, which never returns (a
|
// configuration before entering the VM loop, which never returns (a
|
||||||
// normal payload exit ends the child through the VMM's own exit).
|
// normal payload exit ends the child through the VMM's own exit).
|
||||||
@@ -321,11 +329,6 @@ pub fn run_prepared(rootfs: &PreparedRootfs, options: &RunOptions) -> Result<i32
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The parent holds the terminal's mode across an interactive
|
|
||||||
// machine: libkrun restores through the child's exit observers,
|
|
||||||
// which a SIGKILLed machine never runs. (Only a tty is saved, so a
|
|
||||||
// piped -i run is a no-op here.)
|
|
||||||
let tty_restore = TtyRestore::save(interactive);
|
|
||||||
let result = if interactive {
|
let result = if interactive {
|
||||||
wait_machine(
|
wait_machine(
|
||||||
child,
|
child,
|
||||||
@@ -405,7 +408,16 @@ fn stream_console(
|
|||||||
// both reports and reaps.
|
// both reports and reaps.
|
||||||
let mut status: libc::c_int = 0;
|
let mut status: libc::c_int = 0;
|
||||||
// Safety: waitpid with WNOHANG only; the pid is our own child.
|
// Safety: waitpid with WNOHANG only; the pid is our own child.
|
||||||
let r = unsafe { libc::waitpid(child, &mut status, libc::WNOHANG) };
|
let r = loop {
|
||||||
|
let r = unsafe { libc::waitpid(child, &mut status, libc::WNOHANG) };
|
||||||
|
// A caught signal must not abort the run and orphan the
|
||||||
|
// machine: EINTR only says look again.
|
||||||
|
if r == -1 && std::io::Error::last_os_error().kind() == std::io::ErrorKind::Interrupted
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break r;
|
||||||
|
};
|
||||||
if r == child {
|
if r == child {
|
||||||
reaped.store(status, std::sync::atomic::Ordering::SeqCst);
|
reaped.store(status, std::sync::atomic::Ordering::SeqCst);
|
||||||
done = true;
|
done = true;
|
||||||
@@ -452,7 +464,15 @@ fn wait_machine(
|
|||||||
let mut status: libc::c_int = 0;
|
let mut status: libc::c_int = 0;
|
||||||
// Safety: waitpid on our own child; the pointer points at valid
|
// Safety: waitpid on our own child; the pointer points at valid
|
||||||
// storage.
|
// storage.
|
||||||
let ret = unsafe { libc::waitpid(child, &mut status, 0) };
|
let ret = loop {
|
||||||
|
let r = unsafe { libc::waitpid(child, &mut status, 0) };
|
||||||
|
// EINTR: a caught signal interrupts the wait, it does not end
|
||||||
|
// it. Retry instead of erroring out and orphaning the machine.
|
||||||
|
if r == -1 && std::io::Error::last_os_error().kind() == std::io::ErrorKind::Interrupted {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break r;
|
||||||
|
};
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return Err(Error::Other(
|
return Err(Error::Other(
|
||||||
anyhow::Error::from(std::io::Error::last_os_error())
|
anyhow::Error::from(std::io::Error::last_os_error())
|
||||||
|
|||||||
Reference in new issue
Block a user