Home | History | Annotate | Download | only in core
      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