Fil-C on Nix

Filnix wiki

Releases

BIND 9.20.30 · Fil-C 0.686 · October 2026

Fil-BIND: BIND 9 in memory‑safe C

BIND 9.20.30 and every library it uses, compiled with Fil-C. An out-of-bounds access or a use after free stops named instead of corrupting its memory. It is a research build: interesting to evaluate, not a promise.

The idea in 40 seconds. Made with Manim (source).

Try it

The container image is laid out like ISC's internetsystemsconsortium/bind9: configuration in /etc/bind, the working directory /var/cache/bind, and named running as the bind user with the same entrypoint. A configuration that works there should work here.

docker run --rm -p 53:53/udp -p 53:53/tcp \
  -v $PWD/named.conf:/etc/bind/named.conf ghcr.io/mbrock/filnix-bind:9.20.30

The release also has a relocatable tarball, bind-9.20.30-filc-x86_64-linux.tar.xz, that runs on any x86-64 Linux, glibc or musl, without Nix: it carries its own runtime and libraries. With Nix, nix build github:mbrock/filnix#bind-dist, or #legacyPackages.x86_64-linux.pkgsFilc.bind for the package itself.

Why now

ISC has published 37 BIND 9.20 advisories since March 2026, at a rate it attributes to vulnerability hunting with language models. The 21 October release, 9.20.30, fixes CVE-2026-94300: replayed GSS-TSIG messages corrupt memory, with “potential remote code execution”.

Most of the 37 are not memory errors, and Fil-C does nothing about them. Eleven are assertion failures, where named already stops itself, twelve are CPU or memory exhaustion, and eight are logic errors such as cache poisoning. Six are memory-safety bugs, the kind Fil-C is built to catch:

AdvisoryFixed inBugWhat we expect under Fil-C
CVE-2026-943009.20.30Replayed GSS-TSIG messages corrupt memory; potential RCEThe corrupting access stops the process
CVE-2026-35939.20.23Heap use after free in DNS-over-HTTPSThe freed object cannot be read or written
CVE-2026-59479.20.23SIG(0) validation reads a discarded messageStops every time, instead of sometimes reading reused memory
CVE-2026-196629.20.29Use after free of a cached NOQNAME proofAlready a crash; now a defined one
CVE-2026-196669.20.29Use after free in the DNS64 pathAlready a crash; now a defined one
CVE-2026-35919.20.21Stack use after return lets an ACL mismatchUnclear: Fil-C keeps escaped locals alive, so the stale read may succeed

These are expectations from how Fil-C works, not reproductions: we have not replayed these exploits against Fil-BIND, and the build is 9.20.30, which has all of them fixed anyway. The point is the class of bug. When the next one is found, in BIND or in OpenSSL, libuv or nghttp2 underneath it, the likely worst case is a crash: a denial of service rather than a takeover.

How Fil-C stops these

Fil-C gives every pointer an invisible capability that records the object it points into. Each load and store is checked against it, so a write past the end of a buffer stops the program instead of landing in the neighbouring object. free marks the object freed, so a later access through any old pointer also stops, and a garbage collector keeps the memory from being reused while such pointers exist. OpenSSL, libuv, liburcu, nghttp2, libxml2 and the C library are compiled the same way, so the code underneath named is checked too.

BIND recycles some objects itself: its memory pools kept free lists of message names, rdatasets and network sockets, and a recycled object stays valid, so a use after returning it would go unseen. Fil-BIND turns those free lists off, as BIND's own AddressSanitizer builds do.

What it took

BIND itself compiled unchanged. Running it needed three things:

BIND's unit tests then pass, except three that replace a library function by defining it in the test program, which Fil-C's linking does not allow (findings). A signed zone served by one named and validated by another, AXFR, TSIG-signed updates and rndc all work, in plain Debian and Alpine containers as root and as an unprivileged user.

What it costs

Fil-C's checks and garbage collector cost a lot in a server that does little but move small packets through pointer-heavy data structures. We measured with dnsperf against a 20,000-name zone, on an 8-core cloud VM with the load generator on the same machine, comparing Nixpkgs' native BIND 9.20.29 with Fil-BIND:

nativeFil-BIND
Authoritative, 1 thread161,000 qps16,700 qps9.6× slower
Cached resolver answers, 1 thread166,000 qps17,600 qps9.4× slower
Authoritative, 4 threads416,000 qps31,800 qps13× slower
Cached resolver answers, 4 threads337,000 qps36,600 qps9.2× slower
Resident memory, resolver41 MB162 MB≈4×

So roughly ten times the CPU per query, and four times the memory. Seventeen thousand queries a second per core is far below what BIND can do, but well above what many internal, small-office or legacy servers ever see. We have not tuned anything yet.

A research artifact. This is not an ISC build and comes with no support or warranty. Fil-C turns memory corruption into a crash; it does not stop assertion failures, resource exhaustion or cache poisoning, so keep following ISC's advisories and upgrading. If you run an old BIND setup where a crash is better than a compromise and ten times the CPU is affordable, it may be worth evaluating. The build recipe is in mbrock/filnix.