File: mach_port_rendezvous.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (213 lines) | stat: -rw-r--r-- 8,806 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_APPLE_MACH_PORT_RENDEZVOUS_H_
#define BASE_APPLE_MACH_PORT_RENDEZVOUS_H_

#include <dispatch/dispatch.h>
#include <mach/mach.h>
#include <stdint.h>
#include <sys/types.h>

#include <map>
#include <memory>
#include <vector>

#include "base/apple/dispatch_source.h"
#include "base/apple/scoped_mach_port.h"
#include "base/base_export.h"
#include "base/containers/buffer_iterator.h"
#include "base/feature_list.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "build/ios_buildflags.h"

namespace base {

// Mach Port Rendezvous is a technique to exchange Mach port rights across
// child process creation. macOS does not provide a way to inherit Mach port
// rights, unlike what is possible with file descriptors. Port rendezvous
// enables a parent process to register Mach port rights for a nascent child,
// which the child can then retrieve using Mach IPC by looking up the endpoint
// in launchd's bootstrap namespace.
//
// The same mechanism is used on iOS but the Mach IPC endpoint is not found
// via launchd's bootstrap namespace but via an initial XPC connection.
//
// When launching a child process, the parent process' rendezvous server lets
// calling code register a collection of ports for the new child. In order to
// acquire the ports, a child looks up the rendezvous server in the bootstrap
// namespace, and it sends an IPC message to the server, the reply to which
// contains the registered ports.
//
// Port rendezvous is only permitted between a parent and its direct child
// process descendants.

// A MachRendezvousPort contains a single Mach port to pass to the child
// process. The associated disposition controls how the reference count will
// be manipulated.
class BASE_EXPORT MachRendezvousPort {
 public:
  MachRendezvousPort() = default;
  // Creates a rendezvous port that allows specifying the specific disposition.
  MachRendezvousPort(mach_port_t name, mach_msg_type_name_t disposition);
  // Creates a rendezvous port for MACH_MSG_TYPE_MOVE_SEND.
  explicit MachRendezvousPort(apple::ScopedMachSendRight send_right);
  // Creates a rendezvous port for MACH_MSG_TYPE_MOVE_RECEIVE.
  explicit MachRendezvousPort(apple::ScopedMachReceiveRight receive_right);

  // Note that the destructor does not call Destroy() explicitly.
  // To avoid leaking ports, either use dispositions that create rights during
  // transit (MAKE or COPY), or use base::LaunchProcess, which will destroy
  // rights on failure.
  ~MachRendezvousPort();

  // Destroys the Mach port right type conveyed |disposition| named by |name|.
  void Destroy();

  mach_port_t name() const { return name_; }

  mach_msg_type_name_t disposition() const { return disposition_; }

 private:
  mach_port_t name_ = MACH_PORT_NULL;
  mach_msg_type_name_t disposition_ = 0;
  // Copy and assign allowed.
};

// The collection of ports to pass to a child process. There are no restrictions
// regarding the keys of the map. Clients are responsible for avoiding
// collisions with other clients.
using MachPortsForRendezvous = std::map<uint32_t, MachRendezvousPort>;

// Base class that runs a Mach message server, listening to requests on
// mach server port.
class BASE_EXPORT MachPortRendezvousServerBase {
 protected:
  MachPortRendezvousServerBase();
  virtual ~MachPortRendezvousServerBase();

  // The Mach receive right for the server. A send right to this is port is
  // registered in the bootstrap server.
  apple::ScopedMachReceiveRight server_port_;

  // Mach message dispatch source for |server_port_|.
  std::unique_ptr<apple::DispatchSource> dispatch_source_;

  // Ask for the associated ports associated with `audit_token`.
  // Return `std::nullopt` if the client is not authorized to
  // retrieve ports.
  virtual std::optional<MachPortsForRendezvous> PortsForClient(
      audit_token_t audit_token) = 0;

  // Return whether `msg_id` should be accepted along with the known
  // message IDs. Platform-specific subclasses may return additional data
  // based on the `msg_id` within `AdditionalDataForReply`.
  virtual bool IsValidAdditionalMessageId(mach_msg_id_t msg_id) const = 0;

  // Return additional data to be attached to a reply for `request`.
  virtual std::vector<uint8_t> AdditionalDataForReply(
      mach_msg_id_t request) const = 0;

