In eBPF, a cgroup program and a BPF LSM program both sit outside the tracing hooks and both can make the kernel refuse an operation. What does each one attach to, and what does its return value control?
answer
- scope is a cgroup directory, not a function
- attach flags decide inheritance
- same trampoline, different meaning
- zero allows, negative errno denies
- denials compose, permissions do not
basics
~20 sA cgroup program attaches to a cgroup v2 directory and governs every process inside it; a BPF LSM program attaches to a kernel security hook. In both cases the return value decides whether the kernel allows the operation to proceed.
solid answer
~50 sCgroup program types — `BPF_PROG_TYPE_CGROUP_SKB`, `BPF_PROG_TYPE_CGROUP_SOCK`, `BPF_PROG_TYPE_CGROUP_SOCK_ADDR`, `BPF_PROG_TYPE_CGROUP_DEVICE`, `BPF_PROG_TYPE_CGROUP_SYSCTL` — are attached by passing an open file descriptor to a cgroup v2 directory to `bpf(BPF_PROG_ATTACH)` or to a cgroup link. They then run for every process in that cgroup, and for descendants depending on the `BPF_F_ALLOW_MULTI` and `BPF_F_ALLOW_OVERRIDE` flags. Their return value is a verdict: allow the packet, allow the socket operation, or refuse it. `BPF_PROG_TYPE_LSM` is different — it attaches through the same trampoline machinery as fentry, but to a Linux Security Module hook such as `file_open`, and returning 0 allows the operation while a negative errno denies it and becomes the error the syscall returns. Because LSMs are consulted in series and any one of them can refuse, a BPF LSM can only add denials, never grant what SELinux or AppArmor already refused.
go deeper
Recognise that some eBPF programs decide rather than observe, and that cgroup-attached programs apply to the processes in one cgroup. Do not worry about the individual attach types yet.
Explain that a cgroup program is attached by handing the kernel a file descriptor for a cgroup v2 directory, that inheritance depends on the attach flags, and that a BPF LSM program returns 0 to allow or a negative errno to deny.
Show that you treat enforcing hooks as a change to production behaviour: an audit-mode rollout, a known detach procedure, and awareness that a bad verdict surfaces as an application failure rather than as an eBPF error.
Own the policy question: whether enforcement belongs in a BPF LSM alongside the distribution's existing MAC policy or in a system with a clearer audit trail, who may attach at fleet scope, and how conflicting attachments between platform and security agents are arbitrated.
## Observation hooks and decision hooks Most eBPF program types are observation points: the kernel runs your program, ignores what it returns, and carries on. Two families are not. Cgroup-attached programs and BPF LSM programs are **decision hooks** — the kernel asks them a question and obeys the answer. That changes the risk profile completely, and it is why interviews reach for them as a depth probe. ## Cgroup programs: scope comes from the cgroup tree A cgroup program is attached not to a function or a device but to a **cgroup v2 directory**. The loader opens the directory, gets a file descriptor, and passes it as the attach target to `bpf(BPF_PROG_ATTACH)` — or, on modern kernels, creates a cgroup `bpf_link` instead. From then on the program runs for the processes in that cgroup. The family covers several decisions: - `BPF_PROG_TYPE_CGROUP_SKB`, attached as `BPF_CGROUP_INET_INGRESS` or `BPF_CGROUP_INET_EGRESS`, sees packets belonging to sockets owned by the cgroup and returns 1 to let them through or 0 to drop them. - `BPF_PROG_TYPE_CGROUP_SOCK` runs on events such as socket creation (`BPF_CGROUP_INET_SOCK_CREATE`). - `BPF_PROG_TYPE_CGROUP_SOCK_ADDR` runs inside the socket syscalls — `BPF_CGROUP_INET4_CONNECT`, `BPF_CGROUP_INET4_BIND` and their v6 counterparts — where it can refuse the operation or rewrite the address the syscall is about to use. - `BPF_PROG_TYPE_CGROUP_DEVICE` decides device access; on cgroup v2, this is the mechanism behind service-manager device policies, because the old v1 device controller has no v2 equivalent. - `BPF_PROG_TYPE_CGROUP_SYSCTL` mediates reads and writes of sysctl knobs by processes in the cgroup. Inheritance is explicit rather than implied. The attach flags decide what happens when a descendant cgroup also has a program: `BPF_F_ALLOW_MULTI` lets several programs run, `BPF_F_ALLOW_OVERRIDE` lets a child replace the ancestor's. Without either, an attachment is exclusive and a conflicting attach fails. This matters operationally, because a container manager and a security agent both wanting the same hook on the same cgroup is a real collision. The key property to state out loud: **the scope of a cgroup program is a set of processes, not a machine-wide code path**. That is what makes it the natural attach point for per-workload policy. ## BPF LSM: attaching to the security hooks The Linux Security Module framework is a set of hundreds of call sites scattered through the kernel — `file_open`, `bprm_check_security`, `socket_connect` and so on — at which the kernel asks the registered security modules whether an operation may proceed. SELinux and AppArmor are such modules, compiled in. `BPF_PROG_TYPE_LSM`, with expected attach type `BPF_LSM_MAC`, lets an eBPF program register at one of those hooks. Mechanically it uses the same BPF trampoline as fentry, so it gets typed arguments from BTF; semantically it is nothing like a tracing program, because its return value is a verdict: **0 allows, a negative errno denies**, and that errno is what the calling syscall returns to user space. Returning `-EPERM` from an `lsm/file_open` program makes `open()` fail with "Operation not permitted" for whatever your program decided to refuse. Two conditions gate it. The kernel must be built with `CONFIG_BPF_LSM`, and `bpf` must appear in the active LSM list — normally by including it in the `lsm=` kernel command-line parameter. Distributions vary in whether they ship it enabled, so "is BPF LSM available here" is a per-host question, not a per-kernel-version one. The framework's composition rule is worth knowing: the security modules at a hook are consulted in order and **any denial is final**. A BPF LSM program can therefore only tighten policy. It cannot grant an operation SELinux has already refused, and it cannot be used to punch a hole through an existing MAC policy — which is also the reassuring half of the security story. ## Why these hooks demand more care A buggy kprobe program wastes cycles and prints nonsense. A buggy cgroup or LSM program breaks the workload: connections refused, files unopenable, a service that starts and immediately fails in a way that looks nothing like an eBPF problem. Two habits follow. First, develop the logic at an observation hook and only move it to the enforcing hook once the verdict logic is right — many teams run such programs in an audit mode that always allows and merely records what it would have denied. Second, know how you would remove it under pressure: an attachment made by a process that has exited does not go away on its own, and finding it means going and looking rather than restarting the service.
- Two agents want to attach a program at the same cgroup hook. What decides whether the second attach succeeds?The attach flags. With `BPF_F_ALLOW_MULTI`, several programs can be attached and all of them run. With `BPF_F_ALLOW_OVERRIDE`, a descendant cgroup's program may replace the ancestor's for processes in that subtree. With neither, the attachment is exclusive and the second attach is refused — a real source of conflict between a container runtime and a security agent.
- Can a BPF LSM program be used to grant a process access that SELinux denies?No. The LSM framework consults the registered modules at a hook and any denial is final, so the verdicts compose as an AND. A BPF LSM program can only add restrictions on top of whatever policy is already in force. That is a deliberate design property, not a gap to work around.
- How would you develop an enforcing cgroup or LSM program without breaking the workload while you iterate?Run it in audit mode: always return the allowing value, and record what the program would have denied through a map or a ring buffer. Compare the recorded verdicts against reality until the false-positive rate is zero, then flip to enforcing. Keep the detach command ready, since a bad verdict looks like an application failure, not an eBPF one.
saying these in an interview costs you the question
- Thinks cgroup programs run machine-wide like a kprobe
- Says a BPF LSM can grant access another LSM denied
- Assumes any cgroup hook is inherited automatically
- Treats the return value of every eBPF program as ignored
- Believes BPF LSM works on any kernel with eBPF support