Live data from Hacker News

Memory safety absolutists

itsallaboutthebit.com

41–50 of 272 posts

Re: Memory safety absolutists

#41

This is perhaps a different take, but one reason I love Zig and C is because I've learned how to reason in pointers. I've worked with Rust in the past on a ~12,000 LOC side project, so I was all in with tree ownership and XOR mutability. But it turns out there's all sorts of delightful data structures that you can only express with unsafe in Rust. Intrusive doubly linked lists are the coolest thing ever. The fact tha…

> But it turns out there's all sorts of delightful data structures that you can only express with unsafe in Rust. Intrusive doubly linked lists are the coolest thing ever.

I don't mean this to be a gotcha, but I think it's important to say that we can have these in rust too! The only difference is that the first thing we have to talk about is you the user can go about using an intrusive collection correctly. I love the cordyceps crate for this. If you want to be able to use your type as a linked list node, you must implement a Linked trait, the documentation of which clearly explains how to use it safely: https://docs.rs/cordyceps/latest/cordyceps/trait.Linked.html

Re: Memory safety absolutists

#42
Personally, I see these language fights as being a bit pointless. They are trying to optimize at the wrong layer. Unless one is working with a dependently typed language, which requires a proof assistant to discharge type checks, then it's all just a question of where you make the trade-off. Don't care about memory leaks or deadlocks as part of your soundness guarantees, but can't have GC? Use Rust. Okay with runtime overhead to verify checks? Consider fil extensions.

If you want to go deeper, skip the language wars entirely. Tooling does what these languages can't do. I have had great success model checking C with CBMC. Not only does this prevent memory safety issues (including memory leaks), but this also prevents deadlocks. Bonus: you can write user contracts and invariants to verify that every execution path of a function fulfills these contracts and invariants.

Kani comes close with Rust. It doesn't yet have decent concurrency support, but there is nothing that prevents this from being added in the future. The ability to write custom contracts and enforce custom invariants more than makes up for its lack of concurrency support. Similar technology could be used or adapted for Zig or C++.

Use whichever language you like. Just, please look into model checking it. The technology scales just fine, once you get over the learning curve and learn how to use compositional verification.

Re: Memory safety absolutists

#43

> But Rust is unsafe, isn't it? It has unsafe after all! If you want to be that strict, or in other words, if you are a memory safety absolutist, that may well be true for you. I, and I hope most people, am more pragmatic than that. So... Yes, Rust is less safe. Just that now the Rust apologist wants to back away from that and say that actually memory safety isn't actually the end all be all. C has a lot of security…

Like everything, it's about trade-offs. C is really unsafe. Rust is almost entirely safe, and breaks the ecosystem just a little bit. Fil-C is entirely safe, and breaks the ecosystem entirely.

Re: Memory safety absolutists

#44

Repeat after me: Rust is not just memory safe C++! Memory safety is a really big reason to use Rust, but it is very very far from the only reason. Even if Fil-C was magically zero-overhead (that is basically what CHERI is), I would still rather use Rust. Rust has so many advantages over something like Fil-C it's hard to list them. I think Fil-C is a great project but it's only really relevant if you have no choice bu…

[deleted]

Re: Memory safety absolutists

#45

> But Rust is unsafe, isn't it? It has unsafe after all! If you want to be that strict, or in other words, if you are a memory safety absolutist, that may well be true for you. I, and I hope most people, am more pragmatic than that. So... Yes, Rust is less safe. Just that now the Rust apologist wants to back away from that and say that actually memory safety isn't actually the end all be all. C has a lot of security…

You can just...not use it though? It's not comparable to trying to avoid it in C/C++ because there's literally just one keyword to never use and then you're good, compared to it being possible to silently introduce in any number of ways. I put a lint in my Cargo.toml to forbid unsafe code in my projects, and now I'm guaranteed not to write unsafe code.

To apply your logically consistently, you'd also have to throw out Go[1], Java[2], C#[3], and plenty of other languages. I guess you can define a logically consistent taxonomy of languages where Fil-C is the only "safe" language any everything else is unsafe, but then the burden is on you to prove that it's a useful way of thinking about them. Meanwhile, the way that people who consider Go and Java and Rust to be safe and C/C++ to be unsafe will continue to see actual real-world differences in outcomes when using a safe language compared to unsafe ones.

