A supervisor records PID 4127, performs unrelated work, then sends a signal to 4127. Between those steps, the original process can exit and the kernel can eventually assign the same numeric PID to another process. The integer still names a process, but not necessarily the process that the supervisor intended to affect.

Linux PID file descriptors, commonly called pidfds, move that boundary from repeated numeric lookup to a file descriptor that refers to a particular task. That change is narrow but security-relevant: operations that accept a pidfd can stay bound to the task selected when the reference was acquired rather than resolving a reusable number again.

Numeric PIDs are names, not durable handles

A positive PID is meaningful within a PID namespace and can be reused after the corresponding process is gone. Code that reads a PID, checks a property, and later invokes an operation by the same number has a time gap in which process identity can change.

A prior existence check does not close that gap. For example, a successful kill(pid, 0) establishes facts about the process identified by that PID at the time of the call. A later kill(pid, SIGTERM) performs another PID lookup. The two calls do not form one atomic identity-bound operation.

This distinction matters most when the later action carries authority: terminating a process, entering its namespaces, obtaining one of its file descriptors, or applying another process-directed operation. Permission checks remain necessary, but permission alone does not prove that a recycled numeric identifier still denotes the intended target.

pidfd_open converts a current PID lookup into a kernel reference

pidfd_open(pid, flags) obtains a file descriptor referring to the task selected by pid. The lookup still begins with a numeric PID, so acquisition can fail if the target is already gone. Once the call succeeds, however, the resulting descriptor refers to that selected task rather than becoming a standing instruction to resolve the numeric PID again.

The descriptor is created with close-on-exec set. It also participates in normal file-descriptor lifetime rules: duplicating it creates another descriptor for the same underlying reference, and closing the last copy releases the caller’s reference.

For software that creates the target process itself, clone() or clone3() with CLONE_PIDFD can return a pidfd as part of process creation. That arrangement avoids a separate post-creation PID-to-pidfd acquisition step. pidfd_open() remains the normal interface for acquiring a pidfd for an already existing process.

Signaling through pidfd_send_signal removes PID-reuse retargeting

pidfd_send_signal() sends a signal to the process referred to by a pidfd. The kernel still applies signal permission rules; possessing the descriptor is not a blanket authorization grant.

The identity property is separate from authorization. If the referenced process exits, the pidfd does not silently begin referring to a later process that receives the same numeric PID. A signal operation through that pidfd therefore cannot be retargeted solely by PID reuse.

That property makes a common supervisor sequence materially different:

int pfd = syscall(SYS_pidfd_open, pid, 0);
if (pfd == -1)
    /* target was not acquired */;

/* policy checks and other work */

if (syscall(SYS_pidfd_send_signal, pfd, SIGTERM, NULL, 0) == -1)
    /* handle permission, lifetime, or other errors */;

The pidfd stabilizes target identity across the gap. It does not stabilize credentials, executable image, security label, or application state. If policy depends on those mutable properties, the policy needs its own rules for when they are sampled and whether later transitions are acceptable.

Pollability turns process exit into descriptor state

A pidfd obtained through the supported pidfd interfaces can be monitored with poll(), select(), or epoll(). Process termination makes the descriptor report readiness according to the documented pidfd polling semantics. This lets an event loop associate lifecycle observation with the same kernel reference used for process-directed operations.

That is stronger than repeatedly probing a PID for existence. Polling a pidfd observes the lifecycle of the referenced task; it does not switch to a replacement task after numeric PID reuse.

Waiting has additional relationship rules. A pidfd does not make every referenced process waitable by every caller. waitid() behavior still depends on the process relationship and the way the pidfd was obtained. Stable identity and permission to reap or wait are distinct properties.

pidfd_getfd carries a separate ptrace access boundary

pidfd_getfd(pidfd, targetfd, flags) can duplicate a file descriptor from the process referenced by pidfd. The new descriptor refers to the same open file description as the target descriptor, so file offset and file status flags are shared at that level.

This operation is intentionally not authorized merely by possession of a pidfd. Linux subjects it to a PTRACE_MODE_ATTACH_REALCREDS access check. The pidfd selects the target process without PID-reuse ambiguity; the ptrace access machinery decides whether the caller may cross the process boundary to obtain the descriptor.

That separation is important in capability-oriented designs. A pidfd is a stable reference, but different operations using that reference impose their own authorization conditions. Treating every pidfd as equivalent to unrestricted control over the process would overstate the interface’s security semantics.

PID namespaces still shape acquisition and operation

PID values are namespace-relative. A caller can only pass a PID value that has meaning from its namespace view when acquiring a pidfd, and process-directed operations retain namespace-specific restrictions defined by their interfaces.

The pidfd reduces ambiguity after acquisition; it does not flatten PID namespaces into a global process namespace. A broker that receives numeric PIDs from another namespace still needs an explicit protocol for the namespace in which those numbers are interpreted. Passing an already acquired pidfd over an AF_UNIX socket with SCM_RIGHTS can move a kernel reference between cooperating processes, subject to the receiving process’s ability to use each subsequent operation.

This also separates provenance from authority. Receiving a pidfd can identify a task reference chosen elsewhere, but it does not prove that every operation on that task is permitted or appropriate.

Stable identity narrows one class of process-control race

pidfds remove a specific failure mode from Linux process control: a later pidfd-based operation remains associated with the task referenced by the descriptor instead of re-resolving a reusable PID. This is especially useful when selection and action are separated by event-loop delays, policy evaluation, IPC, or asynchronous supervision.

The boundary remains deliberately limited. A pidfd does not freeze process attributes, bypass signal or ptrace permission checks, guarantee that the task remains alive, or assign application-level meaning to the process. Its security value is the stable kernel identity reference. Systems that keep authorization, mutable process state, namespace interpretation, and lifecycle handling separate can use that property without turning it into a broader guarantee than Linux provides.