-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
[android] Add support for android's file descriptor ownership tagging to libstd. #74860
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @nikomatsakis (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
Android's libc provides facilities to mark file descriptor ownership, in order to prevent people from closing file descriptors that they don't own. Add support for this to FileDesc, which should cover all of the relevant file descriptor-owning types in Rust.
I left a comment about this on Zulip -- I'm not sure the proper procedure here, but I'm inclined to land this PR. Probably it should be tagged with |
Thinking a bit about it, since it is a user-visible change, I think an FCP is probably appropriate. @jmgao I'm curious if you could give some examples of what kinds of bugs you expect to find with this change -- e.g., would it help to detect broken code from users? Only if they are using the |
Also, cc @rust-lang/libs -- this seems to lie somewhere on the boundary of "implementation detail" and "public facing API change", so maybe y'all want to weigh in? |
Should |
Yeah, it'll detect when user code incorrectly transfers ownership, either in (via Here's an example of a bug in Android that's the latter scenario: an fd is owned by a Java ParcelFIleDescriptor, and some C++ code grabbed it and passed it directly to some other code that expected to own the fd.
Oops, yes, I'll fix this shortly. Before merging this PR in general, I need to find somewhere to add some tests, but I'm not entirely clear on where they should live, since they're Android-only (and behavior will also depend on what version of Android it's running on). Any suggestions? |
The documentation for The documentation for |
discussed at today's T-compiler triage meeting. The T-compiler members present at the meeting didn't have any strong opinions here. We agreed with @nikomatsakis that this warrants an FCP of some sort. There was general consensus that this is, at its heart, a question of what the publicly specified API for Rust's So we collectively decided at the meeting that while this should require an FCP, it should really solely require an FCP of the members of T-libs; T-compiler is going to opt out of participating in the FCP. Thus, I am removing the T-compiler label, keeping the T-libs label, and I will go see if I can find a T-libs member to start up the FCP process for this PR. |
@rfcbot fcp merge |
Team member @Amanieu has proposed to merge this. The next step is review by the rest of the tagged team members: No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
Somewhat of a nitpicky question: is "undefined behavior" the proper phrase to use for this? It's definitely incorrect, but it can't lead to the compiler using the fact that you did that to make assumptions that transitively breaks code elsewhere. The phrasing I'm thinking of now is something along the lines of:
This method does not pass ownership of the raw file descriptor to the caller. The descriptor is only guaranteed to be valid while the original object has not yet been destroyed.
+Do not attempt to take ownership of this file descriptor (e.g. by using `FromRawFd`). On Android API level 30 and beyond, file descriptor ownership is enforced, which will lead to the process aborting if this occurs.
This function consumes ownership of the specified file descriptor. The returned object will take responsibility for closing it when the object goes out of scope.
+The file descriptor consumed must not be already owned by another object. On Android API level 30 and beyond, file descriptor ownership is enforced, which will lead to the process aborting if this occurs. |
Undefined behavior doesn't have to be linked to the compiler, it can come from the library as well: in this case it means that behavior may change in the future and may lead to unsoundness via unsafe assumptions (i.e. writing to the wrong FD). I think that undefined behavior is appropriate in this case: Android happens to catch these cases, but other platforms do not. |
The final comment period, with a disposition to merge, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. The RFC will be merged soon. |
Hey @rust-lang/libs, I see that the FCP has completed, but I think this concern raised by @the8472 merits an official response:
My impression is that we don't have a workaround or alternative for this pattern, so I'm wondering whether you all consider this a blocking concern to be resolved. |
Thanks for the ping @nikomatsakis. I've added it to the agenda for the libs meeting, which starts in a few hours. |
It seems to me that because of rust/library/std/src/sys/unix/stdio.rs Lines 36 to 39 in 7d747db
even a simple println!("hello"); println!("world"); would already break, as it calls write twice, which would end up tagging stdout twice. Am I missing something?
|
IMO this is definitely a blocking concern. I think the behavior proposed by @the8472 might be the best option:
|
@rfcbot cancel |
@KodrAus proposal cancelled. |
This API is also new. Since CI is setup to only test against a fairly old minimum API level this change won't be exercised in CI and thus it won't catch broken uses. |
@jmgao Ping from triage! What's the next steps here? |
@jmgao Ping from triage, i'm closing this due to inactivity. Feel free to reopen or create a new PR when you have time to work on this again. Thanks! |
With the recent IO safety concept being introduced into std, perhaps this has a reasonable route forward again? |
@rustbot label -to-announce |
@rustbot label +to-announce |
Android has started investigating using Rust for system components, and one of the biggest historical sources of bugs we've had in cross-language interop has been caused by confusion around file descriptor ownership, since you're forced to pass file descriptors across language boundaries as a bare int without any type information about whether ownership is being transferred. The bugs this results in are horrible to debug, because they're nondeterministic and manifest as impossible behavior occurring on different threads. (e.g. [1], [2])
We've added some facilities to our libc to detect/prevent this class of bugs. Basically, user code can tell libc that a file descriptor can only be closed with a specified token, and to abort/log an error if someone tries to close the fd without the token (the behavior is process-wide and configurable at runtime, but the default on Android R (API level 30) and beyond is to abort). We've found dozens of bugs in Android with this, and hopefully prevented many more from ever being checked in.
This PR makes libstd use this on android when available. It potentially changes behavior, but only for code that's either broken, or very weird (e.g.
File::from_raw_fd(fd).into_raw_fd()
sequences which construct a File that owns the fd, and then removes it from the File before drop), and also only for unsafe code, I believe.(also, as another commit in this PR, I improved the error handling on non-android close. Let me know if I should split this out to a separate PR)(whoops, didn't actually build this commit: I'll do this as a followup)