Inside a Pass: How a Path Becomes a Security Signal

OS-Sanitizer’s intercept_path pass makes dynamic defect inference concrete. It examines a path-opening operation not as one function call, but as a sequence of checks, traversals, permissions, and identities. The important judgment is whether readers can see why ownership, privilege, timing, and every component of a path can change the meaning of an otherwise ordinary operation—and how a pass can preserve enough of that context to report a possibility without claiming that an attack has been proved.

The same check can be safe or dangerous

The motivating code performs a familiar task: it checks whether a requested path names a directory and then opens it so that a recursive operation can continue. Nothing about those two steps, read in isolation, announces a security failure. The risk appears in the interval between them and in the people who can change the objects being examined.

That interval is the source of a time-of-check/time-of-use, or TOCTOU, weakness. A check describes the state at one moment. The later use relies on that state still being true. If an intermediate path component changes between the two moments, the operation may act on a different object from the one the check considered.

Even this description needs a boundary. The same implementation can be safe in one environment and dangerous in another. If a privileged user owns the entire tree and no other actor can modify it while the operation runs, the check-then-use pattern may never lead to unwanted behavior in that environment. The code may still violate a best-practice recommendation, but the local conditions do not expose the race.

Change the context and the judgment changes. A less-privileged user may control an intermediate directory or link. A privileged process may apply the routine to content owned by someone else. A downstream container-management tool may inherit a general-purpose library function and use it at a privilege boundary that the library’s original authors did not anticipate. In those settings, the path is not just a string supplied to a function. It is a chain of objects with different owners, permissions, and opportunities to change.

This is why “the program passed its tests” does not settle the question. The tests may have exercised paths in a trusted tree, with one owner and no concurrent modification. They establish that the routine behaved acceptably in those conditions. They do not establish that the same routine preserves its safety guarantee when the path crosses a boundary controlled by another actor.

The paper’s example uses several versions of this distinction. A controlled tree can make the operation benign. A privileged operation on a path containing a component that another user can alter can make redirection possible. A path that passes through an unexpected link can reveal a mismatch between the name the caller supplied and the object the kernel eventually reaches.