-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
AsFd impl for Stdout/StdoutLock can violate locking guarantees of those types. #114140
Comments
I think Converting back and forth between |
No that's not enough. You can get multiple Well, I suppose we could acquire the lock before returning the borrowedfd... but we'd have to tell libraries to not use |
Fair. But using the fd returned by However, I do see your point. |
The mutex protects the buffer.
Lots of existing Rust code does things like The existing long-standing situation in Rust is that the raw fd safety condition does not include "you must respect the I recommend just adding documentation to warn users of the potential hazards. |
i.e. access is synchronized, not just the buffer. |
My vote is for weakening the guarantees, but I also agree that @the8472 is reading too much into the existing guarantees and that they don't say anything about the stdout stream.
When I read this line I hear "There's a global buffer where everyone can play nice and synchronize their writes to stdout, but that says nothing about others writing to stdout elsewhere". |
Nominally that doesn't exist inside Rust. We assume exclusive ownership of file descriptors. This is part of IO Safety. |
I need to reread the io safety docs, but then doesn't that mean any method of getting a file descriptor through unsafe to 0,1,2 is disallowed? I would be quite annoyed if a systems programming language said you can't mess with those FDs. |
Accessing stdio fds through 0,1,2 is fine, in std-using code. std holds these descriptors live with an effectively |
I disagree. Just because you know they're open does not mean they're safe to use. If a regular file is passed as Stdout and it gets mmaped then something futzing around with the file can lead to unsafety. |
But many things already assume exclusive access to the FDs. @sunfishcode already mentions piping a jpeg file to stdout in rust-lang/libs-team#148 (comment). This can only work with exclusive access. If we say that code cannot assume exclusive ownership of the stdio handles then reliable communication with the environment (via that mechanism) becomes impossible. That's nonsense imo. |
The situation could perhaps be papered over if we had a |
The question is whether outside code "interrupting" things printed to stdout is considered a logic problem (like a bad Interrupting some jpeg or JSON printed to stdout sounds to me to be more of a logic issue than a potential soundness issue? |
I do agree this method is very unfortunate. It means that when I get an owned FD, I cannot know whether anyone else is also reading/writing that FD. This makes it fundamentally impossible to have guarantees like "uninterrupted stdout" if even just a I would prefer if IO safety actually expressed exclusive ownership of the FD. Right now one could use IO safety to define a custom |
In principle a tool could try to mmap its inputs/outputs (for performance reasons). Then something else accessing fd 0/1/2 could become unsound. |
Yes, in principle. The question is whether that's something that makes sense to support. You can just use a different FD instead, one you actually control and make sure nobody will |
I opened #114167 for the general problem around |
This is a guarantee that we can't really make: if it's unsound to write to |
Well, such a library is unsound if you're trying to write a file to stdout, e.g. a binary that's going to be executed later. The C library would just corrupt that. Contracts matter! But as I commented previously #114140 (comment) it's probably better to add a |
Right, but Rust code can do arbitrary bad misbehavior which is not unsound - I don't think breaking existing code and practice is worth it to ensure a guarantee that we might not even be making. I think a |
Stdout/Stderr are very much shared resources, so I don't think we can say that it is a soundness bug when a binary dumped there gets interrupted by other things. That's a logic bug IMO. Even if the binary is executed later -- it's not a Rust soundness bug for a "compiler implemented in Rust" to produce a bad binary. For instance, a bug in a MIR transform is a logic bug from the perspective of Rust-the-language-used-to-implement-rustc, not a soundness bug. (It becomes a soundness bug only via the bootstraping cycle, but that's not a concern here.) |
The stdout/err APIs guarantee that they're globally synchronized:
This can be relied on to write whole lines at a time, e.g. for json-lines style logging where multiple threads shouldn't interleave their output. It can be ok if some random other library or thread outputs non-JSON output, but it's important that it's not emitted in the middle of a line that's currently being emitted.
The combination of
impl AsFd for Stdout
,try_clone_to_owned()
andimpl From<OwnedFd> for File
can then be used to break that guarantee.AsRawFd
is somewhat less of an issue because any use of the createdRawFd
should be unsafe.Possible solutions:
Related: rust-lang/libs-team#148 (comment) and following.
The text was updated successfully, but these errors were encountered: