| 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 |