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