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 5 #include "mojo/core/platform_handle_in_transit.h" 6 7 #include <utility> 8 9 #include "base/logging.h" 10 #include "base/process/process_handle.h" 11 #include "build/build_config.h" 12 13 #if defined(OS_WIN) 14 #include <windows.h> 15 16 #include "base/win/scoped_handle.h" 17 #endif 18 19 namespace mojo { 20 namespace core { 21 22 namespace { 23 24 #if defined(OS_WIN) 25 HANDLE TransferHandle(HANDLE handle, 26 base::ProcessHandle from_process, 27 base::ProcessHandle to_process) { 28 BOOL result = 29 ::DuplicateHandle(from_process, handle, to_process, &handle, 0, FALSE, 30 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); 31 if (result) { 32 return handle; 33 } else { 34 DPLOG(ERROR) << "DuplicateHandle failed"; 35 return INVALID_HANDLE_VALUE; 36 } 37 } 38 39 void CloseHandleInProcess(HANDLE handle, const ScopedProcessHandle& process) { 40 DCHECK_NE(handle, INVALID_HANDLE_VALUE); 41 DCHECK(process.is_valid()); 42 43 // The handle lives in |process|, so we close it there using a special 44 // incantation of |DuplicateHandle()|. 45 // 46 // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724251 for 47 // this usage of |DuplicateHandle()|, particularly where it says "to close a 48 // handle from the source process...". Note that although the documentation 49 // says that the target *handle* address must be NULL, it seems that the 50 // target process handle being NULL is what really matters here. 51 BOOL result = ::DuplicateHandle(process.get(), handle, NULL, &handle, 0, 52 FALSE, DUPLICATE_CLOSE_SOURCE); 53 if (!result) { 54 DPLOG(ERROR) << "DuplicateHandle failed"; 55 } 56 } 57 #endif 58 59 } // namespace 60 61 PlatformHandleInTransit::PlatformHandleInTransit() = default; 62 63 PlatformHandleInTransit::PlatformHandleInTransit(PlatformHandle handle) 64 : handle_(std::move(handle)) {} 65 66 PlatformHandleInTransit::PlatformHandleInTransit( 67 PlatformHandleInTransit&& other) { 68 *this = std::move(other); 69 } 70 71 PlatformHandleInTransit::~PlatformHandleInTransit() { 72 #if defined(OS_WIN) 73 if (!owning_process_.is_valid()) { 74 DCHECK_EQ(remote_handle_, INVALID_HANDLE_VALUE); 75 return; 76 } 77 78 CloseHandleInProcess(remote_handle_, owning_process_); 79 #endif 80 } 81 82 PlatformHandleInTransit& PlatformHandleInTransit::operator=( 83 PlatformHandleInTransit&& other) { 84 #if defined(OS_WIN) 85 if (owning_process_.is_valid()) { 86 DCHECK_NE(remote_handle_, INVALID_HANDLE_VALUE); 87 CloseHandleInProcess(remote_handle_, owning_process_); 88 } 89 90 remote_handle_ = INVALID_HANDLE_VALUE; 91 std::swap(remote_handle_, other.remote_handle_); 92 #elif defined(OS_MACOSX) && !defined(OS_IOS) 93 mach_port_name_ = MACH_PORT_NULL; 94 std::swap(mach_port_name_, other.mach_port_name_); 95 #endif 96 handle_ = std::move(other.handle_); 97 owning_process_ = std::move(other.owning_process_); 98 return *this; 99 } 100 101 PlatformHandle PlatformHandleInTransit::TakeHandle() { 102 DCHECK(!owning_process_.is_valid()); 103 return std::move(handle_); 104 } 105 106 void PlatformHandleInTransit::CompleteTransit() { 107 #if defined(OS_WIN) 108 remote_handle_ = INVALID_HANDLE_VALUE; 109 #endif 110 handle_.release(); 111 owning_process_ = ScopedProcessHandle(); 112 } 113 114 bool PlatformHandleInTransit::TransferToProcess( 115 ScopedProcessHandle target_process) { 116 DCHECK(target_process.is_valid()); 117 DCHECK(!owning_process_.is_valid()); 118 DCHECK(handle_.is_valid()); 119 #if defined(OS_WIN) 120 remote_handle_ = 121 TransferHandle(handle_.ReleaseHandle(), base::GetCurrentProcessHandle(), 122 target_process.get()); 123 if (remote_handle_ == INVALID_HANDLE_VALUE) 124 return false; 125 #endif 126 owning_process_ = std::move(target_process); 127 return true; 128 } 129 130 #if defined(OS_WIN) 131 // static 132 PlatformHandle PlatformHandleInTransit::TakeIncomingRemoteHandle( 133 HANDLE handle, 134 base::ProcessHandle owning_process) { 135 return PlatformHandle(base::win::ScopedHandle( 136 TransferHandle(handle, owning_process, base::GetCurrentProcessHandle()))); 137 } 138 #endif 139 140 #if defined(OS_MACOSX) && !defined(OS_IOS) 141 // static 142 PlatformHandleInTransit PlatformHandleInTransit::CreateForMachPortName( 143 mach_port_t name) { 144 if (name == MACH_PORT_NULL) { 145 return PlatformHandleInTransit( 146 PlatformHandle(base::mac::ScopedMachSendRight())); 147 } 148 149 PlatformHandleInTransit handle; 150 handle.mach_port_name_ = name; 151 return handle; 152 } 153 #endif 154 155 } // namespace core 156 } // namespace mojo 157