A diagnostic agent may need bytes from another process without stopping that process or attaching a traditional debugger. Linux process_vm_readv() provides that data path: the caller supplies local buffers and address ranges in a target process, and the kernel transfers bytes between the two address spaces.
The interface is powerful because the target does not explicitly send the data. Its security boundary therefore sits outside the target’s application protocol. Linux gates the operation with a ptrace access-mode check, while the memory transfer itself remains subject to the target’s changing virtual-memory layout.
Permission is a process-inspection decision
process_vm_readv() does not grant arbitrary same-host memory access. Permission to access another process is governed by a PTRACE_MODE_ATTACH_REALCREDS check. That check belongs to the broader ptrace access-control machinery used when one process can inspect or alter sensitive state belonging to another.
The decision can depend on credentials, capabilities, the target’s dumpable state, and active Linux Security Module policy. As a result, matching a target PID is not sufficient authority. A process supervisor, debugger, crash collector, or observability agent still operates within the kernel’s process-inspection policy.
This also means deployment policy can change the result without changing the process_vm_readv() call itself. LSM policy and ptrace restrictions are part of the effective trust boundary around the syscall.
Address vectors describe two independent address spaces
The syscall accepts arrays of struct iovec for the local and remote sides. Remote entries identify virtual addresses in the target process; local entries identify destinations in the caller.
struct iovec local = {
.iov_base = buffer,
.iov_len = sizeof buffer,
};
struct iovec remote = {
.iov_base = remote_address,
.iov_len = sizeof buffer,
};
ssize_t n = process_vm_readv(pid, &local, 1, &remote, 1, 0);The local and remote vector lengths do not need to match entry by entry. Linux processes the vectors in array order, filling local entries and consuming remote entries according to their lengths. The flags argument is currently required to be zero.
The caller therefore needs two forms of correctness at once: authorization to inspect the process and accurate knowledge of the target’s address layout. The first is enforced by access control; the second is a property of the target’s current memory state.
Remote mappings are validated during transfer
Linux validates the local vector metadata before beginning the operation, but remote memory regions are checked as the transfer reaches them. A remote address that becomes invalid, or an entry that reaches an inaccessible region, can therefore stop an operation after earlier bytes have already moved.
A successful return value can be smaller than the total requested length. Callers that require an exact snapshot must compare the returned byte count with the intended length rather than treating every nonnegative result as complete.
This behavior matters for reads of data structures that can cross page boundaries. A remote range can begin in a valid page and continue into a page that is unavailable. The earlier part may already have been copied when the later access fails.
The syscall documentation recommends avoiding a single remote vector element that crosses a page boundary when reading unknown-length data such as a remotely stored C string. Splitting the remote range at page boundaries limits the point at which an inaccessible page can interrupt a vector element.
A permitted read is not an atomic snapshot
The data transfer is not guaranteed to be atomic. The target process can continue executing and modifying memory while the caller reads it. Two fields copied during one operation can therefore reflect different moments if the target changes them concurrently.
That property separates access from consistency:
ptrace access check -> may the caller inspect this process?
address validity -> can these remote ranges be accessed now?
application state -> do the copied bytes form a coherent snapshot?process_vm_readv() addresses the first two at kernel boundaries. It does not create an application-level snapshot protocol for mutable state.
A monitoring system that reads lock-free counters may accept that trade-off. A crash-analysis tool requiring a coherent object graph may need a separate coordination mechanism, process stop, versioning scheme, or data format designed for concurrent observation. The syscall alone does not supply those semantics.
Numeric process identity remains a separate concern
The target is selected by a numeric PID. That interface does not turn the PID into a persistent object reference. Long-running control software must still account for process lifetime and PID reuse when deciding which task an identifier represents.
The access check protects the process that currently owns the selected identity, but application logic still needs to bind its authorization decision to the intended process instance. A stale PID cached across lifecycle events can point at a different task later.
This concern is distinct from the memory permission check. Kernel authorization can correctly reject or permit access to the current target while the caller has already selected the wrong process due to stale identity state.
Reading and writing share a transport shape but not a security consequence
Linux also provides process_vm_writev(), which transfers bytes in the opposite direction. Both interfaces use local and remote vectors and both are governed by ptrace-style access control, but writing another process’s memory changes target state rather than merely observing it.
Operational policy should therefore avoid treating cross-process memory transfer as one undifferentiated capability. A component that needs observation may have no legitimate reason to mutate target memory. Kernel and LSM policy, process architecture, and privilege assignment should reflect the narrower requirement where possible.
The distinction also matters during incident analysis. A read path can expose credentials, cryptographic material, or application data present in memory. A write path can alter control data or application state. Neither operation should be inferred to be harmless merely because it avoids a traditional ptrace() attach sequence.
The security boundary combines authority, identity, and memory stability
process_vm_readv() is best treated as a privileged observation channel whose contract spans several independent conditions. The caller must pass the ptrace access check, identify the intended live process, provide valid remote addresses, and handle a transfer that may be partial or concurrent with target mutation.
Those conditions produce a narrower guarantee than the phrase cross-process read can suggest. Successful authorization establishes that the kernel permits the inspection relationship at that moment. It does not certify the semantic identity of the bytes, freeze the target, or guarantee that a requested multi-page range arrives as one coherent snapshot.
For security-sensitive tooling, those boundaries should remain explicit. Process-inspection policy decides access, process-lifecycle logic decides target identity, and application-specific coordination decides whether copied memory is consistent enough for the operation being performed.