Hacker News new | past | comments | ask | show | jobs | submit login
Rust 1.59.0 with inline assembly support etc. (rust-lang.org)
233 points by 0xedb on Feb 24, 2022 | hide | past | favorite | 91 comments



Not mentioned, but the previous release's feature of capturing idents in format strings automatically did not work for `unreachable!`, ie `let x = 5; unreachable!("{x}");` panicked with the message `"{x}"` instead of `"5"`. This is fixed in 1.59


Glad to see inline asm is finally stabilised, that's one less reason to use nightly for osdev!


And support for the `#[naked]` attribute will likely be stabilizing within the next few months (source: I'm getting sponsored to work on it), allowing one to write functions that have precise control over the stack, which is useful for interrupt handlers.


Don't you want "interrupt ABIs" for that specific usecase?

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.


I'm not aware of any proposal for interrupt ABIs, but in the meantime naked functions essentially allow you to create ad-hoc interruput ABIs. Do you have a link?


https://github.com/rust-lang/rust/issues/40180 was the first one; and Phil has created a pre-RFC a few days ago: https://internals.rust-lang.org/t/pre-rfc-interrupt-calling-...

(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.)


It's a shame LLVM isn't more explicit about calling conventions (especially since the front-end has to lower most of the platform-specific C ABI details, short of the exact list of registers or stack offsets, into LLVM IR anyway).

(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)


Does asm! now give you as much power as llvm_asm! did? I remember a while ago that there were some constructs only possible with the latter.


So asm! is basically the same functionality, except it's been stripped down to features that are more easily verified for correctness, and some of the syntax has been tweaked to be more understandable. The original asm! macro just passed everything through to the LLVM inline asm construct in raw form, and was renamed llvm_asm! to allow people to keep compatibility while the cleaned-up version of asm! was worked on.

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.


Can you give more specifics? Backend-wise, the new asm support is still using LLVM's assembly facilities, so it should be just as capable. As far as the user-facing portion is concerned, some features of the `asm!` macro are still unstable (most notably const operands and sym operands), but I'm not aware of any feature that doesn't have an equivalent.


Unfortunately, I no longer remember my old example, and I wasn't able to find a good new example. So maybe it is as powerful now.


Dear Rust Team, Thank you for your hard work! Rust helps me be more productive. It makes our systems more reliable, which reduces stress. And sometimes I have fun while working with it. :)


Good stuff in here. But since they found a bug which caused them to disable incremental compilation by default for this release, I'll likely skip this one and wait a few weeks for 1.60.0 (which these release notes state will likely address the problem and restore default incremental compilation)


