To introduce Fil-C, since it’s quite new:
Fil-C is a fanatically compatible memory-safe implementation of C and C++. Lots of software compiles and runs with Fil-C with zero or minimal changes. All memory safety errors are caught as Fil-C panics. Fil-C achieves this using a combination of concurrent garbage collection and invisible capabilities (InvisiCaps). Every possibly-unsafe C and C++ operation is checked. Fil-C has no
unsafestatement and only limited FFI to unsafe code.
- Memory Safety: Advanced runtime checks to prevent exploitable memory safety errors. Unlike other approaches to increasing the safety of C, Fil-C achieves complete memory safety with zero escape hatches.
- C and C++ Compatibility: Your C or C++ software most likely compiles and runs in Fil-C with zero changes. Many open source programs, including CPython, OpenSSH, GNU Emacs, and Wayland work great in Fil-C. Even advanced features like threads, atomics, exceptions, signal handling,
longjmp/setjmp, and shared memory (mmapstyle or Sys-V style) work. It’s possible to run a totally memory safe Linux userland, including GUI, with Fil-C.- Modern Tooling: Compiler is based on a recent version of clang (20.1.8), supports all clang extensions, most GCC extensions, and works with existing C/C++ build systems (make, autotools, cmake, meson, etc).
I managed to figure out how to build Fil-C’s from source as a Nix derivation: see my Fil-C flake. It has several working memory safe output packages: bash, coreutils, sed/grep/awk, OpenSSL, SQLite, ncurses, Nethack, tmux, Perl, Tcl, nano, and even Kitty-DOOM which runs smoothly even over SSH, after I patched John Carmack’s custom allocator from 1993 so it uses 8 byte alignments, since Fil-C crashes immediately if you even try to read a misaligned pointer.
If you cachix use filc you can immediately try it out, otherwise building the toolchain will take around an hour (YMMV).
mbrock@igloo:~$ nix run github:mbrock/filnix#sqlite
SQLite version 3.48.0 2025-01-14 11:05:00
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite>
Here compiling an awful C program in the dev shell:
mbrock@igloo:~$ nix develop github:mbrock/filnix
Fil-C 0.673 clang version 20.1.8
clang /nix/store/y3g4zb1bj9xbzk90qi24zal9kvk66wr1-filc-cc-wrapper-/bin/clang
pizlo /nix/store/kly8ckcgy2qp1l8bh827bklqkcsc4ffx-libpizlo-git
glibc /nix/store/h2i130kriv09kddm0mqq6w858hxxyzqx-libmojo-2.40
libc++ /nix/store/nfwkr16gjss8nhji4p2dn2baqbmazr0i-filc-libcxx-git
(nix:filc-dev-env) mbrock@igloo:~$ clang -v
Fil-C 0.673 clang version 20.1.8
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /nix/store/41qxs3gbk4gvf2pdwg0q7k9lzfha8jch-filc0-git/bin
Build config: +assertions
Found candidate GCC installation: /nix/store/kxm6wzygmj1439ylqdgyl9zqjgf10dy7-gcc-14.3.0/lib/gcc/x86_64-unknown-linux-gnu/14.3.0
Selected GCC installation: /nix/store/kxm6wzygmj1439ylqdgyl9zqjgf10dy7-gcc-14.3.0/lib/gcc/x86_64-unknown-linux-gnu/14.3.0
Candidate multilib: .;@m64
Selected multilib: .;@m64
[...some ugly harmless warnings I should fix...]
(nix:filc-dev-env) mbrock@igloo:~$ cat > foo.c
void main () { int foo[100]; foo[101] = 0; }
(nix:filc-dev-env) mbrock@igloo:~$ clang foo.c -o foo
[...warnings, complaints...]
2 warnings generated.
(nix:filc-dev-env) mbrock@igloo:~$ ./foo
filc safety error: cannot write pointer with ptr >= upper.
pointer: stack_optimized(offset=404,size=400)
expected 4 writable bytes.
semantic origin:
<somewhere>: main
check scheduled at:
<somewhere>: main
../sysdeps/nptl/libc_start_call_main.h:58:16: __libc_start_call_main
../csu/libc-start.c:161:3: __libc_start_main
<runtime>: start_program
[1662962] filc panic: thwarted a futile attempt to violate memory safety.
Trace/breakpoint trap (core dumped)
If you are familiar with Nixpkgs cross compilation and toolchain bootstrapping, I hope this catches your attention! Because I really am not familiar with it, and there’s something about the recursive fixpoints and whatnot that seems to just completely fry my brain.
But I think we should aim to integrate Fil-C as a distinct ABI with a platform called x86_64-unknown-linux-filc or something, and work towards a pkgsCross.filc. Doesn’t that make sense?
Wouldn’t this be quite an extraordinary benefit?
It seems that for example NixOS could quite easily be the first distribution beyond Fil-C’s own Linux-From-Scratch distribution to offer completely memory safe versions of OpenSSH, bash, etc. Other distributions will find it more difficult to integrate two completely incompatible ABIs in the same FHS sysroot. So Nix truly shines here.
Fil-C does require patching many programs. I wanted to know how the patches were actually done, so I used a bunch of Claude subagents to simultaneously read through all of the combined patches that I extracted from the upstream ports collection, then summarized those reports into Fil-C Porting Patterns: A Technical Analysis, where you can learn about the changes needed to build e.g. Perl, Python, parts of systemd, and so on.
I came across Fil-C and when I noticed the peculiar difficulties around the ABI mismatch, and their different approaches to coping with it, it seemed like a match made in heaven for Nix, and I thought somebody has to get started on it, so I did. I’d love to hear what you all think. And if anybody wants to try it, fork it, fix it, merge it, point me to secret good resources, or even hop on a call and exchange some knowledge, that’d all be awesome.
(I also hang out on the Fil-C Discord where Filip Pizlo is also responsive and helpful if anyone wants to chat.)