Rust 1.59.0 with inline assembly support etc.
blog.rust-lang.org
blog.rust-lang.org
IIRC `#[naked]` are only really guaranteed to allow inline assembly, so it's like defining a function in `global_asm!` except for monomorphization and generally interfacing better with the rest of the language.
(That said you're right that naked functions still have a place for doing this, I can't imagine the compiler would grow stable support for every random ABI someone could come up with. But for more well-known ones it's a nice thing.)
(if it was more expressive, we could have e.g. specifiers similar to inline `asm!`, but on a function signature, to fully describe its call ABI)
In the meanwhile, I agree - if you need some entry-point to have a custom ABI (especially if you need it parameterized through generics), `#[naked]` + inline `asm!` is the only viable option for now.
Especially with const generics, you can do some cool stuff to make very compact trampolines that e.g. embed immediates directly into instructions (though it's sad that `const` operands in inline `asm!` aren't stable yet).
But for interrupt ABIs I would hope that LLVM can fully support them and avoid any inefficiencies that "whole body is one inline `asm!`" could create by blocking LLVM from doing optimizations, clever register allocation, etc.
(that is, they let the user control when they do something special with inline `asm!` vs regular code that LLVM understands)
I think the biggest missing features are the more exotic, non-register constraints, but some of the reasons those are omitted are because of unclear coordination between Rust code and the assembly language string.
It sends a signal to everybody that language correctness takes precedence over this extremely important feature that more developers depend on than the new features in the release, instead of treating the two with the same seriousness.
https://play.rust-lang.org/?version=stable&mode=debug&editio...
struct Helper<B: Buffer> { x: [u8; B::SIZE], }
http://venge.net/graydon/talks/intro-talk-2.pdf
> Rust is a language that mostly cribs from past languages. Nothing new
[…]
> Rust picks from 80s / early 90s languages
> inout(reg) a,
inout looks like a function but I don't see how that works with the `a` variable. Is this some magic specifically for asm!?
Luckily there's still enums. Not as flexible but they make sense to me and don't sprinkle annotation boilerplate everywhere.
Rust's standard library provides std::fs::remove_dir_all("/some/dir"), in C++ you will find the very similar std::filesystem::remove_all("/some/dir")
As you might anticipate, these functions get rid of the directory /some/dir if necessary deleting sub-directories and files recursively in order to achieve that.
They both explain that they won't delete other things, even if there's a symlink in /some/dir or some child that points to those outside things, the symlink gets deleted but not the thing it pointed to.
What kind of terrible unsafety was discovered in CVE-2022-21658?
Until Rust 1.58.1 you could exploit a TOCTOU race condition to blow away other people's stuff using symlinks when they made this call.
And where is the CVE for the same bug in C++? There isn't one. C++ standards people argued that the C++ standard already actually says that none of their filesystem API is safe to use if you have a "concurrent environment" in which such races are possible. Standard C++ programs are only intended to run on a machine where they have exclusive control over the entire system for the entire length of their runtime and so when your C++ program deletes files you thought it couldn't delete that was actually your fault for not being familiar with every caveat and ruling in the entire C++ standard.
What should you do about that? I believe the answer is obvious: Use Rust
The Posix file API is shot through with races. Patching around them is a game of whack-a-mole. C++ cannot fix Posix. Rust cannot fix Posix.
It is dishonest to assert that "C++ programs are only intended to run on a machine where they have exclusive control over the entire system". The system a program is a part of is expected to have enough control over what it operates on that its semantics are well defined. No Standard can do more. Rust can do no more. I doubt Rust pretends to more.
The extent of its "acknowledgement" is that it says elsewhere in the standard that if there's any filesystem race you get Undefined Behaviour. C++ programmers can't do anything with this except, as I explained, insist on exclusive control over the system where their program runs during the entirety of its runtime to avoid this Undefined Behaviour.
ISO Standards do not editorialize about things they do not define; they simply do not define them. For what the ISO C++ Standard does not define, you must look elsewhere if you want a definition. Often there is one, elsewhere.
Every last jot and tittle of what a Rust program does is Undefined Behavior as far as every ISO Standard is concerned, but Rust coders rely on other definitions, too, including a specified binding to ISO Posix where relevant, and the various CPU and OS specs.
It is generally advisable not to pontificate on things you do not understand to people who do.
And the handling of that CVE does illustrate a key difference between Rust and C++. Rust says "it's our fault if we get the intuitive semantics wrong". C++ says "it's your fault for relying on your intuition". It does no good to pretend there's no difference between the two philosophies.
https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd...
And if you want a specific example, thread cancelation cannot be wrapped in safe Rust, because many details depend on the actual UNIX is being used.
https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xsh...
There are a lot of things that cannot be exposed in safe Rust (and that's OK, that's what `unsafe` is made for).
There's a big difference between “using this is unsafe, and you must use the `unsafe` keyword and be sure to manually check your invariants” and “oops you used a safe function from the standard library and it was in fact unsafe too bad”.
So "related to memory corruption" is either a deliberately vague statement such that you can count "deleted files you didn't intend" as "memory corruption" or you're missing the true extent of what's offered here.
Type safety could also be argued to just be about "memory corruption" but because of the newtype idiom Rust's type safety ensures all sorts of critical things that you wouldn't associate with "memory corruption" as it is ordinarily understood, starting with obvious built-ins like str really being UTF-8 and Saturating<i16> really having saturating arithmetic operations despite only being an ordinary 16-bit integer - but building up to Mutex<Thing> genuinely not letting you access the Thing without taking the Mutex, and GPIO types that reflect the reality that this GPIO is either a PCM device, or it is a serial port, but it by definition is only one thing at a time and must be explicitly switched between them if that's what you want to do.
If Rust safety goals is to be dependent typed safety language instead of memory, then quite a few knobs are missing.
For some time now the situation is that a POC exists which works just fine against C++ programs, but no longer works against Rust programs with current Rust. That seems much more like a real guarantee to me than a fake one.
> If Rust safety goals is to be dependent typed safety language
Rust does not offer full-blown dependent types today, and so far as I know has no plans to do so, although it would be convenient if it was easy (it isn't easy).
But (safe) Rust does have working type safety and this already meaningfully makes us safer as I explained already.
Erecting a fencepost and expecting attackers to run headlong into the fencepost and knock themselves out is not security. Actual attackers just go around.
Again, it is unwise to explain things you do not understand to people who do understand them.
As to the fencepost analogy I will note that Rust fixed the problem a little over a month ago and ever since C++ proponents have been running into that particular fencepost quite certain that the resulting brain damage is proof of just how smart they are for not fixing bugs.
But it reflects also, perhaps unfairly, on Rust and its creators, who did not ask for foolish promotion.
Also, while this isn't a guarantee, Rust's type system and control flow regarding error handling can help avoid mistakes you sometime see in C (the infamous “goto fail” for instance). Obviously, since C++ is already much more expressive than C, Rust is less of an improvement from C++ in that regard.
So now is safety memory safety, or POSIX cross platform semantics safety?
Better make your mind what safe actually means in Rust.
Rust cannot “fix Posix”, but the stdlib (and others) can definitely work around its quirks. See how this[1] library sidesteps a thread-safety issue when using localtime_r(3).
Bragging about how you put in your broken fix and somebody else didn't tends to attract what may be unwelcome attention.
The specific thing being fixed here was a TOCTOU race and thus the fix is just to hold on to the thing being checked so you can use the same thing and eliminate the race. Well, of course I say "just" but it was a lot of work.
There is no attacker involved in this discussion: the entire purpose of Rust is to prevent the user to use a dangerous method without opting into unsafe.
If The user opt into unsafe, there maybe a way for an attacker to exploit a user's mistake, but as long as the user doesn't they'll be safe, because what Rust does is forbidding the user to shoot themselves in the foot.
It is a big difference to sell Rust stating that the number of exploits cases will be lower, which they are, or asserting they don't happen at all.
When the seller tells a message the audience doesn't believe into, the audience loses interest.
Indeed. Rust decided by policy that it needs to not only fix mistakes, but also tell people about them.
There's a big difference though between the fact that such a large piece of software isn't perfect (to be expected) and the choice to silently ignore whole categories of problem and pretend they just don't exist or blame the programmer for holding it wrong.
C++ isn't perfect and there are many issues to improve, but I also don't agree that Rust is such a perfection.
Looking forward to a perfect abstraction of POSIX in stable Rust, taking into consideration all the caveats on the standard.
After all, I want my Rust code to be perfectly safe.
Regarding memory safety, C++ says: "It's your fault if you get it wrong." Rust says: "It's our fault if we get it wrong in safe code." There is an enormous qualitative difference (in terms of ease of programming) and quantitative difference (in terms of number of bugs) between the two.
But in the niche it occupies it is objectively gives much higher safety guarantees than the previous generation of languages, much much more so than C, and somewhat more than C++.
Lets advocate Rust yes, but not as it is flawless.
Yet, here it is. I was not fooled, but the attitude left a deep impression.
It was not my proposal, and I was not persecuted, so that projection is pure fantasy.
Besides, C++ ships features at a glacial pace compared to Rust, so I'm not sure what your point is.
How Rust does things has probably evolved; anyway, I hope it has. But it is, for me, Somebody Else's Problem. Other people are more directly exposed.