Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix panic=abort tests on fuchsia #127595

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
52 changes: 52 additions & 0 deletions library/std/src/os/fuchsia/process.rs
Original file line number Diff line number Diff line change
@@ -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;
Comment on lines +8 to +26
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not thrilled about adding these constants here since std::sys::pal::unix::process::zircon exists. However, since std::sys::pal::unix::process::zircon is private, I didn't want to cross the bridge of exposing parts of that module in this change.

Copy link
Contributor

@tgross35 tgross35 Dec 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could/should these be added to libc instead and then used in both places?


#[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<i32>;
}

#[unstable(feature = "fuchsia_exit_status", issue = "none")]
impl ExitStatusExt for process::ExitStatus {
fn task_retcode(&self) -> Option<i32> {
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
}
})
}
}
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
41 changes: 23 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 _, 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;
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,28 @@ pub fn get_result_from_exit_code(
time_opts: &Option<time::TestTimeOptions>,
exec_time: &Option<time::TestExecTime>,
) -> 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<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 +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 {
Expand Down
16 changes: 15 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,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<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());

// 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());
}
}
Loading