Memory Safety: From Niche To Mainstream
For decades, memory safety flaws have fueled countless security incidents across the industry, undermining trust in technology and causing billions in losses. Traditional measures—such as code audits, fuzzing, and exploit mitigations—have helped but fallen short of stopping the problem, all while driving up costs.
Promising advances in hardware are emerging. Features such as ARM’s Memory Tagging Extension (MTE) which is equipped since Google Pixel-8 and the Capability Hardware Enhanced RISC Instructions (CHERI) architecture provide valuable, complementary defenses—especially for securing existing code.
New languages like Rust are also reshaping the way we think about systems programming by making memory safety a core design principle rather than an afterthought. They enforce strict ownership and borrowing rules at compile time, eliminating entire classes of vulnerabilities without sacrificing performance. By integrating these guarantees directly into the development process, such languages reduce the long-term maintenance and security burden, allowing teams to focus more on functionality and innovation instead of patching preventable flaws.
However, switching new hardware or language is not always feasible. Many organizations have large codebases written in languages like C and C++ that are critical to their operations. For these teams, the challenge is how to apply memory safety principles without a complete rewrite.
Fil-c: A Memory Safety Language Extension For C/C++
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. Basically, it's based on GC (garbage collection), yes, many people would think that GC has essential caveats to stop program while it is recycling memory, or increase the latency and break the realtime design. However, Fil-C uses a novel approach to GC that avoids these issues, allowing it to be used in real-time systems without introducing significant latency or pauses.
Now you may ask, what's the magic?
Invisicaps and Capability Model
Well, it's impossible to explain everything in a single post, but it's a good start to know its basics.
Fil-C pointers achieve memory safety using a capability model called invisicaps.
- Invisicaps in Fil-C
- Purpose: Provide memory safety without sacrificing most of C’s pointer power.
- Core idea:
Every pointer in Fil-C secretly carries a capability that defines:
- Bounds — which memory region it can access.
- Permissions — what operations it can perform on that memory (read/write/execute).
- Invisibility:
- This capability is not stored in user-visible memory.
- It’s only accessible through Fil-C-specific reflection intrinsics or runtime APIs.
- Ordinary dereferencing or pointer arithmetic cannot reveal it.
- Pointer size:
- Appears to be native size (8 bytes on 64-bit systems).
- Unlike CHERI, which uses wide pointers (≥ 16 bytes), Invisicaps keep normal pointer size.
- Comparison to similar systems:
- CHERI: Visible, wide pointers; capability stored alongside the pointer in memory.
- Fil-C Invisicaps: Capability hidden from the address space, same pointer size as C.
- SoftBound: Metadata stored separately but issues with atomics;
- Fil-C Invisicaps: Have a complete atomic story — races can’t bypass checks, atomic pointer ops remain truly atomic.
- Atomic safety:
Even if two threads race on the same pointer value, invisicap enforcement can’t be bypassed.
It’s basically a Capability Machine Model implemented in a way that keeps the illusion of “normal” C pointers while giving the runtime enough hidden metadata to enforce bounds and permissions, even under concurrency.
To help you understand, this is the actual code snippet of how Invisicaps was prepared in Fil-C:
// This function was finally called by filc_native_zgc_alloc which is wrapped by zgc_alloc mapped to regular malloc
static inline filc_ptr filc_ptr_create_with_lower_and_ptr_and_manual_tracking_yolo(void* lower, void* ptr)
{
filc_ptr result;
result.lower = lower;
result.ptr = ptr;
return result;
}
Filc_ptr is almost certainly a wrapper struct around a native pointer plus hidden capability metadata.
- lower: it’s actually a pointer to auxiliary metadata for the object.
- ptr: actual usable pointer value.
This simple operation is still not Inviscap, I believe we need another post to explain how invisicaps work in detail, but this should give you a good overview of the concept.
FUGC
However, Invisicaps doesn't make good performance on its own, so Fil-C uses a novel garbage collector called FUGC (Fast Universal Garbage Collector) to manage memory safely and efficiently.
FUGC implements a parallel version of Phil's Concurrent Marking (aka on-the-fly grey-stack Dijkstra with a soft handshake fixpoint) with verse_heap SIMD turbosweep. This GC runs alongside your program, cleaning up unused memory without stopping the world. It uses a concurrent “mark and sweep” algorithm where multiple threads mark reachable objects and sweep away the rest, with SIMD-accelerated sweeping for speed. Freed pointers are redirected to a safe dummy object, and objects are never moved, so raw pointers stay valid. Barriers track changes in real time, and a soft handshake between threads ensures no object is missed, even in tricky races, making the system both fast and reliable.
How to use Fil-c?
I'll recommend you just download the binary, rather than compiling from src, since you will compile the whole LLVM.
You may visit here to download: https://github.com/pizlonator/fil-c/tags
And for now it only supports Linux/X86_64.
./setup.sh
You can just use Fil-c to compile your C/C++ code, it will automatically link the runtime library and garbage collector. And the only part you need to change is the header file, you need to include `stdfil.h`.
Let's try what will happend if you access an out-of-bounds memory in C code with fil-c.
#include <stdfil.h>
#include <stdlib.h>
int main()
{
char* p = malloc(16);
p[42] = 100;
return 0;
}
And compile it with Fil-c:
$FILC/build/bin/clang -o test test.c
./test
# filc safety error: cannot write pointer with ptr >= upper.
# pointer: 0x76f24210425a,0x76f242104230,0x76f242104240
# expected 1 writable bytes.
# semantic origin:
# <somewhere>: main
# check scheduled at:
# <somewhere>: main
# src/env/__libc_start_main.c:79:7: __libc_start_main
# <runtime>: start_program
# [517127] filc panic: thwarted a futile attempt to violate memory safety.
# Trace/breakpoint trap (core dumped)
As you may see, when you run the program, it will crash with a memory safety error.
I'll stop here, but you can find more in its GitHub repo, and thanks to the author Filip Jerzy Pizło for creating such a great project!