← Back to research

RESEARCH ARCHIVE / LID

LID | Linux Integrity Drift

Systematic discovery of kernel code paths that bypass LSM security hooks
5 findings LSM bypass kernel 5.2+ AppArmor SELinux io_uring eBPF mount API AF_XDP

The Core Idea

The Linux Security Module framework has maintained a single guarantee for over 20 years: security modules can only add restrictions, never remove them. This guarantee is correct. LID does not break it.

LID finds something different. It identifies kernel subsystems that perform security-sensitive operations without consulting the LSM framework at all. The gate was never breached. It was walked around.

The security policy is enforced correctly. The problem is that the kernel has code paths that never ask.

LSM Hook Architecture

The kernel uses the call_int_hook macro to iterate through registered security modules. Each module returns 0 (allow) or nonzero (deny). The loop short-circuits on the first denial, ensuring modules can only add restrictions.

LSM Hook Architecture: The Normal Path vs. The Bypass NORMAL PATH (hooked) Userspace syscall VFS / Subsystem entry call_int_hook() iterate security modules AppArmor SELinux BPF LSM Policy enforced audit log written BYPASS PATH (no hook) Userspace syscall New subsystem entry NO LSM HOOK CALLED security framework never consulted Operation completes no audit, no policy check LID identifies five kernel subsystems that follow the right path.

Five Findings

LID Finding Overview LID-001 eBPF Pathname Rewriting kprobe on do_sys_openat2 AppArmor blind Zero audit trace CRITICAL gap requires CAP_BPF LID-002 io_uring MSG_RING fd Unprivileged security_file_receive bypassed entirely All LSMs blind CRITICAL gap + escalation unprivileged LID-003 New Mount API Bypass fsopen/fsmount move_mount security_sb_mount never called HIGH gap requires mount privs LID-004 BPF Token Blindness AppArmor implements 0 of 9 hooks Complete blindness to BPF token ops HIGH gap requires CAP_BPF LID-005 AF_XDP tc Egress Bypass Raw AF_XDP frames bypass tc filters Container network filter evasion MEDIUM gap requires CAP_NET_RAW

Detailed Findings

LID-001 | Critical Visibility Gap

eBPF Pathname Rewriting

A BPF kprobe attached to do_sys_openat2 uses bpf_probe_write_user to rewrite the filename in the user space buffer before the kernel copies it. Combined with a hard link at an AppArmor-allowed path, the kernel resolves the rewritten path, AppArmor evaluates the allowed path instead of the denied one, and the access succeeds with zero audit trace.

The attack exploits a fundamental gap between AppArmor's pathname-based access control model and eBPF's ability to operate before any LSM hook fires. Two hard links to the same inode are treated as entirely different files by AppArmor policy. The BPF program selectively targets specific process and path combinations, making the bypass transparent, selective, and audit-invisible.

LID-001: Pathname Rewriting Attack Flow User Process open("/tmp/secret") Kernel do_sys_openat2() BPF kprobe fires bpf_probe_write_user() rewrites to "/tmp/.bypass" Kernel copies path "/tmp/.bypass" VFS resolves path same inode via hardlink AppArmor ALLOW fd returned | secret data accessible | no audit log
PropertyValue
Hook bypassedAll pathname-based LSM checks
PrerequisiteCAP_BPF + CAP_PERFMON (or root)
AppArmor visibilityNone (zero audit entries)
SELinux visibilitySees bypass path, not original
SelectivityPer process, per path
ReversibilityRemoving BPF program restores enforcement
LID-002 | Critical Visibility Gap + Practical Escalation

io_uring MSG_RING File Descriptor Transfer

The io_uring MSG_RING operation allows one io_uring instance to inject a file descriptor into another ring's fixed file table. This fd transfer completely bypasses security_file_receive(), the LSM hook that AppArmor and SELinux rely on for cross-process file descriptor validation.

Unlike other LID findings, this vector operates from unprivileged userspace with no special capabilities required. A regular user can transfer arbitrary file descriptors between io_uring instances without any LSM evaluation. The transferred fd is invisible to every registered security module.

PropertyValue
Hook bypassedsecurity_file_receive()
PrerequisiteNone (unprivileged)
All LSMs blindYes
Crosses privilege boundaryYes
Kernel version5.18+ (MSG_RING introduction)
LID-003 | High Visibility Gap

New Mount API Bypass

The new mount API introduced with fsopen(), fsmount(), and move_mount() bypasses security_sb_mount(), which is the primary hook that AppArmor and SELinux use to enforce mount policy. The new API path performs mount operations through a different code path that was never wired into the existing LSM mount hook.

PropertyValue
Hook bypassedsecurity_sb_mount()
Syscallsfsopen, fsmount, move_mount
ImpactMount policy enforcement bypassed
PrerequisiteMount privileges in namespace
LID-004 | High Visibility Gap

BPF Token AppArmor Blindness

The BPF token subsystem (kernel 6.9+) introduces nine security hooks for token creation, delegation, and usage. AppArmor implements zero of these nine hooks. Any BPF token operation occurs with complete AppArmor blindness, no policy evaluation, and no audit logging.

PropertyValue
Hooks missing9 out of 9 BPF token hooks
AppArmor coverage0%
Kernel version6.9+
ImpactFull BPF token delegation without MAC
LID-005 | Medium Visibility Gap

AF_XDP tc Egress Bypass

Raw network frames transmitted through AF_XDP sockets bypass the tc (traffic control) egress path entirely. Container environments that rely on tc filters for network policy enforcement (including some configurations of Docker and plain iptables setups) are blind to AF_XDP transmitted traffic. The frames go directly to the NIC driver queue, skipping all tc qdisc and filter processing.

PropertyValue
Bypassed layertc egress filters
PrerequisiteCAP_NET_RAW + AF_XDP support
Cilium affectedNo (uses XDP/BPF hooks)
Plain Docker affectedYes (tc based filtering)

Visibility Gap Matrix

Finding Visibility Gap Practical Escalation Privilege Required LSMs Affected
LID-001 Critical Limited (already privileged) CAP_BPF + CAP_PERFMON AppArmor, audit
LID-002 Critical High (unprivileged) None All LSMs
LID-003 High Medium Mount privileges AppArmor, SELinux
LID-004 High Limited CAP_BPF AppArmor (0/9 hooks)
LID-005 Medium Situational CAP_NET_RAW tc based filters

Architectural Implications

LID reveals a design tension in the Linux security architecture. The LSM framework correctly enforces that modules can only add restrictions. The eBPF subsystem provides unrestricted hook points that operate before LSM checks. Newer kernel subsystems (io_uring, the new mount API, BPF tokens) introduce operations that were never connected to existing LSM hooks.

The result is that the security perimeter, as understood by AppArmor and SELinux policy, does not match the actual kernel attack surface. Defenders who rely on LSM-based policy enforcement and audit logging have blind spots in their visibility model. Operations can occur, data can be accessed, and file descriptors can be transferred without any security module being consulted.

This is not a bug in any individual component. It is the natural consequence of a kernel that evolves faster than its security framework.