A process calls execve() for a binary on a monitored filesystem, but the kernel does not immediately complete the execution open. A fanotify group has requested FAN_OPEN_EXEC_PERM, so the access waits while a userspace listener receives an event and returns FAN_ALLOW or FAN_DENY. The mechanism inserts a synchronous userspace decision into a filesystem operation that would otherwise proceed after ordinary kernel permission checks.

That interception point is useful for policy engines that need information outside normal inode permissions, but it creates a distinct enforcement boundary. Availability now depends on a userspace responder, event coverage depends on the selected fanotify marks and event classes, and the mechanism does not convert every form of file use into a mediated operation.

Permission events are synchronous gates

Fanotify separates notification events from permission events. A notification such as FAN_OPEN reports activity without asking the listener to authorize it. Permission events instead suspend the triggering operation until the listener writes a struct fanotify_response containing the event file descriptor and a response value.

Three established permission event types cover different access points. FAN_OPEN_PERM mediates an open request, FAN_ACCESS_PERM mediates read access, and FAN_OPEN_EXEC_PERM mediates an open with execution intent. These events require a fanotify group created with FAN_CLASS_PRE_CONTENT or FAN_CLASS_CONTENT; FAN_CLASS_NOTIF cannot issue access decisions.

The distinction matters because the event name defines the gate. A policy interested specifically in direct program execution can use FAN_OPEN_EXEC_PERM without treating every ordinary data-file open as an execution decision. Conversely, an FAN_OPEN_PERM decision occurs at open time and does not represent a continuing authorization lease for every later operation that an application might perform through other kernel interfaces.

The event carries an object, not merely a pathname

In the traditional event format, fanotify can supply a file descriptor referring to the object associated with the event. The listener can inspect that object while the triggering task waits. The open file description used for this event descriptor carries an internal FMODE_NONOTIFY flag, suppressing recursive fanotify events when the listener accesses the notified object through that descriptor.

This object-oriented handoff avoids making a reconstructed pathname the sole identity for a decision. Pathnames can change through rename operations and can expose the same underlying object through multiple namespace routes. Newer fanotify reporting modes can also attach file-handle and process information records, subject to the selected initialization flags and kernel support.

The event metadata also reports the PID of the triggering process by default, or a thread ID when FAN_REPORT_TID is requested. That identifier is useful context, but it should not be treated as a durable process identity after the event lifecycle. Numeric process identifiers can be reused after a task exits; policy that performs asynchronous follow-up needs a separate strategy for stable process references.

Execution mediation has a narrower trigger than script execution

FAN_OPEN_EXEC_PERM is tied to files opened with execution intent by direct execution interfaces such as execve() and execveat(). It does not automatically identify a script merely because an interpreter later reads that script as data. When a script is passed to an interpreter, the interpreter’s executable open and its later read of the script are different operations.

This boundary prevents an execution-open policy from being described as universal code-content mediation. A deployment that needs to inspect interpreter inputs must account for the relevant read or open paths as well as direct executable opens. Dynamic linkers and other executable objects may also generate execution-open events when they are themselves opened in that mode.

The policy therefore depends on an explicit model of which kernel operations represent the security-sensitive transition. Matching a filename suffix or assuming that every executed program maps to one FAN_OPEN_EXEC_PERM event is not a sufficient model.

Mark scope defines the enforcement surface

Fanotify marks can target individual filesystem objects, mounts, or filesystem instances, depending on the mark type and requested event features. A mount mark covers accesses made through that marked mount. The same underlying filesystem object can be reachable through another mount, and an event reached through that other route is not automatically covered by the first mount mark.

Filesystem marks provide a broader scope across mounts of a filesystem instance. Directory marks have different child-event semantics and are not a general recursive-tree primitive. Selecting mark scope is consequently part of the security boundary rather than an operational detail.

This also separates fanotify from pathname allowlists. The kernel generates events according to marks associated with filesystem objects and scopes. Userspace may derive names for policy or logging, but the enforcement coverage comes from those marks and event masks.

The responder becomes part of availability

A permission event remains pending after userspace reads it until a decision is written. Slow inspection therefore adds latency directly to the blocked file operation. A stalled listener can hold operations in place, making queue handling, bounded inspection work, and failure behavior part of the service’s availability design.

Closing the final descriptor for a fanotify group releases the group. Outstanding permission events are allowed when the group is closed. That behavior means listener termination is not equivalent to a permanent deny state. A deployment that requires fail-closed semantics across daemon failure needs surrounding supervision and system policy rather than assuming that the fanotify group itself supplies that guarantee.

Queue capacity is another boundary. Fanotify can report FAN_Q_OVERFLOW when the event queue exceeds its limit. Lost notification events primarily damage observation, while a design that relies on complete event history for later policy state can suffer a deeper consistency problem. Resource limits and overflow handling need to match the policy architecture.

Fanotify does not observe every path to file content

Fanotify reports events caused by userspace through supported filesystem API paths. Its documented limitations include file accesses or modifications arising from mmap(), msync(), and munmap() not being reported as ordinary fanotify access or modification events. Remote activity on network filesystems is also outside the model when it does not originate through the local userspace filesystem API.

These exclusions matter when fanotify is used as an enforcement component rather than an audit feed. A permission gate can be exact for the operations it intercepts while still leaving other data paths outside its event model. Kernel discretionary access control, mandatory access control, mount policy, file integrity mechanisms, and application isolation remain separate controls with different coverage.

Fanotify permission events are therefore best treated as a synchronous policy hook with explicitly bounded triggers. Their security value comes from placing selected filesystem operations behind a userspace decision; their limitation is equally concrete: the resulting boundary is only as broad as the event types, mark scope, kernel behavior, and responder lifecycle that define it.