platform_handle_in_transit.cc 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Copyright 2018 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. #include "mojo/core/platform_handle_in_transit.h"
  5. #include <utility>
  6. #include "base/debug/alias.h"
  7. #include "base/logging.h"
  8. #include "base/process/process_handle.h"
  9. #include "build/build_config.h"
  10. #if BUILDFLAG(IS_WIN)
  11. #include <ntstatus.h>
  12. #include <windows.h>
  13. #include "base/win/nt_status.h"
  14. #include "base/win/scoped_handle.h"
  15. #endif
  16. namespace mojo {
  17. namespace core {
  18. namespace {
  19. #if BUILDFLAG(IS_WIN)
  20. HANDLE TransferHandle(HANDLE handle,
  21. base::ProcessHandle from_process,
  22. base::ProcessHandle to_process,
  23. PlatformHandleInTransit::TransferTargetTrustLevel trust) {
  24. if (trust == PlatformHandleInTransit::kUntrustedTarget) {
  25. // TODO(https://crbug.com/1335974): Implement additional constraints
  26. // regarding what type of handles may or may not be transferred to untrusted
  27. // processes.
  28. }
  29. HANDLE out_handle;
  30. BOOL result =
  31. ::DuplicateHandle(from_process, handle, to_process, &out_handle, 0, FALSE,
  32. DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
  33. if (result) {
  34. return out_handle;
  35. }
  36. const DWORD error = ::GetLastError();
  37. // ERROR_ACCESS_DENIED may indicate that the remote process (which could be
  38. // either the source or destination process here) is already terminated or has
  39. // begun termination and therefore no longer has a handle table. We don't want
  40. // these cases to crash because we know they happen in practice and are
  41. // largely unavoidable.
  42. if (error == ERROR_ACCESS_DENIED &&
  43. base::win::GetLastNtStatus() == STATUS_PROCESS_IS_TERMINATING) {
  44. DVLOG(1) << "DuplicateHandle from " << from_process << " to " << to_process
  45. << " for handle " << handle
  46. << " failed due to process termination";
  47. return INVALID_HANDLE_VALUE;
  48. }
  49. base::debug::Alias(&handle);
  50. base::debug::Alias(&from_process);
  51. base::debug::Alias(&to_process);
  52. base::debug::Alias(&error);
  53. PLOG(FATAL) << "DuplicateHandle failed from " << from_process << " to "
  54. << to_process << " for handle " << handle;
  55. return INVALID_HANDLE_VALUE;
  56. }
  57. void CloseHandleInProcess(HANDLE handle, const base::Process& process) {
  58. DCHECK_NE(handle, INVALID_HANDLE_VALUE);
  59. DCHECK(process.IsValid());
  60. // The handle lives in |process|, so we close it there using a special
  61. // incantation of |DuplicateHandle()|.
  62. //
  63. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724251 for
  64. // this usage of |DuplicateHandle()|, particularly where it says "to close a
  65. // handle from the source process...". Note that although the documentation
  66. // says that the target *handle* address must be NULL, it seems that the
  67. // target process handle being NULL is what really matters here.
  68. BOOL result = ::DuplicateHandle(process.Handle(), handle, nullptr, &handle, 0,
  69. FALSE, DUPLICATE_CLOSE_SOURCE);
  70. if (!result) {
  71. DPLOG(ERROR) << "DuplicateHandle failed";
  72. }
  73. }
  74. #endif
  75. } // namespace
  76. PlatformHandleInTransit::PlatformHandleInTransit() = default;
  77. PlatformHandleInTransit::PlatformHandleInTransit(PlatformHandle handle)
  78. : handle_(std::move(handle)) {}
  79. PlatformHandleInTransit::PlatformHandleInTransit(
  80. PlatformHandleInTransit&& other) {
  81. *this = std::move(other);
  82. }
  83. PlatformHandleInTransit::~PlatformHandleInTransit() {
  84. #if BUILDFLAG(IS_WIN)
  85. if (!owning_process_.IsValid()) {
  86. DCHECK_EQ(remote_handle_, INVALID_HANDLE_VALUE);
  87. return;
  88. }
  89. CloseHandleInProcess(remote_handle_, owning_process_);
  90. #endif
  91. }
  92. PlatformHandleInTransit& PlatformHandleInTransit::operator=(
  93. PlatformHandleInTransit&& other) {
  94. #if BUILDFLAG(IS_WIN)
  95. if (owning_process_.IsValid()) {
  96. DCHECK_NE(remote_handle_, INVALID_HANDLE_VALUE);
  97. CloseHandleInProcess(remote_handle_, owning_process_);
  98. }
  99. remote_handle_ = INVALID_HANDLE_VALUE;
  100. std::swap(remote_handle_, other.remote_handle_);
  101. #endif
  102. handle_ = std::move(other.handle_);
  103. owning_process_ = std::move(other.owning_process_);
  104. return *this;
  105. }
  106. PlatformHandle PlatformHandleInTransit::TakeHandle() {
  107. DCHECK(!owning_process_.IsValid());
  108. return std::move(handle_);
  109. }
  110. void PlatformHandleInTransit::CompleteTransit() {
  111. #if BUILDFLAG(IS_WIN)
  112. remote_handle_ = INVALID_HANDLE_VALUE;
  113. #endif
  114. handle_.release();
  115. owning_process_ = base::Process();
  116. }
  117. bool PlatformHandleInTransit::TransferToProcess(
  118. base::Process target_process,
  119. TransferTargetTrustLevel trust) {
  120. DCHECK(target_process.IsValid());
  121. DCHECK(!owning_process_.IsValid());
  122. DCHECK(handle_.is_valid());
  123. #if BUILDFLAG(IS_WIN)
  124. remote_handle_ =
  125. TransferHandle(handle_.ReleaseHandle(), base::GetCurrentProcessHandle(),
  126. target_process.Handle(), trust);
  127. if (remote_handle_ == INVALID_HANDLE_VALUE)
  128. return false;
  129. #endif
  130. owning_process_ = std::move(target_process);
  131. return true;
  132. }
  133. #if BUILDFLAG(IS_WIN)
  134. // static
  135. bool PlatformHandleInTransit::IsPseudoHandle(HANDLE handle) {
  136. // Note that there appears to be no official documentation covering the
  137. // existence of specific pseudo handle values. In practice it's clear that
  138. // e.g. -1 is the current process, -2 is the current thread, etc. The largest
  139. // negative value known to be an issue with DuplicateHandle in the fuzzer is
  140. // -12.
  141. //
  142. // Note that there is virtually no risk of a real handle value falling within
  143. // this range and being misclassified as a pseudo handle.
  144. constexpr int kMinimumKnownPseudoHandleValue = -12;
  145. const auto value = static_cast<int32_t>(reinterpret_cast<uintptr_t>(handle));
  146. return value < 0 && value >= kMinimumKnownPseudoHandleValue;
  147. }
  148. // static
  149. PlatformHandle PlatformHandleInTransit::TakeIncomingRemoteHandle(
  150. HANDLE handle,
  151. base::ProcessHandle owning_process) {
  152. return PlatformHandle(base::win::ScopedHandle(
  153. TransferHandle(handle, owning_process, base::GetCurrentProcessHandle(),
  154. kTrustedTarget)));
  155. }
  156. #endif
  157. } // namespace core
  158. } // namespace mojo