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