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