910e62b5创建于 1月15日历史提交
// 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