1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "test/mac/mach_multiprocess.h"
16 
17 #include <Availability.h>
18 #include <bsm/libbsm.h>
19 
20 #include <memory>
21 #include <string>
22 
23 #include "base/auto_reset.h"
24 #include "base/mac/scoped_mach_port.h"
25 #include "gtest/gtest.h"
26 #include "test/errors.h"
27 #include "test/mac/mach_errors.h"
28 #include "util/file/file_io.h"
29 #include "util/mach/bootstrap.h"
30 #include "util/mach/mach_extensions.h"
31 #include "util/mach/mach_message.h"
32 #include "util/misc/implicit_cast.h"
33 #include "util/misc/random_string.h"
34 #include "util/misc/scoped_forbid_return.h"
35 
36 namespace {
37 
38 // The “hello” message contains a send right to the child process’ task port.
39 struct SendHelloMessage : public mach_msg_base_t {
40   mach_msg_port_descriptor_t port_descriptor;
41 };
42 
43 struct ReceiveHelloMessage : public SendHelloMessage {
44   union {
45     mach_msg_trailer_t trailer;
46     mach_msg_audit_trailer_t audit_trailer;
47   };
48 };
49 
50 }  // namespace
51 
52 namespace crashpad {
53 namespace test {
54 
55 namespace internal {
56 
57 struct MachMultiprocessInfo {
MachMultiprocessInfocrashpad::test::internal::MachMultiprocessInfo58   MachMultiprocessInfo()
59       : service_name(),
60         local_port(MACH_PORT_NULL),
61         remote_port(MACH_PORT_NULL),
62         child_task(TASK_NULL) {
63   }
64 
65   std::string service_name;
66   base::mac::ScopedMachReceiveRight local_port;
67   base::mac::ScopedMachSendRight remote_port;
68   base::mac::ScopedMachSendRight child_task;  // valid only in parent
69 };
70 
71 }  // namespace internal
72 
MachMultiprocess()73 MachMultiprocess::MachMultiprocess() : Multiprocess(), info_(nullptr) {
74 }
75 
Run()76 void MachMultiprocess::Run() {
77   ASSERT_EQ(info_, nullptr);
78   std::unique_ptr<internal::MachMultiprocessInfo> info(
79       new internal::MachMultiprocessInfo);
80   base::AutoReset<internal::MachMultiprocessInfo*> reset_info(&info_,
81                                                               info.get());
82 
83   return Multiprocess::Run();
84 }
85 
~MachMultiprocess()86 MachMultiprocess::~MachMultiprocess() {
87 }
88 
PreFork()89 void MachMultiprocess::PreFork() {
90   ASSERT_NO_FATAL_FAILURE(Multiprocess::PreFork());
91 
92   // Set up the parent port and register it with the bootstrap server before
93   // forking, so that it’s guaranteed to be there when the child attempts to
94   // look it up.
95   info_->service_name = "org.chromium.crashpad.test.mach_multiprocess.";
96   info_->service_name.append(RandomString());
97 
98   info_->local_port = BootstrapCheckIn(info_->service_name);
99   ASSERT_TRUE(info_->local_port.is_valid());
100 }
101 
LocalPort() const102 mach_port_t MachMultiprocess::LocalPort() const {
103   EXPECT_TRUE(info_->local_port.is_valid());
104   return info_->local_port.get();
105 }
106 
RemotePort() const107 mach_port_t MachMultiprocess::RemotePort() const {
108   EXPECT_TRUE(info_->remote_port.is_valid());
109   return info_->remote_port.get();
110 }
111 
ChildTask() const112 task_t MachMultiprocess::ChildTask() const {
113   EXPECT_TRUE(info_->child_task.is_valid());
114   return info_->child_task.get();
115 }
116 
MultiprocessParent()117 void MachMultiprocess::MultiprocessParent() {
118   ReceiveHelloMessage message = {};
119 
120   kern_return_t kr = mach_msg(&message.header,
121                               MACH_RCV_MSG | kMachMessageReceiveAuditTrailer,
122                               0,
123                               sizeof(message),
124                               info_->local_port.get(),
125                               MACH_MSG_TIMEOUT_NONE,
126                               MACH_PORT_NULL);
127   ASSERT_EQ(kr, MACH_MSG_SUCCESS) << MachErrorMessage(kr, "mach_msg");
128 
129   // Comb through the entire message, checking every field against its expected
130   // value.
131   EXPECT_EQ(message.header.msgh_bits,
132             MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MOVE_SEND) |
133                 MACH_MSGH_BITS_COMPLEX);
134   ASSERT_EQ(message.header.msgh_size, sizeof(SendHelloMessage));
135   EXPECT_EQ(message.header.msgh_local_port, info_->local_port);
136   ASSERT_EQ(message.body.msgh_descriptor_count, 1u);
137   EXPECT_EQ(message.port_descriptor.disposition,
138             implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_MOVE_SEND));
139   ASSERT_EQ(
140       message.port_descriptor.type,
141       implicit_cast<mach_msg_descriptor_type_t>(MACH_MSG_PORT_DESCRIPTOR));
142   ASSERT_EQ(message.audit_trailer.msgh_trailer_type,
143             implicit_cast<mach_msg_trailer_type_t>(MACH_MSG_TRAILER_FORMAT_0));
144   ASSERT_EQ(message.audit_trailer.msgh_trailer_size,
145             sizeof(message.audit_trailer));
146   EXPECT_EQ(message.audit_trailer.msgh_seqno, 0u);
147 
148   // Check the audit trailer’s values for sanity. This is a little bit of
149   // overkill, but because the service was registered with the bootstrap server
150   // and other processes will be able to look it up and send messages to it,
151   // these checks disambiguate genuine failures later on in the test from those
152   // that would occur if an errant process sends a message to this service.
153 #if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8
154   uid_t audit_auid;
155   uid_t audit_euid;
156   gid_t audit_egid;
157   uid_t audit_ruid;
158   gid_t audit_rgid;
159   pid_t audit_pid;
160   au_asid_t audit_asid;
161   audit_token_to_au32(message.audit_trailer.msgh_audit,
162                       &audit_auid,
163                       &audit_euid,
164                       &audit_egid,
165                       &audit_ruid,
166                       &audit_rgid,
167                       &audit_pid,
168                       &audit_asid,
169                       nullptr);
170 #else
171   uid_t audit_auid = audit_token_to_auid(message.audit_trailer.msgh_audit);
172   uid_t audit_euid = audit_token_to_euid(message.audit_trailer.msgh_audit);
173   gid_t audit_egid = audit_token_to_egid(message.audit_trailer.msgh_audit);
174   uid_t audit_ruid = audit_token_to_ruid(message.audit_trailer.msgh_audit);
175   gid_t audit_rgid = audit_token_to_rgid(message.audit_trailer.msgh_audit);
176   pid_t audit_pid = audit_token_to_pid(message.audit_trailer.msgh_audit);
177   au_asid_t audit_asid = audit_token_to_asid(message.audit_trailer.msgh_audit);
178 #endif
179   EXPECT_EQ(audit_euid, geteuid());
180   EXPECT_EQ(audit_egid, getegid());
181   EXPECT_EQ(audit_ruid, getuid());
182   EXPECT_EQ(audit_rgid, getgid());
183   ASSERT_EQ(audit_pid, ChildPID());
184 
185   ASSERT_EQ(AuditPIDFromMachMessageTrailer(&message.trailer), ChildPID());
186 
187   auditinfo_addr_t audit_info;
188   int rv = getaudit_addr(&audit_info, sizeof(audit_info));
189   ASSERT_EQ(rv, 0) << ErrnoMessage("getaudit_addr");
190   EXPECT_EQ(audit_auid, audit_info.ai_auid);
191   EXPECT_EQ(audit_asid, audit_info.ai_asid);
192 
193   // Retrieve the remote port from the message header, and the child’s task port
194   // from the message body.
195   info_->remote_port.reset(message.header.msgh_remote_port);
196   info_->child_task.reset(message.port_descriptor.name);
197 
198   // Verify that the child’s task port is what it purports to be.
199   int mach_pid;
200   kr = pid_for_task(info_->child_task.get(), &mach_pid);
201   ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "pid_for_task");
202   ASSERT_EQ(mach_pid, ChildPID());
203 
204   MachMultiprocessParent();
205 
206   info_->remote_port.reset();
207   info_->local_port.reset();
208 }
209 
MultiprocessChild()210 void MachMultiprocess::MultiprocessChild() {
211   ScopedForbidReturn forbid_return;
212 
213   // local_port is not valid in the forked child process.
214   ignore_result(info_->local_port.release());
215 
216   info_->local_port.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
217   ASSERT_NE(info_->local_port, kMachPortNull);
218 
219   // The remote port can be obtained from the bootstrap server.
220   info_->remote_port = BootstrapLookUp(info_->service_name);
221   ASSERT_NE(info_->remote_port, kMachPortNull);
222 
223   // The “hello” message will provide the parent with its remote port, a send
224   // right to the child task’s local port receive right. It will also carry a
225   // send right to the child task’s task port.
226   SendHelloMessage message = {};
227   message.header.msgh_bits =
228       MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND) |
229       MACH_MSGH_BITS_COMPLEX;
230   message.header.msgh_size = sizeof(message);
231   message.header.msgh_remote_port = info_->remote_port.get();
232   message.header.msgh_local_port = info_->local_port.get();
233   message.body.msgh_descriptor_count = 1;
234   message.port_descriptor.name = mach_task_self();
235   message.port_descriptor.disposition = MACH_MSG_TYPE_COPY_SEND;
236   message.port_descriptor.type = MACH_MSG_PORT_DESCRIPTOR;
237 
238   kern_return_t kr = mach_msg(&message.header,
239                               MACH_SEND_MSG,
240                               message.header.msgh_size,
241                               0,
242                               MACH_PORT_NULL,
243                               MACH_MSG_TIMEOUT_NONE,
244                               MACH_PORT_NULL);
245   ASSERT_EQ(kr, MACH_MSG_SUCCESS) << MachErrorMessage(kr, "mach_msg");
246 
247   MachMultiprocessChild();
248 
249   info_->remote_port.reset();
250   info_->local_port.reset();
251 
252   // Close the write pipe now, for cases where the parent is waiting on it to
253   // be closed as an indication that the child has finished.
254   CloseWritePipe();
255 
256   // Wait for the parent process to close its end of the pipe. The child process
257   // needs to remain alive until then because the parent process will attempt to
258   // verify it using the task port it has access to via ChildTask().
259   CheckedReadFileAtEOF(ReadPipeHandle());
260 
261   if (testing::Test::HasFailure()) {
262     // Trigger the ScopedForbidReturn destructor.
263     return;
264   }
265 
266   forbid_return.Disarm();
267 }
268 
269 }  // namespace test
270 }  // namespace crashpad
271