diff --git a/library/std/src/os/fuchsia/mod.rs b/library/std/src/os/fuchsia/mod.rs index cd1b8233eb3ec..91422a91f4c47 100644 --- a/library/std/src/os/fuchsia/mod.rs +++ b/library/std/src/os/fuchsia/mod.rs @@ -3,4 +3,6 @@ #![stable(feature = "raw_ext", since = "1.1.0")] pub mod fs; +#[unstable(feature = "fuchsia_exit_status", issue = "none")] +pub mod process; pub mod raw; diff --git a/library/std/src/os/fuchsia/process.rs b/library/std/src/os/fuchsia/process.rs new file mode 100644 index 0000000000000..88bbf1944edf8 --- /dev/null +++ b/library/std/src/os/fuchsia/process.rs @@ -0,0 +1,52 @@ +//! Fuchsia-specific extensions to primitives in the [`std::process`] module. +//! +//! [`std::process`]: crate::process + +use crate::process; +use crate::sealed::Sealed; + +#[unstable(feature = "fuchsia_exit_status", issue = "none")] +pub type zx_status_t = i32; +#[unstable(feature = "fuchsia_exit_status", issue = "none")] +pub const ZX_TASK_RETCODE_SYSCALL_KILL: zx_status_t = -1024; +#[unstable(feature = "fuchsia_exit_status", issue = "none")] +pub const ZX_TASK_RETCODE_OOM_KILL: zx_status_t = -1025; +#[unstable(feature = "fuchsia_exit_status", issue = "none")] +pub const ZX_TASK_RETCODE_POLICY_KILL: zx_status_t = -1026; +#[unstable(feature = "fuchsia_exit_status", issue = "none")] +pub const ZX_TASK_RETCODE_VDSO_KILL: zx_status_t = -1027; +/// On Zircon (the Fuchsia kernel), an abort from userspace calls the +/// LLVM implementation of __builtin_trap(), e.g., ud2 on x86, which +/// raises a kernel exception. If a userspace process does not +/// otherwise arrange exception handling, the kernel kills the process +/// with this return code. +#[unstable(feature = "fuchsia_exit_status", issue = "none")] +pub const ZX_TASK_RETCODE_EXCEPTION_KILL: zx_status_t = -1028; +#[unstable(feature = "fuchsia_exit_status", issue = "none")] +pub const ZX_TASK_RETCODE_CRITICAL_PROCESS_KILL: zx_status_t = -1029; + +#[unstable(feature = "fuchsia_exit_status", issue = "none")] +pub trait ExitStatusExt: Sealed { + /// If the task was killed, returns the `ZX_TASK_RETCODE_*`. + #[must_use] + fn task_retcode(&self) -> Option; +} + +#[unstable(feature = "fuchsia_exit_status", issue = "none")] +impl ExitStatusExt for process::ExitStatus { + fn task_retcode(&self) -> Option { + self.code().and_then(|code| { + if code == ZX_TASK_RETCODE_SYSCALL_KILL + || code == ZX_TASK_RETCODE_OOM_KILL + || code == ZX_TASK_RETCODE_POLICY_KILL + || code == ZX_TASK_RETCODE_VDSO_KILL + || code == ZX_TASK_RETCODE_EXCEPTION_KILL + || code == ZX_TASK_RETCODE_CRITICAL_PROCESS_KILL + { + Some(code) + } else { + None + } + }) + } +} diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 7aff7fe1fdd68..0134c15187cbf 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -23,6 +23,7 @@ #![feature(process_exitcode_internals)] #![feature(panic_can_unwind)] #![feature(test)] +#![cfg_attr(target_os = "fuchsia", feature(fuchsia_exit_status))] #![allow(internal_features)] // Public reexports diff --git a/library/test/src/test_result.rs b/library/test/src/test_result.rs index 98c54f038da6c..a0d024732422d 100644 --- a/library/test/src/test_result.rs +++ b/library/test/src/test_result.rs @@ -1,8 +1,10 @@ use std::any::Any; use std::process::ExitStatus; +#[cfg(target_os = "fuchsia")] +use std::os::fuchsia::process::{ExitStatusExt as _, ZX_TASK_RETCODE_EXCEPTION_KILL}; #[cfg(unix)] -use std::os::unix::process::ExitStatusExt; +use std::os::unix::process::ExitStatusExt as _; use super::bench::BenchSamples; use super::options::ShouldPanic; @@ -21,14 +23,6 @@ pub const TR_OK: i32 = 50; #[cfg(windows)] const STATUS_FAIL_FAST_EXCEPTION: i32 = 0xC0000409u32 as i32; -// On Zircon (the Fuchsia kernel), an abort from userspace calls the -// LLVM implementation of __builtin_trap(), e.g., ud2 on x86, which -// raises a kernel exception. If a userspace process does not -// otherwise arrange exception handling, the kernel kills the process -// with this return code. -#[cfg(target_os = "fuchsia")] -const ZX_TASK_RETCODE_EXCEPTION_KILL: i32 = -1028; - #[derive(Debug, Clone, PartialEq)] pub enum TestResult { TrOk, @@ -101,10 +95,28 @@ pub fn get_result_from_exit_code( time_opts: &Option, exec_time: &Option, ) -> TestResult { - let result = match status.code() { + // Upon a panic, a Fuchsia process will trigger a kernel exception + // that, if uncaught, will cause the kernel to kill the process with + // ZX_TASK_RETCODE_EXCEPTION_KILL. Though unlikely, the same code could be + // returned for other unhandled exceptions too. Even in those cases the test + // should still fail and the printed stacktrace from the kernel should + // sufficienly compensate for omitting this return code from test output. + #[cfg(target_os = "fuchsia")] + let result = match status.task_retcode() { + Some(ZX_TASK_RETCODE_EXCEPTION_KILL) => Some(TestResult::TrFailed), + _ => None, + }; + #[cfg(not(target_os = "fuchsia"))] + let result: Option = None; + + let result = result.unwrap_or_else(|| match status.code() { Some(TR_OK) => TestResult::TrOk, #[cfg(windows)] Some(STATUS_FAIL_FAST_EXCEPTION) => TestResult::TrFailed, + #[cfg(any(windows, unix))] + Some(code) => TestResult::TrFailedMsg(format!("got unexpected return code {code}")), + #[cfg(not(any(windows, unix)))] + Some(_) => TestResult::TrFailed, #[cfg(unix)] None => match status.signal() { Some(libc::SIGABRT) => TestResult::TrFailed, @@ -113,16 +125,9 @@ pub fn get_result_from_exit_code( } None => unreachable!("status.code() returned None but status.signal() was None"), }, - // Upon an abort, Fuchsia returns the status code ZX_TASK_RETCODE_EXCEPTION_KILL. - #[cfg(target_os = "fuchsia")] - Some(ZX_TASK_RETCODE_EXCEPTION_KILL) => TestResult::TrFailed, #[cfg(not(unix))] None => TestResult::TrFailedMsg(format!("unknown return code")), - #[cfg(any(windows, unix))] - Some(code) => TestResult::TrFailedMsg(format!("got unexpected return code {code}")), - #[cfg(not(any(windows, unix)))] - Some(_) => TestResult::TrFailed, - }; + }); // If test is already failed (or allowed to fail), do not change the result. if result != TestResult::TrOk { diff --git a/tests/ui/process/signal-exit-status.rs b/tests/ui/process/signal-exit-status.rs index a6acea476367a..5dfa65864cb0f 100644 --- a/tests/ui/process/signal-exit-status.rs +++ b/tests/ui/process/signal-exit-status.rs @@ -2,20 +2,34 @@ //@ ignore-wasm32 no processes //@ ignore-sgx no processes //@ ignore-windows -//@ ignore-fuchsia code returned as ZX_TASK_RETCODE_EXCEPTION_KILL, FIXME (#58590) #![feature(core_intrinsics)] +#![cfg_attr(target_os = "fuchsia", feature(fuchsia_exit_status))] use std::env; use std::process::Command; +#[cfg(target_os = "fuchsia")] +use std::os::fuchsia::process::{ExitStatusExt, ZX_TASK_RETCODE_EXCEPTION_KILL}; + pub fn main() { let args: Vec = env::args().collect(); if args.len() >= 2 && args[1] == "signal" { // Raise an aborting signal without UB core::intrinsics::abort(); } else { + // Spawn a child process that will raise an aborting signal let status = Command::new(&args[0]).arg("signal").status().unwrap(); + + #[cfg(not(target_os = "fuchsia"))] assert!(status.code().is_none()); + + // Upon abort(), a Fuchsia process will trigger a kernel exception + // that, if uncaught, will cause the kernel to kill the process with + // ZX_TASK_RETCODE_EXCEPTION_KILL. The same code could be + // returned for a different unhandled exception, but the simplicity of + // the program under test makes such an exception unlikely. + #[cfg(target_os = "fuchsia")] + assert_eq!(Some(ZX_TASK_RETCODE_EXCEPTION_KILL), status.task_retcode()); } }