xref: /aosp_15_r20/external/google-breakpad/src/common/mac/MachIPC.mm (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
1// Copyright 2007 Google LLC
2//
3// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are
5// met:
6//
7//     * Redistributions of source code must retain the above copyright
8// notice, this list of conditions and the following disclaimer.
9//     * Redistributions in binary form must reproduce the above
10// copyright notice, this list of conditions and the following disclaimer
11// in the documentation and/or other materials provided with the
12// distribution.
13//     * Neither the name of Google LLC nor the names of its
14// contributors may be used to endorse or promote products derived from
15// this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28//
29//  MachIPC.mm
30//  Wrapper for mach IPC calls
31
32#import <stdio.h>
33#import "MachIPC.h"
34#include "common/mac/bootstrap_compat.h"
35
36namespace google_breakpad {
37//==============================================================================
38MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() {
39  head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
40
41  // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage()
42  head.msgh_local_port = MACH_PORT_NULL;
43  head.msgh_reserved = 0;
44  head.msgh_id = 0;
45
46  SetDescriptorCount(0);  // start out with no descriptors
47
48  SetMessageID(message_id);
49  SetData(NULL, 0);       // client may add data later
50}
51
52//==============================================================================
53// returns true if successful
54bool MachMessage::SetData(void* data,
55                          int32_t data_length) {
56  // first check to make sure we have enough space
57  size_t size = CalculateSize();
58  size_t new_size = size + data_length;
59
60  if (new_size > sizeof(MachMessage)) {
61    return false;  // not enough space
62  }
63
64  GetDataPacket()->data_length = EndianU32_NtoL(data_length);
65  if (data) memcpy(GetDataPacket()->data, data, data_length);
66
67  CalculateSize();
68
69  return true;
70}
71
72//==============================================================================
73// calculates and returns the total size of the message
74// Currently, the entire message MUST fit inside of the MachMessage
75//    messsage size <= sizeof(MachMessage)
76mach_msg_size_t MachMessage::CalculateSize() {
77  size_t size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
78
79  // add space for MessageDataPacket
80  int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3;
81  size += 2*sizeof(int32_t) + alignedDataLength;
82
83  // add space for descriptors
84  size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor);
85
86  head.msgh_size = static_cast<mach_msg_size_t>(size);
87
88  return head.msgh_size;
89}
90
91//==============================================================================
92MachMessage::MessageDataPacket* MachMessage::GetDataPacket() {
93  size_t desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount();
94  MessageDataPacket* packet =
95    reinterpret_cast<MessageDataPacket*>(padding + desc_size);
96
97  return packet;
98}
99
100//==============================================================================
101void MachMessage::SetDescriptor(int n,
102                                const MachMsgPortDescriptor& desc) {
103  MachMsgPortDescriptor* desc_array =
104    reinterpret_cast<MachMsgPortDescriptor*>(padding);
105  desc_array[n] = desc;
106}
107
108//==============================================================================
109// returns true if successful otherwise there was not enough space
110bool MachMessage::AddDescriptor(const MachMsgPortDescriptor& desc) {
111  // first check to make sure we have enough space
112  int size = CalculateSize();
113  size_t new_size = size + sizeof(MachMsgPortDescriptor);
114
115  if (new_size > sizeof(MachMessage)) {
116    return false;  // not enough space
117  }
118
119  // unfortunately, we need to move the data to allow space for the
120  // new descriptor
121  u_int8_t* p = reinterpret_cast<u_int8_t*>(GetDataPacket());
122  bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t));
123
124  SetDescriptor(GetDescriptorCount(), desc);
125  SetDescriptorCount(GetDescriptorCount() + 1);
126
127  CalculateSize();
128
129  return true;
130}
131
132//==============================================================================
133void MachMessage::SetDescriptorCount(int n) {
134  body.msgh_descriptor_count = n;
135
136  if (n > 0) {
137    head.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
138  } else {
139    head.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX;
140  }
141}
142
143//==============================================================================
144MachMsgPortDescriptor* MachMessage::GetDescriptor(int n) {
145  if (n < GetDescriptorCount()) {
146    MachMsgPortDescriptor* desc =
147      reinterpret_cast<MachMsgPortDescriptor*>(padding);
148    return desc + n;
149  }
150
151  return nil;
152}
153
154//==============================================================================
155mach_port_t MachMessage::GetTranslatedPort(int n) {
156  if (n < GetDescriptorCount()) {
157    return GetDescriptor(n)->GetMachPort();
158  }
159  return MACH_PORT_NULL;
160}
161
162#pragma mark -
163
164//==============================================================================
165// create a new mach port for receiving messages and register a name for it
166ReceivePort::ReceivePort(const char* receive_port_name) {
167  mach_port_t current_task = mach_task_self();
168
169  init_result_ = mach_port_allocate(current_task,
170                                    MACH_PORT_RIGHT_RECEIVE,
171                                    &port_);
172
173  if (init_result_ != KERN_SUCCESS)
174    return;
175
176  init_result_ = mach_port_insert_right(current_task,
177                                        port_,
178                                        port_,
179                                        MACH_MSG_TYPE_MAKE_SEND);
180
181  if (init_result_ != KERN_SUCCESS)
182    return;
183
184  mach_port_t task_bootstrap_port = 0;
185  init_result_ = task_get_bootstrap_port(current_task, &task_bootstrap_port);
186
187  if (init_result_ != KERN_SUCCESS)
188    return;
189
190  init_result_ = breakpad::BootstrapRegister(
191      bootstrap_port,
192      const_cast<char*>(receive_port_name),
193      port_);
194}
195
196//==============================================================================
197// create a new mach port for receiving messages
198ReceivePort::ReceivePort() {
199  mach_port_t current_task = mach_task_self();
200
201  init_result_ = mach_port_allocate(current_task,
202                                    MACH_PORT_RIGHT_RECEIVE,
203                                    &port_);
204
205  if (init_result_ != KERN_SUCCESS)
206    return;
207
208  init_result_ =   mach_port_insert_right(current_task,
209                                          port_,
210                                          port_,
211                                          MACH_MSG_TYPE_MAKE_SEND);
212}
213
214//==============================================================================
215// Given an already existing mach port, use it.  We take ownership of the
216// port and deallocate it in our destructor.
217ReceivePort::ReceivePort(mach_port_t receive_port)
218  : port_(receive_port),
219    init_result_(KERN_SUCCESS) {
220}
221
222//==============================================================================
223ReceivePort::~ReceivePort() {
224  if (init_result_ == KERN_SUCCESS)
225    mach_port_deallocate(mach_task_self(), port_);
226}
227
228//==============================================================================
229kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage* out_message,
230                                          mach_msg_timeout_t timeout) {
231  if (!out_message) {
232    return KERN_INVALID_ARGUMENT;
233  }
234
235  // return any error condition encountered in constructor
236  if (init_result_ != KERN_SUCCESS)
237    return init_result_;
238
239  out_message->head.msgh_bits = 0;
240  out_message->head.msgh_local_port = port_;
241  out_message->head.msgh_remote_port = MACH_PORT_NULL;
242  out_message->head.msgh_reserved = 0;
243  out_message->head.msgh_id = 0;
244
245  mach_msg_option_t options = MACH_RCV_MSG;
246  if (timeout != MACH_MSG_TIMEOUT_NONE)
247    options |= MACH_RCV_TIMEOUT;
248  kern_return_t result = mach_msg(&out_message->head,
249                                  options,
250                                  0,
251                                  sizeof(MachMessage),
252                                  port_,
253                                  timeout,              // timeout in ms
254                                  MACH_PORT_NULL);
255
256  return result;
257}
258
259#pragma mark -
260
261//==============================================================================
262// get a port with send rights corresponding to a named registered service
263MachPortSender::MachPortSender(const char* receive_port_name) {
264  mach_port_t task_bootstrap_port = 0;
265  init_result_ = task_get_bootstrap_port(mach_task_self(),
266                                         &task_bootstrap_port);
267
268  if (init_result_ != KERN_SUCCESS)
269    return;
270
271  init_result_ = bootstrap_look_up(task_bootstrap_port,
272                    const_cast<char*>(receive_port_name),
273                    &send_port_);
274}
275
276//==============================================================================
277MachPortSender::MachPortSender(mach_port_t send_port)
278  : send_port_(send_port),
279    init_result_(KERN_SUCCESS) {
280}
281
282//==============================================================================
283kern_return_t MachPortSender::SendMessage(MachSendMessage& message,
284                                          mach_msg_timeout_t timeout) {
285  if (message.head.msgh_size == 0) {
286    return KERN_INVALID_VALUE;    // just for safety -- never should occur
287  };
288
289  if (init_result_ != KERN_SUCCESS)
290    return init_result_;
291
292  message.head.msgh_remote_port = send_port_;
293
294  kern_return_t result = mach_msg(&message.head,
295                                  MACH_SEND_MSG | MACH_SEND_TIMEOUT,
296                                  message.head.msgh_size,
297                                  0,
298                                  MACH_PORT_NULL,
299                                  timeout,              // timeout in ms
300                                  MACH_PORT_NULL);
301
302  return result;
303}
304
305}  // namespace google_breakpad
306