A service can validate a pathname and still open a different object if the namespace changes between validation and use. Symbolic links, mount topology, rename operations, and special procfs links make pathname resolution a kernel operation with state that can change concurrently. Linux openat2() addresses part of this boundary by attaching resolution constraints to the lookup that produces the file descriptor.

The security property is narrower than generic path sanitization. openat2() does not declare a pathname safe. It lets a caller ask the kernel to reject specific resolution behavior while the kernel performs the walk.

Resolution policy travels with the open operation

openat2() accepts an open_how structure containing normal open flags plus a resolve field. A call can anchor a relative path at a directory file descriptor and apply constraints in the same kernel operation:

struct open_how how = {
    .flags = O_RDONLY | O_CLOEXEC,
    .resolve = RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS,
};

int fd = syscall(SYS_openat2, dirfd, path, &how, sizeof(how));

RESOLVE_BENEATH rejects resolution that escapes above the supplied directory through components such as .. or through absolute path resolution. It also currently disables magic-link resolution. The documented interface recommends specifying RESOLVE_NO_MAGICLINKS explicitly when that property is required, rather than relying on the current implication.

This arrangement removes a common split between a userspace check and a later open. The pathname can still race with namespace changes, but the requested resolution conditions are evaluated by the kernel during the operation that returns the descriptor.

RESOLVE_IN_ROOT changes the lookup root for one operation

RESOLVE_IN_ROOT treats the directory referenced by dirfd as the root for pathname resolution during that call. Absolute paths are interpreted relative to that directory, and absolute symbolic links are handled relative to the same temporary root. .. components at the root do not escape it.

This resembles selected path-resolution semantics of chroot() without changing the process root directory. It is an operation-scoped lookup rule, not a process-wide filesystem jail. File descriptors already held by the process remain usable according to their own permissions, and other syscalls are not automatically confined by this flag.

RESOLVE_BENEATH and RESOLVE_IN_ROOT express different policies and are not intended to be combined. A caller should select the model that matches its pathname contract rather than treating every resolve flag as additive hardening.

RESOLVE_NO_SYMLINKS rejects symbolic-link resolution in every pathname component. It also implies the effect of RESOLVE_NO_MAGICLINKS. This is stricter than rejecting only a final symbolic link with flags such as O_NOFOLLOW, because intermediate components are covered as well.

RESOLVE_NO_MAGICLINKS targets kernel magic links such as selected entries exposed by procfs. These objects can have semantics beyond ordinary symbolic links, including references to kernel-held objects. Blocking magic links while permitting ordinary symbolic links can therefore be a distinct policy.

A service that permits symlinks inside a managed tree may prefer RESOLVE_BENEATH plus an explicit magic-link restriction. A service whose object model forbids symlinks entirely can request RESOLVE_NO_SYMLINKS. The correct constraint follows the namespace semantics the application intends to expose.

Mount crossings form another trust boundary

RESOLVE_NO_XDEV rejects traversal across mount points, including bind mounts. This can prevent a path walk from entering a filesystem mounted beneath an otherwise accepted directory tree.

The restriction is operationally strong. Legitimate directory hierarchies can contain mount points, and bind mounts are common in isolated runtime layouts. Enabling the flag therefore changes which namespace structures are reachable; it is not a cost-free validation option.

Mount restrictions also do not replace object authorization. Reaching a file without crossing a mount says nothing by itself about whether the caller should receive that file. Normal DAC, capability, and Linux Security Module checks still participate where applicable.

Cached resolution trades completeness for a nonblocking lookup condition

RESOLVE_CACHED asks the kernel to complete resolution using cached lookup data without I/O or filesystem revalidation that would require leaving the fast path. If that condition cannot be met, the call can fail with EAGAIN so the application can retry without the flag or delegate the operation elsewhere.

This flag is primarily a lookup-execution constraint rather than a confinement primitive. It should not be interpreted as proof that a cached pathname is more trustworthy. Security policy still comes from the other resolution rules and the ordinary permission checks applied to the resulting lookup.

A returned descriptor is the durable result of the path walk

The main architectural gain appears after successful resolution. Code can perform later operations through the returned file descriptor instead of reconstructing authority from the original pathname. The descriptor references the opened object even if names in the surrounding directory are subsequently renamed.

That does not make every later operation race-free. Applications that reopen child names, derive new paths, or make authorization decisions from mutable metadata introduce additional boundaries. openat2() constrains one path-resolution operation; it does not convert a mutable filesystem namespace into an immutable capability graph.

Used with a directory descriptor and a deliberately selected resolve policy, openat2() moves pathname escape checks into the same kernel path walk that grants the file reference. Its security value comes from that atomic enforcement point, while its limits remain defined by the exact flags, filesystem topology, retained descriptors, and authorization checks around the call.