Skip to content

I/O safety of signal-hook self-pipe APIs #196

@hanna-kruppe

Description

@hanna-kruppe

In recent years, Rust has adopted a notion of I/O safety that conflicts with some traditional APIs dealing with raw file descriptors (or Win32 handles). In particular, std::io documentation says:

To uphold I/O safety, it is crucial that no code acts on file descriptors it does not own or borrow, and no code closes file descriptors it does not own. In other words, a safe function that takes a regular integer, treats it as a file descriptor, and acts on it, is unsound.

Some safe APIs in signal-hook predate this decree and conflict with it:

All of these take an FD as RawFd (alias for c_int) directly or via conversion traits , so they can be called with any plain old integer and are thus considered unsound today. For example, pipe::register_raw(signal, 1) compiles, puts stdout into non-blocking mode, and writes an X to stdout whenever the signal occurs. Unfortunately there's no way to fix this without a semver-breaking change (i.e., bumping signal-hook 0.3 -> 0.4):

  • Mark these functions unsafe and pass the I/O safety requirements to callers. This is a breaking change, and requires all callers to write some extra unsafe themselves. This I/O safety concept also isn't the most intuitive in practice (e.g., it's non-obvious that impl IntoRawFd is the wrong trait bound for a safe function taking ownership of an FD), so I wouldn't be surprised if it just pushes the unsoundness into callers.
  • Change the parameter types / trait bounds to make the functions safe. For example, I believe pipe::register_raw could accept OwnedFd (since it ultimately closes the FD if the action is later unregistered). This is also a breaking change, harder to get right on the library side, and requires bumping MSRV to 1.66 to get access to the new vocabulary types. On the bright side, it's more friendly to users of the library in the long run, since it allows doing a lot of things without any unsafe (since many types in std and the ecosystem implement AsFd and Into<OwnedFd>).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions