Memory Safety Absolutists

76 pointsposted 12 hours ago
by drogus

102 Comments

himata4113

9 hours ago

My biggest problem with the refusal to be memory safe is the fact that those problems end up becoming my problems when I am forced to use these applications and I have to think about how there might be a zero-click zero-day that uses an overflow in some random codec. Not as a software developer, but a regular person I want my application to be written in rust or at least use fil-c at bare minimum.

Now as a software developer I feel like this is even more important because I use libraries maintained by thousands of other developers that might also use applications that have these exploits which get their systems compromised pushing malware to thousands of other developers which end up compromising even more libraries.

I believe that memory safety should be the standard for software that thousands if not millions rely on and that it shouldn't be some political issue of X is better, Y is that, Z is something else.

But then again, social engineering is the primary source of malware spread so I don't know.

consumer451

8 hours ago

It seems to me that memory safety might be the difference between the software engineering and Software Engineering. As in, an actual Engineering discipline.

However, I should probably pipe down, as I would not call myself either one.

himata4113

7 hours ago

There are too many opensource projects that show that even with good engineering discipline humans are flawed creatures.

Memory safety is just little thing that makes sure that when you write code at 4am that it will not leak memory via trivial mistakes such as forgetting to free something, freeing something twice or passing a freed pointer. I believe AI agents shine here the most because the they do not get tired and are getting pretty damn predictable.

inigyou

4 hours ago

How many bugs in qmail though?

kelnos

3 hours ago

I don't think we should set our expectations based on an extreme outlier. qmail is special, and we can't expect most software to get to its level of security/safety.

Put another way: if you have to rely on programmer skill or attention to detail in order to guarantee something, that will always be a losing bet, on average. The existence of a tiny percentage of programmers that can clear that high bar does not make it a valid strategy.

himata4113

4 hours ago

It had one bad cve it seems, but that's exactly what I mean. It only takes one mistake, of course you can learn and never make those mistakes again, however, that is an unrealistic expectation in software that receives hundreds of feature updates a year especially when it comes to core applications as basic as communication when it wants to support image previews, reels and whatnot.

Brian_K_White

3 hours ago

There will always be one, so "it only takes one" is meaningless and invalid. That leaves less is better than more, and any form of less is as good as any other form of less.

NBJack

4 hours ago

If as a user you're willing to pay these library/application owners and premium to do so, by all means; this is a reasonable demand. But short of a massive campaign to educate and change minds, I can't see the average user caring enough.

himata4113

4 hours ago

My biggest gripe honestly is businesses rather than any particular opensource project, opensource projects can often get away with these issues getting caught by the many eyes looking at them before they ever make it to mainline.

smj-edison

8 hours ago

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 that I can have a LRU cache that changes what's at the front by shuffling some pointers, while still keeping stable addresses so the hash map stays stable? That's freaking cool. Atomic operations with pointers for linked lists is really slick for implementing an allocator's free list between threads. Being able to walk a live heap using a breadth first search by just... following pointers, made the elegance of Dijkstra's algorithm come alive.

Now, maybe I'm only running into these algorithms because these are the problems I'm running into, but in the Rust community I consistently got the message that linked lists were a legacy data structure. In some cases they are, but when they do apply they do so brilliantly.

I know working in Zig leaves plenty of room for memory unsafety. Perhaps that's a bad thing. But I'm implementing an interpreter, and these algorithms are essential for it to run fast. I really do need to be aware of where every allocation happens, and what instructions the computer is running. So for now I stick with Zig, even though I know I'm opening myself up to memory exploits.

safercplusplus

an hour ago

Well, there is another safe option for flexible pointers if you can compile as C++ [1]. In C++ you can have non-owning "smart" pointers with run-time-checked lifetimes [2][3]. Importantly, there is no run-time overhead to dereference them. (That is for the "never-null" versions, otherwise there's a null check.) Assignment has additional run-time cost, but pointer assignments are generally much less prevalent in performance-sensitive inner loops than dereferences, right?

And you can statically verify [4] your raw pointers, so that you only need to use the run-time-checked pointers for the more exotic lifetime relationships.

(That said, for the situations where Fil-C acceptably solves your problem, it's probably the more practical, complete and well-supported solution. And since not many seem to be explicitly mentioning it, the recent Fil-C demonstration of memory-safe linux userspace is rather impressive, right? I've heard that IT security is a $100B industry. I'm guessing an inappropriately low proportion of those resources are being invested in Fil-C. :)

[1] https://github.com/duneroadrunner/SaferCPlusPlus-AutoTransla...

[2] https://github.com/duneroadrunner/SaferCPlusPlus#norad-point...

[3] https://github.com/duneroadrunner/SaferCPlusPlus#tnoradproxy...

[4] https://github.com/duneroadrunner/scpptool

afdbcreid

8 hours ago

I absolutely agree learning C is a good thing! In fact, there are two camps of Rust people: one that argue that you should not learn C before learning Rust, and one that argues that you should.

Also, while such data structures/algorithms are rare, and more importantly, they can be written once and used many times, someone still needs to write them!

Here is some online content on this side of Rust:

- Learn Rust the Dangerous Way - https://cliffle.com/p/dangerust/

- Learn Rust With Entirely Too Many Linked Lists - https://rust-unofficial.github.io/too-many-lists/

- The Rustonomicon - https://doc.rust-lang.org/nomicon/

smj-edison

8 hours ago

Thank you! I've seen the latter two mentioned in the past, but I never got very far into the books before getting confused with all the moving pieces. I think now that I've worked a lot in languages that put structs and pointers in the forefront, I'd understand it a lot better. Perhaps I'll have to go back and give them a proper read this time...

jagged-chisel

4 hours ago

> The fact that I can have a LRU cache that changes what's at the front by shuffling some pointers, while still keeping stable addresses so the hash map stays stable? That's freaking cool. Atomic operations with pointers for linked lists is really slick for implementing an allocator's free list between threads. Being able to walk a live heap using a breadth first search by just... following pointers, made the elegance of Dijkstra's algorithm come alive.

I feel like these kinds of things should be transformations that the compiler can use to turn Provably Safe code into performant code, i.e. you write the safe code and, once the borrow checker is happy, the compiler does its magic. I have no idea how to codify any of it into a compiler pass, or whether it's actually possible...

nitros

8 hours ago

> 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

smj-edison

7 hours ago

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.

aw1621107

4 hours ago

> Like why do I need to use `addr_of_mut!`?

As of Rust 1.82.0 [0] you no longer need to!

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

The addr_of_mut docs [1] give a pretty decent explanation of its reason for existence; in short, it lets you get a pointer to something without needing to create potentially-invalid intermediate references. It (and &raw) probably aren't going to be needed if all you need is a &mut, though.

> Does cordyceps have a derive macro?

Doesn't appear to from a quick glance, and given what's in the safety section of the docs [2] it'd probably need to be marked unsafe [3]. Unsafe attributes are new to Rust 2024, though, so that might be a bit new.

[0]: https://blog.rust-lang.org/2024/10/17/Rust-1.82.0/#native-sy...

[1]: https://doc.rust-lang.org/std/ptr/macro.addr_of_mut.html

[2]: https://docs.rs/cordyceps/latest/cordyceps/trait.Linked.html...

[3]: https://doc.rust-lang.org/edition-guide/rust-2024/unsafe-att...

afdbcreid

an hour ago

> > Like why do I need to use `addr_of_mut!`?

> As of Rust 1.82.0 [0] you no longer need to!

That's true but misleading. I'm pretty sure the question was why a normal reference is bad. You don't need `addr_of_mut!()`, but you do need `&raw mut`.

NobodyNada

4 hours ago

> what's difficult about unsafe rust, which is how unergonomic pointers are. Like why do I need to use `addr_of_mut!`?

Things are slowly getting better here! '&raw'/'&raw mut' reference operators were stabilized a couple years ago.

Another ergonomic improvement in the pipeline is a better way to access fields behind pointers, something like C's -> operator. This is taking some time to design because there are things other than raw pointers that would benefit from a generalized field projection mechanism, like Pin, NonNull, and (potentially user-defined) smart pointer types.

kibwen

4 hours ago

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

That documentation appears to be out of date. The `addr_of_mut` macro was elevated to a first-class language feature, `&raw mut` (there's also a corresponding `&raw` operator). These two operators differ from the usual `&mut` and `&` in that they create raw pointers rather than references; prior to the introduction of this feature (or the aforementioned macros that served as precursors) to create a raw pointer you might need to have done a cast like `&foo as *const` in order to create a raw pointer by casting from a reference, but this could have safety consequences if the temporary reference was to an invalid object. Therefore `&raw` and `&raw mut` were introduced to create raw pointers directly without introducing an intermediate reference, which was arguably the most subtle footgun in unsafe Rust for a few years, and it's nice that it's now addressed.

conradludgate

8 hours ago

Given your performance concerns, I'm imagining you're unlikely to run Zig-Fil in practice, due to the GC overhead?

smj-edison

8 hours ago

Yeah, probably not. I wouldn't mind fuzzing it with Zig-Fil, but the interpreter (Zicl) is embedded in a larger C project that uses a lot of dynamic linking, so chances of using it in production is pretty much zero. I do have a lot of asserts that only have a small overhead, so I'll probably use ReleaseSafe most of the time, since it does catch a lot of the issues.

Panzerschrek

37 minutes ago

The main problem of Fil-C or similar solutions is not that they provide absolute safety with no escape hatch (unlike languages with unsafe keyword). The problem is that they provide an excuse to keep using terrible programming languages like C and C++ allowing memory safety issues in the first place.

inigyou

4 hours ago

I think the "Fil-C is safer than Rust" thing is an understandable reaction to 10 years of Rust evangelists telling people they have to use Rust otherwise they're stupid and wrong.

NBJack

4 hours ago

We can learn a lot about memory safety from Rust. But if we can achieve the same dream of Fil-C for other more common languages that need it, Rust's popularity will likely wane.

kelnos

3 hours ago

I doubt it. I am a big fan of Rust's memory safety, but it's not just memory safety that draws me to Rust. Having a strong, featureful type system is also a big plus for me, and even if Fil-C could someday magically compile any program or library without modification, and its performance trade offs could be mitigated, I'd still write Rust, because C is miserable to write. (And I say this as a 25+ year C programmer.)

20k

4 hours ago

Fil-C has a large performance impact though, and it'll always be reasonably significant. Rusts popularity has always been the combo of safety and performance, otherwise you might as well just use C#

xboxnolifes

3 hours ago

> Rusts popularity has always been the combo of safety and performance, otherwise you might as well just use C#

Combined with the ability to compile to a single native binary, having a solid package management solution, a fairly advanced type system, and a solid selection of nice language features. Usually you need to give up at least one of those.

sanxiyn

3 hours ago

More people should use C#, to be honest.

Aurornis

2 hours ago

The reasonable reaction is to ignore the vocal minority.

It’s tiresome when other language communities start engaging in a battle with a vocal minority of another community.

Every language has its annoying maximalist pushers. The play is to ignore them and do what decisions are best for the language, not to let yourself get dragged down into petty language wars where nobody wins.

Animats

7 hours ago

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

pizlonator

5 hours ago

Invisicaps are not exactly fat pointers.

Fat pointers show up inline in memory, which has a bunch of problems:

- sizeof(void*) changes

- either you let the bounds get corrupted by bad casts, unions, and other issues, or you impose restrictions that prevent unions from working compatibly, or you end up supporting unions by having issues with races, or you need special hardware. Invisicaps sidestep all of those issues

Animats

2 hours ago

The "flight pointer" looks an awful lot like a fat pointer. See the diagram at "The intuition of inviscaps" in [1]. A "flight pointer" has a pointer to the beginning of the buffer, which they call the "lower bound ptr", and a pointer to someplace within the buffer, which they call the "integer ptr". The pointer to the beginning of the buffer lets the checker find the size of the buffer, which is stored preceding the buffer. The compiler has to arrange things so that the lower bound ptr is carried around wherever a "flight pointer" goes. This is supposed to be invisible to the programmer.

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

pizlonator

2 hours ago

other implementations of fat pointers (that I’m aware of) have no distinction between flight and rest; they store what I call flight pointers in memory literally.

The closest technique to invisicaps is softbound, but that has issues that invisicaps resolve (better story for races, more comprehensive safety for all of the C and C++ languages, no need for large virtual memory reservations, and lock freedom)

inigyou

8 hours ago

I don't think you need to be an absolutist or only use memory safe languages but it's very obvious that we need to do a whole lot better than we actually do. Rewriting in Rust or any other language is one way to do that, but not a perfect one. I'll accept C++ when most C++ software has 1 in 50 chance of an RCE and not just a 1 in 50 chance of a known RCE. I think we can get there but we are not currently there.

Coreutils has a good track record. Ffmpeg doesn't. They should have rewritten ffmpeg in Rust, not coreutils.

There are other approaches though like formal verification.

bpavuk

8 hours ago

ffmpeg is actually asm and for a good reason - it has to go fucking fast or else media playback will take too much resources

inigyou

5 hours ago

Great then let's prove that ASM is correct.

the reference C code is just as bad.

afdbcreid

8 hours ago

If you go by this metric (has a RCE and not known to have RCE), I'm pretty sure the actual statistics are more like 1 in 2, at least. And probably more than one RCE.

GaggiX

8 hours ago

>They should have rewritten ffmpeg in Rust, not coreutils.

Wait for an AI company to promote their new model by porting the entire ffmpeg to Rust (half ironically).

xprnio

8 hours ago

Didn’t the primary maintainer already signal burnout or frustration or something like that? If an AI company actually did port ffmpeg *successfully* (not like claude CC), the dude might get the release he might have been yearning for

throwlifeaway

9 hours ago

Pizlo is not a memory safety absolutist. His rhetoric towards rust is a tactic specifically designed to draw more attention to him and his project. It is amplified by people who already had a bone to pick with rust and take joy in giving rust folk "a taste of their own medicine," so to speak. Articles like this are taking the bait.

pizlonator

5 hours ago

What a weird take.

I think the limits of Rust’s memory safety are interesting to discuss, as are the limits of Fil-C’s perf and practicality.

It’s best to discuss these things rationally, rather than accusing folks of trying to draw attention

my-next-account

an hour ago

I don't think it's too weird of a take, we do things for many reasons. I get that it feels bad to have that directed at you as an accusation. It's rational to discuss the intent behind actions, but I think the parent is hinting at "so we shouldn't take what Pizlo says seriously", which isn't a good conclusion.

nanolith

7 hours ago

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.

noelwelsh

8 hours ago

Good article. Not much to add to it, other than I think more people should look at modal type systems like found in Scala 3 and OxCaml. If you want safe arena allocation they are a lot more ergonomic than Rust's approach.

pizlonator

4 hours ago

The problem with this post is the extent to which it shows that its author has an unhealthy obsession with me personally. It’s weird but also oddly flattering.

I don’t dislike Rust, and when I point out that Rust is not fully memory safe, it’s because I find the details here super interesting. It’s interesting that Rust deliberately chooses to have an unsafe subset. It’s interesting how that leaks out to the rest of the language. It’s interesting because us language designers ought to be thinking about how this could be avoided. It’s a hard problem! And that makes it fun!

If you do read what I say on twitter, you’ll find praise for Rust, many concessions about Rust being faster than Fil-C, as well as a wide range of other opinions. When I point out Rust’s unsafety, I’m just citing facts. It’s interesting how doing that really seems to upset some folks.

Pointing out a limitation in a technology does not imply dislike, and it’s best not to take it personally.

Aurornis

an hour ago

> I don’t dislike Rust

As someone who really enjoys seeing all of the different programming languages and their approaches to different problems, it’s becoming obnoxious to hear the constant battles among people who think programming languages need to become part of your identity. The endless battles of superiority and tit-for-tat responses feel petty and distracting.

It’s refreshing to get back to people who just want to try different things and experiment without making everything into a battle where each side scores points against the other.

pizlonator

an hour ago

Memory safety can be defined in more than one way, so it’s super worthwhile to figure out how to define it and what meets the definition and what doesn’t.

We should do more of that as a community. It’s important stuff. Ima do my part so you’ll likely see me poke at how Fil-C does a thing that Rust doesn’t do. If you read my arguments unemotionally, I think you’ll get a deeper appreciation for Fil-C, Rust, and memory safe language design generally.

What we shouldn’t do is reduce the discussion to claiming in a blog post that so-and-so “doesn’t like” such-and-such.

yosefk

44 minutes ago

I think Fil-C is awesome and I very much like the style in which you present your work as well. (Hope I'm not being too emotional here!)

FWIW, I can see where TFA is coming from and I had the urge to write something "in defense of Rust" after listening to / reading some of your recent comments. I think your sister comment about the limits to Rust memory safety being real as much as the limits to Fil-C's performance and practicality is spot on, but the feeling (those emotions again) one gets from much of your commentary is that you downplay the latter quite a bit.

I think the real argument for Fil-C is that you aren't going to "rewrite in Rust" but you can often realistically recompile in Fil-C and pay the performance penalty; the fact that that Rust rewrite could also end up less safe is I think should be argued mainly from the AI angle where the only practical way is an LLM based rewrite but that would have a load of unsafe - way more than a "proper" rewrite - to be practical and the recent $100K Bun rewrite is a testament to that.

(I would love nothing more than for Fil-C to become as widely used as it practically can, since no other approach does nearly as much to secure C / C++ code and a lot of said code is out there; and security aside, it could prevent a lot of data corruption that most major C++ programs gift to their users.)

leni536

9 hours ago

Fil-C is basically an alternate ABI and libc runtime. Otherwise it is not tied to C and I see no reason it couldn't be targeted by Rust or Zig.

sanxiyn

3 hours ago

I think Fil-C ABI implemented by all of C, Rust, and Zig is the future. But that would need some sort of stability for interoperation, and stabilizing ABI takes time: Rust still doesn't have one (while Swift spent enormous amount of effort to have one). Experimental implementation is probably still worth doing.

yjftsjthsd-h

8 hours ago

It currently is at least associated with C in the sense that it takes C code as an input. But yes, I would be very happy to see its approach applied to more languages.

kmeisthax

8 hours ago

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 stuck with languages made prior to Java - which in practice meant just C - or the one language everyone tried to staple every new programming paradigm onto, C++.

In fact, part of why C++ became such an untameable beast of a language is because it became load-bearing for non-GC projects. Anything not in the ISO C++ standard was, in practice, something you just couldn't do in native code. Oh, of course you can't introspect structs in a compiled language, of course macros are useless and unhygenic, of course templates have to be monomorphized and bloat your binary size. And so the pressure for C++ to be an all-singing, all-dancing, all-dressed language grew.

Rust's big story is memory safety, but the more Rust I wrote, the more I realized that the memory safety is only part of the picture. Rust has a very nicely curated selection of features that allows the language to remain understandable despite the sophistication of the compiler. Memory safety is a selling point, sure, but it is also the lubricant that makes working with those features pleasant.

If you want a real complaint about Rust, it's that some features are oversimplified in ways that make certain scenarios harder and make some features way more "magic" than they should be. Have you ever tried writing Futures code without making use of the async keyword? It's nearly impossible, for several reasons; the main being that Rust's type systems cannot express self-referential borrows. This also makes returning a reference to something in an Rc or RefCell you own more difficult[0]. And there are numerous other states memory can be in that are hidden from Rust's type system. Rust can't even represent a real destructor fn. Dropping a value multiple times, or using it after it's been dropped, is explicitly forbidden; but Drop impls still can't take values out of themselves because Rust doesn't have an "owned reference" - i.e. memory you can take values from but can't deallocate.

But none of this compromises memory safety - it just makes certain things harder than they should be.

[0] Strictly speaking, there's an owning_ref crate that manages this; stdlib is also working on a "mapped mutex guard" type that would do the same thing without a dependency.

manithree

8 hours ago

> I was not aware of the antagonism from the Zig .. people to Rust, but it makes absolutely no sense.

Lol, have you ever watch Andrew Kelley speak? I've only seen 3 or 4 talks he's given, but he frequently makes not-so-subtle digs towards Rust. Zig's home page prominently displays "Focus on debugging your application rather than debugging your programming language knowledge" which sounds horrible to me, but is clearly an anti-rust statement. Competition is good, and Rust and Zig cater to different people, but when the primary personality behind Zig is leads the way, it shouldn't be a surprise that some tribalism-influenced followers lean hard into it.

chaz72

12 minutes ago

“Focus on debugging your application rather than debugging your programming language knowledge” to me sounds like it is directed primarily at C and C++., it’s a much more direct translation.

KPGv2

4 minutes ago

> "Focus on debugging your application rather than debugging your programming language knowledge"

This sounds like it's made by someone who fears discovering they don't know something more than discovering they made a mistake.

I am the reverse: I would prefer to debug my programming language knowledge over debugging my application. I hate debugging applications specifically because making a mistake is SO much worse to me than discovering there is a fact I didn't know yet.

And, of course, debugging your application is discovering there are unknown unknowns in deployment, the worst of the quadrant of knowns and unknowns.

estebank

8 hours ago

The thing that has changed is that there are spaces where security is being taken more seriously, and C's memory unsafety became a deal breaker there. I do think that providing a mechanism to run existing C software that isn't performance sensitive in a way that mitigates its limitations is very worthwhile.

I think the "static analysis" and the "runtime checks" approaches are complementary, not in opposition, making any noise around having to choose one or the other moot.

davidgay

8 hours ago

> Like, of course you can prevent all memory safety errors by using a garbage collector. That has been known since the 90s.

No, it's been known since GC was invented.

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

Having done all of my research on memory safety after Java was released, I find this statement a little exaggerated... (e.g., https://barnowl.org/research/pubs/98-pldi-regions.pdf, https://barnowl.org/research/pubs/07-hotos-linux.pdf)

saghm

8 hours ago

Yeah, my understanding (which is not from first-hand experience) is that a huge part of the origin of Rust was that there was a lot of research in memory safety techniques that had been going mostly unused by mainstream languages, so the idea was "Why not try to make a language using some of it?"

ssokolow

4 hours ago

*nod* They're from before I started bookmarking rigorously, but in the early days of Rust, there was a sentiment in the blog posts from the people developing it that the point of Rust was "to give good ideas a second chance" and that Rust was intentionally boring and un-innovative.

zbentley

7 hours ago

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

kmeisthax

an hour ago

You forgot the other three thirds GC doesn't handle: in-bounds writes to memory currently in use by another processor. One of the basic ideas behind Rust's memory safety story is that eliminating race conditions necessarily requires a good memory safety story, especially regarding temporal memory safety.

...of course, the managed code languages had an answer to this: put a lock on every managed object you allocate.

But they didn't actually make you acquire the lock - it only comes into play if you declare a method to be synchronized or if you acquire the lock externally. Which is a problem because most of these languages ALSO are designed to act as a security boundary for untrusted code - something Rust doesn't even offer! Which means even though the language doesn't enforce race-freedom, it has to enforce enough of it anyway to keep racy code from corrupting the language heap (which is now security-sensitive). No other language (not even Rust) does this - if you write a race in unsafe Rust, it is actually UB, but in Java you actually still have some guarantees about what your program will do.

Actually, let me throw a bone to the Zig people: Unsafe Rust actually imposes more UB than C does, and it is more difficult to write sound unsafe Rust. If you're writing FFI code[0], you're probably fine. But if you're writing new memory abstractions, you're probably going to run into Rust's requirement that all mutable references be exclusive. If this ever fails, your program is already in UB and all bets are off. Morally speaking, Rust sprinkles the equivalent of C's restrict keyword over every &mut in your program. In fact, this UB is so strict that the Rustc people have had to turn this on and off multiple times in the past because LLVM would miscompile sound / safe Rust code if it was told about the aliasing restriction.

In practice, however, this doesn't really matter. Rust has all sorts of soundness holes nobody would ever trigger by accident. There's a long standing trait-handling bug that lets you confuse memory types in safe Rust; and any OS that exposes process memory as a writable file lets you do the same thing. In Java, at least the former would be a CVSS 10.0 security vulnerability, but it doesn't matter in Rust, because safe Rust is not a security boundary. It's a set of tools to keep you from shooting yourself in the foot. And, in practice, Rust does a pretty good job of that.

[0] Or more generally, unsafe code where you have two unsafe pieces that you have to guarantee are used together. For example, if you have a C-style callback API that takes a function pointer and a data pointer, and then calls the function with the data, you have to use Box::into_raw() and Box::from_raw() to maintain ownership over the data. from_raw is unsafe, but all the obvious ways of using it are sound.

tptacek

4 hours ago

Once again, people are grappling with an axiomatic definition of "memory safety" and avoiding the fact that it's a term of art with a very specific meaning: comprehensive protection from The Memory Corruption Vulnerabilities, which include overflows, the lifecycle vulnerabilities like UAF and type confusion, and uninitialized variables.

To the extent Fil-C and Rust both address these vulnerabilities, and don't include design features that in any practical way admit them, they're memory safe. What always feels like is missing from these kinds of analyses is that there are lots of memory-safe programming environments. Almost every Java, Python, Ruby, Javascript, and Go program is memory safe, in the real meaning of the term.

The competition to lock in and promote adoption of the "most" memory-safe language is a category error... unless you do what every language-war argument does, and redefine the term.

blurgrin

8 hours ago

> Our historical data for C and C++ shows a density of closer to 1,000 memory safety vulnerabilities per MLOC.

Unfortunately, I've never seen a version of this data targeting modern C++ (>=11, with smart pointers, already 15 years old).

bloaf

8 hours ago

As long as rowhammer is still out there, aint none of your memory safe. Fixing rowhammer is the memory safety absolutism I want to hear more about.

inigyou

8 hours ago

ECC memory fixes rowhammer. And random bitflips. Everyone should have ECC memory, but Intel disagrees because they are greedy.

My system sometimes detects a few bitflips per day.

userbinator

an hour ago

My system sometimes detects a few bitflips per day.

Perhaps you should replace your RAM, or it's a sign that you have a massive source of radiation lurking somewhere?

bloaf

8 hours ago

There have been demonstrated rowhammer-based ECC bypass attacks. ECC mitigates but does not fix rowhammer.

enbugger

8 hours ago

Funny to see how Rust devs start to make up new labels when it turns out some language performs better then their language.

rowanG077

8 hours ago

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.

muvlon

7 hours ago

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.

rowanG077

7 hours ago

I agree. Rust does not go far enough imo. Still catching most memory problems at compile time is still much superior.

Animats

6 hours ago

The important thing from a performance standpoint is to be able to hoist bounds checks out of inner loops. This avoids a check on each iteration. Languages which have constructs such as

   for foo in bartab {}
can usually hoist checks out of inner loops almost for free.

I used to argue with the C++ committee about to when it's OK to catch an error early. If you write

    #define LEN 1000
    int tab[LEN];
    for (auto p = tab; p++; p <= LEN) { *p = 0; }
that's a buffer overflow. The question is, is the compiler allowed to generate checking code which will abort the program before entering the loop? Or does it have to execute all the iterations up to the subscript error?

I argued that it's legit to catch an error at the point it becomes inevitable. This leads to bikeshedding objections: "But what if a signal interrupts the loop before it runs off the end". That's why you need a language where undefined behavior has been nailed down to do this optimization properly.

ssokolow

4 hours ago

True... but if you go by what you're most likely to encounter, I'd argue the biggest unaddressed flaw in Rust is the lack of a current, maintained analogue to tools like Rustig! and findpanics, which would analyze your compiled, optimized binary and report any code paths which lead to panics.

I don't like having to contort my code to fit each unit of work into the API of `std::panic::catch_unwind` so I can responsibily distrust the transitive dependencies beyond the reach of my Clippy lints.

antonvs

4 hours ago

If you really believed that, you’d be programming in something like ATS, Idris, or Spark Ada. The reason that (I’m guessing) you don’t is that it takes a lot more effort to write general purpose code in those languages.

Rust is pretty much state of the art in this area for general-purpose, non-GC programming languages, and people still complain about the effort involved in writing memory-safe code with it.

If you really want complete memory safety, use a garbage collected language.

wat10000

8 hours ago

It’s the exact opposite for me. Catching it at compile time is great, but the most important thing by far is that it’s caught somewhere instead of creating a security issue.

quotemstr

3 hours ago

> And I also think it is totally fine to use Rust even if you could use a GC language like Go or Fil-C.

The problem with choosing Rust over a GC language like the above (or a good one, like OCaml) isn't that it's not "fine" to use Rust, but that manual memory management is an inefficient use of developer time. That's an issue for the developer, and one he inflicts on himself, not an issue for the end-user. Both Rust and (safe) GC languages provide memory safety, after all.

Shorel

7 hours ago

How is this better than the GC in D?

hmry

8 hours ago

It's annoying how many comment sections online are now just Rust vs Fil-C / Zig flamewars, and the creators of those languages are deliberately fanning the flames.

I also feel there's a second dimension to the politics, where Rust is the "woke" language and C / Zig / Odin are now the "anti-woke" languages. At least, going by their most vocal online communities.

I'm sure offline there's still engineering decisions being made (I hope).

estebank

8 hours ago

Could you point me at the members of the Rust project doing so? I'd want to have a word with them (I'm a member of t-compiler).

IshKebab

8 hours ago

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 but to use C. If you can use Rust you also get:

* Compile time memory safety (Fil-C / CHERI are run-time).

* A modern functional-style type system (helps prevent logic bugs).

* Tree ownership (helps prevent logic bugs)

* Sane toolchain (helps prevent hair loss).

* Easy dependencies.

* Vastly more things checked at compile time.

Also I find it amusing how this post claimed it wasn't about Rust and then spent the whole time talking about it.

Rust is great. I don't think that really needs to be debated any more.

hkalbasi

8 hours ago

Someone should fire up an AI and create Fil-Rust by connecting the Fil-C llvm backend to rustc, ending this flamewar. I guess it is well within the capabilities of a Fable class model, even if the driver doesn't have experience in compiler development.

yjftsjthsd-h

8 hours ago

> 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 problems. Rust has less, at the cost of breaking the ecosystem. Fil-C has even less, and breaks the ecosystem. Why is the place Rust stops now suddenly good enough?

afdbcreid

7 hours ago

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.

saghm

7 hours ago

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

yjftsjthsd-h

7 hours ago

> To apply your logically consistently, you'd also have to throw out Go[1], Java[2], C#[3], and plenty of other languages

Yes? Is Fil-C not obviously safer than those?

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

It seems like "can switch on unsafe whenever" vs "has no escape hatches" is an obvious line in the sand and a very useful distinction with real world implications.

> Maybe Fil-C will be able to provide as large benefits to running C safely in production like its proponents claim in the future

Why in the future? It exists and can be used right now. In fact, much of its appeal is being able to use existing code without needing to port to a new language; pragmatism seems like an argument in its favor.

saghm

6 hours ago

> It seems like "can switch on unsafe whenever" vs "has no escape hatches" is an obvious line in the sand and a very useful distinction with real world implications.

Sure, although to me, the obvious distinction is that one of them gives you more flexibility (since neither Go nor Fil-C is at risk for someone accidentally writing unsafe code).

> Why in the future? It exists and can be used right now. In fact, much of its appeal is being able to use existing code without needing to port to a new language; pragmatism seems like an argument in its favor.

Because "can be used" is not a statement of present use, but potential future use. You seem to be missing my entire point about theory versus practice; plenty of things might be useful in theory but don't ever get widely used in practice (e.g. because they're not user friendly enough, or they require constraints that aren't acceptable to most users). I'm not making a claim about whether Fil-C has these issues, but making a claim that as yet, there does not seem to be widespread usage of it. If it's really so seamless to drop-in as a replacement for C that's strictly safer without downsides that make it unappealing to most people, I'd expect that to change. If that doesn't happen, I'd consider that evidence that it's not actually providing meaningful safety in the real world (in the https://xkcd.com/1312/ sense)

ssokolow

4 hours ago

Bearing in mind that "there should be no room for a language above assembly and below Rust" (i.e. something like what C or C++ is to Python, Java, C#, etc.) was an intentional design decision for Rust which resulted in `unsafe`.

saghm

3 hours ago

It seems very common for people to misunderstand that having strict accounting for unsafe is part of why Rust does what it does so well rather than just disallowing unsafe outright. I think it's easily confused because being able to choose "how much unsafe do I want to allow, and where do I want to allow it" allows the choice "I don't want any in my own code" to be easily enforced, and that's by far the most common amount needed, but it's certainly not the only useful one. Being able to handle the edge cases without having to give up the checks entirely is a huge improvement over most alternatives.

conradludgate

8 hours ago

I don't immediately follow how Rust breaks the ecosystem. Rust has very good C FFI support and fully supports the default C ABI. It just requires unsafe bindings.

yjftsjthsd-h

8 hours ago

In practice, my Python packages broke when they dropped the C crypto support. I'm sure it was theoretically possible to avoid that, but they didn't. And that's ecosystem breakage. (And if the fix is that I the user must go manually install or worse compile extra things, that's definitely ecosystem breakage)

jvuygbbkuurx

8 hours ago

Unsafe doesn't disable the borrow checker

yjftsjthsd-h

8 hours ago

I don't follow how that impacts the argument. Rust with unsafe is certainly safer than C, but I don't see that that changes anything at hand.

dadrian

3 hours ago

There are only three programs where memory safety matters: HTTP server, browsers, and operating systems. In practice, really just browsers and operating systems. Memory safety schemes that don't work for those systems are primarily cosplaying if their goal is safety.

bastawhiz

3 hours ago

That's really just not true. Easy counterexample: any codec should be written in a memory safe language.

Really anything that deals with untrusted input should be memory safe. Your TLS library. A load balancer. Your password manager.

procflora

3 hours ago

And how about safety of life systems? They use computers in chemical plants and on planes, you know lol. Integer overflows have a body count!

xboxnolifes

3 hours ago

I kinda wish we didn't push with the term "memory safe", and instead had a push with "correct". If your software isn't memory safe, it does not work correctly. We should aim to have software that works correctly.

userbinator

an hour ago

As long as the corporates continue to erode personal freedom with their hostilities, I consider these "memory safety absolutists" authoritarians, because humans will always make mistakes one way or another, and that should be the natural state of things, as attempts to banish all human error under some guise of "safety" will eventually lead to a horrible dystopia.

"Freedom is not worth having if it does not include the freedom to make mistakes."

"They who can give up essential liberty to obtain a little temporary safety, deserve neither liberty nor safety."

userbinator

8 minutes ago

Face the truth and see the reality of what you're making: better nooses to put around everyone's necks.