logo

Linux - KVM

In the Linux kernel, Landlock is a security module (specifically, a Linux Security Module or LSM) that allows unprivileged processes to restrict themselves. Instead of relying on a global system-wide security policy (like standard SELinux or AppArmor), a program can use Landlock to safely sandpile/sandbox itself by restricting its own access to the filesystem, network, or other system resources.

The Landlock ABI (Application Binary Interface) version is a number that represents the set of security features and kernel capabilities currently supported by the runtime Linux kernel's Landlock implementation.

Why is the Landlock ABI Versioned?

Unlike traditional Linux system calls that maintain absolute backward compatibility forever, Landlock is designed to be extensible. As developers add new security features to Landlock (e.g., restricting network sockets instead of just filesystem directories), they increment the ABI version.

Because Landlock allows a program to restrict itself, the program needs a way to ask the kernel: "What version of the Landlock interface do you support, and what restrictions can I actually enforce on this machine?"

How the ABI Mechanism Works in Code

When a developer wants to sandbox a process using Landlock, the workflow typically follows these steps:

  1. Query the ABI: The application calls the landlock_create_ruleset system call with a special flag (LANDLOCK_CREATE_RULESET_VERSION) to ask the kernel for the highest supported ABI version.
  2. Handle Backward/Forward Compatibility:
    • If the kernel's ABI version is 0, Landlock is disabled or unsupported. The application must decide whether to fail or run unsandboxed.
    • If the kernel's ABI is lower than what the application was compiled for, the application gracefully downgrades its requests, enforcing only the rules the current kernel understands.
  3. Create the Ruleset: The application creates a ruleset matching the supported ABI attributes.
  4. Enforce: The process calls prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) and restricts itself.

History of Landlock ABI Versions

Each major milestone in Landlock's development increments the ABI. Here is a historical roadmap of what features were introduced with each version:

ABI Version 1 (Linux 5.13)

The initial release focused entirely on basic filesystem access control. It introduced a ruleset attribute called handled_access_fs which allowed a process to restrict:

  • Reading/writing files (LANDLOCK_ACCESS_FS_READ_FILE, LANDLOCK_ACCESS_FS_WRITE_FILE)
  • Creating/removing directories and devices
  • Executing files

ABI Version 2 (Linux 5.19)

Introduced support for File Renaming and Moving.

  • Added LANDLOCK_ACCESS_FS_REFER. This allowed processes to safely move or link files between directories without inadvertently bypassing security policies.

ABI Version 3 (Linux 6.2)

Introduced Truncate Support.

  • Added LANDLOCK_ACCESS_FS_TRUNCATE. Prior to this version, a process with write access could truncate a file length via truncate() or open(..., O_TRUNC), which wasn't explicitly covered by standard write permissions.

ABI Version 4 (Linux 6.7)

Introduced Network Access Control. This expanded Landlock beyond the filesystem for the first time.

  • Added the handled_access_net ruleset attribute.
  • Introduced LANDLOCK_ACCESS_NET_BIND_TCP and LANDLOCK_ACCESS_NET_CONNECT_TCP to restrict a sandboxed application from listening on specific TCP ports or connecting to external TCP endpoints.

ABI Version 5 (Linux 6.10)

Introduced IOCTL Restriction.

  • Added LANDLOCK_ACCESS_FS_IOCTL_DEV. This allowed rulesets to explicitly control or deny ioctl calls on character or block devices, which historically have been common vectors for kernel privilege escalation exploits.

Example: Checking the ABI Version in C

Below is a simple example demonstrating how an application checks the Landlock ABI version at runtime using the standard system call wrappers:

#include <linux/landlock.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>

#ifndef landlock_create_ruleset
static inline int landlock_create_ruleset(
    const struct landlock_ruleset_attr *const attr,
    const size_t size, const __u32 flags)
{
    return syscall(__NR_landlock_create_ruleset, attr, size, flags);
}
#endif

int main() {
    // Requesting the version requires passing NULL to the first two arguments
    int abi_version = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);

    if (abi_version < 0) {
        perror("Landlock is not supported by this kernel");
        return 1;
    }

    printf("The current Linux Kernel Landlock ABI version is: %d\n", abi_version);
    return 0;
}