-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Rust's stdio should not ignore EBADF error on non-windows platforms #47271
Comments
There's not really anything we can do about reuse, we can't protect against that at all. What we can do, however, is protect against accidentally misconfigured dameons or things like that. If you spawn a process that doesn't actually have stdin/stdout/stderr then it's consistent with Windows to ignore that error. Naturally in libstd, however, we go to great lengths to ensure that all spawned processes have a stdout/stdin/stderr. I don't think this has convinced me (personally) that we should change this behavior, but if you've got an example of how this could come up in practice we could always bring it up with the broader libs team! |
It's not "consistent", because there is zero similarity between situations on Windows and Linux, and so comparing them for consistency here makes no sense. For Windows running without streams is not an exceptional situation. On the contrary, it's overwhelmingly common for GUI apps to be without console output. It makes sense to not panic there, because the dangerous descriptor reuse problem doesn't occur, and there exists On Linux such situation is nothing but exceptional. It would be very rarely that Descriptor reuse can't be prevented, but that doesn't mean we shouldn't attempt to mitigate obviously dangerous situations instead papering over enormous platform differences just because outward similarity between For the 0.01% cases when the user would be actually developing an app that would be OK with encountering closed stdio descriptors on Linux, he will either do anything to NOT use Such explicit mechanism which would also help in cases such as #33736 (comment) E.g. if there was a way to reset streams to https://github.com/rust-lang/rust/blob/master/src/libstd/io/stdio.rs#L102 then the " On a related note, I may also argue that instead of current Windows's I also suspect that in cases when But I would welcome @retep998 's validation on these matters. |
To reiterate, because reading your message again I began to doubt that you understand the implications, and you appear to have asked for examples when the problem can come up in practice.
on Linux the process will invariably kill your kittens and set your house on fire. You just don't do that on Linux, ever. A daemon that is accidentally started like that simply has no way of NOT breaking everything in very ingenious ways. You should consider yourself lucky if the very first remote TCP connection that it opens doesn't contain hacker's input for something that it would believe to be entered by local user on the console in interactive mode. You will be even more lucky if the very second file that it opens is not a sqlite production database, into which it will eventually print an error about how it can't read the database. Linux descriptor reuse logic means, that if that daemon, e.g. goes through a list of files and prints a string to stdout for every one of them, then each and every one of them may be consistently opened as descriptor 1 and promptly corrupted. You can't paper over any such "daemon misconfiguration" by hiding errors. Period. The standard way to start silent daemons on Linux includes providing |
Just so that everyone understands Linux descriptor reuse on a concrete example. https://play.rust-lang.org/?gist=b44be7df66e7dd59ce25209927523782&version=stable |
Currently libstd does not cache the result of If a process starts without a console the handles returned by If the user manually closes the console window, then it generates a If the user calls |
So, from this I gather that if the console is allocated and so |
Triage: code lives here now Lines 236 to 241 in 16957bd
|
I've discovered, that
std::io::stdin/out/err()
streams unconditionally ignoreEBADF
-like IO errors on all platforms. This is done by checking the read/write error in ahandle_ebadf()
function.rust/src/libstd/io/stdio.rs
Lines 123 to 128 in 1ccb50e
It appears, that this behavior was first introduced here a7bbd7d
The commit clearly has Windows in mind, where it appears the standard streams may be unavailable. But on Linux, the streams are expected to be always present, so there's no reason to ignore
EBADF
in the first place, as it indicates that something is very wrong.Not only that, but due to file descriptor reuse behavior on Unixes, if descriptors 0/1/2 are not open, sometimes the very next calls to
open()
will allocate them. This means, that a program running without properly preallocated 0/1/2 descriptors may start happilyprintln!()
-ing over its own sqlite database, or send private execution logs across a tcp connection.So, if
std::io::stdout/err()
happens to discover that something yanked the descriptors from under program's feet, the proper response is not to silently ignoreEBADF
, but to panic(), before something else unwittingly allocated it with likely disastrous consequences.The text was updated successfully, but these errors were encountered: