Security vulnerability in Rust standard library
blog.rust-lang.org
blog.rust-lang.org
Do people know the state in other languages?
It's commonly used in C/C++ communities (elsewhere too, but not as often).
I feel like this sort of thing can be utilized as some sort of dev honeypot.
In any case, I see no harm in any of this. Bits are cheap.
There are many UB cases in C/C++ many programmers don't suspect (more subtle than dereferencing null), and this regularly introduces very hard-to-debug bugs and securities issues. Rust does (mostly) get rid of them so you can be sure that your code has no unexpected side-effects.
This, to me, reads like it implies that reading the spec shouldn't be necessary to use a language correctly. The issue with that is that the spec is the only document you need to read, understand and follow in order to write safe, cross-implementation-compatible code.
When you work with low-level languages and refuse to read the spec carefully, you are to blame more than the language or the spec.
> this regularly introduces very hard-to-debug bugs and securities issues
Yeah, that's true. The reality is that a lot of developers come from a C-syntax language (like C#, C, Java, ...) and assume that it's all the same because it looks the same. Mostly they follow video tutorials on youtube and blog-y websites instead of standard-adhering documentation (like cppreference.com). I've seen C++ developers that, after building 10k+ line of code programs, never knew why to use `delete`, what RAII even is, and have not a clue about what a mutex does.
These are fundamental education problems. Rust has the benefit of being different enough from common "first languages" that one is inclined to look at the documentation.
I'm not the only one who is endlessly tired of being told that the design of C++, with its undefined behavior and "need to read the spec", is somehow at fault for developers being too stubborn, entitled or stupid to take 3 minutes to read and understand the documentation.
When a Rust developer makes a security mistake, causes a crash or other unexpected behavior, the developer is likely at fault. With C++, it's always "oh c++ is just like that". It's a complete mystery to me how people cannot understand that sometimes a language with lots of power can have pitfalls that need to be avoided by the programmer.
This is true to some extent but I think there is a reasonable argument that the language specs are generally geared towards people building compilers or similar tools, and it's not reasonable to expect everyone who works with a language to be intimately familiar with the spec at that level. In the case of C++, the language complexity seems to be at the point where even people who are highly-skilled and work on tools like Clang are still hesitant to say they fully understand the rules around undefined behaviour.
When the situation with the language is such that no human or program yet invented can confidently assert the defined-ness of any particular bit of code, it's 100% the language's fault. Who else could be at fault for it being impossible to write safely?
An amusing anecdote is that there's a conference for UB detectors called sv-comp. They have a suite of example programs with completely defined behavior that are used to detect false-positives. People keep finding UB in the C examples [2].
[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p170...
[2] https://runtimeverification.com/blog/mare-than-14-of-sv-comp...
Understanding C++ from first principles to the level that you never write UB, by reading the spec, is much closer to a 3 decades task than a 3 minute task.
https://en.cppreference.com/w/cpp/filesystem
> The behavior is undefined if the calls to functions in this library introduce a file system race, that is, when multiple threads, processes, or computers interleave access and modification to the same object in a file system.
---
Golang also seems vulnerable to the same issue
https://github.com/golang/go/blob/d15481b8c7f5f73a8b987a0c1d...
Line 78 checks that the path isn't a symlink (time-of-check). Then line 97 calls openFdAt which on line 174 opens the path by name, without NOFOLLOW (time-of-use).
I bet this is a pretty common vulnerability.
Here's an RFC that tries to extend lifetimes to file handles: https://github.com/rust-lang/rfcs/blob/master/text/3128-io-s...
Process A can unlink the descriptor, swap the directory entry out for some other object or whatever, and Process B will still have a handle to the same initial object.
Nothing stops A from mutating the filesystem object in other ways. For example, A's write to the middle of the file or truncation of the file will be visible to B. But merely unlinking the file doesn't alter B's view of the object.
> - macOS before version 10.10 (Yosemite)
> - REDOX
It's funny to me that they chose to include RedoxOS in this security advisory. For those of you serving your customers over RedoxOS in production, beware.
The vulnerability mentions a userspace compromise aimed at a system directory, not another sensitive user directory.
In my mind, a user trying to delete say /usr/bin on a POSIX system is going to be slapped down immediately unless they are root, or have the proper group access, and this is not the responsibility of any standard library, it’s the responsibility of the fs layer and kernel.
So, what am I misunderstanding? Opening up a link to /usr/bin doesn’t ever give me permissions on /usr/bin over what I have.
> If std::fs::remove_dir_all followed symbolic links, they could find a privileged program that removes a directory they have access to (called temp/), create a symlink from temp/foo to sensitive/, and wait for the privileged program to delete foo/
This is a classic confused deputy exploit - the attacker doesn't need access to the system directory, they just need to be able to point the deputy at that directory. The race condition on checking if the directory is a link means it's possible to trick the privileged program into misusing its privileges.
Does that mean that any Rust program compiled with any affected version of the standard library is affected? How would I even find those?
Statically linked binary make deployments really easy, but this one scenario where they have real downsides.
Most programs don't use `remove_dir_all`, and a smaller fraction of those use it on a directory that untrusted actors can write to.
But yes, tracking down those that do is a challenge.
As for other situations: when Rust programs are built with Cargo, it creates a Cargo.lock file, which is a machine-readable record of all dependencies and their checksums used in the build. Distros could archive it and later use it track all affected dependencies. Unfortunately, libstd is a magical compiler built-in, not a regular dependency, so it doesn't get recorded that way.
This is a weird comment, especially on this fix. The bug was reported back in 2018. From the linked blog post:
We also want to thank Florian Weimer for reviewing the UNIX-like fix and for
reporting the same issue back in 2018, even though the Security Response WG
didn't realize the severity of the issue at the time.
See: https://github.com/rust-lang/rust/issues/48504That's true for everything, right? Anyone running older versions of Java, Python, Ruby, Go, C++ (gcc/llvm), etc. that don't update all are exposed to unpatched security vulnerabilities, in both their 3P library dependencies, stdlib, and compilers/interpreters.
I don't see how Rust can force people to update any more than any other language.
Also testing the compiler against the whole ecosystem on crates.io before releasing a new version helps building trust in the backwards compatibility of new compiler versions.
So they are doing something to establish a culture where updating your compiler regularly is encouraged. I don't have actual numbers and I don't know how enterprise Rust shops handle their compiler versions, but they are doing things to encourage (not force) users to keep their compilers up to date.
Additionally, quite a lot of software seems to be distributed in self-contained format; for example many Java apps (even those intended to be installed on a server) come bundled with JRE, and Linux GUI apps and games are usually distributed in Flatpak/Snap/AppImage.
Of course let's not forget about distributing software as Docker images, which is probably the most common way software is installed in many server environments.
https://github.com/tokio-rs/tokio/blob/b42f21ec3e212ace25331...