Linux security tools increasingly rely on eBPF for things like packet filtering, traffic accounting, intrusion detection, and policy enforcement. eBPF is extremely powerful—but writing correct and verifier-friendly eBPF programs is still harder than it should be.
At first glance, we already have two solid options: C and Rust. So why build a new language at all?
This post explains the motivation behind building a domain-specific language (DSL) for security policies that compiles to eBPF.
What Makes eBPF Different
eBPF programs run inside the Linux kernel, which means they must pass a strict verifier before they can be loaded. The verifier enforces rules such as:
- All loops must be bounded
- Memory access must be provably safe
- Pointer arithmetic is heavily restricted
- No panics, unwinding, or dynamic allocation
Many errors are discovered after compilation, when the verifier rejects the program—often with cryptic messages.
Writing eBPF in C: Flexible but Fragile
C is the native language for eBPF and gives full control, but that control comes with sharp edges:
- Manual bounds checks everywhere
- Easy to write code that compiles but fails verification
- Verifier errors are often hard to map back to source logic
In practice, development becomes a loop of compile → load → verifier error → retry.
Rust for eBPF: Safer, But Not a Perfect Fit
Rust improves safety significantly at the type and memory level, but eBPF breaks some of Rust’s assumptions:
-
panicis considered safe in Rust, but not supported in eBPF - Large parts of the standard library are unavailable
- Some safety guarantees exist at a level eBPF simply doesn’t allow
Rust remains a great language, but when targeting eBPF it still requires working around a general-purpose model that wasn’t designed for verifier-driven execution.
The Core Problem
The real issue isn’t C or Rust—it’s the mismatch between general-purpose language semantics and eBPF verifier constraints.
Security policies are usually:
- Bounded
- Predictable
- Declarative in nature
Yet we express them using languages that allow unbounded and unverifiable behavior by default.
Why a DSL Makes Sense for Security Policy
Instead of fighting the verifier after writing code, a DSL can encode verifier constraints directly into the language:
- Only bounded loops are allowed
- Memory access patterns are restricted by design
- Ranges and limits can be explicit
- Unsafe constructs simply don’t exist
This shifts feedback from runtime verifier errors to compile-time language errors.
Introducing Solnix (Briefly)
Solnix is an experimental, security-policy-focused DSL designed specifically for eBPF use cases.
Key ideas:
- Verifier-aware semantics
- Restricted memory and control flow
- Compiler generates strict, verifier-friendly eBPF C code
It’s not meant to replace Rust or C—only to specialize for a domain where constraints are unavoidable.
Benefits of This Approach
- Fewer verifier rejections
- Faster iteration cycles
- Clearer compile-time errors
- Security guarantees by construction
- Less trial-and-error in kernel space
Closing Thoughts
eBPF has changed how we build Linux security systems, but the tooling gap is still real. For highly constrained domains like security policy enforcement, a verifier-aware DSL can reduce complexity and improve reliability.
If you’re curious or want to share feedback, you can find more here:
https://solnix-lang.org
I’d love to hear your thoughts—should this kind of problem be solved with better libraries, compiler extensions, or purpose-built languages?
Top comments (0)