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

Document and stablize functions from rustix::runtime. #1314

Open
sunfishcode opened this issue Feb 6, 2025 · 3 comments
Open

Document and stablize functions from rustix::runtime. #1314

sunfishcode opened this issue Feb 6, 2025 · 3 comments

Comments

@sunfishcode
Copy link
Member

sunfishcode commented Feb 6, 2025

We should work towards moving functions currently in rustix::runtime out into the public API modules. This should involve thinking about their safety conditions, and figuring out what kind of public API works for the expected use cases. Also, rustix::runtime is not currently implemented for the libc backend, and it's desirable for rustix to expos the same features across its two backends, so that users (including indirect users) have the choice of whether to use the libc backend.

For example, rustix::runtime::sigaction exposes kernel-level signal registration function that bypasses libc. Libc implementations don't expect this to ever happen, so doing so may invoke undefined behavior. The precise conditions aren't documented, and could change with libc versions. As far as I can tell, the only guidance we could give for some functions would be "don't ever call this in a process which also contains a libc, or anything acting like a libc, including origin". And that's in addition to regular safety conditions, such as that the various raw pointers have to be used properly, and that don't SIGSEGV and SIGBUS shouldn't be ignored, because it could potentially bypass stack overflow guards, and all the safety considerations around signal handlers, and probably more.

In general, the main options for any given function in rustix::runtime include:

  • Make it public in rustix, with sufficiently imposing documentation, and either add a libc backend implementation using libc::syscall (if it's not incompatible with libc), or add further documentation saying "using this will prevent your users from enabling rustix's libc backed; please consider what you do here" or so, or
  • Have rustix expose a higher-level or more specialized API for it.
  • Expose it through origin instead of rustix (eg. origin::signal::sigaction).

Which of these three is best for any given function depends on what kinds of use cases people will have. So for any function in rustix::runtime that anyone would like to see documented and stabilized, please comment about it here, and describe your use case!

@sunfishcode
Copy link
Member Author

sunfishcode commented Feb 6, 2025

An example of pulling a function out of rustix::runtime is #1133. In making the function public, we're renaming it from the Linux-specific exit_group to the more descriptive immediately_exit, implementing it for the libc backend, and adding a sufficiently imposing documentation comment.

@sunfishcode
Copy link
Member Author

I should also say, PRs are welcome but not necessary here. Just posting a comment here would be helpful!

sunfishcode added a commit that referenced this issue Feb 7, 2025
sunfishcode added a commit that referenced this issue Feb 11, 2025
* Document unimplemented functions.

Add documentation for several functions which rustix does not implement,
or not yet implement, so that users searching for them may learn more.

* Add a link to #1314.
@metent
Copy link
Contributor

metent commented Feb 13, 2025

I think syscalls like rt_sigaction are too low-level compared to the rest of the functions provided by rustix. And these syscalls wouldn't be compatible with the libc backend, which most users rely on for non-linux platforms. If feasible, I think providing a high-level abstraction which is compatible with, and mimics glibc/musl would be a better option. If it's not feasible, or out of scope for rustix, then I don't think having a public API in rustix itself would have much use case for these functions.
Once such pragmatic higher-level abstraction that might be possible currently is sigignore(signal), which is just one simple abstraction, is trivial to implement for other platforms (libc::signal(libc::SIGINT, libc::SIG_IGN);), and it is guaranteed to work correctly (as far as I've tested). This would work very well with sigsuspend if it is moved into the public API.

use rustix::process::Signal;
use rustix::runtime::{sigaction, Sigaction, Sigset};

fn main() {
    unsafe {
        sigaction(
            Signal::INT,
            Some(Sigaction {
                sa_handler_kernel: Some(std::mem::transmute(libc::SIG_IGN)),
                sa_flags: 0,
                sa_restorer: None,
                sa_mask: Sigset { sig: [0] },
            }),
        )
        .unwrap();
        let mut set: libc::sigset_t = std::mem::zeroed();
        libc::sigemptyset(&mut set);
        libc::sigsuspend(&set);
    }
}

sigsuspend is a much safer alternative to sigaction/signal as it doesn't involve a handler, and thus doesn't require the caller to write async-signal-safe code. Although fairly limited on the surface level (it can only be used for 'catching' signals which can be ignored like SIGCHLD, SIGCONT, etc.) it will act as a sane alternative to sigaction when combined with sigignore and called in another thread. It is thread-safe for all major POSIX platforms (at least for linux/macos/bsd's/solaris-like) and if it is not thread-safe for some other platform, setting sigset is atomic so it would be a data race in the worst case which is still 'safe' rust.

The only unsafe part we would have to deal with, is restricting the signal which is set as an argument in either function. If complete control over the APIs is still needed, I'd suggest doing something like this:

struct Signo(i32);

impl From<SafeSignals> for Signo {
	...
}

enum ForbiddenSignals {
	// Will probably cause undefined behavior
	SigIll = c::SIGILL,
	SigTrap = c::SIGTRAP,
	SigAbrt = c::SIGABRT,
	SigBus = c::SIGBUS,
	SigFpe = c::SIGFPE,
	SigSegv = c::SIGSEGV,
	SigStkFlt = c::SIGSTKFLT,
	SigSys = c::SIGSYS,
	SigEmt = c::SIGEMT,

	// Internal signals used by OS, that shouldn't be handled
	SigThr = c::SIGTHR,
	SigLibRt = c::SIGLIBRT,
}

enum SafeSignals {
	// Every other signal
	...
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants