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 safety of fork() and fix tests #695

Merged
merged 3 commits into from
Jul 23, 2017
Merged

Document safety of fork() and fix tests #695

merged 3 commits into from
Jul 23, 2017

Conversation

jonas-schievink
Copy link
Contributor

@jonas-schievink jonas-schievink commented Jul 20, 2017

Some tests were invoking non-async-signal-safe functions from the child
process after a fork. Since they might be invoked in parallel, this
could lead to problems.

cc #586

Copy link
Contributor

@Susurrus Susurrus left a comment

Choose a reason for hiding this comment

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

I think this makes sense, setting fork to be unsafe. Just a few minor nits here to clean up. Also @asomers will need to take a look at this.

let pid = fork();

// Safe: Child only calls `_exit`, which is signal-safe
let pid = unsafe { fork() };
match pid {
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's get rid of the pid variable completely here for consistency with other tests and since it isn't used anywhere else.

src/unistd.rs Outdated
///
/// # Safety
///
/// In a multithreaded program, only [async-signal-safe] functions may be called
Copy link
Contributor

Choose a reason for hiding this comment

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

Your comments talk about libc::exit and libc::pause being safe to use here. Let's explicitly list those here. We should also state that std::process:exit is not safe.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

libc::exit actually isn't safe, only _exit is

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, that's what I meant. We should document a few of the safe and unsafe things for reference. The big thing about this is that how would the user figure out what is safe or not. I don't know if we have any guidance on that end, however.

Copy link
Member

@asomers asomers left a comment

Choose a reason for hiding this comment

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

I like all of the changes that you've made to the tests. But I still think it's premature to mark fork as unsafe. It's not that I think fork is safe; it's that I think consistency between nix and libstd is more important.

Nobody has yet come up with a POC that violates Rust's memory safety rules by forking. Until that happens, I don't think libstd is going to change.

@@ -111,7 +114,10 @@ macro_rules! execve_test_factory(
// data from `reader`.
let (reader, writer) = pipe().unwrap();

match fork().unwrap() {
// Safe: Child calls `exit`, `dup` and the provided `exec*` family function.
Copy link
Member

Choose a reason for hiding this comment

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

If you're going to list them all, you should add close

@jonas-schievink
Copy link
Contributor Author

Nobody has yet come up with a POC that violates Rust's memory safety rules by forking. Until that happens, I don't think libstd is going to change.

And this is a very bad thing IMO. Maybe it's hard to come up with a POC on the currently supported configurations, but what about other allocator implementations, other libcs, other operating systems, other architectures where this is a more serious issue? Even if those aren't supported by Rust today, even if those configurations don't even exist today - the fact that POSIX says "don't do this it's unsafe" should be more than enough of a reason to proceed with extreme caution.

A language that aims to guarantee memory safety shouldn't do things like this.

@asomers
Copy link
Member

asomers commented Jul 20, 2017

POSIX says it's "unsafe", but the libstd guys say it's not unsafe. unsafe means something very particular in Rust. You can easily do things that are "unsafe" without using any unsafe code, like leak memory, trigger deadlocks, etc. In order to convince libstd to change, we'll probably have to come up with a POC that violates one of Rust's narrow memory safety rules, for example by overrunning an array or using after free.

@jonas-schievink
Copy link
Contributor Author

Yeah, seems like POSIX leaves the meaning of "safe" completely undefined. Depending on that, fork might indeed by safe according to Rust's definition of safety. However, common sense makes me doubt that POSIX, which defines a C API, would call something "unsafe" that Rust would deem safe.

@jonas-schievink
Copy link
Contributor Author

As an aside, from the docs of pthread_atfork:

If a fork() call in a multi-threaded process leads to a child fork handler calling any function that is not async-signal-safe, the behavior is undefined.

I would be very surprised if "the behaviour is undefined" means something different than UB in C in this context, and the child handler is basically just code that is executed by the child after a fork, so there isn't much of a difference to what we're dealing with. (UB is strictly worse than memory unsafety, after all)

@asomers
Copy link
Member

asomers commented Jul 21, 2017

I don't want to argue with you about the safety of fork, because I don't completely disagree with you. But nix needs to maintain consistency with libc, and since libc is far more important than nix, we need to be having the conversation over there. For now, please revert the changes to fork and let's just merge the changes to the tests and to fork's documentation.

@Susurrus
Copy link
Contributor

I'd like to echo what @asomers says here. Rust has a pretty narrow definition of "unsafe" and whatever libc goes with we'll stick with for convention. That said, it is worth bringing up the invariants for these functions so that users can be aware.

@asomers
Copy link
Member

asomers commented Jul 22, 2017

PR #566 just added a new fork call in a test. So when you rebase, please replace its sys::process::exit with _exit

Some tests were invoking non-async-signal-safe functions from the child
process after a `fork`. Since they might be invoked in parallel, this
could lead to problems.
Note that ptrace isn't documented as signal-safe, but it's supposed to
just be a light syscall wrapper, so it should be fine.
@jonas-schievink jonas-schievink changed the title Make fork() unsafe and fix tests Document safety of fork() and fix tests Jul 22, 2017
@asomers
Copy link
Member

asomers commented Jul 23, 2017

Thanks for the PR @jonas-schievink . Looks like we can sneak this one into 0.9.0

bors r+

bors bot added a commit that referenced this pull request Jul 23, 2017
695: Document safety of `fork()` and fix tests r=asomers

Some tests were invoking non-async-signal-safe functions from the child
process after a `fork`. Since they might be invoked in parallel, this
could lead to problems.

cc #586
@bors
Copy link
Contributor

bors bot commented Jul 23, 2017

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

Successfully merging this pull request may close these issues.

3 participants