  // The server-side Mach message handler. Called by |dispatch_source_| when a
  // message is received.
  void HandleRequest();

  // Returns a buffer containing a well-formed Mach message, destined for
  // `reply_port` containing descriptors for the specified `ports` and
  // `additional_data`.
  std::unique_ptr<uint8_t[]> CreateReplyMessage(
      mach_port_t reply_port,
      const MachPortsForRendezvous& ports,
      std::vector<uint8_t> additional_data);
};

// Client class for accessing the memory object exposed by the
// MachPortRendezvousServer.
class BASE_EXPORT MachPortRendezvousClient {
 public:
  MachPortRendezvousClient(const MachPortRendezvousClient&) = delete;
  MachPortRendezvousClient& operator=(const MachPortRendezvousClient&) = delete;

  // Connects to the MachPortRendezvousServer and requests any registered Mach
  // ports. This only performs the rendezvous once. Subsequent calls to this
  // method return the same instance. If the rendezvous fails, which can happen
  // if the server is not available or if the server fails the code signature
  // validation and requirement check, this returns null. Acquiring zero ports
  // from the exchange is not considered a failure.
  static MachPortRendezvousClient* GetInstance();

  // Returns the Mach send right that was registered with |key|. If no such
  // right exists, or it was already taken, returns an invalid right. Safe to
  // call from any thread. DCHECKs if the right referenced by |key| is not a
  // send or send-once right.
  apple::ScopedMachSendRight TakeSendRight(
      MachPortsForRendezvous::key_type key);

  // Returns the Mach receive right that was registered with |key|. If no such
  // right exists, or it was already taken, returns an invalid right. Safe to
  // call from any thread. DCHECKs if the right referenced by |key| is not a
  // receive right.
  apple::ScopedMachReceiveRight TakeReceiveRight(
      MachPortsForRendezvous::key_type key);

  // Returns the number of ports in the client. After PerformRendezvous(), this
  // reflects the number of ports acquired. But as rights are taken, this
  // only reflects the number of remaining rights.
  size_t GetPortCount();

 protected:
  MachPortRendezvousClient();
  virtual ~MachPortRendezvousClient();

  // Perform platform-specific validation on a received message and the peer
  // that sent it.
  virtual bool ValidateMessage(mach_msg_base_t* message,
                               BufferIterator<uint8_t> body) = 0;

  // Sends the actual IPC message to |server_port| and parses the reply.
  bool SendRequest(apple::ScopedMachSendRight server_port,
                   mach_msg_id_t request_id,
                   size_t additional_response_data_size = 0)
      EXCLUSIVE_LOCKS_REQUIRED(lock_);

  // Returns a MachRendezvousPort for a given key and removes it from the
  // |ports_| map. If an entry does not exist for that key, then a
  // MachRendezvousPort with MACH_PORT_NULL is returned.
  MachRendezvousPort PortForKey(MachPortsForRendezvous::key_type key);

  Lock lock_;
  // The collection of ports that was acquired.
  MachPortsForRendezvous ports_ GUARDED_BY(lock_);
};

namespace internal {

// This limit is arbitrary and can be safely increased in the future.
inline constexpr size_t kMaximumRendezvousPorts = 6;

enum MachRendezvousMsgId : mach_msg_id_t {
  kMachRendezvousMsgIdRequest = 'mrzv',
  kMachRendezvousMsgIdResponse = 'MRZV',

#if BUILDFLAG(IS_MAC)
  // When MachPortRendezvousClientMac has a `ProcessRequirement` that requests
  // dynamic-only validation, it will request that the server provide a copy of
  // its Info.plist data in the rendezvous response. Dynamic-only validation
  // validates the running process without enforcing that it matches its on-disk
  // representation. This is necessary when validating applications such as
  // Chrome that may be updated on disk while the application is running.
  //
  // The Info.plist data ends up passed to `SecCodeCopyGuestWithAttributes`,
  // where it is validated against the hash stored within the code signature
  // before using it to evaluate any requirements involving Info.plist data.
  kMachRendezvousMsgIdRequestWithInfoPlistData = 'mrzV',
#endif  // BUILDFLAG(IS_MAC)
};

}  // namespace internal

}  // namespace base

#endif  // BASE_APPLE_MACH_PORT_RENDEZVOUS_H_