OLD | NEW |
| (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 | |
OLD | NEW |