pidfd_getfd Duplicates a Target Descriptor into the Calling Process

pidfd_getfd() can place a duplicate of another process’s open file descriptor into the caller’s descriptor table. The returned descriptor is local to the caller, but it refers to the same open file description as the selected descriptor in the target process.

That distinction matters. This operation does not reopen a pathname, reconstruct a socket, or create an independent file position. It duplicates an existing kernel reference across a process boundary.

The pidfd and targetfd identify different objects

The call takes a PID file descriptor and a descriptor number from the target process:

int fd = syscall(SYS_pidfd_getfd, pidfd, targetfd, 0);

pidfd identifies the target task. It can be obtained with pidfd_open() for an existing process. targetfd is interpreted inside that target’s file descriptor table, not inside the caller’s table.

On success, the kernel allocates a new descriptor number in the caller. The numeric value of that new descriptor has no required relationship to targetfd.

The flags argument is reserved and currently must be zero. A nonzero value fails with EINVAL.

Both descriptors share one open file description

The key semantic property is shared open-file state. The target descriptor and the returned descriptor refer to the same open file description.

For a regular file, that means operations that advance the file offset through one descriptor affect the offset observed through the other. File status flags associated with the open file description are shared as well.

This is different from independently calling open() on the same pathname. Separate open() calls normally create separate open file descriptions, so their offsets and open-file status can evolve independently.

The same shared-object rule extends beyond regular files. If the target descriptor refers to a socket, the duplicate refers to that same underlying socket object rather than a newly created socket with similar parameters.

Descriptor flags are a separate layer. Linux sets FD_CLOEXEC on the descriptor returned by pidfd_getfd(), so an execve() in the caller does not retain it unless that flag is cleared explicitly.

The target process does not need to send the descriptor

UNIX domain sockets can transfer descriptor references with SCM_RIGHTS, but that path requires communication between the processes. The sender must participate in constructing and sending the control message.

pidfd_getfd() has a different coordination model. The target process does not need to execute a descriptor-transfer protocol. Given an eligible PID file descriptor, a valid target descriptor number, and sufficient permission, the caller can request the duplicate directly from the kernel.

This makes the interface suitable for process supervisors, inspection systems, and other designs in which descriptor access is controlled externally rather than negotiated through an application protocol. It does not make descriptor access unrestricted.

A ptrace access check forms the permission boundary

Cross-process descriptor duplication is guarded by a PTRACE_MODE_ATTACH_REALCREDS access check. Failure of that check produces EPERM.

The ptrace access machinery considers process credentials, capabilities, target state such as dumpability, and Linux Security Module policy. As a result, matching user IDs alone do not imply that every deployment permits the operation. Policies such as Yama restrictions can further constrain attach-class access.

This permission model is materially stronger than treating a pidfd as an authorization token. Possession of a pidfd identifies a process without relying on a reusable numeric PID alone, but pidfd_getfd() still performs its own access check.

Descriptor validity is evaluated at the call

targetfd must name an open descriptor in the process referred to by pidfd. If it does not, the call fails with EBADF. An invalid PID file descriptor also produces EBADF.

If the referenced process has terminated and has already been waited on, the call can fail with ESRCH. The pidfd prevents ambiguity about process identity, but it does not keep a terminated process’s descriptor table available indefinitely.

The caller also needs room in its own descriptor table. Per-process or system-wide descriptor exhaustion can produce EMFILE or ENFILE.

These failure modes separate three concerns: stable process identity through the pidfd, descriptor existence in the target, and resource availability in the caller.

The duplicate creates a shared operational boundary

After a successful call, the two processes hold descriptor-table entries that converge on the same open file description. Closing either descriptor removes only that process’s reference; it does not invalidate the other descriptor while another reference remains.

Operations that mutate shared open-file state can therefore cross the process boundary. A seek performed by one process can change the position used by the other when the object has a file offset. Changes to shared file status flags can likewise become visible through both references.

That property is the central constraint of pidfd_getfd(): the result is not a snapshot of an external descriptor. It is another live reference to the same kernel open-file state, obtained through a permission-checked process handle.