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

Side by Side Diff: client/crashpad_client_mac.cc

Issue 1421283004: Review helper for https://codereview.chromium.org/1409073013 (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: unindent Created 5 years, 1 month 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 | « no previous file | handler/mac/exception_handler_server.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
OLDNEW
« no previous file with comments | « no previous file | handler/mac/exception_handler_server.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698