FS races in Rust are UB as well, they're just scared to say that very explicitly. They hide behind this phrasing:
"Many I/O functions throughout the standard library are documented to indicate what various library or syscalls they are delegated to. "
And just think about it rationally for a second. The FS is implemented by the kernel, which doesn't give a damn what language you're using. So Rust cannot possibly guarantee anything about it. Everything about Rust's FS API is every bit as full of UB as C++'s because it's a shared system with external processes - Rust can't guarantee shit. They just make you go look up what the syscalls are to find out what the guarantees (or lack-thereof) actually are instead.
And yet, the Rust community takes it seriously, issuing a CVE, a point release and a blog post. You won't find these 3 from all 3 major C++ implementations (clang, gcc and msvc) because they don't take the issue as seriously because their spec says they don't have to.
Rust can't guarantee shit but at least they do shit when there's a problem. They take accountability for their shit.
You keep harping on minor nitpicks, but you can't escape the fact that the C++ community did not take this issue as seriously as Rust did. Therefore it is meaningless to compare CVEs across the two ecosystems, which was the substance of the top level comment.
> because they don't take the issue as seriously because their spec says they don't have to.
And yet they all quickly fixed the issue and nobody tried to hide behind "the spec"
Should there have been a CVE? Probably. But there's no central C++ CVE committee to have done that, either, which to Herb's point there probably should be.
Should there have been a blog post? Probably not. People don't ship their own standard library, so the blog post is low value. What matters is whether or not distros/OS's picked up that fix promptly. Which unfortunately is hard to find out since most Linux distros suck ass at keeping their standard libraries up to date.
I notice you linked the fixes for clang and gcc. Where's the link for MSVC?
> Should there have been a CVE? Probably.
Glad you agree. But the absence of it proves the point I'm trying to make - they didn't take it seriously enough.
> there's no central C++ CVE committee to have done that, either, which to Herb's point
It actually sounds like Herb wants to reduce the the number of C++ CVEs that are filed, not increase them. He very specifically says that a bug shouldn't be enough, there should be a vulnerability. It sounds like he wants to achieve his goal of fewer CVEs by juking the stats, but who knows.
Even worse, in fact. While one can happily argue (non-)justifications for this till everyone's blue in the face, filesystem interfaces (in UNIX, but with a little magic sprinkle in Windows as well) have always allowed for concurrent access to the same file via two different handles. Open the file twice, and use the two - entirely "independent" objects for any language - filedescriptors to change the contents underneath each other. System behaviour here allows for things that "rust as a language" does not (* - I know about inner mutability but that's a different thing; even the data retrieved from a readonly-opened file can change if a second writeable open happened on it). In the end, programming languages and their standard runtimes depend on the behaviour of the operating system. I actually love that rust exposes this via system-specific traits.
The "extreme" would be to go the "Oberon Way" - write the system for the language that implements the system written in that language. Maybe we'll get "somewhere there" with rust one day. Maybe not. Personally, I don't see the value in it, but mileage may vary.
You pointed at the I/O safety notice in Rust's documentation, but now you've pivoted to saying you were really talking about something different.
But wait a second lets go look at that notice again. Rust is yet again delivering a safety feature C++ just does not have. This actually wasn't in Rust 1.0 -- the safety notice didn't appear then because the I/O safety work landed much later. These safe I/O properties are really useful (even if they're not magic, hence the notice) and C++ doesn't have them.
What's particularly going on here is that on a Unix Rust has types named OwnedFd and BorrowedFd which represent file descriptors, these are quietly just a 32-bit integer with a niche, because -1 isn't a valid file descriptor, that's a niche, so Option<OwnedFd> is the same size in memory as a 32-bit integer.
The result is that Rust gets to do the same trick with file descriptors as it does with pointers - a Rust program working with fds uses the same size data structures as you'd write native C (or thin C++) to work with file descriptors, but where that C or C++ would need to remember to write checks for the -1 file descriptor in Rust that's seamless anyway because it is Option<OwnedFd> so that's None.
On Windows they don't have file descriptors, but they do have Handles, the Handles are much more muddled, and so we can't optimise them as well (actually it's a wonder Windows manages to keep all these balls up in their air they're juggled so frantically, there's a lot of references to Raymond Chen's blog) but again Rust provides I/O safety for these types.
This is the purview of a programming language and Rust does a much better job, I wouldn't have gone out of my way to call attention to it, but you did apparently because you mistook this feature (which C++ doesn't have) for a different feature which C++ is bad at.
Everything you just talked about is unrelated to the issue being discussed. This bug isn't a mismanagement of FD lifecycles, which yes Rust will do more safely out of the box than C++. This bug is a TOCTOU issue ( https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use ) and, more broadly, concurrent access of the file system by multiple processes. Rust cannot guarantee anything about that.
Just because you have an FD and you know it's not -1 and you never forget to check that, that doesn't mean anything at all with respect to the concurrent access to the underlying ionode by multiple process for which that FD references.
I understand that the original topic was the TOCTOU bug but again, Rust actually makes explicit promises about what happens here, which it deliver via the appropriate filesystem APIs, whereas what the ISO document tells you for C++ is just you get Undefined Behaviour - absolutely anything might happen - the popular implementations do more but that's not what the standard says.
I mentioned what I/O safety really is because you've stumbled onto it while scrabbling to justify the belief that Rust also has UB for the TOCTOU bug.
And while Android's ScopedFd is more or less what you'd do in C++ it has a number of significant differences, which I'd say are disadvantages:
1: Most importantly OwnedFd is part of Rust's standard library.
2: ScopedFd insists on behaving like an integer, which is very typical in C++ but not in Rust. If we want a File Descriptor, who cares that those are "actually" integers? Thus we can compare a ScopedFd to an integer - is this ScopedFd more than ten?
3: And so ScopedFd can represent an invalid file descriptor. We don't own that of course, and it's not really a file descriptor at all, but we can (and Android does) represent it anyway. OwnedFd only represents valid file descriptors, an Option<OwnedFile> represents the wider category of either a valid file descriptor or not if that's what you meant.
4: Android provides a borrowing mechanic here, but of course doesn't have a borrow checker, so again you don't actually get the safety benefit of BorrowedFd.
The Rust docs are talking about the safety and soundness of keeping track of the fd ownership. The C++ standard says that FS races are UB.