ABI versions
Landlock is versioned: every kernel release that adds new access rights bumps the ABI version. A process queries the running kernel's version, then chooses whether to require a minimum version, downgrade gracefully, or refuse to start.
The Landlock-Sharp binding handles the bookkeeping for you — flags newer than the running kernel are silently dropped so the same C# code keeps working everywhere. This page explains how that works and how to opt out.
The canonical version table lives in the "Backwards and forwards compatibility" section of landlock(7). Always cross-reference the man page when in doubt.
The version table
| ABI | Kernel | What it adds |
|---|---|---|
| 1 | 5.13 | EXECUTE, WRITE_FILE, READ_FILE, READ_DIR, REMOVE_DIR, REMOVE_FILE, MAKE_CHAR, MAKE_DIR, MAKE_REG, MAKE_SOCK, MAKE_FIFO, MAKE_BLOCK, MAKE_SYM |
| 2 | 5.19 | REFER (cross-directory rename/link) |
| 3 | 6.2 | TRUNCATE |
| 4 | 6.7 | BIND_TCP, CONNECT_TCP (network) |
| 5 | 6.10 | IOCTL_DEV |
| 6 | 6.12 | ABSTRACT_UNIX_SOCKET, SIGNAL (scopes) |
| 7 | 6.13 | Audit log control flags on landlock_restrict_self |
Source: landlock(7) — "Backwards and forwards compatibility". Every right introduced in ABI N is available on every kernel with ABI ≥ N.
Querying the kernel at runtime
using Sandbox;
int abi = Landlock.GetAbiVersion();
if (abi < 0)
{
Console.WriteLine("Landlock is unsupported on this kernel.");
}
else
{
Console.WriteLine($"Landlock ABI version: {abi}");
}
GetAbiVersion() is a single syscall — it's safe to call as often as you like. A negative return value means the kernel doesn't recognise the Landlock syscall (typically: kernel < 5.13, or Landlock not enabled in lsm=).
For a friendlier wrapper, Landlock.IsSupported() combines the ABI check with an OS/architecture check and returns bool.
How the binding filters rules
When you build a ruleset, every FileSystem, Network, and Scope flag is silently filtered against the kernel's ABI version. The flag is dropped if it requires a newer kernel.
For example, on a 5.19 kernel (ABI 2):
var sandbox = Landlock.CreateRuleset(
Landlock.FileSystem.READ_FILE, // ABI 1 — kept
Landlock.FileSystem.REFER, // ABI 2 — kept
Landlock.FileSystem.TRUNCATE, // ABI 3 — dropped on 5.19
Landlock.FileSystem.IOCTL_DEV); // ABI 5 — dropped on 5.19
The same code on a 6.10 kernel keeps all four flags. This is the recommended default — your application works on every supported kernel and automatically picks up new rights as kernels are upgraded.
The same filter applies inside AddPathBeneathRule and AddPortRule: an unsupported allowed-action is silently dropped from the rule's bitmap.
FileSystem.CORE — the safe default
Landlock.FileSystem.CORE is a convenience that expands to every filesystem right available on the current kernel. It is the recommended choice when you want to lock down filesystem access completely and let Landlock-Sharp pick up new rights as kernels evolve.
var sandbox = Landlock.CreateRuleset(Landlock.FileSystem.CORE);
CORE does not include IOCTL_DEV (deliberately — it's an unusual operation most apps shouldn't need to handle). Add it explicitly if you want to:
var sandbox = Landlock.CreateRuleset(
Landlock.FileSystem.CORE,
Landlock.FileSystem.IOCTL_DEV);
Requiring a minimum ABI
If your sandbox is only meaningful when certain rights exist (e.g. network sandboxing on 6.7+, IPC scopes on 6.12+), check the ABI version yourself before building the ruleset:
if (Landlock.GetAbiVersion() < 4)
{
throw new PlatformNotSupportedException(
"This service requires Landlock ABI ≥ 4 (Linux 6.7+) for TCP sandboxing.");
}
var sandbox = Landlock.CreateRuleset(
new[] { Landlock.FileSystem.CORE },
new[] { Landlock.Network.BIND_TCP, Landlock.Network.CONNECT_TCP });
This pattern matches the upstream recommendation in the landlock(7) "Compatibility" example. Choose a policy:
- "best effort" — accept the silent filtering, run on any kernel.
- "minimum version" — fail loudly when the kernel is too old.
- "feature negotiation" — query the ABI, build different rulesets for different versions.
Checking individual features
The binding doesn't expose a per-flag "is supported" helper, but you can compute it from the ABI version. The minimum ABI for each flag is documented in the table above. A small extension helper makes this readable:
public static class LandlockAbi
{
public static bool Supports(Landlock.FileSystem flag) => flag switch
{
Landlock.FileSystem.REFER => Landlock.GetAbiVersion() >= 2,
Landlock.FileSystem.TRUNCATE => Landlock.GetAbiVersion() >= 3,
Landlock.FileSystem.IOCTL_DEV => Landlock.GetAbiVersion() >= 5,
_ => Landlock.GetAbiVersion() >= 1,
};
public static bool Supports(Landlock.Network _) => Landlock.GetAbiVersion() >= 4;
public static bool Supports(Landlock.Scope _) => Landlock.GetAbiVersion() >= 6;
}
What about ABI 7's logging flags?
ABI 7 (kernel 6.13) added flags on landlock_restrict_self that change how denied accesses are logged. The binding exposes them as optional parameters on Enforce():
sandbox.Enforce(
disableDenyLogging: false,
enableChildDenyLogging: true,
disabledNestedDomainsLogging: false);
On older kernels these parameters are silently ignored. See Logging for details.
Cross-reference
- landlock(7) — canonical ABI version table.
- Kernel docs — Landlock — narrative explanation of the compatibility model.
- Upstream sample C code — the same pattern in C.
Next: API overview
Now that you understand how the binding negotiates with the kernel, see the API overview for the full surface of the Landlock class.