xref: /aosp_15_r20/external/cronet/base/win/scoped_handle.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_WIN_SCOPED_HANDLE_H_
6 #define BASE_WIN_SCOPED_HANDLE_H_
7 
8 #include <ostream>
9 
10 #include "base/base_export.h"
11 #include "base/check_op.h"
12 #include "base/dcheck_is_on.h"
13 #include "base/gtest_prod_util.h"
14 #include "base/location.h"
15 #include "base/win/windows_types.h"
16 #include "build/build_config.h"
17 
18 // TODO(rvargas): remove this with the rest of the verifier.
19 #if defined(COMPILER_MSVC)
20 #include <intrin.h>
21 #define BASE_WIN_GET_CALLER _ReturnAddress()
22 #elif defined(COMPILER_GCC)
23 #define BASE_WIN_GET_CALLER \
24   __builtin_extract_return_addr(__builtin_return_address(0))
25 #endif
26 
27 namespace base {
28 namespace win {
29 
30 enum class HandleOperation {
31   kHandleAlreadyTracked,
32   kCloseHandleNotTracked,
33   kCloseHandleNotOwner,
34   kCloseHandleHook,
35   kDuplicateHandleHook
36 };
37 
38 std::ostream& operator<<(std::ostream& os, HandleOperation operation);
39 
40 // Generic wrapper for raw handles that takes care of closing handles
41 // automatically. The class interface follows the style of
42 // the ScopedFILE class with two additions:
43 //   - IsValid() method can tolerate multiple invalid handle values such as NULL
44 //     and INVALID_HANDLE_VALUE (-1) for Win32 handles.
45 //   - Set() (and the constructors and assignment operators that call it)
46 //     preserve the Windows LastError code. This ensures that GetLastError() can
47 //     be called after stashing a handle in a GenericScopedHandle object. Doing
48 //     this explicitly is necessary because of bug 528394 and VC++ 2015.
49 template <class Traits, class Verifier>
50 class GenericScopedHandle {
51  public:
52   using Handle = typename Traits::Handle;
53 
GenericScopedHandle()54   GenericScopedHandle() : handle_(Traits::NullHandle()) {}
55 
GenericScopedHandle(Handle handle)56   explicit GenericScopedHandle(Handle handle) : handle_(Traits::NullHandle()) {
57     Set(handle);
58   }
59 
GenericScopedHandle(GenericScopedHandle && other)60   GenericScopedHandle(GenericScopedHandle&& other)
61       : handle_(Traits::NullHandle()) {
62     Set(other.Take());
63   }
64 
65   GenericScopedHandle(const GenericScopedHandle&) = delete;
66   GenericScopedHandle& operator=(const GenericScopedHandle&) = delete;
67 
~GenericScopedHandle()68   ~GenericScopedHandle() { Close(); }
69 
is_valid()70   bool is_valid() const { return Traits::IsHandleValid(handle_); }
71 
72   // TODO(crbug.com/1291793): Migrate callers to is_valid().
IsValid()73   bool IsValid() const { return is_valid(); }
74 
75   GenericScopedHandle& operator=(GenericScopedHandle&& other) {
76     DCHECK_NE(this, &other);
77     Set(other.Take());
78     return *this;
79   }
80 
Set(Handle handle)81   void Set(Handle handle) {
82     if (handle_ != handle) {
83       // Preserve old LastError to avoid bug 528394.
84       auto last_error = ::GetLastError();
85       Close();
86 
87       if (Traits::IsHandleValid(handle)) {
88         handle_ = handle;
89         Verifier::StartTracking(handle, this, BASE_WIN_GET_CALLER,
90                                 GetProgramCounter());
91       }
92       ::SetLastError(last_error);
93     }
94   }
95 
get()96   Handle get() const { return handle_; }
97 
98   // TODO(crbug.com/1291793): Migrate callers to get().
Get()99   Handle Get() const { return get(); }
100 
101   // Transfers ownership away from this object.
release()102   [[nodiscard]] Handle release() {
103     Handle temp = handle_;
104     handle_ = Traits::NullHandle();
105     if (Traits::IsHandleValid(temp)) {
106       Verifier::StopTracking(temp, this, BASE_WIN_GET_CALLER,
107                              GetProgramCounter());
108     }
109     return temp;
110   }
111 
112   // TODO(crbug.com/1291793): Migrate callers to release().
Take()113   [[nodiscard]] Handle Take() { return release(); }
114 
115   // Explicitly closes the owned handle.
Close()116   void Close() {
117     if (Traits::IsHandleValid(handle_)) {
118       Verifier::StopTracking(handle_, this, BASE_WIN_GET_CALLER,
119                              GetProgramCounter());
120 
121       Traits::CloseHandle(handle_);
122       handle_ = Traits::NullHandle();
123     }
124   }
125 
126  private:
127   FRIEND_TEST_ALL_PREFIXES(ScopedHandleDeathTest, HandleVerifierWrongOwner);
128   FRIEND_TEST_ALL_PREFIXES(ScopedHandleDeathTest,
129                            HandleVerifierUntrackedHandle);
130   Handle handle_;
131 };
132 
133 #undef BASE_WIN_GET_CALLER
134 
135 // The traits class for Win32 handles that can be closed via CloseHandle() API.
136 class HandleTraits {
137  public:
138   using Handle = HANDLE;
139 
140   HandleTraits() = delete;
141   HandleTraits(const HandleTraits&) = delete;
142   HandleTraits& operator=(const HandleTraits&) = delete;
143 
144   // Closes the handle.
145   static bool BASE_EXPORT CloseHandle(HANDLE handle);
146 
147   // Returns true if the handle value is valid.
IsHandleValid(HANDLE handle)148   static bool IsHandleValid(HANDLE handle) {
149     return handle != nullptr && handle != INVALID_HANDLE_VALUE;
150   }
151 
152   // Returns NULL handle value.
NullHandle()153   static HANDLE NullHandle() { return nullptr; }
154 };
155 
156 // Do-nothing verifier.
157 class DummyVerifierTraits {
158  public:
159   using Handle = HANDLE;
160 
161   DummyVerifierTraits() = delete;
162   DummyVerifierTraits(const DummyVerifierTraits&) = delete;
163   DummyVerifierTraits& operator=(const DummyVerifierTraits&) = delete;
164 
StartTracking(HANDLE handle,const void * owner,const void * pc1,const void * pc2)165   static void StartTracking(HANDLE handle,
166                             const void* owner,
167                             const void* pc1,
168                             const void* pc2) {}
StopTracking(HANDLE handle,const void * owner,const void * pc1,const void * pc2)169   static void StopTracking(HANDLE handle,
170                            const void* owner,
171                            const void* pc1,
172                            const void* pc2) {}
173 };
174 
175 // Performs actual run-time tracking.
176 class BASE_EXPORT VerifierTraits {
177  public:
178   using Handle = HANDLE;
179 
180   VerifierTraits() = delete;
181   VerifierTraits(const VerifierTraits&) = delete;
182   VerifierTraits& operator=(const VerifierTraits&) = delete;
183 
184   static void StartTracking(HANDLE handle,
185                             const void* owner,
186                             const void* pc1,
187                             const void* pc2);
188   static void StopTracking(HANDLE handle,
189                            const void* owner,
190                            const void* pc1,
191                            const void* pc2);
192 };
193 
194 using UncheckedScopedHandle =
195     GenericScopedHandle<HandleTraits, DummyVerifierTraits>;
196 using CheckedScopedHandle = GenericScopedHandle<HandleTraits, VerifierTraits>;
197 
198 #if DCHECK_IS_ON()
199 using ScopedHandle = CheckedScopedHandle;
200 #else
201 using ScopedHandle = UncheckedScopedHandle;
202 #endif
203 
204 // This function may be called by the embedder to disable the use of
205 // VerifierTraits at runtime. It has no effect if DummyVerifierTraits is used
206 // for ScopedHandle.
207 BASE_EXPORT void DisableHandleVerifier();
208 
209 // This should be called whenever the OS is closing a handle, if extended
210 // verification of improper handle closing is desired. If |handle| is being
211 // tracked by the handle verifier and ScopedHandle is not the one closing it,
212 // a CHECK is generated.
213 BASE_EXPORT void OnHandleBeingClosed(HANDLE handle, HandleOperation operation);
214 
215 }  // namespace win
216 }  // namespace base
217 
218 #endif  // BASE_WIN_SCOPED_HANDLE_H_
219