Chromium Handle Duplication on Microsoft Windows

This article provides a technical overview of how the Chromium web browser manages the duplication of operating system handles across different processes on Microsoft Windows. Because Chromium relies on a strict multi-process architecture for security and stability, unprivileged child processes cannot directly access most system resources. To bypass this limitation safely, Chromium uses a privileged broker process, the Windows DuplicateHandle API, and its Mojo Inter-Process Communication (IPC) system to seamlessly transfer resource handles—such as files, shared memory, and synchronization objects—across process boundaries without compromising the security sandbox.

The Sandbox Constraint

Chromium separates its workload into multiple processes: a single privileged Browser process and multiple unprivileged child processes (such as Renderers, GPU, and Utility processes). On Windows, these child processes operate inside a restrictive security sandbox. They run with low integrity levels and restricted security tokens, meaning the Windows kernel blocks them from opening files, creating shared memory regions, or accessing hardware directly.

When a sandboxed Renderer process needs to read a file or share a memory buffer with the GPU process, it cannot create the Windows HANDLE itself. Instead, the resource must be created elsewhere and transferred into the Renderer's process space.

The Windows DuplicateHandle API

On Microsoft Windows, a HANDLE is not a global pointer; it is an index into a process-specific handle table managed by the OS kernel. Handle 0x100 in the Browser process means something entirely different than Handle 0x100 in a Renderer process.

To share an underlying kernel object (like a file or memory section) between two processes, Windows provides the DuplicateHandle system call. This function takes a handle from a source process, creates a new entry in the target process's handle table pointing to the same kernel object, and returns the new handle value.

To execute DuplicateHandle, the calling process must possess handles to both the source and target processes with the PROCESS_DUP_HANDLE access right.

The Browser Process as a Broker

For security reasons, sandboxed processes do not have PROCESS_DUP_HANDLE permissions for other processes. If a Renderer had this permission, a compromised Renderer could inject handles into other processes or exhaust system resources.

Chromium solves this by designating the main Browser process as the "Broker." When the Browser process spawns a child process, it retains a privileged process handle to that child. Because the Browser process has PROCESS_DUP_HANDLE rights to all its children, it acts as the central authority for moving handles around:

  1. Browser to Child: The Browser creates a resource handle, calls DuplicateHandle targeting the child process, and sends the resulting numeric handle value to the child over an IPC message.
  2. Child to Browser: The child sends the numeric value of its local handle to the Browser via IPC. The Browser then calls DuplicateHandle, using the child as the source and itself as the target.
  3. Child to Child (e.g., Renderer to GPU): The source child sends a request to the Browser broker. The Browser uses DuplicateHandle to copy the handle directly from the source child's handle table into the destination child's handle table, then informs the destination child of the new handle value.

Mojo IPC Integration

Developers working on Chromium rarely call DuplicateHandle manually. Instead, handle duplication is abstracted away by Mojo, Chromium’s IPC system.

When a developer wants to send a Windows handle across processes, they wrap the native Windows HANDLE into a C++ object called mojo::PlatformHandle. When this object is attached to a Mojo message and sent over a pipe, Mojo’s underlying transport layer (ChannelWin) intercepts it:

  • Mojo automatically strips the raw handle from the payload.
  • If the sender is the Broker, Mojo immediately calls DuplicateHandle into the target process.
  • If the sender is a sandboxed process talking to another sandboxed process, Mojo transparently routes the message through the Broker process, which performs the DuplicateHandle operation before forwarding the message to the final destination.
  • Upon arrival in the target process, Mojo reconstructs the mojo::PlatformHandle with the newly duplicated handle value, making it immediately usable by the receiving code.

Lifecycle Management and Security

Managing handle lifetimes across processes is critical to prevent resource leaks and security vulnerabilities. Chromium enforces strict ownership rules using RAII (Resource Acquisition Is Initialization) wrappers, specifically base::win::ScopedHandle.

When a handle is duplicated for IPC, the DUPLICATE_CLOSE_SOURCE flag is frequently used if the sending process no longer needs the resource. This tells the Windows kernel to automatically close the handle in the source process as soon as it is duplicated into the target, ensuring only one process owns the handle at any time.

Furthermore, Chromium uses DuplicateHandle to enforce the principle of least privilege. When the Browser creates a shared memory region, it has full read-write access. Before duplicating that handle into an untrusted Renderer process, the Browser can modify the dwDesiredAccess parameter in DuplicateHandle to strip write permissions. The Renderer receives a valid handle to the exact same memory space, but the Windows kernel will strictly enforce it as read-only.