Framework
Architecture,
security_hook_list, and registrationKey Hooks
inode_permission, file_open, task_kill, socket_connectBPF LSM
bpf_lsm_* hooks for policy without kernel modulesLSM Framework
The LSM framework was introduced to allow multiple security models to coexist in the kernel without requiring separate kernel builds. It works via a static call table (static_calls_table) generated at build time from include/linux/lsm_hook_defs.h. Each hook site in the kernel calls through this table, and each active LSM registers a callback for the hooks it cares about.
Boot-time LSM order is controlled by the lsm= kernel command-line parameter (e.g., lsm=lockdown,yama,selinux,bpf). The security/Kconfig option CONFIG_LSM sets the compile-time default order.
Core Data Structures
struct lsm_id — LSM identity
struct lsm_id — LSM identity
lsm_id. The name must be approved by the LSM maintainers. The id is a stable numeric identifier used by userspace (e.g., for lsm_get_self_attr() syscall).struct security_hook_list — a single hook entry
struct security_hook_list — a single hook entry
security_hook_list entry is created per hook an LSM implements. The LSM_HOOK_INIT() macro fills in the scalls and hook fields:struct lsm_blob_sizes — private data sizing
struct lsm_blob_sizes — private data sizing
lsm_cred_slot(), lsm_file_slot(), etc. accessors to retrieve your blob.struct lsm_info — module registration descriptor
struct lsm_info — module registration descriptor
struct lsm_info in the .lsm_info.init ELF section via DEFINE_LSM(name). The framework discovers and initializes all LSMs in the section at boot.security_add_hooks()
count hook entries from the hooks array with the LSM framework. This must be called from the LSM’s init() function. After this call, the kernel will invoke the registered callbacks at each corresponding hook site.
Array of hook entries initialized with
LSM_HOOK_INIT(). Must be __ro_after_init to prevent post-init modification.Number of entries in
hooks. Typically ARRAY_SIZE(my_lsm_hooks).Pointer to the LSM’s
lsm_id. The framework uses this to associate each hook with its owning LSM for auditing and stacking.Key LSM Hooks
Hook signatures are defined ininclude/linux/lsm_hook_defs.h using the LSM_HOOK(RET, DEFAULT, NAME, ...) macro. Below are the most commonly implemented hooks:
- File and Inode
- Process and Signal
- Network
inode_permission
inode_permission() in fs/namei.c before granting access to an inode. Invoked for every open(), stat(), access(), and directory lookup that checks inode permissions.The inode being accessed. The LSM can inspect
inode->i_security for its private label.Bitmask of requested access:
MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND. May include MAY_NOT_BLOCK when called from a non-blocking context.-EACCES or -EPERM) to deny.file_open
open() after the file has been opened but before it is returned to userspace. The file->f_path and file->f_flags are fully populated. This is the preferred hook for controlling file open access because it fires after path resolution and POSIX DAC checks.The newly opened file. Access
file->f_inode for the inode, file->f_path for the path, and file->f_flags for open flags (O_RDONLY, O_WRONLY, etc.).Writing a Simple LSM Module
Register with DEFINE_LSM
DEFINE_LSM() macro places the lsm_info struct in the .lsm_info.init linker section. The LSM framework scans this section at boot and calls init() for each enabled LSM in the order specified by the lsm= command-line parameter.LSM Stacking
Multiple LSMs can be active simultaneously. The framework calls each LSM’s hook in the order specified bylsm= and uses the most restrictive result: if any hook returns a non-zero (denial) value, the operation is denied.
LSM Ordering
lsm_info.order to control where in the call chain your LSM is placed:
capabilities is always LSM_ORDER_FIRST — it runs before any other LSM on every hook, ensuring POSIX capability checks cannot be bypassed. integrity is LSM_ORDER_LAST to finalize integrity measurements after all access control decisions.LSM Blobs and Private Data
LSMs use blobs to store per-object security labels without modifying core kernel structures. The framework reserves space inside objects based onlsm_blob_sizes and gives each LSM a non-overlapping slice.
The
lbs_* fields in my_blob_sizes are populated by the framework at init time, not by the LSM author. The LSM author only sets the size (number of bytes needed). The framework resolves the actual offset during boot.BPF LSM
BPF LSM (CONFIG_BPF_LSM) allows security policies to be implemented as BPF programs attached to LSM hook points, without writing a kernel module. It uses bpf_lsm_* trampoline functions generated for every hook in lsm_hook_defs.h.
Use cases
- Runtime policy updates without kernel rebuilds
- Per-container or per-cgroup security policies
- Rapid prototyping of new access controls
- Supplementing SELinux or AppArmor with custom rules
Limitations
- Cannot store persistent state across reboots (use maps)
- BPF verifier restricts program complexity
- Cannot return custom error types (limited to standard errno)
- Requires
CAP_BPF+CAP_SYS_ADMINto load programs
Attaching BPF Programs to LSM Hooks
BPF LSM Hook Coverage
All hooks defined ininclude/linux/lsm_hook_defs.h are available as BPF attachment points. The hook name maps directly to the BPF section name:
| LSM Hook | BPF Section |
|---|---|
inode_permission | lsm/inode_permission |
file_open | lsm/file_open |
task_kill | lsm/task_kill |
socket_connect | lsm/socket_connect |
bpf | lsm/bpf |
sk_alloc_security | lsm/sk_alloc_security |
SELinux and AppArmor Reference
SELinux and AppArmor are the two most widely deployed LSMs and serve as the canonical reference implementations.SELinux
SELinux
SELinux implements type enforcement (TE) — every process runs in a security domain (type) and every object (file, socket, etc.) has a type. Access is allowed only if a policy rule explicitly permits the
{subject_type, object_type, object_class, permission} tuple.SELinux stores its labels in security.selinux xattrs on filesystem objects and in kernel security blobs (inode->i_security, sk->sk_security, etc.).AppArmor
AppArmor
AppArmor uses path-based access control — profiles are matched against the absolute file paths being accessed, rather than labels on objects. This makes AppArmor easier to profile for specific applications.Profiles can be in AppArmor profiles for new LSM hooks need the
enforce (deny and log violations) or complain (log only) mode.#include <abstractions/base> directive and follow the syntax:Both SELinux and AppArmor coexist with other LSMs in a stack. For example, a typical desktop Linux system may run
lockdown,capability,yama,apparmor,bpf simultaneously. Each LSM independently evaluates its hooks; all must allow an operation for it to proceed.