Skip to content

ExitCode::success() returns false when exit code is 0 #23

@believeinlain

Description

@believeinlain

I've encountered an error where memfd_exec::process::ExitStatus::success returns false when the execution was successful and returned with status code 0.

Minimal example to reproduce:

use memfd_exec::MemFdExecutable;

fn main() {
    let code = include_bytes!("/usr/bin/echo");

    let exit_status = MemFdExecutable::new("echo", code)
        .arg("Hello")
        .status()
        .expect("failed to execute process");
    // Exit status is 0
    println!("exit status {exit_status:?}");
    // Success fails
    assert!(exit_status.success());
}

It looks like the issue is in ExitStatus::exit_ok:

/// Was termination successful? Returns a Result.
pub fn exit_ok(&self) -> Result<()> {
    // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0.  This is
    // true on all actual versions of Unix, is widely assumed, and is specified in SuS
    // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html .  If it is not
    // true for a platform pretending to be Unix, the tests (our doctests, and also
    // procsss_unix/tests.rs) will spot it.  `ExitStatusError::code` assumes this too.
    #[allow(clippy::useless_conversion)]
    match c_int::try_from(self.0) {
        /* was nonzero */
        Ok(failure) => Err(Error::new(
            std::io::ErrorKind::Other,
            format!("process exited with status {}", failure),
        )),
        /* was zero, couldn't convert */
        Err(_) => Ok(()),
    }
}

This assumes that c_int::try_from(self.0) will fail if self.0 == 0, but a c_int can be 0 and the conversion always succeeds.

The standard library implementation (for std::sys::pal::unix::process::process_inner::ExitStatus) is:

pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
    // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
    // true on all actual versions of Unix, is widely assumed, and is specified in SuS
    // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not
    // true for a platform pretending to be Unix, the tests (our doctests, and also
    // process_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
    match NonZero::try_from(self.0) {
        /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
        /* was zero, couldn't convert */ Err(_) => Ok(()),
    }
}

Which makes sense, because NonZero actually cannot be 0, unlike c_int.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions