blob: bd8d969fa5884a2405db661cd08dd4a604914938 [file] [log] [blame] [view]
Alex Gough93ec01c2025-04-11 04:52:421# Security guidelines for HANDLEs on Windows
2
3TL;DR;
4
5* Use [ScopedHandle](https://source.chromium.org/chromium/chromium/src/+/main:base/win/scoped_handle.h)
6 whenever possible.
7* If you have to use a raw HANDLE:
8 * Reject invalid values using [base::win::IsPseudoHandle()](https://source.chromium.org/chromium/chromium/src/+/main:base/win/windows_handle_util.h)
9 do not just check against INVALID_HANDLE_VALUE,
10 * Initialize and return nullptr on failure.
11 * Avoid storing pseudo handle values.
12 * Provide GetCurrentProcess() or GetCurrentThread() directly to Windows APIs.
13
14## Discussion
15
16Windows HANDLEs are used to represent objects such as Events, Files, Processes
17and Threads. HANDLEs are typedefd as a pointer-sized type but Windows only uses
1832 bits (with sign extension) so that 32 and 64 bit processes can interoperate.
19
20Windows HANDLEs are generally returned by Windows APIs that create or open
21objects, and must be closed once the object is no longer in use to conserve
22system resources. HANDLEs to the same object might have different access rights
23(for instance, a process handle might only allow waiting on exit, or might allow
24full memory access). HANDLEs can also be duplicated, both within the process
25that created them (to allow code with different lifetimes to refer to the same
26underlying object), and to other processes.
27
28HANDLE has several quirks. Many Windows APIs take or return pseudo handle
29values, such as the return from ::GetCurrentProcess() or ::GetCurrentThread().
30These pseudo handle values may overlap with return values from some handle
31manipulation functions, and malicious code could send these placeholder values
32from less privileged processes.
33
34In particular, GetCurrentProcess() == INVALID_HANDLE_VALUE. Code must be very
35careful not to use these pseudo handle values accidentally. For instance, code
36might intend to open and send a file handle to a renderer, but if the file
37opening failed it might accidentally provide INVALID_HANDLE_VALUE as the source
38handle to DuplicateHandle(), resulting in a handle to the current process being
39sent to the renderer, which would be a serious security bug.
40
41In general it should not be necessary for Chromium code to directly manipulate
42Windows HANDLEs, and instead code should use abstractions such as [base::File](https://source.chromium.org/chromium/chromium/src/+/main:base/files/file.h).
43Chromium, via mojo, makes it easy to send appropriately wrapped objects to
44children using [mojo_base](https://source.chromium.org/chromium/chromium/src/+/main:mojo/public/mojom/base/) wrappers.
45
46# Guidelines
47
48## Use ScopedHandle whenever possible
49
50In Chromium, HANDLEs should always be owned by a [base::win::ScopedHandle](https://source.chromium.org/chromium/chromium/src/+/main:base/win/scoped_handle.h)
51and their underlying HANDLE should only be accessed when a related Windows API
52is called. This ensures that HANDLEs are not leaked and that ownership of the
53underlying object in Chromium code is clear. ScopedHandle refuses to adopt or
54represent pseudo handle values, and will not return them from its .get()
55accessor.
56
57When duplicating a ScopedHandles underlying HANDLE always call
58ScopedHandle::is_valid() before calling DuplicateHandle, and return an empty
59ScopedHandle or a raw nullptr HANDLE if duplication fails.
60
61When adopting a HANDLE value into a base::win::ScopedHandle that another process
62duplicated into the current process (e.g. a log file handle on a command line)
63use [base::win::TakeHandleOfType()](https://source.chromium.org/chromium/chromium/src/+/main:base/win/scoped_handle.h)
64to validate that the HANDLE is not a pseudo handle and points to a valid object
65before adopting it.
66
67## Using HANDLE in Chromium
68
69If you must manipulate HANDLEs directly, the following guidelines apply:
70
71Do not store pseudo handles, and instead simply call ::GetCurrentProcess() when
72you need the pseudo handle value. If you need a real handle to a process (and
73[base::Process](https://source.chromium.org/chromium/chromium/src/+/main:base/process/process.h)
74does not provide what you need), you must duplicate to a real HANDLE before
75adopting it.
76
77Use nullptr to represent uninitialized or invalid values. In code that manages
78its own HANDLEs use nullptr to initialize all HANDLE variables and members and
79return nullptr from any accessor that returns a raw HANDLE while the wrapper
80object is invalid. Use [base::win::IsPseudoHandle()](https://source.chromium.org/chromium/chromium/src/+/main:base/win/windows_handle_util.h) to validate HANDLE values
81provided to constructors (do not just check handle != INVALID_HANDLE_VALUE).