Skip to content

Commit

Permalink
Add ExitStatusExt::aborted_code for Fuchsia platform
Browse files Browse the repository at this point in the history
  • Loading branch information
c6c7 committed Jul 13, 2024
1 parent 7e763af commit 1e8eba9
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 19 deletions.
2 changes: 2 additions & 0 deletions library/std/src/os/fuchsia/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
31 changes: 31 additions & 0 deletions library/std/src/os/fuchsia/process.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//! Fuchsia-specific extensions to primitives in the [`std::process`] module.
//!
//! [`std::process`]: crate::process
use crate::process;
use crate::sealed::Sealed;

// 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.
const ZX_TASK_RETCODE_EXCEPTION_KILL: i32 = -1028;

#[unstable(feature = "fuchsia_exit_status", issue = "none")]
pub trait ExitStatusExt: Sealed {
/// If the process was aborted, returns the status code.
#[must_use]
fn aborted_code(&self) -> Option<i32>;
}

#[unstable(feature = "fuchsia_exit_status", issue = "none")]
impl ExitStatusExt for process::ExitStatus {
/// If the process was aborted, returns the status code.
fn aborted_code(&self) -> Option<i32> {
match self.code() {
code @ Some(ZX_TASK_RETCODE_EXCEPTION_KILL) => code,
Some(_) | None => None,
}
}
}
1 change: 1 addition & 0 deletions library/test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 14 additions & 18 deletions library/test/src/test_result.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use std::any::Any;
use std::process::ExitStatus;

#[cfg(target_os = "fuchsia")]
use std::os::fuchsia::process::ExitStatusExt as _;
#[cfg(unix)]
use std::os::unix::process::ExitStatusExt;
use std::os::unix::process::ExitStatusExt as _;

use super::bench::BenchSamples;
use super::options::ShouldPanic;
Expand All @@ -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,
Expand Down Expand Up @@ -101,10 +95,19 @@ pub fn get_result_from_exit_code(
time_opts: &Option<time::TestTimeOptions>,
exec_time: &Option<time::TestExecTime>,
) -> TestResult {
let result = match status.code() {
#[cfg(target_os = "fuchsia")]
let result = status.aborted_code().map(|_| TestResult::TrFailed);
#[cfg(not(target_os = "fuchsia"))]
let result: Option<TestResult> = 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,
Expand All @@ -113,16 +116,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 {
Expand Down
10 changes: 9 additions & 1 deletion tests/ui/process/signal-exit-status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,28 @@
//@ 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;

pub fn main() {
let args: Vec<String> = 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());
#[cfg(target_os = "fuchsia")]
assert!(status.aborted_code().is_some());
}
}

0 comments on commit 1e8eba9

Please sign in to comment.