That is likely just trolling, but is also specially obnoxious considering that Rust does prevent use-after-free bugs as much as Java does (i.e. a lot, but definitely not all).
That is likely just trolling, but is also specially obnoxious considering that Rust does prevent use-after-free bugs as much as Java does (i.e. a lot, but definitely not all).
However, I really do think for a new "systems language" nowadays, you do want to look at how major security holes occur in practice, and have a good story on how users should avoid them.
> Cryptography is a difficult, high-risk domain of programming. The life and well-being of your users may depend on your ability to implement cryptographic applications with due care. Please carefully read all of the documentation, double-check your work, and seek second opinions and independent review of your code. Our documentation and API design aims to prevent easy mistakes from being made, but it is no substitute for a good background in applied cryptography.
We have many safety features built into the language and the standard library is designed to be difficult to use incorrectly. I will address these concerns directly in a subsequent blog post covering the safety and security features of Hare.
The main problem is that some programmers view anything less than what Rust provides as morally unjustified.
That introduction is simply an appeal to "I can write safe correct C." with more words. Which obviously is not true.
No it doesn't. It has a bypassable compile-time verified lifetime system, not an infallible programming guarantee. It leaves it entirely up to the developers to write correct and safe applications and libraries, and merely provides (powerful) tools to help.
(I realize this context was in avoiding common security bugs which are usually less likely in memory-safe languages, but it's important to not overstate the benefits.)
"use-after-free", "data race", "memory corruption", "out-of-bounds write", "read from uninitialized memory", "execute arbitrary code"...
Everybody already knows Rust has unsafe blocks and C FFI. It is not invulnerable to those problems, Rust just makes it very clear where those problems may appear, and if you are smart, you will place most of your code outside of those regions.
Looks like Rust is much safer on practice than what I expected.
Ending up compromised by a problem in tokio, Pin semantics, actix or all the necessary ffi bindings is no different than, say, a C program being compromised by a vulnerability in OpenSSL or libcurl.
A very significant number of memory issues in C stemmed from issues in such single high-profile dependency, so one should not undermine the threat of a bit of unsafe code in the corner of a library.
Rust is better.
It's very much human nature to trace the line in the sand juuuuuust right behind one's heels though, depicting everyone behind as bad and everyone ahead as zealots.
Rust is definitely better, hands down, but insisting on thinking code that interacts with unsafe blocks can be "safe by default" is a dangerously wrong mindset which also makes unsafe blocks proliferate without the necessary caution as the problem seem "contained". Anyone remember the actix unsafe saga?
But even though a program with unsafe blocks (read: all rust programs) are by definition not memory safe - calling a language memory safe on current platforms can to some extend even be considered a misnomer - the assistance provided by rust by default certainly helps make such programs much safer.
That's very different from environments where all code must be correct no matter what. But the impact of bugs isn't what changes.
One is that a large fraction of these security issues are not real, potentially exploitable vulnerabilities, but merely the fact that it is possible to abuse an API to subvert Rust's safety guarantees. These are things would, by the standards of other programming languages, not be worth even reporting and be considered user error.
The other is that a surprisingly large fraction of these are not from regular unsafe Rust code, but from misunderstanding the guarantees a C library makes when creating bindings for it. This is to be expected, as fully understanding those as a library is pretty difficult.
A total of one memory safety issue reported for an entire ecosystem this year so far also seems pretty good.
All in all, I think these are both pretty promising signs that the safety guarantees Rust provides working as intended.
In an ideal la-la land world, there exists an abstract interpreter that can consume safe Rust code and does not enforce any contracts upon the programmer (and hence will never have any undefined behavior). However, real world hardware definitely has contracts which developers have to obey (manually! because of the constraints of actual semiconductor physics! no compiler hand-holding here!). And on top of that all major OSes (Windows, MacOS, Linux) are written in C (so you need unsafe FFI to interact with the OS).
Of course you're right that that isn't true as stated, but I think it's interesting to try to situate this point along a continuum of other similar points:
1. C with Valgrind and sanitizers isn't always memory safe, because those tools are limited by test coverage.
2. Python isn't always memory safe, because many libraries including the standard library call into C code.
3. Pure Python that doesn't call into any C code isn't always memory safe, because the interpreter might have bugs.
4. Provably correct Ada with a provably correct compiler isn't always memory safe, because the proof checker, the compile-time hardware, or the runtime hardware might have bugs.
I think we all agree that there are important differences between 1 and 4, beyond the simple fact that the defects get less common as you go down the list. Here are some things that stand out to me:
- In cases #2 and below, the application code isn't "at fault" for any memory unsafety that comes up, and whatever code is at fault can be fixed to restore memory safety without changing the application.
- In case #1, there's no clear boundary in any sense between "safe code" (which we know isn't at fault for memory unsafety) and "unsafe code" (which might be at fault). There may be a distinction between code that's well covered by tests and code that isn't, for example, but it's often not easy to tell which is which. In case #2 and below, the boundary is pretty clear.
- In case #1, the amount of "unsafe code" in an application probably grows linearly with the size of the application, or maybe we just consider the whole application unsafe. But in cases #3 and #4, unsafe code is confined to low-level dependencies that get a lot of "battle testing" compared to how much code is in them. Case #2 is kind of a gray area, and we need to look at what dependencies the application is using.
So where should we situate Rust in that continuum? Is being able to write unsafe Rust code more or less risky than being able to call into C? It's certainly a lot more convenient to write an `unsafe` block than to cross the FFI barrier, and maybe that convenience is dangerous. On the other hand (contrary to some common misconceptions), unsafe Rust still benefits a lot from the borrow checker and other safety features, and it might end up having a lower rate of defects for that reason. Maybe it's too early to tell?
But anyway yes, I totally agree that the Rust community has a hard time getting the messaging right about how safe code and unsafe code work. But even though this discussion is really important to Rust, I'm not sure it's a "Rust problem" per se. I think it's actually quite difficult to talk clearly and correctly and precisely about memory safety in general.
In practice, this makes vulnerabilities in eg. argument parsers (like the recent "baron samedit" vulnerability in sudo) incredibly unlikely.
Safe Rust cannot ever cause undefined behavior, but Unsafe Rust can. The ultimate merit of Rust is that when you suspect any undefined behavior you only need to check the unsafe part, which is a much smaller percentage of your codebase (as opposed to C/C++ where you need to check the entirety of your code)
Whatever that means lol
You mean the borrow checker? People are working on formally proving that, and have already done so for large subsets of the language.
In other words, incorrect code in "unsafe Rust" can cause safety issues that only appear when you use it in a certain way from "safe Rust".
It is possible for two people who both take security seriously to come away with different take-aways. Once some CVEs are found in Hare you might have some fuel for your argument, but until then it's just speculation.
These are all GC'd languages that run bytecode on an abstract virtual machine. They avoid use-after-free by just not-freeing, if necessary at the cost of leaking unbounded amounts of memory.
This doesn't invalidate the point that borrowing isn't the only way to solve this problem, but there are definitely classes of these bugs for which Rust's borrow checker is the only known production-ready solution that still has manual, deterministic memory management.
Also I was wrong before and you were out of line. Matthew wasn't trolling, he never said you should be held criminally liable. You just made that criminal part up for no reason. Anyone should be held socially liable and shamed if their project has bad security and they refuse to fix it after they knew about it. I think you would even agree with that.
None of what you said is even true, all of those patterns are trivial, except "back-references" which are very slightly non-trivial.
And none of it is relevant. We don't need another memory unsafe language. It's fine if it's a toy, but this obviously isn't. It causes real harm.
They all rely on having a mutable member reference to affect the outside world, which the borrow checker rejects. You can try to make it happen with a generic lifetime parameter for your structs, but you end up making invisible the thing you're pointing at, making it useless.
One can use unsafe to have shared mutability, or sacrifice speed with Rc/RefCell (increments/decrements) or Cell (copying, especially bad when copying Vecs which causes heap allocations).
Basic observers aren't possible. The closest thing we can get is some sort of modified observer-like substance that returns a command, which requires a lot of wiring and incidental complexity. [0] [1]
Dependency injection (the pattern, not the framework) isn't viable for the same reasons as observers: you can't have a mutable reference field without causing a lot of headaches elsewhere.
RAII isn't possible without sacrificing speed or safety. Most usages we see are backed by unsafe (FFI) or RefCell. We can't have multiple objects whose drop() affects the outside world, because we can't have multiple extant &mut references, and we can't just pass them in via parameters (because drop takes none).
Back references aren't possible because of the circular problem (having a mutable reference to your owner means nobody else can read it).
I'm not advocating for Hare, I'm just addressing the "I just don't see the excuse" remark, which can be ignorant of the costs of the borrow checker. The borrow checker is a great step forward, but not always a good tradeoff. An architect needs to be aware of these sacrifices before going all-in on a paradigm.
[0] https://stackoverflow.com/questions/37572734/how-can-i-imple...
[1] https://www.reddit.com/r/rust/comments/pwqju6/is_there_an_un...
idk what tot ell you
Cell is meant to be applied at the "leaves" of a type where individual assignments take place. When used this way, there is no additional copying relative to the straightforward C version. (Bringing up Vec and heap allocations here is also complete nonsense; Cell has nothing to do with Clone.)
Once you wrap your head around that, all your shared mutability examples translate over to Rust trivially. All the "sacrifices" in speed you are imagining are relative to `restrict`/`&mut T`, not the usual baseline of a simple mutable object.
To see it in action: Have a Database object, and try to have multiple Transaction objects that might commit something to it, in their drop().
It's unfortunately not possible, because they can't all have a &mut Database as struct fields.
We can sacrifice speed (by using Cell's copying or Rc's counting) or safety (by using unsafe). Most RAII we see uses unsafe FFI under the hood, which is why it was so surprising to me.
The very specific API design that you've described is not possible in Rust, but it is strange to equate this with the entirety of RAII. In any case, there are many alternative APIs (some with no sacrifice in speed or safety!) that are perfectly possible in Rust.
You need shared references for your DB, implying you need interior mutability. This is how Statements are implemented in real-world rust database drivers such as rusqlite (any operation on a db is done through a shared reference). The fact that a very real package is doing it proves that the pattern you're talking about is, in fact, possible.
However, achieving something like what you want is still more than possible in Rust. You can do this with the pattern of 'interior mutability', which in its simplest form is just a Mutex. This allows upgrading a shared reference to an exclusive reference, so that you can safely mutate an object while upholding the expectations that a mutable reference is exclusive, and a non-mutable reference does not change from under your feet.
Of course, for a database, you will probably want a more advanced implementation of interior mutability, so that you can commit multiple transactions at the same time. (Or not, it seems to work quite well for SQLite.)
Why does anyone need an excuse to build anything? He’s doing this in his free time. He doesn’t need the internet’s permission.
> Part of our work in developing Hare is laying the groundwork for a collaborative, productive, healthy community that people want to work in
The goal to have a healthy community is laudable but it comes from the top and hitting out at others doesn't set a good foundation. I'd recommend rising above by responding to critique without emotion.
That's solely my point.
> Look if you can't understand that this is a thing that will happen in the real world and that people will potentially suffer as a result you shouldn't be writing a crypto library.
Which is still far from suggesting someone should be prosecuted.
But it confirms what I was thinking, going from "liable" to "criminally prosecuted" is a pretty big stretch imo.
Reading the original comments about being "liable", it felt like another way of saying "there are consequences to your decisions, and as the author you bear some responsibility of what you put out there", which imo is pretty far from how the author of this blog post described it, hence me calling "a pretty big stretch"