Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(15)

Side by Side Diff: util/test/mac/mach_multiprocess.cc

Issue 482483002: Add MachMultiprocess and its test (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@bootstrap
Patch Set: Update documentation Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « util/test/mac/mach_multiprocess.h ('k') | util/test/mac/mach_multiprocess_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 "util/test/mac/mach_multiprocess.h"
16
17 #include <AvailabilityMacros.h>
18 #include <bsm/libbsm.h>
19 #include <servers/bootstrap.h>
20 #include <signal.h>
21 #include <stdlib.h>
22 #include <sys/wait.h>
23
24 #include <string>
25
26 #include "base/auto_reset.h"
27 #include "base/files/scoped_file.h"
28 #include "base/logging.h"
29 #include "base/mac/scoped_mach_port.h"
30 #include "base/rand_util.h"
31 #include "base/strings/stringprintf.h"
32 #include "gtest/gtest.h"
33 #include "util/mach/bootstrap.h"
34 #include "util/test/errors.h"
35 #include "util/test/mac/mach_errors.h"
36
37 namespace {
38
39 class ScopedNotReached {
40 public:
41 ScopedNotReached() {}
42 ~ScopedNotReached() { abort(); }
43
44 private:
45 DISALLOW_COPY_AND_ASSIGN(ScopedNotReached);
46 };
47
48 } // namespace
49
50 namespace crashpad {
51 namespace test {
52
53 using namespace testing;
54
55 MachMultiprocess::MachMultiprocess()
56 : child_pid_(0),
57 pipe_fd_(-1),
58 local_port_(MACH_PORT_NULL),
59 remote_port_(MACH_PORT_NULL),
60 child_task_(MACH_PORT_NULL) {
61 }
62
63 void MachMultiprocess::Run() {
64 int pipe_fds[2];
65 int rv = pipe(pipe_fds);
66 ASSERT_EQ(0, rv) << ErrnoMessage("pipe");
67
68 base::ScopedFD read_pipe(pipe_fds[0]);
69 base::ScopedFD write_pipe(pipe_fds[1]);
70
71 // Set up the parent port and register it with the bootstrap server before
72 // forking, so that it’s guaranteed to be there when the child attempts to
73 // look it up.
74 std::string service_name = "com.googlecode.crashpad.test.mach_multiprocess.";
75 for (int index = 0; index < 16; ++index) {
76 service_name.append(1, base::RandInt('A', 'Z'));
77 }
78
79 mach_port_t local_port;
80 kern_return_t kr =
81 BootstrapCheckIn(bootstrap_port, service_name, &local_port);
82 ASSERT_EQ(BOOTSTRAP_SUCCESS, kr)
83 << BootstrapErrorMessage(kr, "bootstrap_check_in");
84 base::mac::ScopedMachReceiveRight local_port_owner(local_port);
85
86 pid_t pid = fork();
87 ASSERT_GE(pid, 0) << ErrnoMessage("fork");
88
89 // The “hello” message contains a send right to the child process’ task port.
90 struct SendHelloMessage : public mach_msg_base_t {
91 mach_msg_port_descriptor_t port_descriptor;
92 };
93
94 struct ReceiveHelloMessage : public SendHelloMessage {
95 mach_msg_audit_trailer_t audit_trailer;
96 };
97
98 if (pid > 0) {
Robert Sesek 2014/08/19 17:27:51 Split this into private helper routines DoParent()
Mark Mentovai 2014/08/20 00:08:48 rsesek wrote:
99 // Parent.
100 base::AutoReset<pid_t> reset_child_pid(&child_pid_, pid);
101
102 // The parent uses the read end of the pipe.
103 write_pipe.reset();
104 base::AutoReset<int> reset_pipe_fd(&pipe_fd_, read_pipe.get());
105
106 base::AutoReset<mach_port_t> reset_local_port(&local_port_,
107 local_port_owner);
108
109 ReceiveHelloMessage message = {};
110
111 kr = mach_msg(&message.header,
112 MACH_RCV_MSG |
113 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
114 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT),
115 0,
116 sizeof(message),
117 local_port_,
118 MACH_MSG_TIMEOUT_NONE,
119 MACH_PORT_NULL);
120 ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg");
121
122 // Comb through the entire message, checking every field against its
Robert Sesek 2014/08/19 17:27:51 This might be excessive, but that's fine.
123 // expected value.
124 EXPECT_EQ(MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MOVE_SEND) |
125 MACH_MSGH_BITS_COMPLEX,
126 message.header.msgh_bits);
127 ASSERT_EQ(sizeof(SendHelloMessage), message.header.msgh_size);
128 EXPECT_EQ(local_port_, message.header.msgh_local_port);
129 ASSERT_EQ(1u, message.body.msgh_descriptor_count);
130 EXPECT_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_MOVE_SEND),
131 message.port_descriptor.disposition);
132 ASSERT_EQ(static_cast<mach_msg_descriptor_type_t>(MACH_MSG_PORT_DESCRIPTOR),
133 message.port_descriptor.type);
134 ASSERT_EQ(static_cast<mach_msg_trailer_type_t>(MACH_MSG_TRAILER_FORMAT_0),
135 message.audit_trailer.msgh_trailer_type);
136 ASSERT_EQ(sizeof(message.audit_trailer),
137 message.audit_trailer.msgh_trailer_size);
138 EXPECT_EQ(0u, message.audit_trailer.msgh_seqno);
139
140 // Check the audit trailer’s values for sanity.
141 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
142 uid_t audit_auid;
143 uid_t audit_euid;
144 gid_t audit_egid;
145 uid_t audit_ruid;
146 gid_t audit_rgid;
147 pid_t audit_pid;
148 au_asid_t audit_asid;
149 audit_token_to_au32(message.audit_trailer.msgh_audit,
150 &audit_auid,
151 &audit_euid,
152 &audit_egid,
153 &audit_ruid,
154 &audit_rgid,
155 &audit_pid,
156 &audit_asid,
157 NULL);
158 #else
159 uid_t audit_auid = audit_token_to_auid(message.audit_trailer.msgh_audit);
160 uid_t audit_euid = audit_token_to_euid(message.audit_trailer.msgh_audit);
161 gid_t audit_egid = audit_token_to_egid(message.audit_trailer.msgh_audit);
162 uid_t audit_ruid = audit_token_to_ruid(message.audit_trailer.msgh_audit);
163 gid_t audit_rgid = audit_token_to_rgid(message.audit_trailer.msgh_audit);
164 pid_t audit_pid = audit_token_to_pid(message.audit_trailer.msgh_audit);
165 au_asid_t audit_asid =
166 audit_token_to_asid(message.audit_trailer.msgh_audit);
167 #endif
168 EXPECT_EQ(geteuid(), audit_euid);
169 EXPECT_EQ(getegid(), audit_egid);
170 EXPECT_EQ(getuid(), audit_ruid);
171 EXPECT_EQ(getgid(), audit_rgid);
172 ASSERT_EQ(pid, audit_pid);
173
174 auditinfo_addr_t audit_info;
175 rv = getaudit_addr(&audit_info, sizeof(audit_info));
176 ASSERT_EQ(0, rv) << ErrnoMessage("getaudit_addr");
177 EXPECT_EQ(audit_info.ai_auid, audit_auid);
178 EXPECT_EQ(audit_info.ai_asid, audit_asid);
179
180 // Retrieve the remote port from the message header, and the child’s task
181 // port from the message body.
182 base::mac::ScopedMachSendRight remote_port_owner(
183 message.header.msgh_remote_port);
184 base::mac::ScopedMachSendRight child_task_owner(
185 message.port_descriptor.name);
186 base::AutoReset<mach_port_t> reset_remote_port(&remote_port_,
187 remote_port_owner);
188 base::AutoReset<mach_port_t> reset_child_task(&child_task_,
189 child_task_owner);
190
191 // Verify that the child’s task port is what it purports to be.
192 int mach_pid;
193 kr = pid_for_task(child_task_, &mach_pid);
194 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "pid_for_task");
195 ASSERT_EQ(pid, mach_pid);
196
197 Parent();
198
199 remote_port_ = MACH_PORT_NULL;
Robert Sesek 2014/08/19 17:27:51 Do you need to do this? Won't the AutoReset do it
Mark Mentovai 2014/08/20 00:08:48 rsesek wrote:
200 local_port_ = MACH_PORT_NULL;
201 remote_port_owner.reset();
202 local_port_owner.reset();
203
204 pipe_fd_ = -1;
205 read_pipe.reset();
206
207 int status;
208 pid_t wait_pid = waitpid(pid, &status, 0);
209 ASSERT_EQ(pid, wait_pid) << ErrnoMessage("waitpid");
210 if (status != 0) {
211 std::string message;
212 if (WIFEXITED(status)) {
213 message = base::StringPrintf("Child exited with code %d",
214 WEXITSTATUS(status));
215 } else if (WIFSIGNALED(status)) {
216 message = base::StringPrintf("Child terminated by signal %d (%s) %s",
217 WTERMSIG(status),
218 strsignal(WTERMSIG(status)),
219 WCOREDUMP(status) ? " (core dumped)" : "");
220 }
221 ASSERT_EQ(0, status) << message;
222 }
223 } else {
224 // Child.
225 ScopedNotReached must_not_leave_this_scope;
226
227 // local_port is not valid in the forked child process.
228 ignore_result(local_port_owner.release());
229 local_port = MACH_PORT_NULL;
230
231 // The child uses the write end of the pipe.
232 read_pipe.reset();
233 base::AutoReset<int> reset_pipe_fd(&pipe_fd_, write_pipe.get());
234
235 kr = mach_port_allocate(
236 mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &local_port);
237 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_allocate");
238 local_port_owner.reset(local_port);
239 base::AutoReset<mach_port_t> reset_local_port(&local_port_,
240 local_port_owner);
241
242 // The remote port can be obtained from the bootstrap server.
243 mach_port_t remote_port;
244 kr = bootstrap_look_up(bootstrap_port, service_name.c_str(), &remote_port);
Robert Sesek 2014/08/19 17:27:51 This doesn't require that stupid const_cast<>?
Mark Mentovai 2014/08/20 00:08:48 rsesek wrote:
245 ASSERT_EQ(BOOTSTRAP_SUCCESS, kr)
246 << BootstrapErrorMessage(kr, "bootstrap_look_up");
247 base::mac::ScopedMachSendRight remote_port_owner(remote_port);
248 base::AutoReset<mach_port_t> reset_remote_port(&remote_port_,
249 remote_port_owner);
250
251 // The “hello” message will provide the parent with its remote port, a send
252 // right to the child task’s local port receive right. It will also carry a
253 // send right to the child task’s task port.
254 SendHelloMessage message = {};
255 message.header.msgh_bits =
256 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND) |
257 MACH_MSGH_BITS_COMPLEX;
258 message.header.msgh_size = sizeof(message);
259 message.header.msgh_remote_port = remote_port_;
260 message.header.msgh_local_port = local_port_;
261 message.body.msgh_descriptor_count = 1;
262 message.port_descriptor.name = mach_task_self();
263 message.port_descriptor.disposition = MACH_MSG_TYPE_COPY_SEND;
264 message.port_descriptor.type = MACH_MSG_PORT_DESCRIPTOR;
265
266 kr = mach_msg(&message.header,
267 MACH_SEND_MSG,
268 message.header.msgh_size,
269 0,
270 MACH_PORT_NULL,
271 MACH_MSG_TIMEOUT_NONE,
272 MACH_PORT_NULL);
273 ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg");
274
275 Child();
276
277 remote_port_ = MACH_PORT_NULL;
278 local_port_ = MACH_PORT_NULL;
279 remote_port_owner.reset();
280 local_port_owner.reset();
281
282 pipe_fd_ = -1;
283 write_pipe.reset();
284
285 if (Test::HasFailure()) {
286 // Trigger the ScopedNotReached destructor.
287 return;
288 }
289
290 exit(0);
291 }
292 }
293
294 MachMultiprocess::~MachMultiprocess() {
295 }
296
297 pid_t MachMultiprocess::ChildPID() const {
298 DCHECK_NE(child_pid_, 0);
299 return child_pid_;
300 }
301
302 int MachMultiprocess::PipeFD() const {
303 DCHECK_NE(pipe_fd_, -1);
304 return pipe_fd_;
305 }
306
307 mach_port_t MachMultiprocess::LocalPort() const {
308 DCHECK_NE(local_port_, static_cast<mach_port_t>(MACH_PORT_NULL));
309 return local_port_;
310 }
311
312 mach_port_t MachMultiprocess::RemotePort() const {
313 DCHECK_NE(remote_port_, static_cast<mach_port_t>(MACH_PORT_NULL));
314 return remote_port_;
315 }
316
317 mach_port_t MachMultiprocess::ChildTask() const {
318 DCHECK_NE(child_task_, static_cast<mach_port_t>(MACH_PORT_NULL));
319 return child_task_;
320 }
321
322 } // namespace test
323 } // namespace crashpad
OLDNEW
« no previous file with comments | « util/test/mac/mach_multiprocess.h ('k') | util/test/mac/mach_multiprocess_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698