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

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

Issue 1051533002: test: Move util/test to its own top-level directory, test (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Rebase Created 5 years, 8 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
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
21 #include <string>
22
23 #include "base/auto_reset.h"
24 #include "base/logging.h"
25 #include "base/mac/scoped_mach_port.h"
26 #include "base/memory/scoped_ptr.h"
27 #include "base/rand_util.h"
28 #include "gtest/gtest.h"
29 #include "util/file/file_io.h"
30 #include "util/mach/mach_extensions.h"
31 #include "util/mach/mach_message.h"
32 #include "util/misc/scoped_forbid_return.h"
33 #include "util/test/errors.h"
34 #include "util/test/mac/mach_errors.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 {
58 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
73 MachMultiprocess::MachMultiprocess() : Multiprocess(), info_(nullptr) {
74 }
75
76 void MachMultiprocess::Run() {
77 ASSERT_EQ(nullptr, info_);
78 scoped_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
86 MachMultiprocess::~MachMultiprocess() {
87 }
88
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 = "com.googlecode.crashpad.test.mach_multiprocess.";
96 for (int index = 0; index < 16; ++index) {
97 info_->service_name.append(1, base::RandInt('A', 'Z'));
98 }
99
100 mach_port_t local_port;
101 kern_return_t kr = bootstrap_check_in(bootstrap_port,
102 info_->service_name.c_str(),
103 &local_port);
104 ASSERT_EQ(BOOTSTRAP_SUCCESS, kr)
105 << BootstrapErrorMessage(kr, "bootstrap_check_in");
106 info_->local_port.reset(local_port);
107 }
108
109 mach_port_t MachMultiprocess::LocalPort() const {
110 EXPECT_NE(kMachPortNull, info_->local_port);
111 return info_->local_port;
112 }
113
114 mach_port_t MachMultiprocess::RemotePort() const {
115 EXPECT_NE(kMachPortNull, info_->remote_port);
116 return info_->remote_port;
117 }
118
119 task_t MachMultiprocess::ChildTask() const {
120 EXPECT_NE(TASK_NULL, info_->child_task);
121 return info_->child_task;
122 }
123
124 void MachMultiprocess::MultiprocessParent() {
125 ReceiveHelloMessage message = {};
126
127 kern_return_t kr = mach_msg(&message.header,
128 MACH_RCV_MSG | kMachMessageReceiveAuditTrailer,
129 0,
130 sizeof(message),
131 info_->local_port,
132 MACH_MSG_TIMEOUT_NONE,
133 MACH_PORT_NULL);
134 ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg");
135
136 // Comb through the entire message, checking every field against its expected
137 // value.
138 EXPECT_EQ(MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MOVE_SEND) |
139 MACH_MSGH_BITS_COMPLEX,
140 message.header.msgh_bits);
141 ASSERT_EQ(sizeof(SendHelloMessage), message.header.msgh_size);
142 EXPECT_EQ(info_->local_port, message.header.msgh_local_port);
143 ASSERT_EQ(1u, message.body.msgh_descriptor_count);
144 EXPECT_EQ(implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_MOVE_SEND),
145 message.port_descriptor.disposition);
146 ASSERT_EQ(implicit_cast<mach_msg_descriptor_type_t>(MACH_MSG_PORT_DESCRIPTOR),
147 message.port_descriptor.type);
148 ASSERT_EQ(implicit_cast<mach_msg_trailer_type_t>(MACH_MSG_TRAILER_FORMAT_0),
149 message.audit_trailer.msgh_trailer_type);
150 ASSERT_EQ(sizeof(message.audit_trailer),
151 message.audit_trailer.msgh_trailer_size);
152 EXPECT_EQ(0u, message.audit_trailer.msgh_seqno);
153
154 // Check the audit trailer’s values for sanity. This is a little bit of
155 // overkill, but because the service was registered with the bootstrap server
156 // and other processes will be able to look it up and send messages to it,
157 // these checks disambiguate genuine failures later on in the test from those
158 // that would occur if an errant process sends a message to this service.
159 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
160 uid_t audit_auid;
161 uid_t audit_euid;
162 gid_t audit_egid;
163 uid_t audit_ruid;
164 gid_t audit_rgid;
165 pid_t audit_pid;
166 au_asid_t audit_asid;
167 audit_token_to_au32(message.audit_trailer.msgh_audit,
168 &audit_auid,
169 &audit_euid,
170 &audit_egid,
171 &audit_ruid,
172 &audit_rgid,
173 &audit_pid,
174 &audit_asid,
175 nullptr);
176 #else
177 uid_t audit_auid = audit_token_to_auid(message.audit_trailer.msgh_audit);
178 uid_t audit_euid = audit_token_to_euid(message.audit_trailer.msgh_audit);
179 gid_t audit_egid = audit_token_to_egid(message.audit_trailer.msgh_audit);
180 uid_t audit_ruid = audit_token_to_ruid(message.audit_trailer.msgh_audit);
181 gid_t audit_rgid = audit_token_to_rgid(message.audit_trailer.msgh_audit);
182 pid_t audit_pid = audit_token_to_pid(message.audit_trailer.msgh_audit);
183 au_asid_t audit_asid = audit_token_to_asid(message.audit_trailer.msgh_audit);
184 #endif
185 EXPECT_EQ(geteuid(), audit_euid);
186 EXPECT_EQ(getegid(), audit_egid);
187 EXPECT_EQ(getuid(), audit_ruid);
188 EXPECT_EQ(getgid(), audit_rgid);
189 ASSERT_EQ(ChildPID(), audit_pid);
190
191 ASSERT_EQ(ChildPID(), AuditPIDFromMachMessageTrailer(&message.trailer));
192
193 auditinfo_addr_t audit_info;
194 int rv = getaudit_addr(&audit_info, sizeof(audit_info));
195 ASSERT_EQ(0, rv) << ErrnoMessage("getaudit_addr");
196 EXPECT_EQ(audit_info.ai_auid, audit_auid);
197 EXPECT_EQ(audit_info.ai_asid, audit_asid);
198
199 // Retrieve the remote port from the message header, and the child’s task port
200 // from the message body.
201 info_->remote_port.reset(message.header.msgh_remote_port);
202 info_->child_task.reset(message.port_descriptor.name);
203
204 // Verify that the child’s task port is what it purports to be.
205 int mach_pid;
206 kr = pid_for_task(info_->child_task, &mach_pid);
207 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "pid_for_task");
208 ASSERT_EQ(ChildPID(), mach_pid);
209
210 MachMultiprocessParent();
211
212 info_->remote_port.reset();
213 info_->local_port.reset();
214 }
215
216 void MachMultiprocess::MultiprocessChild() {
217 ScopedForbidReturn forbid_return;;
218
219 // local_port is not valid in the forked child process.
220 ignore_result(info_->local_port.release());
221
222 info_->local_port.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
223 ASSERT_NE(kMachPortNull, info_->local_port);
224
225 // The remote port can be obtained from the bootstrap server.
226 mach_port_t remote_port;
227 kern_return_t kr = bootstrap_look_up(
228 bootstrap_port, info_->service_name.c_str(), &remote_port);
229 ASSERT_EQ(BOOTSTRAP_SUCCESS, kr)
230 << BootstrapErrorMessage(kr, "bootstrap_look_up");
231 info_->remote_port.reset(remote_port);
232
233 // The “hello” message will provide the parent with its remote port, a send
234 // right to the child task’s local port receive right. It will also carry a
235 // send right to the child task’s task port.
236 SendHelloMessage message = {};
237 message.header.msgh_bits =
238 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND) |
239 MACH_MSGH_BITS_COMPLEX;
240 message.header.msgh_size = sizeof(message);
241 message.header.msgh_remote_port = info_->remote_port;
242 message.header.msgh_local_port = info_->local_port;
243 message.body.msgh_descriptor_count = 1;
244 message.port_descriptor.name = mach_task_self();
245 message.port_descriptor.disposition = MACH_MSG_TYPE_COPY_SEND;
246 message.port_descriptor.type = MACH_MSG_PORT_DESCRIPTOR;
247
248 kr = mach_msg(&message.header,
249 MACH_SEND_MSG,
250 message.header.msgh_size,
251 0,
252 MACH_PORT_NULL,
253 MACH_MSG_TIMEOUT_NONE,
254 MACH_PORT_NULL);
255 ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg");
256
257 MachMultiprocessChild();
258
259 info_->remote_port.reset();
260 info_->local_port.reset();
261
262 // Close the write pipe now, for cases where the parent is waiting on it to
263 // be closed as an indication that the child has finished.
264 CloseWritePipe();
265
266 // Wait for the parent process to close its end of the pipe. The child process
267 // needs to remain alive until then because the parent process will attempt to
268 // verify it using the task port it has access to via ChildTask().
269 CheckedReadFileAtEOF(ReadPipeHandle());
270
271 if (testing::Test::HasFailure()) {
272 // Trigger the ScopedForbidReturn destructor.
273 return;
274 }
275
276 forbid_return.Disarm();
277 }
278
279 } // namespace test
280 } // namespace crashpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698