-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Windows - std::fs bug - specific file name causes thread to hang or false positive on file open. #83751
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
Comments
That's because any file named Reading from it presumably will block until the console is closed, so the hang is expected. |
If you use a verbatim prefix ( |
The main issue here is, in the case of read_to_string which hangs, you can check if it exists first with Path::new("con.txt").exists(), this gives the create answer of false, but according to the read_to_string API doc - "This function will return an error if path does not already exist." So one would expect that error to return, rather than DOS your app. |
The |
The docs rather concretely state "Returns |
The docs are wrong, unfortunately. The function itself is in the process of being deprecated and replaced with Just to be clear on what this problem is, the path in: Path::new("con.txt") Will be rewritten by the OS as (roughly speaking): Path::new(r"\\.\CON") This does actually exist. It's the So when using |
That statement cannot be seen as universally true because it is then qualified by
So file can exist without
It may be better, but I think it won't cover all edge-cases either, so it shouldn't be taken as canonical definition for existence. E.g. unix different calls are used for metadata and open. So one can return |
Sure, but accepting the caveat furthers the point, the restrictions on permissions return the safer option of false! |
I'm not sure what the point is. Which is a different operation then trying to check if something exists and thus may give different results. This is a general principle with filesystems. If you want to do a check for something it's best to just perform the operation you want to check, rather than some proxy-operation, because the proxy can give different results than the actual operation due to weird differences. This is also the source of TOCTOU attacks. If you want to avoid legacy handling use verbatim-prefixed absolute paths. If you want to detect legacy parsing you can |
I guess the point is there should be a safe API that guards against this, otherwise every single user API that accepts consumer provided file names to be attempted to be read need to manually guard against these filenames themselves (thus assuming all users know about these OS nuances - I did not before today), or face being DOS'd. |
If you're accepting untrusted paths over the network to write into/read from the filesystem then you probably have bigger issues. E.g. they could contain absolute paths or If you're accepting arbitrary input from a trusted user then the user can still shoot themselves in the foot. E.g. by attempting to read huge files that will cause the process to run out of memory since
Take std::path::Prefix for example, nuances are not masked. |
Note that you would probably get the same effects on cmd via |
If you have a file server that accepts a file name to be read, knowing that it can be guarded against that with the current implementation of Path.exists() for these, clearly exceptional, windows filenames, are you saying that it is acceptable for the read_to_string method (the convenience method to read a file) to hang? If that's the case, fine - then document it as such. |
Neither But the problem isn't specific to those two methods. Any naive approach to interacting with the filesystem will have pitfalls. We could add some caveats to those particular methods, but that doesn't solve the general problem because there's so much ground to cover. |
I fear you're in love with that hill you occupy. How about a method that understands that Windows (bizarrely) will translate con.txt to Path::new(r"\.\CON") as per @ChrisDenton's observation, causing this issue, and guard against it? |
I believe canonicalize already does that, which is why I suggested it. But i don't have a windows machine nearby to test on. Give it a try.
The issue is that On unix systems these kinds issues are fairly common. One way to check for them is opening the file and then doing Not that that is bulletproof either. If you want to deal with IO hangs the usual approaches would be interrupting hung threads from another thread, helper processes that can be killed and restarted after a timeout or using nonblocking IO APIs where requests can be canceled. |
I would agree that this can be a surprising behavior of Windows but it is the way that platform is intended to operate. Windows is hardly exceptional in this manner though as your program will hang on both macOS and Linux if a user supplies Perhaps the |
When trying to open a file (which in this case did not exist) that starts with con. - eg: con.txt, con.toml - fs::read_to_string will hang the thread. File::open will incorrectly succeed, and subsequently panic when methods on the file handle are called.
I tried this code:
I expected to see this happen: File not found error returned.
Instead, this happened: Thread hang or incorrect result as per description above.
Meta
Bug exists in nightly as well as stable.
rustc --version --verbose
:Backtrace
The text was updated successfully, but these errors were encountered: