A simplified model of Fil-C (corsix.org)

by aw1621107 136 comments 208 points
Read article View on HN

136 comments

[−] hsaliak 27d ago
I made https://github.com/hsaliak/filc-bazel-template bazel target for people who may want to use these two together to make hermetic builds with it.
[−] avadodin 27d ago
It could be a fun exercise to add invisicaps to something like chibicc/slimcc.

It leaves room for experimentation with reference counting and variations on the invisible capability system which could provide memory savings at the expense of some extra indirection.

[−] quotemstr 27d ago
Fil-C is not memory safe under data races. The capability and pointer values tear under assignment, which means that if you get the wrong thread interleaving, you can access an object through a wrong pointer, causing arbitrary program misbehavior.

This limitation would be fine if Fil-C proponents (including its author) didn't try to shout down anyone pointing out this limitation.

[−] cyberax 26d ago
It is, because it uses atomic ops. It is one of the main sources of overhead, unfortunately.
[−] quotemstr 24d ago
No, it is not, as has been covered extensively. Atomic operations in Fil-C tear.

  // global
  foo* p = initial();

  // Thread 1  
  p = something_else();

  // Thread 2
  p[attacker_controlled_index] = value;
  
There are interleavings in which p has the value of initial() but the capability of something_else(), or vice versa, which means that an attacker who can perform memory access with an offset into p can access the wrong object through p. This is a violation of memory safety as commonly understood.

But sure, you can just bleat Pizlo's claims of safety instead of engaging with the substance of his runtime model. The point is that Fil-C does not provide full memory safety, and cannot until it updates pointer and capability atomically, and it can't do that without paying much more for general memory access than it does today.

[−] dfawcus 24d ago
I understand it works _if_ you declare p as being volatile, or explicitly tag it as _Atomic.

i.e., either of these forms:

    foo * volatile p;
    foo * _Atomic p;
Or at least it did when I was performing a similar experiment.
[−] quotemstr 23d ago
Sure. Or use a mutex, or use any of the other zillion concurrency-safe constructs out there. Still, Fil-C is memory-safe only up to data-race freedom. This bound is still a plenty useful, but Pizlo shouldn't be going around saying Fil-C is memory-safe in general when it ain't.
[−] teo_zero 27d ago
Could anyone understand what this sentence means?

> Upon freeing an unreachable AllocationRecord, call filc_free on it.

I think the intention was to say: before freeing an unreachable AR, free the memory pointed to by its visible_bytes and invisible_bytes fields.

[−] whatsakandr 27d ago
Fil-C is one of the most underrated projects I've ever seen. All this "rewrite it in rust for safety" just sounds stupid when you can compile your C program completely memory safe.
[−] tialaramex 27d ago
So, a few things, some of which others have touched on:

1. Fil-C is slower and bigger. Noticeably so. If you were OK with slower and bigger then the rewrite you should have considered wasn't to Rust in the last ten years but to Java or C# much earlier. That doesn't invalidate Fil'C's existence, but I want to point that out.

2. You're still writing C. If the program is finished or just occasionally doing a little bit of maintenance that's fine. I wrote C for most of my career, it's not a miserable language, and you are avoiding a rewrite. But if you're writing much new code Rust is just so much nicer. I stopped writing any C when I learned Rust.

3. This is runtime safety and you might need more. Rust gives you a bit more, often you can express at compile time things Fil-C would only have checked at runtime, but you might need everything and languages like WUFFS deliver that. WUFFS doesn't have runtime checks. It has proved to its satisfaction during compilation that your code is safe, so it can be executed at runtime in absolute safety. Your code might be wrong. Maybe your WUFFS GIF flipper actually makes frog GIFs purple instead of flipping them. But it can't crash, or execute x86 machine code hidden in the GIF, or whatever, that's the whole point.

[−] whatsakandr 27d ago
Yes it's slower, but it works. It's being built by one single dad who focused on compatibility before speed.

I'm not convinced that tying the lifetimes into the type system is the correct way to do memory management. I've read too many articles of people being forced into refactoring the entire codebase to implement a feature.

[−] saghm 27d ago
You initially said that rewriting in Rust "seems stupid" based on what Fil-C provides, and someone pointed out technical reasons why it still might be useful in some circumstances. It's great that a single dad is able to build Fil-C on his own, and you're certainly entitled to the opinion that you don't like the idea of lifetimes in the type system, but it's genuinely hard to tell if there's a specific technical point you're trying to make or you dislike Rust so much that you're interpreting someone disagreeing with you about whether Fil-C obviates it as somehow being a personal attack on the author.
[−] achierius 27d ago
I can tell you that it's not that he's setting aside speed -- the fact that it's as fast as it is is an achievement. But there is a degree of unavoidable overhead -- IIRC his goal is to get it down to 20-30% for most workloads, but beyond that you're running into the realities of runtime bounds checks, materializing the flight ptrs, etc.
[−] gmueckl 27d ago
20% to 30% slower would be amazing for all the extra runtime work that is required in my limited understanding. This would be good enough for a whole lot of serious applications.
[−] brucehoult 27d ago

> built by one single dad

Not some random dad, but a GC expert and former leader of the JavaScript VM team at Apple.

[−] fooker 27d ago

> tying the lifetimes into the type system is the correct way to do memory management.

Type systems used to be THE sexy PL research topic for about twenty years or so, so all the programming languages innovation has been about doing everything with type systems.

[−] miki123211 27d ago
I think the core advantage of Fil-C (that Java and C# don't have) is that it moves the decision between security and performance to the user, not the programmer.

Imagine you're writing a library for, let's say, astronomical and orbital calculations. Writing it in Java means that it's always going to be slow. If you write it in C, NASA may decide to compile it with a normal compiler (because it won't ever be exposed to malicious inputs), while an astronomy website operator may use the Fil-C version for the extra security, at the cost of having to use slightly more computing resources, which are abundant on Earth.

This doesn't negate the advantages of Rust, which lets you get speed and performance at the same time.

[−] cxr 27d ago

> Fil-C is slower and bigger

It's not any slower or (proportionally) bigger compared to the experience you would have had 20 years ago running all sorts of utilities that happen to be the best candidates for Fil-C, and people got along just fine. How fast do ls and mkdir need to be?

[−] IshKebab 27d ago
I think the problem with this logic is that it views language performance on an absolute scale, whereas people actually care about it on a relative scale compared to how fast it could be.

If you tell your boss "We spent $1m on servers this month and that's as cheap as its possible to be" he'll be like "ok fine". If you say "We spent $1m on servers this month but if we just disable this compiler security flag it could be $500k." ... you can guess what will happen.

(Counterpoint though: people use Python.)

But counter-counterpoint: Rust does so much more than preventing runtime memory errors. Even if Fil-C had no overhead (or I was using CHERI) I would still use Rust.

[−] adgjlsfhk1 27d ago
buy that logic (which I somewhat agree with), we should be rewriting all of these tools in C# or some similar native gc'd language. C and rust both take on a ton of complexity to squeeze out the last 2x of speed, but if we no longer care about that, we should drop C in a heartbeat
[−] baranul 26d ago

> the rewrite you should have considered wasn't to Rust in the last ten years but to Java or C# much earlier.

That doesn't quite make sense. The point of Fil-C, is to not have to rewrite in any other language, because it's still C. But now, there are safety benefits, though there is a trade-off with size and speed. Even in that context, size and speed, it can be very acceptable to many people and Fil-C will improve in that department as time goes on.

> Rust is just so much nicer.

That clearly is your own personal opinion that not everyone shares. There are many people who do not like Rust.

[−] blub 27d ago
All of these didn’t prevent Go from competing with Rust and I’m guessing that Fil-C will be the better choice in some cases.

Rust has managed to establish itself as a player, but it’s only the best choice for a limited amount of projects, like some (but not all) browser code or kernel code. Go, C++, C with Fil-C) have solid advantages of their own.

To name two:

* idiomatic code is easier to write in any of these languages compared to Rust, because one can shortcut thinking about ownership. Rust idiomatic code requires it.

* less effort needed to protect from supply-chain attacks

[−] up2isomorphism 27d ago
The fundamental question is that if an address is “safe” is a runtime thing, which in some cases you can decide it in compile time but not always. To force that during coding is just handicapping oneself to be “safe”. Which you can do the same in C (or mostly any language if you want it)
[−] gnabgib 27d ago
Not here, lots of discussion:

Fil-Qt: A Qt Base build with Fil-C experience (143 points, 3 months ago, 134 comments) https://news.ycombinator.com/item?id=46646080

Linux Sandboxes and Fil-C (343 points, 4 months ago, 156 comments) https://news.ycombinator.com/item?id=46259064

Ported freetype, fontconfig, harfbuzz, and graphite to Fil-C (67 points, 5 months ago, 56 comments) https://news.ycombinator.com/item?id=46090009

A Note on Fil-C (241 points, 5 months ago, 210 comments) https://news.ycombinator.com/item?id=45842494

Notes by djb on using Fil-C (365 points, 6 months ago, 246 comments) https://news.ycombinator.com/item?id=45788040

Fil-C: A memory-safe C implementation (283 points, 6 months ago, 135 comments) https://news.ycombinator.com/item?id=45735877

Fil's Unbelievable Garbage Collector (603 points, 7 months ago, 281 comments) https://news.ycombinator.com/item?id=45133938

[−] omcnoe 27d ago
The issue with Fil-C is that it's runtime memory safety. You can still write memory-unsafe code, just now it is guaranteed to crash rather than being a potential vulnerability.

Guaranteed memory safety at compile time is clearly the better approach when you care about programs that are both functionally correct and memory safe. If I'm writing something that takes untrusted user input like a web API memory safety issues still end up as denial-of-service vulns. That's better, but it's still not great.

Not to disparage the Fil-C work, but the runtime approach has limitations.

[−] pizlonator 27d ago
Thanks for the love man!

> "rewrite it in rust for safety" just sounds stupid

To be fair, Fil-C is quite a bit slower than Rust, and uses more memory.

On the other hand, Fil-C supports safe dynamic linking and is strictly safer than Rust.

It's a trade off, so do what you feel

[−] dataflow 27d ago

> Fil-C is one of the most unrated projects I've ever seen

When's the last time you told a C/C++ programmer you could add a garbage collector to their program, and saw their eyes light up?

[−] kbolino 27d ago
Fil-C has two major downsides: it slows programs down and it doesn't interoperate with non-Fil-C code, not even libc. That second problem complicates using it on systems other than Linux (even BSDs and macOS) and integrating it with other safe languages.
[−] rvz 27d ago
It makes more sense for new software to be written in Rust, rather than a full rewrite of existing C/C++ software to Rust in the same codebase.

Fil-C just does the job with existing software in C or C++ without an expensive and bug riddled re-write and serves as a quick protection layer against the common memory corruption bugs found in those languages.

[−] GaggiX 27d ago
Fil-C is much slower, no free lunch, if you want the language to be fast and memory safe you need to add restrictions to allow proper static analysis of the code.
[−] krackers 24d ago
I don't think fil-c is a drop in C replacement, there are things you can do in C such as certain types of pointer abuse that fil-c prohibits (e.g. cast pointer to int, then back). It's probably easier to port an existing C project to Fil-C than to rewrite it entirely though.
[−] Panzerschrek 27d ago
Fil-C is only good if one needs to recompile an existing C program and gain extra safety without caring much about result performance. And even in such case I doubt it's useful, since existing code should be already well-tested and (almost) bug-free.

If new code needs to be written, there is no reason to use Fil-C, since better languages with build-in security mechanisms exist.

[−] DetroitThrow 27d ago
I write C++ for my job everyday, and claiming Fil-C does the same thing as Rust (and that people who do rewrites in Rust are stupid) sounds braindead.

I love Fil-C. It's underrated. Not the same niche as Rust or Ada.

[−] adamnemecek 27d ago
It really doesn't, Rust is a better language.
[−] vzaliva 27d ago
This is yet another variant of the "fat pointers" technique, which has been implemented and rejected many times due to either insufficient security guarantees, inability to cross non-fat ABI boundaries, or the overhead it introduces.