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

Side by Side Diff: util/win/exception_handler_server.cc

Issue 1301853002: win: Crash handler server (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: oops Created 5 years, 3 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 | « util/win/exception_handler_server.h ('k') | util/win/exception_handler_server_test.cc » ('j') | 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 2015 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/win/exception_handler_server.h"
16
17 #include <string.h>
18
19 #include "base/logging.h"
20 #include "base/rand_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "minidump/minidump_file_writer.h"
24 #include "snapshot/crashpad_info_client_options.h"
25 #include "snapshot/win/process_snapshot_win.h"
26 #include "util/file/file_writer.h"
27 #include "util/misc/tri_state.h"
28 #include "util/misc/uuid.h"
29 #include "util/win/registration_protocol_win.h"
30
31 namespace crashpad {
32
33 namespace {
34
35 decltype(GetNamedPipeClientProcessId)* GetNamedPipeClientProcessIdFunction() {
36 static decltype(GetNamedPipeClientProcessId)* func =
37 reinterpret_cast<decltype(GetNamedPipeClientProcessId)*>(GetProcAddress(
38 GetModuleHandle(L"kernel32.dll"), "GetNamedPipeClientProcessId"));
39 return func;
40 }
41
42 HANDLE DuplicateEvent(HANDLE process, HANDLE event) {
43 HANDLE handle;
44 if (DuplicateHandle(GetCurrentProcess(),
45 event,
46 process,
47 &handle,
48 SYNCHRONIZE | EVENT_MODIFY_STATE,
49 false,
50 0)) {
51 return handle;
52 }
53 return nullptr;
54 }
55
56 } // namespace
57
58 namespace internal {
59
60 //! \brief Context information for the named pipe handler threads.
61 class PipeServiceContext {
62 public:
63 PipeServiceContext(HANDLE port,
64 HANDLE pipe,
65 ExceptionHandlerServer::Delegate* delegate,
66 base::Lock* clients_lock,
67 std::set<internal::ClientData*>* clients,
68 uint64_t shutdown_token)
69 : port_(port),
70 pipe_(pipe),
71 delegate_(delegate),
72 clients_lock_(clients_lock),
73 clients_(clients),
74 shutdown_token_(shutdown_token) {}
75
76 HANDLE port() const { return port_; }
77 HANDLE pipe() const { return pipe_.get(); }
78 ExceptionHandlerServer::Delegate* delegate() const { return delegate_; }
79 base::Lock* clients_lock() const { return clients_lock_; }
80 std::set<internal::ClientData*>* clients() const { return clients_; }
81 uint64_t shutdown_token() const { return shutdown_token_; }
82
83 private:
84 HANDLE port_; // weak
85 ScopedKernelHANDLE pipe_;
86 ExceptionHandlerServer::Delegate* delegate_; // weak
87 base::Lock* clients_lock_; // weak
88 std::set<internal::ClientData*>* clients_; // weak
89 uint64_t shutdown_token_;
90
91 DISALLOW_COPY_AND_ASSIGN(PipeServiceContext);
92 };
93
94 //! \brief The context data for registered threadpool waits.
95 //!
96 //! This object must be created and destroyed on the main thread. Access must be
97 //! guarded by use of the lock() with the exception of the threadpool wait
98 //! variables which are accessed only by the main thread.
99 class ClientData {
100 public:
101 ClientData(HANDLE port,
102 ExceptionHandlerServer::Delegate* delegate,
103 ScopedKernelHANDLE process,
104 WinVMAddress exception_information_address,
105 WAITORTIMERCALLBACK dump_request_callback,
106 WAITORTIMERCALLBACK process_end_callback)
107 : dump_request_thread_pool_wait_(INVALID_HANDLE_VALUE),
108 process_end_thread_pool_wait_(INVALID_HANDLE_VALUE),
109 lock_(),
110 port_(port),
111 delegate_(delegate),
112 dump_requested_event_(
113 CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
114 process_(process.Pass()),
115 exception_information_address_(exception_information_address) {
116 RegisterThreadPoolWaits(dump_request_callback, process_end_callback);
117 }
118
119 ~ClientData() {
120 // It is important that this only access the threadpool waits (it's called
121 // from the main thread) until the waits are unregistered, to ensure that
122 // any outstanding callbacks are complete.
123 UnregisterThreadPoolWaits();
124 }
125
126 base::Lock* lock() { return &lock_; }
127 HANDLE port() const { return port_; }
128 ExceptionHandlerServer::Delegate* delegate() const { return delegate_; }
129 HANDLE dump_requested_event() const { return dump_requested_event_.get(); }
130 WinVMAddress exception_information_address() const {
131 return exception_information_address_;
132 }
133 HANDLE process() const { return process_.get(); }
134
135 private:
136 void RegisterThreadPoolWaits(WAITORTIMERCALLBACK dump_request_callback,
137 WAITORTIMERCALLBACK process_end_callback) {
138 if (!RegisterWaitForSingleObject(&dump_request_thread_pool_wait_,
139 dump_requested_event_.get(),
140 dump_request_callback,
141 this,
142 INFINITE,
143 WT_EXECUTEDEFAULT)) {
144 LOG(ERROR) << "RegisterWaitForSingleObject dump requested";
145 }
146
147 if (!RegisterWaitForSingleObject(&process_end_thread_pool_wait_,
148 process_.get(),
149 process_end_callback,
150 this,
151 INFINITE,
152 WT_EXECUTEONLYONCE)) {
153 LOG(ERROR) << "RegisterWaitForSingleObject process end";
154 }
155 }
156
157 // This blocks until outstanding calls complete so that we know it's safe to
158 // delete this object. Because of this, it must be executed on the main
159 // thread, not a threadpool thread.
160 void UnregisterThreadPoolWaits() {
161 UnregisterWaitEx(dump_request_thread_pool_wait_, INVALID_HANDLE_VALUE);
162 dump_request_thread_pool_wait_ = INVALID_HANDLE_VALUE;
163 UnregisterWaitEx(process_end_thread_pool_wait_, INVALID_HANDLE_VALUE);
164 process_end_thread_pool_wait_ = INVALID_HANDLE_VALUE;
165 }
166
167 // These are only accessed on the main thread.
168 HANDLE dump_request_thread_pool_wait_;
169 HANDLE process_end_thread_pool_wait_;
170
171 base::Lock lock_;
172 // Access to these fields must be guarded by lock_.
173 HANDLE port_; // weak
174 ExceptionHandlerServer::Delegate* delegate_; // weak
175 ScopedKernelHANDLE dump_requested_event_;
176 ScopedKernelHANDLE process_;
177 WinVMAddress exception_information_address_;
178
179 DISALLOW_COPY_AND_ASSIGN(ClientData);
180 };
181
182 } // namespace internal
183
184 ExceptionHandlerServer::Delegate::~Delegate() {
185 }
186
187 ExceptionHandlerServer::ExceptionHandlerServer(Delegate* delegate)
188 : delegate_(delegate),
189 port_(CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1)),
190 clients_lock_(),
191 clients_() {
192 }
193
194 ExceptionHandlerServer::~ExceptionHandlerServer() {
195 }
196
197 void ExceptionHandlerServer::Run(const std::string& pipe_name) {
198 uint64_t shutdown_token = base::RandUint64();
199 // We create two pipe instances, so that there's one listening while the
200 // PipeServiceProc is processing a registration.
201 ScopedKernelHANDLE thread_handles[2];
202 base::string16 pipe_name_16(base::UTF8ToUTF16(pipe_name));
203 for (int i = 0; i < arraysize(thread_handles); ++i) {
204 HANDLE pipe =
205 CreateNamedPipe(pipe_name_16.c_str(),
206 PIPE_ACCESS_DUPLEX,
207 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
208 arraysize(thread_handles),
209 512,
210 512,
211 0,
212 nullptr);
213
214 // Ownership of this object (and the pipe instance) is given to the new
215 // thread. We close the thread handles at the end of the scope. They clean
216 // up the context object and the pipe instance on termination.
217 internal::PipeServiceContext* context =
218 new internal::PipeServiceContext(port_.get(),
219 pipe,
220 delegate_,
221 &clients_lock_,
222 &clients_,
223 shutdown_token);
224 thread_handles[i].reset(
225 CreateThread(nullptr, 0, &PipeServiceProc, context, 0, nullptr));
226 }
227
228 delegate_->ExceptionHandlerServerStarted();
229
230 // This is the main loop of the server. Most work is done on the threadpool,
231 // other than process end handling which is posted back to this main thread,
232 // as we must unregister the threadpool waits here.
233 for (;;) {
234 OVERLAPPED* ov = nullptr;
235 ULONG_PTR key = 0;
236 DWORD bytes = 0;
237 GetQueuedCompletionStatus(port_.get(), &bytes, &key, &ov, INFINITE);
238 if (!key) {
239 // Shutting down.
240 break;
241 }
242
243 // Otherwise, this is a request to unregister and destroy the given client.
244 // delete'ing the ClientData blocks in UnregisterWaitEx to ensure all
245 // outstanding threadpool waits are complete. This is important because the
246 // process handle can be signalled *before* the dump request is signalled.
247 internal::ClientData* client = reinterpret_cast<internal::ClientData*>(key);
248 {
249 base::AutoLock lock(clients_lock_);
250 clients_.erase(client);
251 }
252 delete client;
253 }
254
255 // Signal to the named pipe instances that they should terminate.
256 for (int i = 0; i < arraysize(thread_handles); ++i) {
257 ClientToServerMessage message;
258 memset(&message, 0, sizeof(message));
259 message.type = ClientToServerMessage::kShutdown;
260 message.shutdown.token = shutdown_token;
261 ServerToClientMessage response;
262 SendToCrashHandlerServer(base::UTF8ToUTF16(pipe_name),
263 reinterpret_cast<ClientToServerMessage&>(message),
264 &response);
265 }
266
267 for (auto& handle : thread_handles)
268 WaitForSingleObject(handle.get(), INFINITE);
269
270 // Deleting ClientData does a blocking wait until the threadpool executions
271 // have terminated when unregistering them.
272 {
273 base::AutoLock lock(clients_lock_);
274 for (auto* client : clients_)
275 delete client;
276 clients_.clear();
277 }
278 }
279
280 void ExceptionHandlerServer::Stop() {
281 // Post a null key (third argument) to trigger shutdown.
282 PostQueuedCompletionStatus(port_.get(), 0, 0, nullptr);
283 }
284
285 // This function must be called with service_context.pipe() already connected to
286 // a client pipe. It exchanges data with the client and adds a ClientData record
287 // to service_context->clients().
288 //
289 // static
290 bool ExceptionHandlerServer::ServiceClientConnection(
291 const internal::PipeServiceContext& service_context) {
292 ClientToServerMessage message;
293
294 if (!LoggingReadFile(service_context.pipe(), &message, sizeof(message)))
295 return false;
296
297 switch (message.type) {
298 case ClientToServerMessage::kShutdown: {
299 if (message.shutdown.token != service_context.shutdown_token()) {
300 LOG(ERROR) << "forged shutdown request, got: "
301 << message.shutdown.token;
302 return false;
303 }
304 ServerToClientMessage shutdown_response = {0};
305 LoggingWriteFile(service_context.pipe(),
306 &shutdown_response,
307 sizeof(shutdown_response));
308 return true;
309 }
310
311 case ClientToServerMessage::kRegister:
312 // Handled below.
313 break;
314
315 default:
316 LOG(ERROR) << "unhandled message type: " << message.type;
317 return false;
318 }
319
320 decltype(GetNamedPipeClientProcessId)* get_named_pipe_client_process_id =
321 GetNamedPipeClientProcessIdFunction();
322 if (get_named_pipe_client_process_id) {
323 // GetNamedPipeClientProcessId is only available on Vista+.
324 DWORD real_pid = 0;
325 if (get_named_pipe_client_process_id(service_context.pipe(), &real_pid) &&
326 message.registration.client_process_id != real_pid) {
327 LOG(ERROR) << "forged client pid, real pid: " << real_pid
328 << ", got: " << message.registration.client_process_id;
329 return false;
330 }
331 }
332
333 // We attempt to open the process as us. This is the main case that should
334 // almost always succeed as the server will generally be more privileged. If
335 // we're running as a different user, it may be that we will fail to open
336 // the process, but the client will be able to, so we make a second attempt
337 // having impersonated the client.
338 HANDLE client_process = OpenProcess(
339 PROCESS_ALL_ACCESS, false, message.registration.client_process_id);
340 if (!client_process) {
341 if (!ImpersonateNamedPipeClient(service_context.pipe())) {
342 PLOG(ERROR) << "ImpersonateNamedPipeClient";
343 return false;
344 }
345 HANDLE client_process = OpenProcess(
346 PROCESS_ALL_ACCESS, false, message.registration.client_process_id);
347 PCHECK(RevertToSelf());
348 if (!client_process) {
349 LOG(ERROR) << "failed to open " << message.registration.client_process_id;
350 return false;
351 }
352 }
353
354 internal::ClientData* client;
355 {
356 base::AutoLock lock(*service_context.clients_lock());
357 client =
358 new internal::ClientData(service_context.port(),
359 service_context.delegate(),
360 ScopedKernelHANDLE(client_process),
361 message.registration.exception_information,
362 &OnDumpEvent,
363 &OnProcessEnd);
364 service_context.clients()->insert(client);
365 }
366
367 // Duplicate the events back to the client so they can request a dump.
368 ServerToClientMessage response;
369 response.registration.request_report_event = reinterpret_cast<uint32_t>(
370 DuplicateEvent(client->process(), client->dump_requested_event()));
371
372 if (!LoggingWriteFile(service_context.pipe(), &response, sizeof(response)))
373 return false;
374
375 return false;
376 }
377
378 // static
379 DWORD __stdcall ExceptionHandlerServer::PipeServiceProc(void* ctx) {
380 internal::PipeServiceContext* service_context =
381 reinterpret_cast<internal::PipeServiceContext*>(ctx);
382 DCHECK(service_context);
383
384 for (;;) {
385 bool ret = ConnectNamedPipe(service_context->pipe(), nullptr);
386 if (!ret && GetLastError() != ERROR_PIPE_CONNECTED) {
387 PLOG(ERROR) << "ConnectNamedPipe";
388 } else if (ServiceClientConnection(*service_context)) {
389 break;
390 }
391 DisconnectNamedPipe(service_context->pipe());
392 }
393
394 delete service_context;
395
396 return 0;
397 }
398
399 // static
400 void __stdcall ExceptionHandlerServer::OnDumpEvent(void* ctx, BOOLEAN) {
401 // This function is executed on the thread pool.
402 internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);
403 base::AutoLock lock(*client->lock());
404
405 // Capture the exception.
406 unsigned int exit_code = client->delegate()->ExceptionHandlerServerException(
407 client->process(), client->exception_information_address());
408
409 TerminateProcess(client->process(), exit_code);
410 }
411
412 // static
413 void __stdcall ExceptionHandlerServer::OnProcessEnd(void* ctx, BOOLEAN) {
414 // This function is executed on the thread pool.
415 internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);
416 base::AutoLock lock(*client->lock());
417
418 // Post back to the main thread to have it delete this client record.
419 PostQueuedCompletionStatus(client->port(), 0, ULONG_PTR(client), nullptr);
420 }
421
422 } // namespace crashpad
OLDNEW
« no previous file with comments | « util/win/exception_handler_server.h ('k') | util/win/exception_handler_server_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698