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

Side by Side Diff: client/crashpad_client_mac.cc

Issue 785233011: Add CrashpadClient (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Address review feedback Created 5 years, 11 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
« no previous file with comments | « client/crashpad_client.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "client/crashpad_client.h"
16
17 #include <mach/mach.h>
18 #include <sys/wait.h>
19 #include <unistd.h>
20
21 #include "base/logging.h"
22 #include "base/posix/eintr_wrapper.h"
23 #include "base/strings/stringprintf.h"
24 #include "client/crashpad_client.h"
25 #include "util/mach/child_port_handshake.h"
26 #include "util/mach/exception_ports.h"
27 #include "util/mach/mach_extensions.h"
28 #include "util/posix/close_multiple.h"
29
30 namespace crashpad {
31
32 CrashpadClient::CrashpadClient()
33 : exception_port_() {
34 }
35
36 CrashpadClient::~CrashpadClient() {
37 }
38
39 bool CrashpadClient::StartHandler(
40 const base::FilePath& handler,
41 const std::vector<std::string>& handler_arguments) {
42 DCHECK_EQ(exception_port_, kMachPortNull);
43
44 // Set up the arguments for execve() first. These aren’t needed until execve()
45 // is called, but it’s dangerous to do this in a child process after fork().
46 ChildPortHandshake child_port_handshake;
47 int handshake_fd = child_port_handshake.ReadPipeFD();
48 std::string handshake_fd_arg =
49 base::StringPrintf("--handshake-fd=%d", handshake_fd);
50
51 const std::string& handler_s = handler.value();
52 const char* const handler_c = handler_s.c_str();
53
54 // Use handler as argv[0], followed by handler_arguments, handshake_fd_arg,
55 // and a nullptr terminator.
56 std::vector<const char*> argv(1, handler_c);
57 argv.reserve(1 + handler_arguments.size() + 1 + 1);
58 for (const std::string& handler_argument : handler_arguments) {
59 argv.push_back(handler_argument.c_str());
60 }
61 argv.push_back(handshake_fd_arg.c_str());
62 argv.push_back(nullptr);
63
64 // Double-fork(). The three processes involved are parent, child, and
65 // grandchild. The grandchild will become the handler process. The child exits
66 // immediately after spawning the grandchild, so the grandchild becomes an
67 // orphan and its parent process ID becomes 1. This relieves the parent and
68 // child of the responsibility for reaping the grandchild with waitpid() or
69 // similar. The handler process is expected to outlive the parent process, so
70 // the parent shouldn’t be concerned with reaping it. This approach means that
71 // accidental early termination of the handler process will not result in a
72 // zombie process.
73 pid_t pid = fork();
74 if (pid < 0) {
75 PLOG(ERROR) << "fork";
76 return false;
77 }
78
79 if (pid == 0) {
80 // Child process.
81
82 // Call setsid(), creating a new process group and a new session, both led
83 // by this process. The new process group has no controlling terminal. This
84 // disconnects it from signals generated by the parent process’ terminal.
85 //
86 // setsid() is done in the child instead of the grandchild so that the
87 // grandchild will not be a session leader. If it were a session leader, an
88 // accidental open() of a terminal device without O_NOCTTY would make that
89 // terminal the controlling terminal.
90 //
91 // It’s not desirable for the handler to have a controlling terminal. The
92 // handler monitors clients on its own and manages its own lifetime, exiting
93 // when it loses all clients and when it deems it appropraite to do so. It
94 // may serve clients in different process groups or sessions than its
95 // original client, and receiving signals intended for its original client’s
96 // process group could be harmful in that case.
97 PCHECK(setsid() != -1) << "setsid";
98
99 pid = fork();
100 if (pid < 0) {
101 PLOG(FATAL) << "fork";
102 }
103
104 if (pid > 0) {
105 // Child process.
106
107 // _exit() instead of exit(), because fork() was called.
108 _exit(EXIT_SUCCESS);
109 }
110
111 // Grandchild process.
112
113 CloseMultipleNowOrOnExec(STDERR_FILENO + 1, handshake_fd);
114
115 // &argv[0] is a pointer to a pointer to const char data, but because of how
116 // C (not C++) works, execvp() wants a pointer to a const pointer to char
117 // data. It modifies neither the data nor the pointers, so the const_cast is
118 // safe.
119 execvp(handler_c, const_cast<char* const*>(&argv[0]));
120 PLOG(FATAL) << "execvp " << handler_s;
121 }
122
123 // Parent process.
124
125 // waitpid() for the child, so that it does not become a zombie process. The
126 // child normally exits quickly.
127 int status;
128 pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0));
129 PCHECK(wait_pid != -1) << "waitpid";
130 DCHECK_EQ(wait_pid, pid);
131
132 if (WIFSIGNALED(status)) {
133 LOG(WARNING) << "intermediate process: signal " << WTERMSIG(status);
134 } else if (!WIFEXITED(status)) {
135 DLOG(WARNING) << "intermediate process: unknown termination " << status;
136 } else if (WEXITSTATUS(status) != EXIT_SUCCESS) {
137 LOG(WARNING) << "intermediate process: exit status " << WEXITSTATUS(status);
138 }
139
140 // Rendezvous with the handler running in the grandchild process.
141 exception_port_.reset(child_port_handshake.RunServer());
142
143 return exception_port_ ? true : false;
144 }
145
146 bool CrashpadClient::UseHandler() {
147 DCHECK_NE(exception_port_, kMachPortNull);
148
149 // Set the exception handler for EXC_CRASH, EXC_RESOURCE, and EXC_GUARD.
150 //
151 // EXC_CRASH is how most crashes are received. Most other exception types such
152 // as EXC_BAD_ACCESS are delivered to a host-level exception handler in the
153 // kernel where they are converted to POSIX signals. See 10.9.5
154 // xnu-2422.115.4/bsd/uxkern/ux_exception.c catch_mach_exception_raise(). If a
155 // core-generating signal (triggered through this hardware mechanism or a
156 // software mechanism such as abort() sending SIGABRT) is unhandled and the
157 // process exits, the exception becomes EXC_CRASH. See 10.9.5
158 // xnu-2422.115.4/bsd/kern/kern_exit.c proc_prepareexit().
159 //
160 // EXC_RESOURCE and EXC_GUARD do not become signals or EXC_CRASH exceptions.
161 // The host-level exception handler in the kernel does not receive these
162 // exception types, and even if it did, it would not map them to signals.
163 // Instead, the first Mach service loaded by the root (process ID 1) launchd
164 // with a boolean “ExceptionServer” property in its job dictionary (regardless
165 // of its value) or with any subdictionary property will become the host-level
166 // exception handler for EXC_CRASH, EXC_RESOURCE, and EXC_GUARD. See 10.9.5
167 // launchd-842.92.1/src/core.c job_setup_exception_port(). Normally, this job
168 // is com.apple.ReportCrash.Root, the systemwide Apple Crash Reporter. Since
169 // it is impossible to receive EXC_RESOURCE and EXC_GUARD exceptions through
170 // the EXC_CRASH mechanism, an exception handler must be registered for them
171 // by name if it is to receive these exception types. The default task-level
172 // handler for these exception types is set by launchd in a similar manner.
173 //
174 // EXC_MASK_RESOURCE and EXC_MASK_GUARD are not available on all systems, and
175 // the kernel will reject attempts to use them if it does not understand them,
176 // so AND them with ExcMaskAll(). EXC_MASK_CRASH is not present in
177 // ExcMaskAll() but is always supported. See the documentation for
178 // ExcMaskAll().
179 ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL);
180 if (!exception_ports.SetExceptionPort(
181 EXC_MASK_CRASH |
182 ((EXC_MASK_RESOURCE | EXC_MASK_GUARD) & ExcMaskAll()),
183 exception_port_,
184 EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
185 MACHINE_THREAD_STATE)) {
186 return false;
187 }
188
189 return true;
190 }
191
192 } // namespace crashpad
OLDNEW
« no previous file with comments | « client/crashpad_client.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698