From 6c63d1dfea79bd7737486ad9476dfbcc839cd6fd Mon Sep 17 00:00:00 2001 From: ZauberNerd Date: Tue, 23 Jun 2026 20:59:36 +0000 Subject: [PATCH] init: tolerate closed stdio when the console is not yet ready The kernel's handoff of stdin/stdout/stderr to PID 1 is best-effort: if hvc0 is not registered when the kernel execs init, init inherits closed fds 0/1/2 ("unable to open an initial console" in the kernel log). Rust std's sanitize_standard_fds() then runs before main(), cannot backfill /dev/null (devtmpfs is unmounted), and aborts -- rebooting before the workload runs. This is the flaky configure-vm-2cpu-1GiB failure (empty output): boot timing intermittently delays hvc0 (gated on the secondary vCPU) past the kernel's initial-console-open. KRUN_ENOMEM_WORKAROUND widens the window; the race is latent without it. A C init tolerates closed stdio. Restore that with an .init_array constructor that reopens 0/1/2 (console, else /dev/null) before the runtime check; setup_redirects() rewires them to the krun ports as before. Assisted-by: Claude Code / claude-opus-4.8 Signed-off-by: ZauberNerd --- init/src/main.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/init/src/main.rs b/init/src/main.rs index 1d2de30..6e54788 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -10,6 +10,50 @@ mod fs; #[cfg(feature = "timesync")] mod timesync; +/// Ensure init has open stdin/stdout/stderr. +/// +/// The kernel hands init closed fds 0/1/2 when the console (hvc0) is not yet +/// registered as it execs init, and Rust std's sanitize_standard_fds() aborts on +/// that before main() runs. Open them first (the console, else /dev/null); +/// setup_redirects() rewires them to the krun ports later. +#[cfg(target_os = "linux")] +extern "C" fn ensure_stdio() { + use nix::fcntl::{self, OFlag}; + use nix::mount::{self, MsFlags}; + use nix::sys::stat::Mode; + use nix::unistd; + use std::os::fd::AsRawFd; + + if unsafe { libc::fcntl(libc::STDIN_FILENO, libc::F_GETFD) } >= 0 { + return; + } + let _ = std::fs::create_dir_all("/dev"); + let _ = mount::mount( + Some("devtmpfs"), + "/dev", + Some("devtmpfs"), + MsFlags::empty(), + None::<&str>, + ); + + let Ok(fd) = fcntl::open(c"/dev/console", OFlag::O_RDWR, Mode::empty()) + .or_else(|_| fcntl::open(c"/dev/null", OFlag::O_RDWR, Mode::empty())) + else { + return; + }; + let _ = unistd::dup2_stdin(&fd); + let _ = unistd::dup2_stdout(&fd); + let _ = unistd::dup2_stderr(&fd); + if fd.as_raw_fd() <= libc::STDERR_FILENO { + // fd is a stdio slot that dup2 just wrote to — don't close it. + std::mem::forget(fd); + } +} +#[cfg(target_os = "linux")] +#[used] +#[unsafe(link_section = ".init_array")] +static ENSURE_STDIO_CTOR: extern "C" fn() = ensure_stdio; + fn main() -> anyhow::Result<()> { #[cfg(target_os = "freebsd")] freebsd::open_console(); -- 2.51.2