|  | // 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 |