{
  "id": 19939148,
  "title": "openat2 Path Confinement Still Needs a Trusted Directory Descriptor",
  "url": "https://urgent.news/2026/11/14/openat2-path-confinement-still-needs-a-trusted-directory-descriptor",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-11-14T07:21:05.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/sorrelthorncroft/openat2-path-confinement-still-needs-a-trusted-directory-descriptor-4c9f"
  },
  "original_language": "en",
  "account": "The Linux openat2() system call offers developers tools to address two key issues when a service accepts a user-supplied filename: keeping the resolved path within an intended directory and avoiding a race condition where an attacker might swap a path component between validation and use. While openat2() provides useful solutions for both problems, it is not a complete authorization system.\n\nThe process begins with a directory file descriptor supplied by the caller, which openat2() uses as a reference point for resolving the provided pathname. Opening this descriptor once, from a trusted base directory, creates a stable reference to the intended directory tree. Resolving the same pathname from the process's working directory is less secure because the working directory can be altered or manipulated by surrounding code.\n\nThe open_how structure controls how the pathname is resolved within openat2(). The RESOLVE_BENEATH option rejects pathnames that venture outside the designated directory tree or result in an absolute path. Conversely, RESOLVE_IN_ROOT treats the supplied directory as a temporary root, resolving absolute paths as though they originated from there. Developers must select the appropriate resolution mode based on their input contract and rigorously test it under various conditions, such as paths containing \"..\", absolute paths, symbolic links, and magic links.\n\nAdditionally, the flags associated with the directory descriptor are crucial. The O_CLOEXEC flag ensures the descriptor is not inherited by child processes, preventing potential leaks. The descriptor returned by openat2() should be utilized for subsequent operations like reading, examining, or passing to worker functions. Reopening the pathname after the call introduces a new resolution challenge.\n\nOpenat2() does not extend file permissions beyond what was originally provided; standard discretionary access controls remain in effect, and a directory descriptor does not inherently grant access to protected files. Furthermore, it cannot guarantee the safety of the file's content. A file may lie within the allowed directory tree yet still harbor malicious data. Path confinement and content validation are independent mechanisms that must be employed together for robust security.\n\nWhen handling errors, it's essential to differentiate between various failure codes. EXDEV, ELOOP, and EAGAIN can signal a blocked escape attempt, a symbolic-link conflict, or a race condition that the kernel was unable to resolve. Failing to distinguish these error types can obscure policy issues for operators. Proper logging should identify the error class and encode paths safely to avoid inadvertently revealing attacker-controlled input.\n\nFor privilege separation, the recommended approach is to combine a stable base directory descriptor with least-privilege file access and a minimal set of system calls. Extensive testing should cover scenarios involving symbolic links pointing outside the protected tree, renamed directories, bind mounts, and concurrent replacement attempts. These tests are designed to evaluate the boundaries of the application's claimed enforcement.\n\nIn summary, the key to effective path confinement in openat2() is to maintain a trusted directory descriptor, apply a clear resolution policy, utilize the returned descriptor for subsequent operations, and thoroughly test error cases. While openat2() mitigates certain pathname race conditions, it does not substitute for comprehensive access control, sandboxing, or careful scrutiny of the contents that an opened file is permitted to contain.",
  "summary": "openat2 Path Confinement Still Needs a Trusted Directory Descriptor A service that accepts a user-supplied filename has two problems to solve. It must keep the resolved path inside an intended directory, and it must avoid a race in which an attacker swaps a path component between validation and use. Linux openat2() gives developers useful tools for both problems, but the call is not a complete…",
  "key_points": [
    "openat2() call helps prevent path resolution outside intended directory",
    "RESOLVEBENEATH option restricts pathnames to designated directory tree",
    "Proper error handling crucial to identify policy issues and attacker-controlled input"
  ],
  "editors_take": "Relying on openat2() without a trusted directory descriptor leaves systems vulnerable to directory escapes, meaning developers must treat this system call as a tool rather than a complete security solution.",
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}