| // Copyright 2018 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "mojo/public/cpp/platform/platform_handle.h" | 
 |  | 
 | #include <tuple> | 
 |  | 
 | #include "base/logging.h" | 
 | #include "build/build_config.h" | 
 |  | 
 | #if BUILDFLAG(IS_WIN) | 
 | #include <windows.h> | 
 |  | 
 | #include "base/win/scoped_handle.h" | 
 | #elif BUILDFLAG(IS_FUCHSIA) | 
 | #include <lib/fdio/limits.h> | 
 | #include <unistd.h> | 
 | #include <zircon/status.h> | 
 |  | 
 | #include "base/fuchsia/fuchsia_logging.h" | 
 | #elif BUILDFLAG(IS_APPLE) | 
 | #include <mach/vm_map.h> | 
 |  | 
 | #include "base/apple/mach_logging.h" | 
 | #include "base/apple/scoped_mach_port.h" | 
 | #endif | 
 |  | 
 | #if BUILDFLAG(IS_POSIX) | 
 | #include <unistd.h> | 
 |  | 
 | #include "base/files/scoped_file.h" | 
 | #endif | 
 |  | 
 | namespace mojo { | 
 |  | 
 | namespace { | 
 |  | 
 | #if BUILDFLAG(IS_WIN) | 
 | base::win::ScopedHandle CloneHandle(const base::win::ScopedHandle& handle) { | 
 |   DCHECK(handle.is_valid()); | 
 |  | 
 |   // If a caller does not correctly check the handle returned by file and pipe | 
 |   // creation APIs, or directly provides a pseudo handle value like | 
 |   // ::GetCurrentThread(), then it would result in the destination process | 
 |   // getting full control over the calling process (see http://crbug.com/243339 | 
 |   // for an example of this vulnerability). HandleTraits for Windows rejects | 
 |   // pseudo handle values, but check again here for defense-in-depth. | 
 |   if (!handle.is_valid()) { | 
 |     return base::win::ScopedHandle(); | 
 |   } | 
 |  | 
 |   HANDLE dupe = nullptr; | 
 |   if (!::DuplicateHandle(::GetCurrentProcess(), handle.Get(), | 
 |                          ::GetCurrentProcess(), &dupe, 0, FALSE, | 
 |                          DUPLICATE_SAME_ACCESS)) { | 
 |     return base::win::ScopedHandle(); | 
 |   } | 
 |   DCHECK_NE(dupe, INVALID_HANDLE_VALUE); | 
 |   return base::win::ScopedHandle(dupe); | 
 | } | 
 | #elif BUILDFLAG(IS_FUCHSIA) | 
 | zx::handle CloneHandle(const zx::handle& handle) { | 
 |   DCHECK(handle.is_valid()); | 
 |  | 
 |   zx::handle dupe; | 
 |   zx_status_t result = handle.duplicate(ZX_RIGHT_SAME_RIGHTS, &dupe); | 
 |   if (result != ZX_OK) | 
 |     ZX_DLOG(ERROR, result) << "zx_duplicate_handle"; | 
 |   return std::move(dupe); | 
 | } | 
 | #elif BUILDFLAG(IS_APPLE) | 
 | base::apple::ScopedMachSendRight CloneMachPort( | 
 |     const base::apple::ScopedMachSendRight& mach_port) { | 
 |   DCHECK(mach_port.is_valid()); | 
 |  | 
 |   kern_return_t kr = mach_port_mod_refs(mach_task_self(), mach_port.get(), | 
 |                                         MACH_PORT_RIGHT_SEND, 1); | 
 |   if (kr != KERN_SUCCESS) { | 
 |     MACH_DLOG(ERROR, kr) << "mach_port_mod_refs"; | 
 |     return base::apple::ScopedMachSendRight(); | 
 |   } | 
 |   return base::apple::ScopedMachSendRight(mach_port.get()); | 
 | } | 
 | #endif | 
 |  | 
 | #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
 | base::ScopedFD CloneFD(const base::ScopedFD& fd) { | 
 |   DCHECK(fd.is_valid()); | 
 |   return base::ScopedFD(dup(fd.get())); | 
 | } | 
 | #endif | 
 |  | 
 | }  // namespace | 
 |  | 
 | PlatformHandle::PlatformHandle() = default; | 
 |  | 
 | PlatformHandle::PlatformHandle(PlatformHandle&& other) { | 
 |   *this = std::move(other); | 
 | } | 
 |  | 
 | #if BUILDFLAG(IS_WIN) | 
 | PlatformHandle::PlatformHandle(base::win::ScopedHandle handle) | 
 |     : type_(Type::kHandle), handle_(std::move(handle)) {} | 
 | #elif BUILDFLAG(IS_FUCHSIA) | 
 | PlatformHandle::PlatformHandle(zx::handle handle) | 
 |     : type_(Type::kHandle), handle_(std::move(handle)) {} | 
 | #elif BUILDFLAG(IS_APPLE) | 
 | PlatformHandle::PlatformHandle(base::apple::ScopedMachSendRight mach_port) | 
 |     : type_(Type::kMachSend), mach_send_(std::move(mach_port)) {} | 
 | PlatformHandle::PlatformHandle(base::apple::ScopedMachReceiveRight mach_port) | 
 |     : type_(Type::kMachReceive), mach_receive_(std::move(mach_port)) {} | 
 | #endif | 
 |  | 
 | #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
 | PlatformHandle::PlatformHandle(base::ScopedFD fd) | 
 |     : type_(Type::kFd), fd_(std::move(fd)) { | 
 | #if BUILDFLAG(IS_FUCHSIA) | 
 |   DCHECK_LT(fd_.get(), FDIO_MAX_FD); | 
 | #endif | 
 | } | 
 | #endif | 
 |  | 
 | PlatformHandle::~PlatformHandle() = default; | 
 |  | 
 | PlatformHandle& PlatformHandle::operator=(PlatformHandle&& other) { | 
 |   type_ = other.type_; | 
 |   other.type_ = Type::kNone; | 
 |  | 
 | #if BUILDFLAG(IS_WIN) | 
 |   handle_ = std::move(other.handle_); | 
 | #elif BUILDFLAG(IS_FUCHSIA) | 
 |   handle_ = std::move(other.handle_); | 
 | #elif BUILDFLAG(IS_APPLE) | 
 |   mach_send_ = std::move(other.mach_send_); | 
 |   mach_receive_ = std::move(other.mach_receive_); | 
 | #endif | 
 |  | 
 | #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
 |   fd_ = std::move(other.fd_); | 
 | #endif | 
 |  | 
 |   return *this; | 
 | } | 
 |  | 
 | // static | 
 | void PlatformHandle::ToMojoPlatformHandle(PlatformHandle handle, | 
 |                                           MojoPlatformHandle* out_handle) { | 
 |   DCHECK(out_handle); | 
 |   out_handle->struct_size = sizeof(MojoPlatformHandle); | 
 |   if (handle.type_ == Type::kNone) { | 
 |     out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_INVALID; | 
 |     out_handle->value = 0; | 
 |     return; | 
 |   } | 
 |  | 
 |   do { | 
 | #if BUILDFLAG(IS_WIN) | 
 |     out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE; | 
 |     out_handle->value = | 
 |         static_cast<uint64_t>(HandleToLong(handle.TakeHandle().Take())); | 
 |     break; | 
 | #elif BUILDFLAG(IS_FUCHSIA) | 
 |     if (handle.is_handle()) { | 
 |       out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE; | 
 |       out_handle->value = handle.TakeHandle().release(); | 
 |       break; | 
 |     } | 
 | #elif BUILDFLAG(IS_APPLE) | 
 |     if (handle.is_mach_send()) { | 
 |       out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_MACH_SEND_RIGHT; | 
 |       out_handle->value = static_cast<uint64_t>(handle.ReleaseMachSendRight()); | 
 |       break; | 
 |     } else if (handle.is_mach_receive()) { | 
 |       out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_MACH_RECEIVE_RIGHT; | 
 |       out_handle->value = | 
 |           static_cast<uint64_t>(handle.ReleaseMachReceiveRight()); | 
 |       break; | 
 |     } | 
 | #endif | 
 |  | 
 | #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
 |     DCHECK(handle.is_fd()); | 
 |     out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR; | 
 |     out_handle->value = static_cast<uint64_t>(handle.TakeFD().release()); | 
 | #endif | 
 |   } while (false); | 
 |  | 
 |   // One of the above cases must take ownership of |handle|. | 
 |   DCHECK(!handle.is_valid()); | 
 | } | 
 |  | 
 | // static | 
 | PlatformHandle PlatformHandle::FromMojoPlatformHandle( | 
 |     const MojoPlatformHandle* handle) { | 
 |   if (handle->struct_size < sizeof(*handle) || | 
 |       handle->type == MOJO_PLATFORM_HANDLE_TYPE_INVALID) { | 
 |     return PlatformHandle(); | 
 |   } | 
 |  | 
 | #if BUILDFLAG(IS_WIN) | 
 |   if (handle->type != MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE) | 
 |     return PlatformHandle(); | 
 |   return PlatformHandle( | 
 |       base::win::ScopedHandle(LongToHandle(static_cast<long>(handle->value)))); | 
 | #elif BUILDFLAG(IS_FUCHSIA) | 
 |   if (handle->type == MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE) | 
 |     return PlatformHandle(zx::handle(handle->value)); | 
 | #elif BUILDFLAG(IS_APPLE) | 
 |   if (handle->type == MOJO_PLATFORM_HANDLE_TYPE_MACH_SEND_RIGHT) { | 
 |     return PlatformHandle(base::apple::ScopedMachSendRight( | 
 |         static_cast<mach_port_t>(handle->value))); | 
 |   } else if (handle->type == MOJO_PLATFORM_HANDLE_TYPE_MACH_RECEIVE_RIGHT) { | 
 |     return PlatformHandle(base::apple::ScopedMachReceiveRight( | 
 |         static_cast<mach_port_t>(handle->value))); | 
 |   } | 
 | #endif | 
 |  | 
 | #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
 |   if (handle->type != MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR) | 
 |     return PlatformHandle(); | 
 |   return PlatformHandle(base::ScopedFD(static_cast<int>(handle->value))); | 
 | #endif | 
 | } | 
 |  | 
 | void PlatformHandle::reset() { | 
 |   type_ = Type::kNone; | 
 |  | 
 | #if BUILDFLAG(IS_WIN) | 
 |   handle_.Close(); | 
 | #elif BUILDFLAG(IS_FUCHSIA) | 
 |   handle_.reset(); | 
 | #elif BUILDFLAG(IS_APPLE) | 
 |   mach_send_.reset(); | 
 |   mach_receive_.reset(); | 
 | #endif | 
 |  | 
 | #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
 |   fd_.reset(); | 
 | #endif | 
 | } | 
 |  | 
 | void PlatformHandle::release() { | 
 |   type_ = Type::kNone; | 
 |  | 
 | #if BUILDFLAG(IS_WIN) | 
 |   std::ignore = handle_.Take(); | 
 | #elif BUILDFLAG(IS_FUCHSIA) | 
 |   std::ignore = handle_.release(); | 
 | #elif BUILDFLAG(IS_APPLE) | 
 |   std::ignore = mach_send_.release(); | 
 |   std::ignore = mach_receive_.release(); | 
 | #endif | 
 |  | 
 | #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
 |   std::ignore = fd_.release(); | 
 | #endif | 
 | } | 
 |  | 
 | PlatformHandle PlatformHandle::Clone() const { | 
 | #if BUILDFLAG(IS_WIN) | 
 |   return PlatformHandle(CloneHandle(handle_)); | 
 | #elif BUILDFLAG(IS_FUCHSIA) | 
 |   if (is_valid_handle()) | 
 |     return PlatformHandle(CloneHandle(handle_)); | 
 |   return PlatformHandle(CloneFD(fd_)); | 
 | #elif BUILDFLAG(IS_APPLE) | 
 |   if (is_valid_mach_send()) | 
 |     return PlatformHandle(CloneMachPort(mach_send_)); | 
 |   CHECK(!is_valid_mach_receive()) << "Cannot clone Mach receive rights"; | 
 |   return PlatformHandle(CloneFD(fd_)); | 
 | #elif BUILDFLAG(IS_POSIX) | 
 |   return PlatformHandle(CloneFD(fd_)); | 
 | #endif | 
 | } | 
 |  | 
 | }  // namespace mojo |