Index: util/win/exception_handler_server.cc |
diff --git a/util/win/exception_handler_server.cc b/util/win/exception_handler_server.cc |
index 50daa39a92a2035c71f4395c7861a7eb2980c848..2035c57857cd0e98428d0f2a029e71fa0801e92c 100644 |
--- a/util/win/exception_handler_server.cc |
+++ b/util/win/exception_handler_server.cc |
@@ -35,6 +35,29 @@ namespace crashpad { |
namespace { |
+// We create two pipe instances, so that there's one listening while the |
+// PipeServiceProc is processing a registration. |
+const size_t kPipeInstances = 2; |
+ |
+// Wraps CreateNamedPipe() to create a single named pipe instance. |
+// |
+// If first_instance is true, the named pipe instance will be created with |
+// FILE_FLAG_FIRST_PIPE_INSTANCE. This ensures that the the pipe name is not |
+// already in use when created. |
+HANDLE CreateNamedPipeInstance(const std::wstring& pipe_name, |
+ bool first_instance) { |
+ return CreateNamedPipe(pipe_name.c_str(), |
+ PIPE_ACCESS_DUPLEX | |
+ (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE |
+ : 0), |
+ PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, |
+ kPipeInstances, |
+ 512, |
+ 512, |
+ 0, |
+ nullptr); |
+} |
+ |
decltype(GetNamedPipeClientProcessId)* GetNamedPipeClientProcessIdFunction() { |
static const auto get_named_pipe_client_process_id = |
GET_FUNCTION(L"kernel32.dll", ::GetNamedPipeClientProcessId); |
@@ -234,10 +257,10 @@ class ClientData { |
ExceptionHandlerServer::Delegate::~Delegate() { |
} |
-ExceptionHandlerServer::ExceptionHandlerServer(const std::string& pipe_name, |
- bool persistent) |
- : pipe_name_(pipe_name), |
+ExceptionHandlerServer::ExceptionHandlerServer(bool persistent) |
+ : pipe_name_(), |
port_(CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1)), |
+ first_pipe_instance_(), |
clients_lock_(), |
clients_(), |
persistent_(persistent) { |
@@ -246,23 +269,59 @@ ExceptionHandlerServer::ExceptionHandlerServer(const std::string& pipe_name, |
ExceptionHandlerServer::~ExceptionHandlerServer() { |
} |
+void ExceptionHandlerServer::SetPipeName(const std::wstring& pipe_name) { |
+ DCHECK(pipe_name_.empty()); |
+ DCHECK(!pipe_name.empty()); |
+ |
+ pipe_name_ = pipe_name; |
+} |
+ |
+std::wstring ExceptionHandlerServer::CreatePipe() { |
+ DCHECK(!first_pipe_instance_.is_valid()); |
+ |
+ int tries = 5; |
+ std::string pipe_name_base = |
+ base::StringPrintf("\\\\.\\pipe\\crashpad_%d_", GetCurrentProcessId()); |
+ std::wstring pipe_name; |
+ do { |
+ pipe_name = base::UTF8ToUTF16(pipe_name_base); |
+ for (int index = 0; index < 16; ++index) { |
+ pipe_name.append(1, static_cast<wchar_t>(base::RandInt('A', 'Z'))); |
+ } |
+ |
+ first_pipe_instance_.reset(CreateNamedPipeInstance(pipe_name, true)); |
+ |
+ // CreateNamedPipe() is documented as setting the error to |
+ // ERROR_ACCESS_DENIED if FILE_FLAG_FIRST_PIPE_INSTANCE is specified and the |
+ // pipe name is already in use. However it may set the error to other codes |
+ // such as ERROR_PIPE_BUSY (if the pipe already exists and has reached its |
+ // maximum instance count) or ERROR_INVALID_PARAMETER (if the pipe already |
+ // exists and its attributes differ from those specified to |
+ // CreateNamedPipe()). Some of these errors may be ambiguous: for example, |
+ // ERROR_INVALID_PARAMETER may also occur if CreateNamedPipe() is called |
+ // incorrectly even in the absence of an existing pipe by the same name. |
+ // |
+ // Rather than chasing down all of the possible errors that might indicate |
+ // that a pipe name is already in use, retry up to a few times on any error. |
+ } while (!first_pipe_instance_.is_valid() && --tries); |
+ |
+ PCHECK(first_pipe_instance_.is_valid()) << "CreateNamedPipe"; |
+ |
+ SetPipeName(pipe_name); |
+ return pipe_name; |
+} |
+ |
void ExceptionHandlerServer::Run(Delegate* delegate) { |
uint64_t shutdown_token = base::RandUint64(); |
- // We create two pipe instances, so that there's one listening while the |
- // PipeServiceProc is processing a registration. |
- ScopedKernelHANDLE thread_handles[2]; |
- base::string16 pipe_name_16(base::UTF8ToUTF16(pipe_name_)); |
+ ScopedKernelHANDLE thread_handles[kPipeInstances]; |
for (int i = 0; i < arraysize(thread_handles); ++i) { |
- HANDLE pipe = |
- CreateNamedPipe(pipe_name_16.c_str(), |
- PIPE_ACCESS_DUPLEX, |
- PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, |
- arraysize(thread_handles), |
- 512, |
- 512, |
- 0, |
- nullptr); |
- PCHECK(pipe != INVALID_HANDLE_VALUE) << "CreateNamedPipe"; |
+ HANDLE pipe; |
+ if (first_pipe_instance_.is_valid()) { |
+ pipe = first_pipe_instance_.release(); |
+ } else { |
+ pipe = CreateNamedPipeInstance(pipe_name_, false); |
+ PCHECK(pipe != INVALID_HANDLE_VALUE) << "CreateNamedPipe"; |
+ } |
// Ownership of this object (and the pipe instance) is given to the new |
// thread. We close the thread handles at the end of the scope. They clean |
@@ -313,7 +372,7 @@ void ExceptionHandlerServer::Run(Delegate* delegate) { |
message.type = ClientToServerMessage::kShutdown; |
message.shutdown.token = shutdown_token; |
ServerToClientMessage response; |
- SendToCrashHandlerServer(pipe_name_16, |
+ SendToCrashHandlerServer(pipe_name_, |
reinterpret_cast<ClientToServerMessage&>(message), |
&response); |
} |