-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Support setting signal masks for child processes on Unix #100737
Conversation
(rust-highfive has picked a reviewer for you, use r? to override) |
Ah I'm not sure if there should be a reviewer at the moment, this is meant to be a draft PR :) |
2513ed2
to
7e08f95
Compare
@sunshowers I've unassigned a reviewer for now, but please do run |
@@ -128,6 +169,47 @@ pub enum Stdio { | |||
Fd(FileDesc), | |||
} | |||
|
|||
pub struct SignalSet { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be private?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It follows the pattern of the other items in the module which are all declared pub
(though they actually resolve to pub(crate)
).
7e08f95
to
8e6e778
Compare
Updates:
I think this is ready for review now. |
Hey! It looks like you've submitted a new PR for the library teams! If this PR contains changes to any Examples of
|
r? rust-lang/libs-api |
(I still need to write up the change proposal I guess!) edit: done: rust-lang/libs-team#88 |
I guess the problem would be that it changes the documented contract of the That said, I'm not sure if this is worth marking as unsafe, it seems unfortunate if so, but it's probably worth considering carefully if we aren't going to, to avoid a footgun. That said, there's probably enough time to get this right prior to stabilizing, so I don't know that this is a blocker. |
Yeah, that makes sense. Note that existing code won't be affected though -- only users of both |
Yeah, so I suppose we could adjust the safety contract of |
(whoops, closed by accident) The other issue with safety I was thinking of is: what happens if the child itself has a segfault unrelated to the parent, and the parent has masked |
In general it doesn't. It's totally safe to spawn processes that crash or do other UB ( That said, fork/exec stuff is ofc where the line gets blurry here (for example, technically |
We're going to need this for upcoming signal mask support.
I've put up #101077 as a dependent PR on top of this one. |
da45dc3
to
aa251cf
Compare
This commit implements basic support for setting signal masks during process spawning. * With the `posix_spawn` code path, this means setting `posix_spawnattr_setsigmask`. * With the `fork/exec` path, this means calling `pthread_sigmask` in the child before executing it. This also fixes a bug: previously, we were always setting the signal mask to the empty set, breaking tools like `nohup`. With this change, we inherit the signal mask from the parent by default.
aa251cf
to
ac8fac2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The impl looks fine although I'll probably want to review again after the situation with the other PR is resolved.
@@ -296,6 +396,10 @@ impl Command { | |||
pub fn get_pgroup(&self) -> Option<pid_t> { | |||
self.pgroup | |||
} | |||
#[allow(dead_code)] | |||
pub fn get_signal_mask(&self) -> io::Result<&SignalSet> { | |||
self.signal_mask.get_or_try_init(SignalSet::empty) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll want to remove the OnceLock from this if the other PR's FCP doesn't get accepted.
I'm going to mark this as blocked on the other PR, as it's unlikely that we want this impl without that (and the resolution there may change how we approach this API). |
☔ The latest upstream changes (presumably #100786) made this pull request unmergeable. Please resolve the merge conflicts. |
/// success and has no effect. | ||
/// | ||
/// Blocking some signals like `SIGSEGV` can lead to undefined behavior in | ||
/// the child. See the [`pthread_sigmask`] man page for more information. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sigprocmask is probably the more canonical reference.
Also, while the manpage may say that the effect of blocking synchronous traps (ie, SEGV/BUS/FPE/ILL raised while executing an instruction) is undefined, I wouldn't read that as being the same as UB at the Rust/llvm level.
In practice I think it would either deliver the signal as SIG_DFL (ie, Linux), or endlessly re-trap on the same instruction (what x86 macOS seems to do) - either way the program has effectively terminated and won't enter any UB states as far as the program semantics are concerned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heh it's actually the same man page for both pthread_sigmask
and sigprocmask
. I can change the reference in the docs to be sigprocmask
though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh - on Fedora they're separate, but pthread_sigmask has almost no content and refers to sigprocmask for discussion of the detailed semantics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I was referring to the man pages from the official POSIX spec here, as linked in the comment)
Align android `sigaddset` impl with the reference impl from Bionic In rust-lang/rust#100737 I noticed we were treating the sigset_t as an array of bytes, while referencing code from android (https://github.com/aosp-mirror/platform_bionic/blob/ad8dcd6023294b646e5a8288c0ed431b0845da49/libc/include/android/legacy_signal_inlines.h) which treats it as an array of unsigned long. That said, the behavior difference is so subtle here that it's not hard to see why nobody noticed. This fixes the implementation to be equivalent to the one in bionic.
I think this is no longer blocked. |
Yep, planning to get to it soon. Meanwhile I've worked around it on beta Rust (1.66) by calling pthread_sigmask before and after process spawning, which appears to work. |
No rush, just got a ping on it from someone. |
@sunshowers FYI: when a PR is ready for review, send a message containing |
Closing this due to inactivity. Feel free to make a new pr if you wish to continue this. |
Apologies, I started a new full-time job since I originally wrote this and haven't had the time to follow up on it. After #101077, I also figured out a way to do this without needing to add new API surface to std:
(and use a scope guard to ensure that the sigmask is unset in case of panic.) This means that I'm probably not going to pursue this further. However, someone else is welcome to. |
This commit implements basic support for setting signal masks during process spawning.
posix_spawn
code path, this means settingposix_spawnattr_setsigmask
.fork/exec
path, this means callingpthread_sigmask
in the child before executing it.This PR retains the default behavior (empty signal mask). I've also put up #101077 as a dependent PR to change the default behavior.
API change proposal:
Unresolved questions:
SIGSEGV
etc (documented in the POSIX spec) enough reason to mark this unsafe?