As an aside, it continues to be absolutely wild to me to see how most arguments in favor of C/C++ over Rust nowadays are based on framing things theoretically rather than practical ones. It was not all that long ago that the situation was reversed, with people claiming that Rust's benefits were all theoretical when most C/C++ code would have all of the UB found and removed over time. Now that Rust actually has been getting used for real-world stuff for a few years, and the number of vulnerabilities found in C/C++ code does not seem to be going down any time soon, any actual empirical evidence gets handwaved away. Maybe Fil-C will be able to provide as large benefits to running C safely in production like its proponents claim in the future, and if so, that will be a solid argument against the utility of Rust in a lot of situations, but if we're collectively going to shift the standard to discount pragmatism over theory in this debate, we might as well not try to hide it.

[1]: https://pkg.go.dev/unsafe

[2]: https://docs.oracle.com/cd/E92951_01/coherence/java-referenc...

[3]: https://learn.microsoft.com/en-us/dotnet/api/system.runtime....

Re: Memory safety absolutists

#47
post #41

This is perhaps a different take, but one reason I love Zig and C is because I've learned how to reason in pointers. I've worked with Rust in the past on a ~12,000 LOC side project, so I was all in with tree ownership and XOR mutability. But it turns out there's all sorts of delightful data structures that you can only express with unsafe in Rust. Intrusive doubly linked lists are the coolest thing ever. The fact tha…

> But it turns out there's all sorts of delightful data structures that you can only express with unsafe in Rust. Intrusive doubly linked lists are the coolest thing ever. I don't mean this to be a gotcha, but I think it's important to say that we can have these in rust too! The only difference is that the first thing we have to talk about is you the user can go about using an intrusive collection correctly. I love t…

I think this crate description encapsulates what's difficult about unsafe rust, which is how unergonomic pointers are. Like why do I need to use `addr_of_mut!`? I'm sure there's a good reason, as Rust tends to think these problems through, but it's very unintuitive compared to returning `&mut`. I feel like I'm juggling way more concepts, which to be fair helps with safety, but it can obscure the algorithm itself.

Does cordyceps have a derive macro? I can imagine that helps a lot with correct implemention, though when it comes to linked lists I can see people wanting to do it themselves.

Re: Memory safety absolutists

#48

I was not aware of the antagonism from the Zig / Fil-C people to Rust, but it makes absolutely no sense. Like, of course you can prevent all memory safety errors by using a garbage collector. That has been known since the 90s. In fact, there was a good two decades after Java released where all the research on memory safety just stopped, because the standard answer became "use a GC". If you couldn't use GC, you were s…

> you can prevent all memory safety errors by using a garbage collector

Garbage collection and a higher-level language that controls allocations handles 2/3 of the memory-safety problem space (using memory you shouldn't via use-after-free, or using memory you shouldn't before allocation/via arbitrary address access), but the other 1/3 isn't addressed by GC: out-of-bounds access on properly-allocated structures.

GC-or-not doesn't stop a pointerful language from addressing memory off the end of an array, and the best techniques we have to deal with that today are imperfect (but still pretty good!) compiler inference for BCE, memory protection to limit the scope of possible overreach, or slowing down runtime by inserting checks.

The rest of your points are well-taken; just pointing out that GC isn't the solution to all causes of memory-unsafety, just 2/3 of 'em.

Re: Memory safety absolutists

#49
Trying to figure out Fil-C's "inviscaps".[1]

When you allocate space with Fil-C's "malloc", some additional bounds checking data precedes the space the program gets to use. Pointers are "fat pointers", with a pointer to the beginning of the buffer and a pointer to someplace within the buffer, allowing ordinary C pointer manipulation.

There's much new verbiage around this. But it's roughly the same idea as GCC "fat pointers".[2][3] So it's not a new idea. It's one that's been tried several times, but never caught on.

There's a performance penalty. Especially if the compiler can't hoist the checks out of loops.

[1] https://fil-c.org/invisicaps

[2] https://williambader.com/bounds/example.html

[3] https://www.doc.ic.ac.uk/~awl03/projects/miro/MIRO.pdf

Re: Memory safety absolutists

#50

Something that is important to me is that it's caught at compile time. A memory issue is a logic bug. Fil-c simply moves that from undefined behavior/security issue/incorrectness to a crash. That's better than what it was. But I generally don't want my programs to crash. Having memory safety at compile time is much more worthwhile imo.

As a Rust fanboy, I have to concede that Rust also doesn't have pure compile-time memory safety. Some checks are runtime there as well, in particular most of bounds safety.
Post reply on HN