OLD | NEW |
1 // Copyright 2014 The Crashpad Authors. All rights reserved. | 1 // Copyright 2014 The Crashpad Authors. All rights reserved. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with 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 | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
13 // limitations under the License. | 13 // limitations under the License. |
14 | 14 |
15 #include "client/crashpad_client.h" | 15 #include "client/crashpad_client.h" |
16 | 16 |
17 #include <mach/mach.h> | 17 #include <mach/mach.h> |
18 #include <sys/wait.h> | 18 #include <sys/wait.h> |
19 #include <unistd.h> | 19 #include <unistd.h> |
20 | 20 |
21 #include "base/logging.h" | 21 #include "base/logging.h" |
| 22 #include "base/mac/mach_logging.h" |
22 #include "base/posix/eintr_wrapper.h" | 23 #include "base/posix/eintr_wrapper.h" |
23 #include "base/strings/stringprintf.h" | 24 #include "base/strings/stringprintf.h" |
24 #include "util/mach/child_port_handshake.h" | 25 #include "util/mach/child_port_handshake.h" |
25 #include "util/mach/exception_ports.h" | 26 #include "util/mach/exception_ports.h" |
26 #include "util/mach/mach_extensions.h" | 27 #include "util/mach/mach_extensions.h" |
| 28 #include "util/misc/implicit_cast.h" |
27 #include "util/posix/close_multiple.h" | 29 #include "util/posix/close_multiple.h" |
28 | 30 |
29 namespace crashpad { | 31 namespace crashpad { |
30 | 32 |
31 namespace { | 33 namespace { |
32 | 34 |
33 std::string FormatArgumentString(const std::string& name, | 35 std::string FormatArgumentString(const std::string& name, |
34 const std::string& value) { | 36 const std::string& value) { |
35 return base::StringPrintf("--%s=%s", name.c_str(), value.c_str()); | 37 return base::StringPrintf("--%s=%s", name.c_str(), value.c_str()); |
36 } | 38 } |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
70 // so AND them with ExcMaskValid(). EXC_MASK_CRASH is always supported. | 72 // so AND them with ExcMaskValid(). EXC_MASK_CRASH is always supported. |
71 bool SetCrashExceptionPorts(exception_handler_t exception_handler) { | 73 bool SetCrashExceptionPorts(exception_handler_t exception_handler) { |
72 ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL); | 74 ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL); |
73 return exception_ports.SetExceptionPort( | 75 return exception_ports.SetExceptionPort( |
74 (EXC_MASK_CRASH | EXC_MASK_RESOURCE | EXC_MASK_GUARD) & ExcMaskValid(), | 76 (EXC_MASK_CRASH | EXC_MASK_RESOURCE | EXC_MASK_GUARD) & ExcMaskValid(), |
75 exception_handler, | 77 exception_handler, |
76 EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, | 78 EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, |
77 MACHINE_THREAD_STATE); | 79 MACHINE_THREAD_STATE); |
78 } | 80 } |
79 | 81 |
| 82 //! \brief Starts a Crashpad handler. |
| 83 class HandlerStarter final { |
| 84 public: |
| 85 //! \brief Starts a Crashpad handler. |
| 86 //! |
| 87 //! All parameters are as in CrashpadClient::StartHandler(). |
| 88 //! |
| 89 //! \return On success, a send right to the Crashpad handler that has been |
| 90 //! started. On failure, `MACH_PORT_NULL` with a message logged. |
| 91 // static base::mac::ScopedMachSendRight Start( |
| 92 // const base::FilePath& handler, |
| 93 // const base::FilePath& database, |
| 94 // const std::string& url, |
| 95 // const std::map<std::string, std::string>& annotations, |
| 96 // const std::vector<std::string>& arguments) { |
| 97 |
| 98 private: |
| 99 // static bool CommonStart(const base::FilePath& handler, |
| 100 // const base::FilePath& database, |
| 101 // const std::string& url, |
| 102 // const std::map<std::string, std::string>& annotation
s, |
| 103 // const std::vector<std::string>& arguments, |
| 104 // base::mac::ScopedMachReceiveRight receive_right) { |
| 105 |
| 106 DISALLOW_IMPLICIT_CONSTRUCTORS(HandlerStarter); |
| 107 }; |
| 108 |
80 } // namespace | 109 } // namespace |
81 | 110 |
82 CrashpadClient::CrashpadClient() | 111 CrashpadClient::CrashpadClient() |
83 : exception_port_() { | 112 : exception_port_() { |
84 } | 113 } |
85 | 114 |
86 CrashpadClient::~CrashpadClient() { | 115 CrashpadClient::~CrashpadClient() { |
87 } | 116 } |
88 | 117 |
89 bool CrashpadClient::StartHandler( | 118 bool CrashpadClient::StartHandler( |
90 const base::FilePath& handler, | 119 const base::FilePath& handler, |
91 const base::FilePath& database, | 120 const base::FilePath& database, |
92 const std::string& url, | 121 const std::string& url, |
93 const std::map<std::string, std::string>& annotations, | 122 const std::map<std::string, std::string>& annotations, |
94 const std::vector<std::string>& arguments) { | 123 const std::vector<std::string>& arguments) { |
95 DCHECK(!exception_port_.is_valid()); | 124 DCHECK(!exception_port_.is_valid()); |
96 | 125 |
97 // Set up the arguments for execve() first. These aren’t needed until execve() | 126 // exception_port_ = HandlerStarter::Start( |
98 // is called, but it’s dangerous to do this in a child process after fork(). | 127 // handler, database, url, annotations, arguments); |
| 128 |
| 129 // static base::mac::ScopedMachSendRight Start( |
| 130 // const base::FilePath& handler, |
| 131 // const base::FilePath& database, |
| 132 // const std::string& url, |
| 133 // const std::map<std::string, std::string>& annotations, |
| 134 // const std::vector<std::string>& arguments) { |
| 135 base::mac::ScopedMachReceiveRight receive_right( |
| 136 NewMachPort(MACH_PORT_RIGHT_RECEIVE)); |
| 137 if (receive_right == kMachPortNull) { |
| 138 return base::mac::ScopedMachSendRight(); |
| 139 } |
| 140 |
| 141 mach_port_t port; |
| 142 mach_msg_type_name_t right_type; |
| 143 kern_return_t kr = mach_port_extract_right(mach_task_self(), |
| 144 receive_right.get(), |
| 145 MACH_MSG_TYPE_MAKE_SEND, |
| 146 &port, |
| 147 &right_type); |
| 148 if (kr != KERN_SUCCESS) { |
| 149 MACH_LOG(ERROR, kr) << "mach_port_extract_right"; |
| 150 return base::mac::ScopedMachSendRight(); |
| 151 } |
| 152 base::mac::ScopedMachSendRight send_right(port); |
| 153 DCHECK_EQ(port, receive_right.get()); |
| 154 DCHECK_EQ(right_type, |
| 155 implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND)); |
| 156 |
| 157 if (!CommonStart(handler, |
| 158 database, |
| 159 url, |
| 160 annotations, |
| 161 arguments, |
| 162 receive_right.Pass())) { |
| 163 return base::mac::ScopedMachSendRight(); |
| 164 } |
| 165 |
| 166 return send_right; |
| 167 |
| 168 // static bool CommonStart(const base::FilePath& handler, |
| 169 // const base::FilePath& database, |
| 170 // const std::string& url, |
| 171 // const std::map<std::string, std::string>& annotation
s, |
| 172 // const std::vector<std::string>& arguments, |
| 173 // base::mac::ScopedMachReceiveRight receive_right) { |
| 174 // Set up the arguments for execve() first. These aren’t needed until |
| 175 // execve() is called, but it’s dangerous to do this in a child process |
| 176 // after fork(). |
99 ChildPortHandshake child_port_handshake; | 177 ChildPortHandshake child_port_handshake; |
100 base::ScopedFD client_read_fd = child_port_handshake.ClientReadFD(); | 178 base::ScopedFD server_write_fd = child_port_handshake.ServerWriteFD(); |
101 | 179 |
102 // Use handler as argv[0], followed by arguments directed by this method’s | 180 // Use handler as argv[0], followed by arguments directed by this method’s |
103 // parameters and a --handshake-fd argument. |arguments| are added first so | 181 // parameters and a --handshake-fd argument. |arguments| are added first so |
104 // that if it erroneously contains an argument such as --url, the actual |url| | 182 // that if it erroneously contains an argument such as --url, the actual |
105 // argument passed to this method will supersede it. In normal command-line | 183 // |url| argument passed to this method will supersede it. In normal |
106 // processing, the last parameter wins in the case of a conflict. | 184 // command-line processing, the last parameter wins in the case of a |
| 185 // conflict. |
107 std::vector<std::string> argv(1, handler.value()); | 186 std::vector<std::string> argv(1, handler.value()); |
108 argv.reserve(1 + arguments.size() + 2 + annotations.size() + 1); | 187 argv.reserve(1 + arguments.size() + 2 + annotations.size() + 1); |
109 for (const std::string& argument : arguments) { | 188 for (const std::string& argument : arguments) { |
110 argv.push_back(argument); | 189 argv.push_back(argument); |
111 } | 190 } |
112 if (!database.value().empty()) { | 191 if (!database.value().empty()) { |
113 argv.push_back(FormatArgumentString("database", database.value())); | 192 argv.push_back(FormatArgumentString("database", database.value())); |
114 } | 193 } |
115 if (!url.empty()) { | 194 if (!url.empty()) { |
116 argv.push_back(FormatArgumentString("url", url)); | 195 argv.push_back(FormatArgumentString("url", url)); |
117 } | 196 } |
118 for (const auto& kv : annotations) { | 197 for (const auto& kv : annotations) { |
119 argv.push_back( | 198 argv.push_back( |
120 FormatArgumentString("annotation", kv.first + '=' + kv.second)); | 199 FormatArgumentString("annotation", kv.first + '=' + kv.second)); |
121 } | 200 } |
122 argv.push_back(FormatArgumentInt("handshake-fd", client_read_fd.get())); | 201 argv.push_back(FormatArgumentInt("handshake-fd", server_write_fd.get())); |
| 202 |
| 203 const char* handler_c = handler.value().c_str(); |
123 | 204 |
124 // argv_c contains const char* pointers and is terminated by nullptr. argv | 205 // argv_c contains const char* pointers and is terminated by nullptr. argv |
125 // is required because the pointers in argv_c need to point somewhere, and | 206 // is required because the pointers in argv_c need to point somewhere, and |
126 // they can’t point to temporaries such as those returned by | 207 // they can’t point to temporaries such as those returned by |
127 // FormatArgumentString(). | 208 // FormatArgumentString(). |
128 std::vector<const char*> argv_c; | 209 std::vector<const char*> argv_c; |
129 argv_c.reserve(argv.size() + 1); | 210 argv_c.reserve(argv.size() + 1); |
130 for (const std::string& argument : argv) { | 211 for (const std::string& argument : argv) { |
131 argv_c.push_back(argument.c_str()); | 212 argv_c.push_back(argument.c_str()); |
132 } | 213 } |
133 argv_c.push_back(nullptr); | 214 argv_c.push_back(nullptr); |
134 | 215 |
135 // Double-fork(). The three processes involved are parent, child, and | 216 // Double-fork(). The three processes involved are parent, child, and |
136 // grandchild. The grandchild will become the handler process. The child exits | 217 // grandchild. The grandchild will become the handler process. The child |
137 // immediately after spawning the grandchild, so the grandchild becomes an | 218 // exits immediately after spawning the grandchild, so the grandchild |
138 // orphan and its parent process ID becomes 1. This relieves the parent and | 219 // becomes an orphan and its parent process ID becomes 1. This relieves the |
139 // child of the responsibility for reaping the grandchild with waitpid() or | 220 // parent and child of the responsibility for reaping the grandchild with |
140 // similar. The handler process is expected to outlive the parent process, so | 221 // waitpid() or similar. The handler process is expected to outlive the |
141 // the parent shouldn’t be concerned with reaping it. This approach means that | 222 // parent process, so the parent shouldn’t be concerned with reaping it. |
142 // accidental early termination of the handler process will not result in a | 223 // This approach means that accidental early termination of the handler |
143 // zombie process. | 224 // process will not result in a zombie process. |
144 pid_t pid = fork(); | 225 pid_t pid = fork(); |
145 if (pid < 0) { | 226 if (pid < 0) { |
146 PLOG(ERROR) << "fork"; | 227 PLOG(ERROR) << "fork"; |
147 return false; | 228 return false; |
148 } | 229 } |
149 | 230 |
150 if (pid == 0) { | 231 if (pid == 0) { |
151 // Child process. | 232 // Child process. |
152 | 233 |
153 // Call setsid(), creating a new process group and a new session, both led | 234 // Call setsid(), creating a new process group and a new session, both led |
154 // by this process. The new process group has no controlling terminal. This | 235 // by this process. The new process group has no controlling terminal. |
155 // disconnects it from signals generated by the parent process’ terminal. | 236 // This disconnects it from signals generated by the parent process’ |
| 237 // terminal. |
156 // | 238 // |
157 // setsid() is done in the child instead of the grandchild so that the | 239 // setsid() is done in the child instead of the grandchild so that the |
158 // grandchild will not be a session leader. If it were a session leader, an | 240 // grandchild will not be a session leader. If it were a session leader, |
159 // accidental open() of a terminal device without O_NOCTTY would make that | 241 // an accidental open() of a terminal device without O_NOCTTY would make |
160 // terminal the controlling terminal. | 242 // that terminal the controlling terminal. |
161 // | 243 // |
162 // It’s not desirable for the handler to have a controlling terminal. The | 244 // It’s not desirable for the handler to have a controlling terminal. The |
163 // handler monitors clients on its own and manages its own lifetime, exiting | 245 // handler monitors clients on its own and manages its own lifetime, |
164 // when it loses all clients and when it deems it appropraite to do so. It | 246 // exiting when it loses all clients and when it deems it appropraite to |
165 // may serve clients in different process groups or sessions than its | 247 // do so. It may serve clients in different process groups or sessions |
166 // original client, and receiving signals intended for its original client’s | 248 // than its original client, and receiving signals intended for its |
167 // process group could be harmful in that case. | 249 // original client’s process group could be harmful in that case. |
168 PCHECK(setsid() != -1) << "setsid"; | 250 PCHECK(setsid() != -1) << "setsid"; |
169 | 251 |
170 pid = fork(); | 252 pid = fork(); |
171 if (pid < 0) { | 253 if (pid < 0) { |
172 PLOG(FATAL) << "fork"; | 254 PLOG(FATAL) << "fork"; |
173 } | 255 } |
174 | 256 |
175 if (pid > 0) { | 257 if (pid > 0) { |
176 // Child process. | 258 // Child process. |
177 | 259 |
178 // _exit() instead of exit(), because fork() was called. | 260 // _exit() instead of exit(), because fork() was called. |
179 _exit(EXIT_SUCCESS); | 261 _exit(EXIT_SUCCESS); |
180 } | 262 } |
181 | 263 |
182 // Grandchild process. | 264 // Grandchild process. |
183 | 265 |
184 CloseMultipleNowOrOnExec(STDERR_FILENO + 1, client_read_fd.get()); | 266 CloseMultipleNowOrOnExec(STDERR_FILENO + 1, server_write_fd.get()); |
185 | 267 |
186 // &argv_c[0] is a pointer to a pointer to const char data, but because of | 268 // &argv_c[0] is a pointer to a pointer to const char data, but because of |
187 // how C (not C++) works, execvp() wants a pointer to a const pointer to | 269 // how C (not C++) works, execvp() wants a pointer to a const pointer to |
188 // char data. It modifies neither the data nor the pointers, so the | 270 // char data. It modifies neither the data nor the pointers, so the |
189 // const_cast is safe. | 271 // const_cast is safe. |
190 execvp(handler.value().c_str(), const_cast<char* const*>(&argv_c[0])); | 272 execvp(handler_c, const_cast<char* const*>(&argv_c[0])); |
191 PLOG(FATAL) << "execvp " << handler.value(); | 273 PLOG(FATAL) << "execvp " << handler_c; |
192 } | 274 } |
193 | 275 |
194 // Parent process. | 276 // Parent process. |
195 | 277 |
196 client_read_fd.reset(); | 278 // Close the write side of the pipe, so that the handler process is the only |
| 279 // process that can write to it. |
| 280 server_write_fd.reset(); |
197 | 281 |
198 // waitpid() for the child, so that it does not become a zombie process. The | 282 // waitpid() for the child, so that it does not become a zombie process. The |
199 // child normally exits quickly. | 283 // child normally exits quickly. |
200 int status; | 284 int status; |
201 pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0)); | 285 pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0)); |
202 PCHECK(wait_pid != -1) << "waitpid"; | 286 PCHECK(wait_pid != -1) << "waitpid"; |
203 DCHECK_EQ(wait_pid, pid); | 287 DCHECK_EQ(wait_pid, pid); |
204 | 288 |
205 if (WIFSIGNALED(status)) { | 289 if (WIFSIGNALED(status)) { |
206 LOG(WARNING) << "intermediate process: signal " << WTERMSIG(status); | 290 LOG(WARNING) << "intermediate process: signal " << WTERMSIG(status); |
207 } else if (!WIFEXITED(status)) { | 291 } else if (!WIFEXITED(status)) { |
208 DLOG(WARNING) << "intermediate process: unknown termination " << status; | 292 DLOG(WARNING) << "intermediate process: unknown termination " << status; |
209 } else if (WEXITSTATUS(status) != EXIT_SUCCESS) { | 293 } else if (WEXITSTATUS(status) != EXIT_SUCCESS) { |
210 LOG(WARNING) << "intermediate process: exit status " << WEXITSTATUS(status); | 294 LOG(WARNING) << "intermediate process: exit status " |
| 295 << WEXITSTATUS(status); |
211 } | 296 } |
212 | 297 |
213 // Rendezvous with the handler running in the grandchild process. | 298 // Rendezvous with the handler running in the grandchild process. |
214 exception_port_.reset(child_port_handshake.RunServer( | 299 if (!child_port_handshake.RunClient(receive_right.get(), |
215 ChildPortHandshake::PortRightType::kSendRight)); | 300 MACH_MSG_TYPE_MOVE_RECEIVE)) { |
| 301 return false; |
| 302 } |
216 | 303 |
217 return exception_port_.is_valid(); | 304 ignore_result(receive_right.release()); |
| 305 return true; |
| 306 |
| 307 if (!exception_port_.is_valid()) { |
| 308 return false; |
| 309 } |
| 310 |
| 311 return true; |
218 } | 312 } |
219 | 313 |
220 bool CrashpadClient::UseHandler() { | 314 bool CrashpadClient::UseHandler() { |
221 DCHECK(exception_port_.is_valid()); | 315 DCHECK(exception_port_.is_valid()); |
222 | 316 |
223 return SetCrashExceptionPorts(exception_port_.get()); | 317 return SetCrashExceptionPorts(exception_port_.get()); |
224 } | 318 } |
225 | 319 |
226 // static | 320 // static |
227 void CrashpadClient::UseSystemDefaultHandler() { | 321 void CrashpadClient::UseSystemDefaultHandler() { |
228 base::mac::ScopedMachSendRight | 322 base::mac::ScopedMachSendRight |
229 system_crash_reporter_handler(SystemCrashReporterHandler()); | 323 system_crash_reporter_handler(SystemCrashReporterHandler()); |
230 | 324 |
231 // Proceed even if SystemCrashReporterHandler() failed, setting MACH_PORT_NULL | 325 // Proceed even if SystemCrashReporterHandler() failed, setting MACH_PORT_NULL |
232 // to clear the current exception ports. | 326 // to clear the current exception ports. |
233 if (!SetCrashExceptionPorts(system_crash_reporter_handler.get())) { | 327 if (!SetCrashExceptionPorts(system_crash_reporter_handler.get())) { |
234 SetCrashExceptionPorts(MACH_PORT_NULL); | 328 SetCrashExceptionPorts(MACH_PORT_NULL); |
235 } | 329 } |
236 } | 330 } |
237 | 331 |
238 } // namespace crashpad | 332 } // namespace crashpad |
OLD | NEW |