Alternatively you can use Rust beta today: `rustup toolchain install beta && rustup default beta`. You'll be doing everyone a favor, as the fact that relatively few people use beta regularly is part of the reason that this issue didn't surface until the last minute. (Plenty of people use beta for CI, which is great, but CI obviously doesn't exercise incremental compilation).


Errrrr.... so you mean you'd rather have the bug, thank you very much? :) Why not just "export RUSTC_FORCE_INCREMENTAL=1" in your env and hope for the best then? :P


The bug is not present in the current beta or nightly. Waiting for 1.60 or using beta are reasonable things to do.


Isn’t the best solution to stop the release in that case, and wait until the bug is fixed?

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.


It would have to be quite a severe bug in order to justify delaying the release, which IMO was not warranted in this case. AFAICT, the effect of this bug is just that some users cannot compile their code without either clearing out their incremental compilation cache or disabling incremental compilation altogether. If it were truly severe, then they could have also disabled incremental compilation completely; right now it's only off by default.


Correctness takes precedence, which is why the incorrect code has been disabled, and even the full fix is going to go through a whole 6-week beta/release cycle instead of being expedited.


Have to say I’ve not been following this in any detail, but it was also disabled in 1.52.1, has it been re-enabled again in the meantime?

https://blog.rust-lang.org/2021/05/10/Rust-1.52.1.html


Yes, it had. There have been ongoing minor bugs (most people won't hit them, and if they do they can just do a fresh build) which they've been fixing over time. But apparently some of the fixes exposed a worse bug this time around, and it wasn't caught until just before the release.


Part of the reason they weren't caught is because it got fixed on nightly right after the previous beta cut off. If the same bug had been present on nightly it would have been picked up quickly.


Really looking forward to const generics becoming more and more useful in the future! A current limitation I hit yesterday is that you can't use a (non-literal) expression as a const generic parameter yet. I wanted to specify 32KiB as "32 * 1024" but had to write "32768" instead.


You can use constant expressions in const generics in many cases.

https://play.rust-lang.org/?version=stable&mode=debug&editio...


Thanks! I didn't realize that it required curly braces and only went off of the red squiggly line my IDE put there instead of reading the helpful compiler output.


I'm always hitting the limitation that I can't use a const parameter on a trait like a const generic. As an example, this is disallowed:

struct Helper<B: Buffer> { x: [u8; B::SIZE], }


Neat! Interestingly Ada has had this feature since its inception around 1983 (use in action with CPUID - https://gist.github.com/AdaDoom3/6466215).


Rust is meant to be a practical language, not a research project, so it’s not playing the game of firsts:

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


It is nothing new, all high level systems programming languages used to have inline Assembly, and ESPOL in 1961 already had intrisics instead.


> Today's release falls on the day in which the world's attention is captured by the sudden invasion of Ukraine by Putin's forces. Before going into the details of the new Rust release, we'd like to state that we stand in solidarity with the people of Ukraine and express our support for all people affected by this conflict.


Very much a rust newbie. What is this syntax called?

> 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!?


This is specific to asm!. It is a macro, and you can use macros to implement custom syntactic constructs.


Thanks! I'll have to take a look at the Macros section of the book.


The Little Book of Rust Macros[0] is also worth a look imho, it helped me a lot when I started learning macros.

[0] https://veykril.github.io/tlborm/introduction.html


Thanks! Been using rust for a while now and macros seem to be kind of like the final Frontier of Rust development


I don't have any problems with macros but I still don't get traits & trait objects. The combination of weird syntax, error messages and unexpected limitations confuses me every time. I'm getting the impression that abstraction isn't quite my thing.

Luckily there's still enums. Not as flexible but they make sense to me and don't sprinkle annotation boilerplate everywhere.


See also the documentation on inline assembly here: https://doc.rust-lang.org/nightly/reference/inline-assembly.... .


I really appreciated the inline ASM in the nightlies when doing a tutorial on working with the RISC-V architecture. Glad it's gone stable.


Looks like a great release


`ControlFlow` is a nice QoL addition.


Inline assembly? Is that not too unsafe for Rust?


My favorite thing about rust is that it has no magic "don't look behind the curtain and your programs will be safe" guard - instead, you define the guard yourself; it's just unnecessary for 99% of code, and within the 1% where you are defining your own curtain the borrow checker et al still try to uphold outside guarantees, making it difficult to shoot _anything_ besides the correct target with rust.


You can still get it wrong, careful with the advocacy.

https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=rust


So let's take a closer look at the fairly recent CVE-2022-21658.

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


It is a choice between (1) acknowledge it is an OS problem the library can't fix, or (2) pretend the library can fix it. C++ chose one; Rust, you say, chose the other.

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 C++ standard does not, in fact "acknowledge it is an OS problem the library can't fix" in respect of remove_all it plainly says this function doesn't remove files resolved to by symbolic links even though sometimes it does.

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.


C++ programmers rely on definitions from a wide variety of places, not limited to the ISO C++ Standard, and not even limited to Standards. Just writing "#include <unistd.h>" is Undefined Behavior as far as the ISO C++ and C Standards are concerned, but ISO Posix defines it. Linux, the BSDs, MSVC, iOS, Intel, AMD, ARM, and NVidia provide numerous additional definitions programmers rely on. A C++ program's behavior is defined by the aggregate of all of them as they apply to the runtime environment of the program.

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.


Your comment would be better without the last sentence.

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.


You seem to be saying Rust patches up the most obvious Posix holes but leaves the more subtle ones, to make it seem more secure without, in fact, being more secure.


If you fix some issues but not others (the fact that we aren't being specific about which POSIX issues we're referring to is driving me crazy, but anyway) you have in fact improved security. The "well, if it can't be perfect, there's no point in doing anything at all" attitude is deeply pernicious, and it would be unfortunate if that is what C++ has adopted.


If you fix a filed complaint, but leave in place the same means to break in as before, that is what we call "security theater", which has a well-deserved poor reputation.


Can you expand how this is the case here? Where does the Rust standard library leave a “subtle Posix bug” unpatched?


There are so many, if Rust wants to do it right across the board, then for every POSIX API where the behavior isn't fully specified, they need to provide a correct way.

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...


But pthread_cancel isn't exposed in the Rust standard library! It's marked unsafe in libc, as it should be.


That is not the reason why it is marked unsafe, rather how the C FFI works on Rust.

So now is safety memory safety, or POSIX cross platform semantics safety?

Better make your mind what safe actually means in Rust.


the libc crate exposes functions in Rust, some are safe, others aren't, you don't call the libc crate function through FFI. (The libc crate indeed calls the underlying libc through FFI but this isn't the same thing!)


> cannot be wrapped in safe Rust

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”.


The safety problem that gets lost, is that the only guarantees safe Rust can offer are related to memory corruption.


We're literally in a thread about a guarantee that Rust kept (but C++ didn't) for filesystem APIs.

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.


A fake guarantee, because it is impossible to actually enforce it for every filesystem in use.

If Rust safety goals is to be dependent typed safety language instead of memory, then quite a few knobs are missing.


> A fake guarantee, because it is impossible to actually enforce it for every filesystem in use.

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.


Until, of course, a very slightly different POC is coded that works against Rust programs built with current Rust.

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.


Of course, just "a very slightly different POC". And let me guess, because you are "people who do understand them" you could certainly conjure up such a thing, but alas, you just don't have the time right? So, Rust is vulnerable to an imaginary attack that Nathan Myers claims to be able to write, while C++ is vulnerable to an actual attack that we know works.

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.


You embarrass yourself. It brings me no pleasure to watch it.

But it reflects also, perhaps unfairly, on Rust and its creators, who did not ask for foolish promotion.


Almost this, but it's slightly broader: Rust guarantees means you won't hit an undefined behavior in safe Rust (as long as the underlying unsafe Rust is correct, this is an important caveat). This isn't equal to memory corruption, especially because optimizing compilers exist and UB can lead your code to be completely miscompiled (which is both dangerous and really annoying to debug).

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.


> Patching around them is a game of whack-a-mole. C++ cannot fix Posix. Rust cannot fix Posix.

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).

https://github.com/x-hgg-x/tz-rs


It is sometimes possible to work around "quirks", particularly when no malice is intended. When it is a question of security, and fending off attackers, the most obvious fix is rarely any kind of fix at all. Attackers can see what you did, and simply step around it.

Bragging about how you put in your broken fix and somebody else didn't tends to attract what may be unwelcome attention.


You obviously have no idea what you're talking about.

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.


> 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.


You can downplay those CVEs, but the fact is they exist.

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.


> You can downplay those CVEs, but the fact is they exist.

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.


This "..., making it difficult to shoot _anything_ besides the correct target with rust" doesn't seem to follow that policy, rather sends the message that everything just works.

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.


You can still get it wrong, it's just way rarer. (95% of the CVEs listed here are simply “program can trigger UB”, which in the C or C++ land isn't worth a CVE in itself)


Rare is not never, hence why the advocacy message should be done in a better way.


This is why it's more useful to talk about blame: http://blog.pnkfx.org/blog/2022/02/09/what-is-rusts-hole-pur...

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.


Sure, my point was that problems might still happen, and selling Rust as if they don't happen at all doesn't help for advocacy.


I really don’t think people advocate for Rust as it being capable of producing only correct programs.

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++.


They do, I started this thread because of "...making it difficult to shoot _anything_ besides the correct target with rust.", irked me.

Lets advocate Rust yes, but not as it is flawless.


I'd be curious to see how many of those are actually safe code though, eh? I've written hundreds of thousands of lines of Rust. Can we estimate how much UB i've introduced?


It's only usable in unsafe blocks.


unsafe is a part of rust, and this is an important step for OS writers as sometimes it's just more convenient to put in some inline ASM


It was only a few years ago (2015?) that people asking for destructured assignment were treated like simpletons, with a long, jargon-laden explanation how the language formal syntax for destructured "let" was incompatible with assignment syntax as it was then, as if syntax were not something that could be extended anytime, or as if there would be no choice but to express it using the same nonterminals for both.

Yet, here it is. I was not fooled, but the attitude left a deep impression.


The amount of implied persecution in this comment makes it hard to take this seriously. The original proposals for this suggested extending the full pattern grammar to the LHS of assignments, which would have been a bad idea. Instead, and indeed as you suggest, people came up with a different proposal that hardcoded a subset of the pattern grammar into this context in order to support a majority of what people would like to use this feature for. I have a hard time believing anyone associated with the project treated anyone like a simpleton at any point of the process; problems with the proposal were pointed out and solutions were found.


You may believe whatever you like, but the evidence is all preserved where anybody may read it. And, notably, it is now, six, seven? years later; it could have been implemented any time, including right then.

It was not my proposal, and I was not persecuted, so that projection is pure fantasy.


People disagree about proposed language features. That's not a surprise. It's a sign of a healthy community where debate is encouraged.

Besides, C++ ships features at a glacial pace compared to Rust, so I'm not sure what your point is.


It is one thing not to implement a requested feature, entirely another to invent transparently dishonest reasons why it cannot be done.


It's not dishonest to point out that the syntax would have to be made more complex and that there are downsides to that. It takes 30 seconds to find a comment somewhere here on HN decrying the complexity of modern languages. None of those comments are dishonest, even if I don't agree with them.


That would be fine, except that was not at all its substance. The claim was that it was not possible, something I recognized at the time as farcical and a disgrace. I have no recollection who penned it, but can only hope the author has since left the project, or been ejected.


You don't remove people from a project for being wrong about whether a proposed feature works in the syntax. That's silly.


You do remove people from a project for transparently lying to users about whether a requested feature is possible, and making up an elaborate fiction as to why it is not possible. Or, I would.

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.


[flagged]


We've banned this account for egregiously breaking the site guidelines. You can't use HN to attack people.

If you don't want to be banned, you're welcome to email hn@ycombinator.com and give us reason to believe that you'll follow the rules in the future. Besides not attacking people, that would include not posting flamewar comments and not using HN for an agenda the way you have been.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: