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