Home | History | Annotate | Download | only in core
      1 // Copyright 2016 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/mach_port_relay.h"
      6 
      7 #include <mach/mach.h>
      8 
      9 #include <utility>
     10 
     11 #include "base/logging.h"
     12 #include "base/mac/mach_port_util.h"
     13 #include "base/mac/scoped_mach_port.h"
     14 #include "base/metrics/histogram_macros.h"
     15 #include "base/process/process.h"
     16 
     17 namespace mojo {
     18 namespace core {
     19 
     20 namespace {
     21 
     22 // Errors that can occur in the broker (privileged parent) process.
     23 // These match tools/metrics/histograms.xml.
     24 // This enum is append-only.
     25 enum class BrokerUMAError : int {
     26   SUCCESS = 0,
     27   // Couldn't get a task port for the process with a given pid.
     28   ERROR_TASK_FOR_PID = 1,
     29   // Couldn't make a port with receive rights in the destination process.
     30   ERROR_MAKE_RECEIVE_PORT = 2,
     31   // Couldn't change the attributes of a Mach port.
     32   ERROR_SET_ATTRIBUTES = 3,
     33   // Couldn't extract a right from the destination.
     34   ERROR_EXTRACT_DEST_RIGHT = 4,
     35   // Couldn't send a Mach port in a call to mach_msg().
     36   ERROR_SEND_MACH_PORT = 5,
     37   // Couldn't extract a right from the source.
     38   ERROR_EXTRACT_SOURCE_RIGHT = 6,
     39   ERROR_MAX
     40 };
     41 
     42 // Errors that can occur in a child process.
     43 // These match tools/metrics/histograms.xml.
     44 // This enum is append-only.
     45 enum class ChildUMAError : int {
     46   SUCCESS = 0,
     47   // An error occurred while trying to receive a Mach port with mach_msg().
     48   ERROR_RECEIVE_MACH_MESSAGE = 1,
     49   ERROR_MAX
     50 };
     51 
     52 void ReportBrokerError(BrokerUMAError error) {
     53   UMA_HISTOGRAM_ENUMERATION("Mojo.MachPortRelay.BrokerError",
     54                             static_cast<int>(error),
     55                             static_cast<int>(BrokerUMAError::ERROR_MAX));
     56 }
     57 
     58 void ReportChildError(ChildUMAError error) {
     59   UMA_HISTOGRAM_ENUMERATION("Mojo.MachPortRelay.ChildError",
     60                             static_cast<int>(error),
     61                             static_cast<int>(ChildUMAError::ERROR_MAX));
     62 }
     63 
     64 }  // namespace
     65 
     66 // static
     67 base::mac::ScopedMachSendRight MachPortRelay::ReceiveSendRight(
     68     base::mac::ScopedMachReceiveRight port) {
     69   // MACH_PORT_NULL doesn't need translation.
     70   if (!port.is_valid())
     71     return base::mac::ScopedMachSendRight();
     72 
     73   // Take ownership of the receive right. We only need it to read this single
     74   // send right, then it can be closed.
     75   base::mac::ScopedMachSendRight received_port(
     76       base::ReceiveMachPort(port.get()));
     77   if (!received_port.is_valid()) {
     78     ReportChildError(ChildUMAError::ERROR_RECEIVE_MACH_MESSAGE);
     79     DLOG(ERROR) << "Error receiving mach port";
     80     return base::mac::ScopedMachSendRight();
     81   }
     82 
     83   ReportChildError(ChildUMAError::SUCCESS);
     84   return received_port;
     85 }
     86 
     87 MachPortRelay::MachPortRelay(base::PortProvider* port_provider)
     88     : port_provider_(port_provider) {
     89   DCHECK(port_provider);
     90   port_provider_->AddObserver(this);
     91 }
     92 
     93 MachPortRelay::~MachPortRelay() {
     94   port_provider_->RemoveObserver(this);
     95 }
     96 
     97 void MachPortRelay::SendPortsToProcess(Channel::Message* message,
     98                                        base::ProcessHandle process) {
     99   DCHECK(message);
    100   mach_port_t task_port = port_provider_->TaskForPid(process);
    101 
    102   std::vector<PlatformHandleInTransit> handles = message->TakeHandles();
    103   // Message should have handles, otherwise there's no point in calling this
    104   // function.
    105   DCHECK(!handles.empty());
    106   for (auto& handle : handles) {
    107     if (!handle.handle().is_valid_mach_port())
    108       continue;
    109 
    110     if (task_port == MACH_PORT_NULL) {
    111       // Callers check the port provider for the task port before calling this
    112       // function, in order to queue pending messages. Therefore, if this fails,
    113       // it should be considered a genuine, bona fide, electrified, six-car
    114       // error.
    115       ReportBrokerError(BrokerUMAError::ERROR_TASK_FOR_PID);
    116       handle = PlatformHandleInTransit(
    117           PlatformHandle(base::mac::ScopedMachSendRight()));
    118       continue;
    119     }
    120 
    121     mach_port_name_t intermediate_port;
    122     base::MachCreateError error_code;
    123     intermediate_port = base::CreateIntermediateMachPort(
    124         task_port, handle.TakeHandle().TakeMachPort(), &error_code);
    125     if (intermediate_port == MACH_PORT_NULL) {
    126       BrokerUMAError uma_error;
    127       switch (error_code) {
    128         case base::MachCreateError::ERROR_MAKE_RECEIVE_PORT:
    129           uma_error = BrokerUMAError::ERROR_MAKE_RECEIVE_PORT;
    130           break;
    131         case base::MachCreateError::ERROR_SET_ATTRIBUTES:
    132           uma_error = BrokerUMAError::ERROR_SET_ATTRIBUTES;
    133           break;
    134         case base::MachCreateError::ERROR_EXTRACT_DEST_RIGHT:
    135           uma_error = BrokerUMAError::ERROR_EXTRACT_DEST_RIGHT;
    136           break;
    137         case base::MachCreateError::ERROR_SEND_MACH_PORT:
    138           uma_error = BrokerUMAError::ERROR_SEND_MACH_PORT;
    139           break;
    140       }
    141       ReportBrokerError(uma_error);
    142       handle = PlatformHandleInTransit(
    143           PlatformHandle(base::mac::ScopedMachSendRight()));
    144       continue;
    145     }
    146 
    147     handle = PlatformHandleInTransit::CreateForMachPortName(intermediate_port);
    148     ReportBrokerError(BrokerUMAError::SUCCESS);
    149   }
    150   message->SetHandles(std::move(handles));
    151 }
    152 
    153 base::mac::ScopedMachSendRight MachPortRelay::ExtractPort(
    154     mach_port_t port_name,
    155     base::ProcessHandle process) {
    156   // No extraction necessary for MACH_PORT_NULL.
    157   if (port_name == MACH_PORT_NULL)
    158     return base::mac::ScopedMachSendRight();
    159 
    160   mach_port_t task_port = port_provider_->TaskForPid(process);
    161   if (task_port == MACH_PORT_NULL) {
    162     ReportBrokerError(BrokerUMAError::ERROR_TASK_FOR_PID);
    163     return base::mac::ScopedMachSendRight();
    164   }
    165 
    166   mach_port_t extracted_right = MACH_PORT_NULL;
    167   mach_msg_type_name_t extracted_right_type;
    168   kern_return_t kr =
    169       mach_port_extract_right(task_port, port_name, MACH_MSG_TYPE_MOVE_SEND,
    170                               &extracted_right, &extracted_right_type);
    171   if (kr != KERN_SUCCESS) {
    172     ReportBrokerError(BrokerUMAError::ERROR_EXTRACT_SOURCE_RIGHT);
    173     return base::mac::ScopedMachSendRight();
    174   }
    175 
    176   ReportBrokerError(BrokerUMAError::SUCCESS);
    177   DCHECK_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND),
    178             extracted_right_type);
    179   return base::mac::ScopedMachSendRight(extracted_right);
    180 }
    181 
    182 void MachPortRelay::AddObserver(Observer* observer) {
    183   base::AutoLock locker(observers_lock_);
    184   bool inserted = observers_.insert(observer).second;
    185   DCHECK(inserted);
    186 }
    187 
    188 void MachPortRelay::RemoveObserver(Observer* observer) {
    189   base::AutoLock locker(observers_lock_);
    190   observers_.erase(observer);
    191 }
    192 
    193 void MachPortRelay::OnReceivedTaskPort(base::ProcessHandle process) {
    194   base::AutoLock locker(observers_lock_);
    195   for (auto* observer : observers_)
    196     observer->OnProcessReady(process);
    197 }
    198 
    199 }  // namespace core
    200 }  // namespace mojo
    201