Import Cobalt 21.master.0.260628
diff --git a/src/third_party/crashpad/util/win/address_types.h b/src/third_party/crashpad/util/win/address_types.h
new file mode 100644
index 0000000..f26a57e
--- /dev/null
+++ b/src/third_party/crashpad/util/win/address_types.h
@@ -0,0 +1,32 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_ADDRESS_TYPES_H_
+#define CRASHPAD_UTIL_WIN_ADDRESS_TYPES_H_
+
+#include <stdint.h>
+
+namespace crashpad {
+
+//! \brief Type used to represent an address in a process, potentially across
+//!     bitness.
+using WinVMAddress = uint64_t;
+
+//! \brief Type used to represent the size of a memory range (with a
+//!     WinVMAddress), potentially across bitness.
+using WinVMSize = uint64_t;
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_ADDRESS_TYPES_H_
diff --git a/src/third_party/crashpad/util/win/checked_win_address_range.h b/src/third_party/crashpad/util/win/checked_win_address_range.h
new file mode 100644
index 0000000..e86c7b7
--- /dev/null
+++ b/src/third_party/crashpad/util/win/checked_win_address_range.h
@@ -0,0 +1,36 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_CHECKED_WIN_ADDRESS_RANGE_H_
+#define CRASHPAD_UTIL_WIN_CHECKED_WIN_ADDRESS_RANGE_H_
+
+#include "util/numeric/checked_address_range.h"
+#include "util/win/address_types.h"
+
+namespace crashpad {
+
+//! \brief Ensures that a range, composed of a base and a size, does not
+//!     overflow the pointer type of the process it describes a range in.
+//!
+//! This class checks bases of type WinVMAddress and sizes of type WinVMSize
+//! against a process whose pointer type is either 32 or 64 bits wide.
+//!
+//! Aside from varying the overall range on the basis of a process' pointer type
+//! width, this class functions very similarly to CheckedRange.
+using CheckedWinAddressRange =
+    internal::CheckedAddressRangeGeneric<WinVMAddress, WinVMSize>;
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_CHECKED_WIN_ADDRESS_RANGE_H_
diff --git a/src/third_party/crashpad/util/win/command_line.cc b/src/third_party/crashpad/util/win/command_line.cc
new file mode 100644
index 0000000..c015eed
--- /dev/null
+++ b/src/third_party/crashpad/util/win/command_line.cc
@@ -0,0 +1,60 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/command_line.h"
+
+#include <sys/types.h>
+
+namespace crashpad {
+
+// Ref:
+// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
+void AppendCommandLineArgument(const std::wstring& argument,
+                               std::wstring* command_line) {
+  if (!command_line->empty()) {
+    command_line->push_back(L' ');
+  }
+
+  // Don’t bother quoting if unnecessary.
+  if (!argument.empty() &&
+      argument.find_first_of(L" \t\n\v\"") == std::wstring::npos) {
+    command_line->append(argument);
+  } else {
+    command_line->push_back(L'"');
+    for (std::wstring::const_iterator i = argument.begin();; ++i) {
+      size_t backslash_count = 0;
+      while (i != argument.end() && *i == L'\\') {
+        ++i;
+        ++backslash_count;
+      }
+      if (i == argument.end()) {
+        // Escape all backslashes, but let the terminating double quotation mark
+        // we add below be interpreted as a metacharacter.
+        command_line->append(backslash_count * 2, L'\\');
+        break;
+      } else if (*i == L'"') {
+        // Escape all backslashes and the following double quotation mark.
+        command_line->append(backslash_count * 2 + 1, L'\\');
+        command_line->push_back(*i);
+      } else {
+        // Backslashes aren’t special here.
+        command_line->append(backslash_count, L'\\');
+        command_line->push_back(*i);
+      }
+    }
+    command_line->push_back(L'"');
+  }
+}
+
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/command_line.h b/src/third_party/crashpad/util/win/command_line.h
new file mode 100644
index 0000000..c12d653
--- /dev/null
+++ b/src/third_party/crashpad/util/win/command_line.h
@@ -0,0 +1,38 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_COMMAND_LINE_H_
+#define CRASHPAD_UTIL_WIN_COMMAND_LINE_H_
+
+#include <string>
+
+namespace crashpad {
+
+//! \brief Utility function for building escaped command lines.
+//!
+//! This builds a command line so that individual arguments can be reliably
+//! decoded by `CommandLineToArgvW()`.
+//!
+//! \a argument is appended to \a command_line. If necessary, it will be placed
+//! in quotation marks and escaped properly. If \a command_line is initially
+//! non-empty, a space will precede \a argument.
+//!
+//! \param[in] argument The argument to append to \a command_line.
+//! \param[in,out] command_line The command line being constructed.
+void AppendCommandLineArgument(const std::wstring& argument,
+                               std::wstring* command_line);
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_COMMAND_LINE_H_
diff --git a/src/third_party/crashpad/util/win/command_line_test.cc b/src/third_party/crashpad/util/win/command_line_test.cc
new file mode 100644
index 0000000..032d555
--- /dev/null
+++ b/src/third_party/crashpad/util/win/command_line_test.cc
@@ -0,0 +1,168 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/command_line.h"
+
+#include <windows.h>
+#include <shellapi.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "base/scoped_generic.h"
+#include "base/stl_util.h"
+#include "gtest/gtest.h"
+#include "test/errors.h"
+#include "util/win/scoped_local_alloc.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// Calls AppendCommandLineArgument() for every argument in argv, then calls
+// CommandLineToArgvW() to decode the string into a vector again, and compares
+// the input and output.
+void AppendCommandLineArgumentTest(size_t argc, const wchar_t* const argv[]) {
+  std::wstring command_line;
+  for (size_t index = 0; index < argc; ++index) {
+    AppendCommandLineArgument(argv[index], &command_line);
+  }
+
+  int test_argc;
+  wchar_t** test_argv = CommandLineToArgvW(command_line.c_str(), &test_argc);
+
+  ASSERT_TRUE(test_argv) << ErrorMessage("CommandLineToArgvW");
+  ScopedLocalAlloc test_argv_owner(test_argv);
+  ASSERT_EQ(test_argc, static_cast<int>(argc));
+
+  for (size_t index = 0; index < argc; ++index) {
+    EXPECT_STREQ(argv[index], test_argv[index]) << "index " << index;
+  }
+  EXPECT_FALSE(test_argv[argc]);
+}
+
+TEST(CommandLine, AppendCommandLineArgument) {
+  // Most of these test cases come from
+  // https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/,
+  // which was also a reference for the implementation of
+  // AppendCommandLineArgument().
+
+  {
+    SCOPED_TRACE("simple");
+
+    static constexpr const wchar_t* kArguments[] = {
+        L"child.exe",
+        L"argument 1",
+        L"argument 2",
+    };
+    AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
+  }
+
+  {
+    SCOPED_TRACE("path with spaces");
+
+    static constexpr const wchar_t* kArguments[] = {
+        L"child.exe",
+        L"argument1",
+        L"argument 2",
+        L"\\some\\path with\\spaces",
+    };
+    AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
+  }
+
+  {
+    SCOPED_TRACE("argument with embedded quotation marks");
+
+    static constexpr const wchar_t* kArguments[] = {
+        L"child.exe",
+        L"argument1",
+        L"she said, \"you had me at hello\"",
+        L"\\some\\path with\\spaces",
+    };
+    AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
+  }
+
+  {
+    SCOPED_TRACE("argument with unbalanced quotation marks");
+
+    static constexpr const wchar_t* kArguments[] = {
+        L"child.exe",
+        L"argument1",
+        L"argument\"2",
+        L"argument3",
+        L"argument4",
+    };
+    AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
+  }
+
+  {
+    SCOPED_TRACE("argument ending with backslash");
+
+    static constexpr const wchar_t* kArguments[] = {
+        L"child.exe",
+        L"\\some\\directory with\\spaces\\",
+        L"argument2",
+    };
+    AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
+  }
+
+  {
+    SCOPED_TRACE("empty argument");
+
+    static constexpr const wchar_t* kArguments[] = {
+        L"child.exe",
+        L"",
+        L"argument2",
+    };
+    AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
+  }
+
+  {
+    SCOPED_TRACE("funny nonprintable characters");
+
+    static constexpr const wchar_t* kArguments[] = {
+        L"child.exe",
+        L"argument 1",
+        L"argument\t2",
+        L"argument\n3",
+        L"argument\v4",
+        L"argument\"5",
+        L" ",
+        L"\t",
+        L"\n",
+        L"\v",
+        L"\"",
+        L" x",
+        L"\tx",
+        L"\nx",
+        L"\vx",
+        L"\"x",
+        L"x ",
+        L"x\t",
+        L"x\n",
+        L"x\v",
+        L"x\"",
+        L" ",
+        L"\t\t",
+        L"\n\n",
+        L"\v\v",
+        L"\"\"",
+        L" \t\n\v\"",
+    };
+    AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/context_wrappers.h b/src/third_party/crashpad/util/win/context_wrappers.h
new file mode 100644
index 0000000..5d9b183
--- /dev/null
+++ b/src/third_party/crashpad/util/win/context_wrappers.h
@@ -0,0 +1,40 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_CONTEXT_WRAPPERS_H_
+#define CRASHPAD_UTIL_WIN_CONTEXT_WRAPPERS_H_
+
+#include <windows.h>
+
+#include "build/build_config.h"
+
+namespace crashpad {
+
+//! \brief Retrieve program counter from `CONTEXT` structure for different
+//!     architectures supported by Windows.
+inline void* ProgramCounterFromCONTEXT(const CONTEXT* context) {
+#if defined(ARCH_CPU_X86)
+  return reinterpret_cast<void*>(context->Eip);
+#elif defined(ARCH_CPU_X86_64)
+  return reinterpret_cast<void*>(context->Rip);
+#elif defined(ARCH_CPU_ARM64)
+  return reinterpret_cast<void*>(context->Pc);
+#else
+#error Unsupported Windows Arch
+#endif  // ARCH_CPU_X86
+}
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_CONTEXT_WRAPPERS_H_
diff --git a/src/third_party/crashpad/util/win/critical_section_with_debug_info.cc b/src/third_party/crashpad/util/win/critical_section_with_debug_info.cc
new file mode 100644
index 0000000..1613a51
--- /dev/null
+++ b/src/third_party/crashpad/util/win/critical_section_with_debug_info.cc
@@ -0,0 +1,72 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/critical_section_with_debug_info.h"
+
+#include "base/logging.h"
+#include "util/win/get_function.h"
+
+namespace crashpad {
+
+namespace {
+
+bool CrashpadInitializeCriticalSectionEx(
+    CRITICAL_SECTION* critical_section,
+    DWORD spin_count,
+    DWORD flags) {
+  static const auto initialize_critical_section_ex =
+      GET_FUNCTION_REQUIRED(L"kernel32.dll", ::InitializeCriticalSectionEx);
+  BOOL ret =
+      initialize_critical_section_ex(critical_section, spin_count, flags);
+  if (!ret) {
+    PLOG(ERROR) << "InitializeCriticalSectionEx";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+bool InitializeCriticalSectionWithDebugInfoIfPossible(
+    CRITICAL_SECTION* critical_section) {
+  // On XP and Vista, a plain initialization causes the CRITICAL_SECTION to be
+  // allocated with .DebugInfo. On 8 and above, we can pass an additional flag
+  // to InitializeCriticalSectionEx() to force the .DebugInfo on. Before Win 8,
+  // that flag causes InitializeCriticalSectionEx() to fail. So, for XP, Vista,
+  // and 7 we use InitializeCriticalSection(), and for 8 and above,
+  // InitializeCriticalSectionEx() with the additional flag.
+  //
+  // TODO(scottmg): Try to find a solution for Win 7. It's unclear how to force
+  // it on for Win 7, however the Loader Lock does have .DebugInfo so there may
+  // be a way to do it. The comments in winnt.h imply that perhaps it's passed
+  // to InitializeCriticalSectionAndSpinCount() as the top bits of the spin
+  // count, but that doesn't appear to work. For now, we initialize a valid
+  // CRITICAL_SECTION, but without .DebugInfo.
+
+  const DWORD version = GetVersion();
+  const DWORD major_version = LOBYTE(LOWORD(version));
+  const DWORD minor_version = HIBYTE(LOWORD(version));
+  const bool win7_or_lower =
+      major_version < 6 || (major_version == 6 && minor_version <= 1);
+
+  if (win7_or_lower) {
+    InitializeCriticalSection(critical_section);
+    return true;
+  }
+
+  return CrashpadInitializeCriticalSectionEx(
+      critical_section, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO);
+}
+
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/critical_section_with_debug_info.h b/src/third_party/crashpad/util/win/critical_section_with_debug_info.h
new file mode 100644
index 0000000..1271685
--- /dev/null
+++ b/src/third_party/crashpad/util/win/critical_section_with_debug_info.h
@@ -0,0 +1,34 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_CRITICAL_SECTION_WITH_DEBUG_INFO_H_
+#define CRASHPAD_UTIL_WIN_CRITICAL_SECTION_WITH_DEBUG_INFO_H_
+
+#include <windows.h>
+
+namespace crashpad {
+
+//! \brief Equivalent to `InitializeCritialSection()`, but attempts to allocate
+//!     with a valid `.DebugInfo` field on versions of Windows where it's
+//!     possible to do so.
+//!
+//! \return `true` on success, or `false` on failure with a message logged.
+//!     Success means that the critical section was successfully initialized,
+//!     but it does not necessarily have a valid `.DebugInfo` field.
+bool InitializeCriticalSectionWithDebugInfoIfPossible(
+    CRITICAL_SECTION* critical_section);
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_CRITICAL_SECTION_WITH_DEBUG_INFO_H_
diff --git a/src/third_party/crashpad/util/win/critical_section_with_debug_info_test.cc b/src/third_party/crashpad/util/win/critical_section_with_debug_info_test.cc
new file mode 100644
index 0000000..dcc5965
--- /dev/null
+++ b/src/third_party/crashpad/util/win/critical_section_with_debug_info_test.cc
@@ -0,0 +1,34 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/critical_section_with_debug_info.h"
+
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(CriticalSectionWithDebugInfo, CriticalSectionWithDebugInfo) {
+  CRITICAL_SECTION critical_section;
+  ASSERT_TRUE(
+      InitializeCriticalSectionWithDebugInfoIfPossible(&critical_section));
+  EnterCriticalSection(&critical_section);
+  LeaveCriticalSection(&critical_section);
+  DeleteCriticalSection(&critical_section);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/exception_handler_server.cc b/src/third_party/crashpad/util/win/exception_handler_server.cc
new file mode 100644
index 0000000..2593ff2
--- /dev/null
+++ b/src/third_party/crashpad/util/win/exception_handler_server.cc
@@ -0,0 +1,580 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/exception_handler_server.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/rand_util.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "minidump/minidump_file_writer.h"
+#include "snapshot/crashpad_info_client_options.h"
+#include "snapshot/win/process_snapshot_win.h"
+#include "util/file/file_writer.h"
+#include "util/misc/tri_state.h"
+#include "util/misc/uuid.h"
+#include "util/win/get_function.h"
+#include "util/win/handle.h"
+#include "util/win/registration_protocol_win.h"
+#include "util/win/safe_terminate_process.h"
+#include "util/win/xp_compat.h"
+
+namespace crashpad {
+
+namespace {
+
+decltype(GetNamedPipeClientProcessId)* GetNamedPipeClientProcessIdFunction() {
+  static const auto get_named_pipe_client_process_id =
+      GET_FUNCTION(L"kernel32.dll", ::GetNamedPipeClientProcessId);
+  return get_named_pipe_client_process_id;
+}
+
+HANDLE DuplicateEvent(HANDLE process, HANDLE event) {
+  HANDLE handle;
+  if (DuplicateHandle(GetCurrentProcess(),
+                      event,
+                      process,
+                      &handle,
+                      SYNCHRONIZE | EVENT_MODIFY_STATE,
+                      false,
+                      0)) {
+    return handle;
+  }
+  return nullptr;
+}
+
+}  // namespace
+
+namespace internal {
+
+//! \brief Context information for the named pipe handler threads.
+class PipeServiceContext {
+ public:
+  PipeServiceContext(HANDLE port,
+                     HANDLE pipe,
+                     ExceptionHandlerServer::Delegate* delegate,
+                     base::Lock* clients_lock,
+                     std::set<internal::ClientData*>* clients,
+                     uint64_t shutdown_token)
+      : port_(port),
+        pipe_(pipe),
+        delegate_(delegate),
+        clients_lock_(clients_lock),
+        clients_(clients),
+        shutdown_token_(shutdown_token) {}
+
+  HANDLE port() const { return port_; }
+  HANDLE pipe() const { return pipe_.get(); }
+  ExceptionHandlerServer::Delegate* delegate() const { return delegate_; }
+  base::Lock* clients_lock() const { return clients_lock_; }
+  std::set<internal::ClientData*>* clients() const { return clients_; }
+  uint64_t shutdown_token() const { return shutdown_token_; }
+
+ private:
+  HANDLE port_;  // weak
+  ScopedKernelHANDLE pipe_;
+  ExceptionHandlerServer::Delegate* delegate_;  // weak
+  base::Lock* clients_lock_;  // weak
+  std::set<internal::ClientData*>* clients_;  // weak
+  uint64_t shutdown_token_;
+
+  DISALLOW_COPY_AND_ASSIGN(PipeServiceContext);
+};
+
+//! \brief The context data for registered threadpool waits.
+//!
+//! This object must be created and destroyed on the main thread. Access must be
+//! guarded by use of the lock() with the exception of the threadpool wait
+//! variables which are accessed only by the main thread.
+class ClientData {
+ public:
+  ClientData(HANDLE port,
+             ExceptionHandlerServer::Delegate* delegate,
+             ScopedKernelHANDLE process,
+             ScopedKernelHANDLE crash_dump_requested_event,
+             ScopedKernelHANDLE non_crash_dump_requested_event,
+             ScopedKernelHANDLE non_crash_dump_completed_event,
+             WinVMAddress crash_exception_information_address,
+             WinVMAddress non_crash_exception_information_address,
+             WinVMAddress debug_critical_section_address,
+             WAITORTIMERCALLBACK crash_dump_request_callback,
+             WAITORTIMERCALLBACK non_crash_dump_request_callback,
+             WAITORTIMERCALLBACK process_end_callback)
+      : crash_dump_request_thread_pool_wait_(INVALID_HANDLE_VALUE),
+        non_crash_dump_request_thread_pool_wait_(INVALID_HANDLE_VALUE),
+        process_end_thread_pool_wait_(INVALID_HANDLE_VALUE),
+        lock_(),
+        port_(port),
+        delegate_(delegate),
+        crash_dump_requested_event_(std::move(crash_dump_requested_event)),
+        non_crash_dump_requested_event_(
+            std::move(non_crash_dump_requested_event)),
+        non_crash_dump_completed_event_(
+            std::move(non_crash_dump_completed_event)),
+        process_(std::move(process)),
+        crash_exception_information_address_(
+            crash_exception_information_address),
+        non_crash_exception_information_address_(
+            non_crash_exception_information_address),
+        debug_critical_section_address_(debug_critical_section_address) {
+    RegisterThreadPoolWaits(crash_dump_request_callback,
+                            non_crash_dump_request_callback,
+                            process_end_callback);
+  }
+
+  ~ClientData() {
+    // It is important that this only access the threadpool waits (it's called
+    // from the main thread) until the waits are unregistered, to ensure that
+    // any outstanding callbacks are complete.
+    UnregisterThreadPoolWaits();
+  }
+
+  base::Lock* lock() { return &lock_; }
+  HANDLE port() const { return port_; }
+  ExceptionHandlerServer::Delegate* delegate() const { return delegate_; }
+  HANDLE crash_dump_requested_event() const {
+    return crash_dump_requested_event_.get();
+  }
+  HANDLE non_crash_dump_requested_event() const {
+    return non_crash_dump_requested_event_.get();
+  }
+  HANDLE non_crash_dump_completed_event() const {
+    return non_crash_dump_completed_event_.get();
+  }
+  WinVMAddress crash_exception_information_address() const {
+    return crash_exception_information_address_;
+  }
+  WinVMAddress non_crash_exception_information_address() const {
+    return non_crash_exception_information_address_;
+  }
+  WinVMAddress debug_critical_section_address() const {
+    return debug_critical_section_address_;
+  }
+  HANDLE process() const { return process_.get(); }
+
+ private:
+  void RegisterThreadPoolWaits(
+      WAITORTIMERCALLBACK crash_dump_request_callback,
+      WAITORTIMERCALLBACK non_crash_dump_request_callback,
+      WAITORTIMERCALLBACK process_end_callback) {
+    if (!RegisterWaitForSingleObject(&crash_dump_request_thread_pool_wait_,
+                                     crash_dump_requested_event_.get(),
+                                     crash_dump_request_callback,
+                                     this,
+                                     INFINITE,
+                                     WT_EXECUTEDEFAULT)) {
+      LOG(ERROR) << "RegisterWaitForSingleObject crash dump requested";
+    }
+
+    if (!RegisterWaitForSingleObject(&non_crash_dump_request_thread_pool_wait_,
+                                     non_crash_dump_requested_event_.get(),
+                                     non_crash_dump_request_callback,
+                                     this,
+                                     INFINITE,
+                                     WT_EXECUTEDEFAULT)) {
+      LOG(ERROR) << "RegisterWaitForSingleObject non-crash dump requested";
+    }
+
+    if (!RegisterWaitForSingleObject(&process_end_thread_pool_wait_,
+                                     process_.get(),
+                                     process_end_callback,
+                                     this,
+                                     INFINITE,
+                                     WT_EXECUTEONLYONCE)) {
+      LOG(ERROR) << "RegisterWaitForSingleObject process end";
+    }
+  }
+
+  // This blocks until outstanding calls complete so that we know it's safe to
+  // delete this object. Because of this, it must be executed on the main
+  // thread, not a threadpool thread.
+  void UnregisterThreadPoolWaits() {
+    UnregisterWaitEx(crash_dump_request_thread_pool_wait_,
+                     INVALID_HANDLE_VALUE);
+    crash_dump_request_thread_pool_wait_ = INVALID_HANDLE_VALUE;
+    UnregisterWaitEx(non_crash_dump_request_thread_pool_wait_,
+                     INVALID_HANDLE_VALUE);
+    non_crash_dump_request_thread_pool_wait_ = INVALID_HANDLE_VALUE;
+    UnregisterWaitEx(process_end_thread_pool_wait_, INVALID_HANDLE_VALUE);
+    process_end_thread_pool_wait_ = INVALID_HANDLE_VALUE;
+  }
+
+  // These are only accessed on the main thread.
+  HANDLE crash_dump_request_thread_pool_wait_;
+  HANDLE non_crash_dump_request_thread_pool_wait_;
+  HANDLE process_end_thread_pool_wait_;
+
+  base::Lock lock_;
+  // Access to these fields must be guarded by lock_.
+  HANDLE port_;  // weak
+  ExceptionHandlerServer::Delegate* delegate_;  // weak
+  ScopedKernelHANDLE crash_dump_requested_event_;
+  ScopedKernelHANDLE non_crash_dump_requested_event_;
+  ScopedKernelHANDLE non_crash_dump_completed_event_;
+  ScopedKernelHANDLE process_;
+  WinVMAddress crash_exception_information_address_;
+  WinVMAddress non_crash_exception_information_address_;
+  WinVMAddress debug_critical_section_address_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClientData);
+};
+
+}  // namespace internal
+
+ExceptionHandlerServer::Delegate::~Delegate() {
+}
+
+ExceptionHandlerServer::ExceptionHandlerServer(bool persistent)
+    : pipe_name_(),
+      port_(CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1)),
+      first_pipe_instance_(),
+      clients_lock_(),
+      clients_(),
+      persistent_(persistent) {
+}
+
+ExceptionHandlerServer::~ExceptionHandlerServer() {
+}
+
+void ExceptionHandlerServer::SetPipeName(const std::wstring& pipe_name) {
+  DCHECK(pipe_name_.empty());
+  DCHECK(!pipe_name.empty());
+
+  pipe_name_ = pipe_name;
+}
+
+void ExceptionHandlerServer::InitializeWithInheritedDataForInitialClient(
+    const InitialClientData& initial_client_data,
+    Delegate* delegate) {
+  DCHECK(pipe_name_.empty());
+  DCHECK(!first_pipe_instance_.is_valid());
+
+  first_pipe_instance_.reset(initial_client_data.first_pipe_instance());
+
+  // TODO(scottmg): Vista+. Might need to pass through or possibly find an Nt*.
+  size_t bytes = sizeof(wchar_t) * _MAX_PATH + sizeof(FILE_NAME_INFO);
+  std::unique_ptr<uint8_t[]> data(new uint8_t[bytes]);
+  if (!GetFileInformationByHandleEx(first_pipe_instance_.get(),
+                                    FileNameInfo,
+                                    data.get(),
+                                    static_cast<DWORD>(bytes))) {
+    PLOG(FATAL) << "GetFileInformationByHandleEx";
+  }
+  FILE_NAME_INFO* file_name_info =
+      reinterpret_cast<FILE_NAME_INFO*>(data.get());
+  pipe_name_ =
+      L"\\\\.\\pipe" + std::wstring(file_name_info->FileName,
+                                    file_name_info->FileNameLength /
+                                        sizeof(file_name_info->FileName[0]));
+
+  {
+    base::AutoLock lock(clients_lock_);
+    internal::ClientData* client = new internal::ClientData(
+        port_.get(),
+        delegate,
+        ScopedKernelHANDLE(initial_client_data.client_process()),
+        ScopedKernelHANDLE(initial_client_data.request_crash_dump()),
+        ScopedKernelHANDLE(initial_client_data.request_non_crash_dump()),
+        ScopedKernelHANDLE(initial_client_data.non_crash_dump_completed()),
+        initial_client_data.crash_exception_information(),
+        initial_client_data.non_crash_exception_information(),
+        initial_client_data.debug_critical_section_address(),
+        &OnCrashDumpEvent,
+        &OnNonCrashDumpEvent,
+        &OnProcessEnd);
+    clients_.insert(client);
+  }
+}
+
+void ExceptionHandlerServer::Run(Delegate* delegate) {
+  uint64_t shutdown_token = base::RandUint64();
+  ScopedKernelHANDLE thread_handles[kPipeInstances];
+  for (size_t i = 0; i < base::size(thread_handles); ++i) {
+    HANDLE pipe;
+    if (first_pipe_instance_.is_valid()) {
+      pipe = first_pipe_instance_.release();
+    } else {
+      pipe = CreateNamedPipeInstance(pipe_name_, i == 0);
+      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
+    // up the context object and the pipe instance on termination.
+    internal::PipeServiceContext* context =
+        new internal::PipeServiceContext(port_.get(),
+                                         pipe,
+                                         delegate,
+                                         &clients_lock_,
+                                         &clients_,
+                                         shutdown_token);
+    thread_handles[i].reset(
+        CreateThread(nullptr, 0, &PipeServiceProc, context, 0, nullptr));
+    PCHECK(thread_handles[i].is_valid()) << "CreateThread";
+  }
+
+  delegate->ExceptionHandlerServerStarted();
+
+  // This is the main loop of the server. Most work is done on the threadpool,
+  // other than process end handling which is posted back to this main thread,
+  // as we must unregister the threadpool waits here.
+  for (;;) {
+    OVERLAPPED* ov = nullptr;
+    ULONG_PTR key = 0;
+    DWORD bytes = 0;
+    GetQueuedCompletionStatus(port_.get(), &bytes, &key, &ov, INFINITE);
+    if (!key) {
+      // Shutting down.
+      break;
+    }
+
+    // Otherwise, this is a request to unregister and destroy the given client.
+    // delete'ing the ClientData blocks in UnregisterWaitEx to ensure all
+    // outstanding threadpool waits are complete. This is important because the
+    // process handle can be signalled *before* the dump request is signalled.
+    internal::ClientData* client = reinterpret_cast<internal::ClientData*>(key);
+    base::AutoLock lock(clients_lock_);
+    clients_.erase(client);
+    delete client;
+    if (!persistent_ && clients_.empty())
+      break;
+  }
+
+  // Signal to the named pipe instances that they should terminate.
+  for (size_t i = 0; i < base::size(thread_handles); ++i) {
+    ClientToServerMessage message;
+    memset(&message, 0, sizeof(message));
+    message.type = ClientToServerMessage::kShutdown;
+    message.shutdown.token = shutdown_token;
+    ServerToClientMessage response;
+    SendToCrashHandlerServer(pipe_name_,
+                             reinterpret_cast<ClientToServerMessage&>(message),
+                             &response);
+  }
+
+  for (auto& handle : thread_handles)
+    WaitForSingleObject(handle.get(), INFINITE);
+
+  // Deleting ClientData does a blocking wait until the threadpool executions
+  // have terminated when unregistering them.
+  {
+    base::AutoLock lock(clients_lock_);
+    for (auto* client : clients_)
+      delete client;
+    clients_.clear();
+  }
+}
+
+void ExceptionHandlerServer::Stop() {
+  // Post a null key (third argument) to trigger shutdown.
+  PostQueuedCompletionStatus(port_.get(), 0, 0, nullptr);
+}
+
+// This function must be called with service_context.pipe() already connected to
+// a client pipe. It exchanges data with the client and adds a ClientData record
+// to service_context->clients().
+//
+// static
+bool ExceptionHandlerServer::ServiceClientConnection(
+    const internal::PipeServiceContext& service_context) {
+  ClientToServerMessage message;
+
+  if (!LoggingReadFileExactly(
+          service_context.pipe(), &message, sizeof(message)))
+    return false;
+
+  switch (message.type) {
+    case ClientToServerMessage::kShutdown: {
+      if (message.shutdown.token != service_context.shutdown_token()) {
+        LOG(ERROR) << "forged shutdown request, got: "
+                   << message.shutdown.token;
+        return false;
+      }
+      ServerToClientMessage shutdown_response = {};
+      LoggingWriteFile(service_context.pipe(),
+                       &shutdown_response,
+                       sizeof(shutdown_response));
+      return true;
+    }
+
+    case ClientToServerMessage::kPing: {
+      // No action required, the fact that the message was processed is
+      // sufficient.
+      ServerToClientMessage shutdown_response = {};
+      LoggingWriteFile(service_context.pipe(),
+                       &shutdown_response,
+                       sizeof(shutdown_response));
+      return false;
+    }
+
+    case ClientToServerMessage::kRegister:
+      // Handled below.
+      break;
+
+    default:
+      LOG(ERROR) << "unhandled message type: " << message.type;
+      return false;
+  }
+
+  if (message.registration.version != RegistrationRequest::kMessageVersion) {
+    LOG(ERROR) << "unexpected version. got: " << message.registration.version
+               << " expecting: " << RegistrationRequest::kMessageVersion;
+    return false;
+  }
+
+  decltype(GetNamedPipeClientProcessId)* get_named_pipe_client_process_id =
+      GetNamedPipeClientProcessIdFunction();
+  if (get_named_pipe_client_process_id) {
+    // GetNamedPipeClientProcessId is only available on Vista+.
+    DWORD real_pid = 0;
+    if (get_named_pipe_client_process_id(service_context.pipe(), &real_pid) &&
+        message.registration.client_process_id != real_pid) {
+      LOG(ERROR) << "forged client pid, real pid: " << real_pid
+                 << ", got: " << message.registration.client_process_id;
+      return false;
+    }
+  }
+
+  // We attempt to open the process as us. This is the main case that should
+  // almost always succeed as the server will generally be more privileged. If
+  // we're running as a different user, it may be that we will fail to open
+  // the process, but the client will be able to, so we make a second attempt
+  // having impersonated the client.
+  HANDLE client_process = OpenProcess(
+      kXPProcessAllAccess, false, message.registration.client_process_id);
+  if (!client_process) {
+    if (!ImpersonateNamedPipeClient(service_context.pipe())) {
+      PLOG(ERROR) << "ImpersonateNamedPipeClient";
+      return false;
+    }
+    client_process = OpenProcess(
+        kXPProcessAllAccess, false, message.registration.client_process_id);
+    PCHECK(RevertToSelf());
+    if (!client_process) {
+      LOG(ERROR) << "failed to open " << message.registration.client_process_id;
+      return false;
+    }
+  }
+
+  internal::ClientData* client;
+  {
+    base::AutoLock lock(*service_context.clients_lock());
+    client = new internal::ClientData(
+        service_context.port(),
+        service_context.delegate(),
+        ScopedKernelHANDLE(client_process),
+        ScopedKernelHANDLE(
+            CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
+        ScopedKernelHANDLE(
+            CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
+        ScopedKernelHANDLE(
+            CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
+        message.registration.crash_exception_information,
+        message.registration.non_crash_exception_information,
+        message.registration.critical_section_address,
+        &OnCrashDumpEvent,
+        &OnNonCrashDumpEvent,
+        &OnProcessEnd);
+    service_context.clients()->insert(client);
+  }
+
+  // Duplicate the events back to the client so they can request a dump.
+  ServerToClientMessage response;
+  response.registration.request_crash_dump_event =
+      HandleToInt(DuplicateEvent(
+          client->process(), client->crash_dump_requested_event()));
+  response.registration.request_non_crash_dump_event =
+      HandleToInt(DuplicateEvent(
+          client->process(), client->non_crash_dump_requested_event()));
+  response.registration.non_crash_dump_completed_event =
+      HandleToInt(DuplicateEvent(
+          client->process(), client->non_crash_dump_completed_event()));
+
+  if (!LoggingWriteFile(service_context.pipe(), &response, sizeof(response)))
+    return false;
+
+  return false;
+}
+
+// static
+DWORD __stdcall ExceptionHandlerServer::PipeServiceProc(void* ctx) {
+  internal::PipeServiceContext* service_context =
+      reinterpret_cast<internal::PipeServiceContext*>(ctx);
+  DCHECK(service_context);
+
+  for (;;) {
+    bool ret = !!ConnectNamedPipe(service_context->pipe(), nullptr);
+    if (!ret && GetLastError() != ERROR_PIPE_CONNECTED) {
+      PLOG(ERROR) << "ConnectNamedPipe";
+    } else if (ServiceClientConnection(*service_context)) {
+      break;
+    }
+    DisconnectNamedPipe(service_context->pipe());
+  }
+
+  delete service_context;
+
+  return 0;
+}
+
+// static
+void __stdcall ExceptionHandlerServer::OnCrashDumpEvent(void* ctx, BOOLEAN) {
+  // This function is executed on the thread pool.
+  internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);
+  base::AutoLock lock(*client->lock());
+
+  // Capture the exception.
+  unsigned int exit_code = client->delegate()->ExceptionHandlerServerException(
+      client->process(),
+      client->crash_exception_information_address(),
+      client->debug_critical_section_address());
+
+  SafeTerminateProcess(client->process(), exit_code);
+}
+
+// static
+void __stdcall ExceptionHandlerServer::OnNonCrashDumpEvent(void* ctx, BOOLEAN) {
+  // This function is executed on the thread pool.
+  internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);
+  base::AutoLock lock(*client->lock());
+
+  // Capture the exception.
+  client->delegate()->ExceptionHandlerServerException(
+      client->process(),
+      client->non_crash_exception_information_address(),
+      client->debug_critical_section_address());
+
+  bool result = !!SetEvent(client->non_crash_dump_completed_event());
+  PLOG_IF(ERROR, !result) << "SetEvent";
+}
+
+// static
+void __stdcall ExceptionHandlerServer::OnProcessEnd(void* ctx, BOOLEAN) {
+  // This function is executed on the thread pool.
+  internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);
+  base::AutoLock lock(*client->lock());
+
+  // Post back to the main thread to have it delete this client record.
+  PostQueuedCompletionStatus(client->port(), 0, ULONG_PTR(client), nullptr);
+}
+
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/exception_handler_server.h b/src/third_party/crashpad/util/win/exception_handler_server.h
new file mode 100644
index 0000000..994cdba
--- /dev/null
+++ b/src/third_party/crashpad/util/win/exception_handler_server.h
@@ -0,0 +1,140 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_EXCEPTION_HANDLER_SERVER_H_
+#define CRASHPAD_UTIL_WIN_EXCEPTION_HANDLER_SERVER_H_
+
+#include <set>
+#include <string>
+
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "util/file/file_io.h"
+#include "util/win/address_types.h"
+#include "util/win/initial_client_data.h"
+#include "util/win/scoped_handle.h"
+
+namespace crashpad {
+
+namespace internal {
+class PipeServiceContext;
+class ClientData;
+}  // namespace internal
+
+//! \brief Runs the main exception-handling server in Crashpad's handler
+//!     process.
+class ExceptionHandlerServer {
+ public:
+  class Delegate {
+   public:
+    //! \brief Called when the server has created the named pipe connection
+    //!     points and is ready to service requests.
+    virtual void ExceptionHandlerServerStarted() = 0;
+
+    //! \brief Called when the client has signalled that it has encountered an
+    //!     exception and so wants a crash dump to be taken.
+    //!
+    //! \param[in] process A handle to the client process. Ownership of the
+    //!     lifetime of this handle is not passed to the delegate.
+    //! \param[in] exception_information_address The address in the client's
+    //!     address space of an ExceptionInformation structure.
+    //! \param[in] debug_critical_section_address The address in the client's
+    //!     address space of a `CRITICAL_SECTION` allocated with a valid
+    //!     `.DebugInfo` field, or `0` if unavailable.
+    //! \return The exit code that should be used when terminating the client
+    //!     process.
+    virtual unsigned int ExceptionHandlerServerException(
+        HANDLE process,
+        WinVMAddress exception_information_address,
+        WinVMAddress debug_critical_section_address) = 0;
+
+   protected:
+    ~Delegate();
+  };
+
+  //! \brief Constructs the exception handling server.
+  //!
+  //! \param[in] persistent `true` if Run() should not return until Stop() is
+  //!     called. If `false`, Run() will return when all clients have exited,
+  //!     although Run() will always wait for the first client to connect.
+  explicit ExceptionHandlerServer(bool persistent);
+
+  ~ExceptionHandlerServer();
+
+  //! \brief Sets the pipe name to listen for client registrations on.
+  //!
+  //! This method, or InitializeWithInheritedDataForInitialClient(), must be
+  //! called before Run().
+  //!
+  //! \param[in] pipe_name The name of the pipe to listen on. Must be of the
+  //!     form "\\.\pipe\<some_name>".
+  void SetPipeName(const std::wstring& pipe_name);
+
+  //! \brief Sets the pipe to listen for client registrations on, providing
+  //!     the first precreated instance.
+  //!
+  //! This method, or SetPipeName(), must be called before Run(). All of these
+  //! parameters are generally created in a parent process that launches the
+  //! handler. For more details see the Windows implementation of
+  //! CrashpadClient.
+  //!
+  //! \sa CrashpadClient
+  //! \sa RegistrationRequest
+  //!
+  //! \param[in] initial_client_data The handles and addresses of data inherited
+  //!     from a parent process needed to initialize and register the first
+  //!     client. Ownership of these handles is taken.
+  //! \param[in] delegate The interface to which the exceptions are delegated
+  //!     when they are caught in Run(). Ownership is not transferred.
+  void InitializeWithInheritedDataForInitialClient(
+      const InitialClientData& initial_client_data,
+      Delegate* delegate);
+
+  //! \brief Runs the exception-handling server.
+  //!
+  //! \param[in] delegate The interface to which the exceptions are delegated
+  //!     when they are caught in Run(). Ownership is not transferred.
+  void Run(Delegate* delegate);
+
+  //! \brief Stops the exception-handling server. Returns immediately. The
+  //!     object must not be destroyed until Run() returns.
+  void Stop();
+
+  //! \brief The number of server-side pipe instances that the exception handler
+  //!     server creates to listen for connections from clients.
+  static const size_t kPipeInstances = 2;
+
+ private:
+  static bool ServiceClientConnection(
+      const internal::PipeServiceContext& service_context);
+  static DWORD __stdcall PipeServiceProc(void* ctx);
+  static void __stdcall OnCrashDumpEvent(void* ctx, BOOLEAN);
+  static void __stdcall OnNonCrashDumpEvent(void* ctx, BOOLEAN);
+  static void __stdcall OnProcessEnd(void* ctx, BOOLEAN);
+
+  std::wstring pipe_name_;
+  ScopedKernelHANDLE port_;
+  ScopedFileHandle first_pipe_instance_;
+
+  base::Lock clients_lock_;
+  std::set<internal::ClientData*> clients_;
+
+  bool persistent_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer);
+};
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_EXCEPTION_HANDLER_SERVER_H_
diff --git a/src/third_party/crashpad/util/win/exception_handler_server_test.cc b/src/third_party/crashpad/util/win/exception_handler_server_test.cc
new file mode 100644
index 0000000..ce31677
--- /dev/null
+++ b/src/third_party/crashpad/util/win/exception_handler_server_test.cc
@@ -0,0 +1,213 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/exception_handler_server.h"
+
+#include <windows.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "client/crashpad_client.h"
+#include "gtest/gtest.h"
+#include "test/win/win_child_process.h"
+#include "util/thread/thread.h"
+#include "util/win/address_types.h"
+#include "util/win/registration_protocol_win.h"
+#include "util/win/scoped_handle.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// Runs the ExceptionHandlerServer on a background thread.
+class RunServerThread : public Thread {
+ public:
+  // Instantiates a thread which will invoke server->Run(delegate).
+  RunServerThread(ExceptionHandlerServer* server,
+                  ExceptionHandlerServer::Delegate* delegate)
+      : server_(server), delegate_(delegate) {}
+  ~RunServerThread() override {}
+
+ private:
+  // Thread:
+  void ThreadMain() override { server_->Run(delegate_); }
+
+  ExceptionHandlerServer* server_;
+  ExceptionHandlerServer::Delegate* delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(RunServerThread);
+};
+
+class TestDelegate : public ExceptionHandlerServer::Delegate {
+ public:
+  explicit TestDelegate(HANDLE server_ready) : server_ready_(server_ready) {}
+  ~TestDelegate() {}
+
+  void ExceptionHandlerServerStarted() override {
+    SetEvent(server_ready_);
+  }
+  unsigned int ExceptionHandlerServerException(
+      HANDLE process,
+      WinVMAddress exception_information_address,
+      WinVMAddress debug_critical_section_address) override {
+    return 0;
+  }
+
+  void WaitForStart() { WaitForSingleObject(server_ready_, INFINITE); }
+
+ private:
+  HANDLE server_ready_;  // weak
+
+  DISALLOW_COPY_AND_ASSIGN(TestDelegate);
+};
+
+class ExceptionHandlerServerTest : public testing::Test {
+ public:
+  ExceptionHandlerServerTest()
+      : server_(true),
+        pipe_name_(L"\\\\.\\pipe\\test_name"),
+        server_ready_(CreateEvent(nullptr, false, false, nullptr)),
+        delegate_(server_ready_.get()),
+        server_thread_(&server_, &delegate_) {
+    server_.SetPipeName(pipe_name_);
+  }
+
+  TestDelegate& delegate() { return delegate_; }
+  ExceptionHandlerServer& server() { return server_; }
+  Thread& server_thread() { return server_thread_; }
+  const std::wstring& pipe_name() const { return pipe_name_; }
+
+ private:
+  ExceptionHandlerServer server_;
+  std::wstring pipe_name_;
+  ScopedKernelHANDLE server_ready_;
+  TestDelegate delegate_;
+  RunServerThread server_thread_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServerTest);
+};
+
+// During destruction, ensures that the server is stopped and the background
+// thread joined.
+class ScopedStopServerAndJoinThread {
+ public:
+  ScopedStopServerAndJoinThread(ExceptionHandlerServer* server, Thread* thread)
+      : server_(server), thread_(thread) {}
+  ~ScopedStopServerAndJoinThread() {
+    server_->Stop();
+    thread_->Join();
+  }
+
+ private:
+  ExceptionHandlerServer* server_;
+  Thread* thread_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedStopServerAndJoinThread);
+};
+
+TEST_F(ExceptionHandlerServerTest, Instantiate) {
+}
+
+TEST_F(ExceptionHandlerServerTest, StartAndStop) {
+  server_thread().Start();
+  ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(
+      &server(), &server_thread());
+  ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart());
+}
+
+TEST_F(ExceptionHandlerServerTest, StopWhileConnected) {
+  server_thread().Start();
+  ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(
+      &server(), &server_thread());
+  ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart());
+  CrashpadClient client;
+  client.SetHandlerIPCPipe(pipe_name());
+  // Leaving this scope causes the server to be stopped, while the connection
+  // is still open.
+}
+
+std::wstring ReadWString(FileHandle handle) {
+  size_t length = 0;
+  EXPECT_TRUE(LoggingReadFileExactly(handle, &length, sizeof(length)));
+  std::wstring str(length, L'\0');
+  if (length > 0) {
+    EXPECT_TRUE(
+        LoggingReadFileExactly(handle, &str[0], length * sizeof(str[0])));
+  }
+  return str;
+}
+
+void WriteWString(FileHandle handle, const std::wstring& str) {
+  size_t length = str.size();
+  EXPECT_TRUE(LoggingWriteFile(handle, &length, sizeof(length)));
+  if (length > 0) {
+    EXPECT_TRUE(LoggingWriteFile(handle, &str[0], length * sizeof(str[0])));
+  }
+}
+
+class TestClient final : public WinChildProcess {
+ public:
+  TestClient() : WinChildProcess() {}
+
+  ~TestClient() {}
+
+ private:
+  int Run() override {
+    std::wstring pipe_name = ReadWString(ReadPipeHandle());
+    CrashpadClient client;
+    if (!client.SetHandlerIPCPipe(pipe_name)) {
+      ADD_FAILURE();
+      return EXIT_FAILURE;
+    }
+    WriteWString(WritePipeHandle(), L"OK");
+    return EXIT_SUCCESS;
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(TestClient);
+};
+
+TEST_F(ExceptionHandlerServerTest, MultipleConnections) {
+  WinChildProcess::EntryPoint<TestClient>();
+
+  std::unique_ptr<WinChildProcess::Handles> handles_1 =
+      WinChildProcess::Launch();
+  std::unique_ptr<WinChildProcess::Handles> handles_2 =
+      WinChildProcess::Launch();
+  std::unique_ptr<WinChildProcess::Handles> handles_3 =
+      WinChildProcess::Launch();
+
+  // Must ensure the delegate outlasts the server.
+  {
+    server_thread().Start();
+    ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(
+        &server(), &server_thread());
+    ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart());
+
+    // Tell all the children where to connect.
+    WriteWString(handles_1->write.get(), pipe_name());
+    WriteWString(handles_2->write.get(), pipe_name());
+    WriteWString(handles_3->write.get(), pipe_name());
+
+    ASSERT_EQ(ReadWString(handles_3->read.get()), L"OK");
+    ASSERT_EQ(ReadWString(handles_2->read.get()), L"OK");
+    ASSERT_EQ(ReadWString(handles_1->read.get()), L"OK");
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/get_function.cc b/src/third_party/crashpad/util/win/get_function.cc
new file mode 100644
index 0000000..d498d30
--- /dev/null
+++ b/src/third_party/crashpad/util/win/get_function.cc
@@ -0,0 +1,44 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/get_function.h"
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace crashpad {
+namespace internal {
+
+FARPROC GetFunctionInternal(
+    const wchar_t* library, const char* function, bool required) {
+  HMODULE module = LoadLibrary(library);
+  DPCHECK(!required || module) << "LoadLibrary " << base::UTF16ToUTF8(library);
+  if (!module) {
+    return nullptr;
+  }
+
+  // Strip off any leading :: that may have come from stringifying the
+  // function’s name.
+  if (function[0] == ':' && function[1] == ':' &&
+      function[2] && function[2] != ':') {
+    function += 2;
+  }
+
+  FARPROC proc = GetProcAddress(module, function);
+  DPCHECK(!required || proc) << "GetProcAddress " << function;
+  return proc;
+}
+
+}  // namespace internal
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/get_function.h b/src/third_party/crashpad/util/win/get_function.h
new file mode 100644
index 0000000..50e5f15
--- /dev/null
+++ b/src/third_party/crashpad/util/win/get_function.h
@@ -0,0 +1,121 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_GET_FUNCTION_H_
+#define CRASHPAD_UTIL_WIN_GET_FUNCTION_H_
+
+#include <windows.h>
+
+//! \file
+
+namespace crashpad {
+namespace internal {
+
+//! \brief Returns a function pointer to a named function in a library.
+//!
+//! Do not call this directly, use the GET_FUNCTION() or GET_FUNCTION_REQUIRED()
+//! macros instead.
+//!
+//! This accesses \a library by calling `LoadLibrary()` and is subject to the
+//! same restrictions as that function. Notably, it can’t be used from a
+//! `DllMain()` entry point.
+//!
+//! \param[in] library The library to search in.
+//! \param[in] function The function to search for. If a leading `::` is
+//!     present, it will be stripped.
+//! \param[in] required If `true`, require the function to resolve by `DCHECK`.
+//!
+//! \return A pointer to the requested function on success. If \a required is
+//!     `true`, triggers a `DCHECK` assertion on failure, otherwise, `nullptr`
+//!     on failure.
+FARPROC GetFunctionInternal(
+    const wchar_t* library, const char* function, bool required);
+
+//! \copydoc GetFunctionInternal
+template <typename FunctionType>
+FunctionType* GetFunction(
+    const wchar_t* library, const char* function, bool required) {
+  return reinterpret_cast<FunctionType*>(
+      internal::GetFunctionInternal(library, function, required));
+}
+
+}  // namespace internal
+}  // namespace crashpad
+
+//! \brief Returns a function pointer to a named function in a library without
+//!     requiring that it be found.
+//!
+//! If the library or function cannot be found, this will return `nullptr`. This
+//! macro is intended to be used to access functions that may not be available
+//! at runtime.
+//!
+//! This macro returns a properly-typed function pointer. It is expected to be
+//! used in this way:
+//! \code
+//!     static const auto get_named_pipe_client_process_id =
+//!         GET_FUNCTION(L"kernel32.dll", ::GetNamedPipeClientProcessId);
+//!     if (get_named_pipe_client_process_id) {
+//!       BOOL rv = get_named_pipe_client_process_id(pipe, &client_process_id);
+//!     }
+//! \endcode
+//!
+//! This accesses \a library by calling `LoadLibrary()` and is subject to the
+//! same restrictions as that function. Notably, it can’t be used from a
+//! `DllMain()` entry point.
+//!
+//! \param[in] library The library to search in.
+//! \param[in] function The function to search for. A leading `::` is
+//!     recommended when a wrapper function of the same name is present.
+//!
+//! \return A pointer to the requested function on success, or `nullptr` on
+//!     failure.
+//!
+//! \sa GET_FUNCTION_REQUIRED
+#define GET_FUNCTION(library, function)                  \
+    crashpad::internal::GetFunction<decltype(function)>( \
+        library, #function, false)
+
+//! \brief Returns a function pointer to a named function in a library,
+//!     requiring that it be found.
+//!
+//! If the library or function cannot be found, this will trigger a `DCHECK`
+//! assertion. This macro is intended to be used to access functions that are
+//! always expected to be available at runtime but which are not present in any
+//! import library.
+//!
+//! This macro returns a properly-typed function pointer. It is expected to be
+//! used in this way:
+//! \code
+//!     static const auto nt_query_object =
+//!         GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtQueryObject);
+//!     NTSTATUS status =
+//!         nt_query_object(handle, type, &info, info_length, &return_length);
+//! \endcode
+//!
+//! This accesses \a library by calling `LoadLibrary()` and is subject to the
+//! same restrictions as that function. Notably, it can’t be used from a
+//! `DllMain()` entry point.
+//!
+//! \param[in] library The library to search in.
+//! \param[in] function The function to search for. A leading `::` is
+//!     recommended when a wrapper function of the same name is present.
+//!
+//! \return A pointer to the requested function.
+//!
+//! \sa GET_FUNCTION
+#define GET_FUNCTION_REQUIRED(library, function)         \
+    crashpad::internal::GetFunction<decltype(function)>( \
+        library, #function, true)
+
+#endif  // CRASHPAD_UTIL_WIN_GET_FUNCTION_H_
diff --git a/src/third_party/crashpad/util/win/get_function_test.cc b/src/third_party/crashpad/util/win/get_function_test.cc
new file mode 100644
index 0000000..a54ce7d
--- /dev/null
+++ b/src/third_party/crashpad/util/win/get_function_test.cc
@@ -0,0 +1,78 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/get_function.h"
+
+#include <windows.h>
+#include <winternl.h>
+
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(GetFunction, GetFunction) {
+  // Check equivalence of GET_FUNCTION_REQUIRED() with functions that are
+  // available in the SDK normally.
+  EXPECT_EQ(GET_FUNCTION_REQUIRED(L"kernel32.dll", GetProcAddress),
+            &GetProcAddress);
+  EXPECT_EQ(GET_FUNCTION_REQUIRED(L"kernel32.dll", LoadLibraryW),
+            &LoadLibraryW);
+
+  // Make sure that a function pointer retrieved by GET_FUNCTION_REQUIRED() can
+  // be called and that it works correctly.
+  const auto get_current_process_id =
+      GET_FUNCTION_REQUIRED(L"kernel32.dll", GetCurrentProcessId);
+  EXPECT_EQ(get_current_process_id, &GetCurrentProcessId);
+  ASSERT_TRUE(get_current_process_id);
+  EXPECT_EQ(get_current_process_id(), GetCurrentProcessId());
+
+  // GET_FUNCTION_REQUIRED() and GET_FUNCTION() should behave identically when
+  // the function is present.
+  EXPECT_EQ(GET_FUNCTION(L"kernel32.dll", GetCurrentProcessId),
+            get_current_process_id);
+
+  // Using a leading :: should also work.
+  EXPECT_EQ(GET_FUNCTION(L"kernel32.dll", ::GetCurrentProcessId),
+            get_current_process_id);
+  EXPECT_EQ(GET_FUNCTION_REQUIRED(L"kernel32.dll", ::GetCurrentProcessId),
+            get_current_process_id);
+
+  // Try a function that’s declared in the SDK’s headers but that has no import
+  // library.
+  EXPECT_TRUE(GET_FUNCTION_REQUIRED(L"ntdll.dll", RtlNtStatusToDosError));
+
+  // GetNamedPipeClientProcessId() is only available on Vista and later.
+  const auto get_named_pipe_client_process_id =
+      GET_FUNCTION(L"kernel32.dll", GetNamedPipeClientProcessId);
+  const DWORD version = GetVersion();
+  const DWORD major_version = LOBYTE(LOWORD(version));
+  EXPECT_EQ(get_named_pipe_client_process_id != nullptr, major_version >= 6);
+
+  // Test that GET_FUNCTION() can fail by trying a nonexistent library and a
+  // symbol that doesn’t exist in the specified library.
+  EXPECT_FALSE(GET_FUNCTION(L"not_a_real_library.dll", TerminateProcess));
+  EXPECT_FALSE(GET_FUNCTION(L"ntdll.dll", TerminateProcess));
+  EXPECT_FALSE(GET_FUNCTION(L"not_a_real_library.dll", ::TerminateProcess));
+  EXPECT_FALSE(GET_FUNCTION(L"ntdll.dll", ::TerminateProcess));
+
+  // Here it is!
+  EXPECT_TRUE(GET_FUNCTION(L"kernel32.dll", TerminateProcess));
+  EXPECT_TRUE(GET_FUNCTION(L"kernel32.dll", ::TerminateProcess));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/get_module_information.cc b/src/third_party/crashpad/util/win/get_module_information.cc
new file mode 100644
index 0000000..850bfe1
--- /dev/null
+++ b/src/third_party/crashpad/util/win/get_module_information.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/get_module_information.h"
+
+#include "util/win/get_function.h"
+
+namespace crashpad {
+
+BOOL CrashpadGetModuleInformation(HANDLE process,
+                                  HMODULE module,
+                                  MODULEINFO* module_info,
+                                  DWORD cb) {
+#if PSAPI_VERSION == 1
+  static const auto get_module_information =
+    GET_FUNCTION_REQUIRED(L"psapi.dll", GetModuleInformation);
+  return get_module_information(process, module, module_info, cb);
+#elif PSAPI_VERSION == 2
+  return GetModuleInformation(process, module, module_info, cb);
+#endif
+}
+
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/get_module_information.h b/src/third_party/crashpad/util/win/get_module_information.h
new file mode 100644
index 0000000..8fce035
--- /dev/null
+++ b/src/third_party/crashpad/util/win/get_module_information.h
@@ -0,0 +1,35 @@
+// Copyright 2016 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_GET_MODULE_INFORMATION_H_
+#define CRASHPAD_UTIL_WIN_GET_MODULE_INFORMATION_H_
+
+#include <windows.h>
+
+#ifndef PSAPI_VERSION
+#define PSAPI_VERSION 2
+#endif
+#include <psapi.h>
+
+namespace crashpad {
+
+//! \brief Proxy function for `GetModuleInformation()`.
+BOOL CrashpadGetModuleInformation(HANDLE process,
+                                  HMODULE module,
+                                  MODULEINFO* module_info,
+                                  DWORD cb);
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_GET_MODULE_INFORMATION_H_
diff --git a/src/third_party/crashpad/util/win/handle.cc b/src/third_party/crashpad/util/win/handle.cc
new file mode 100644
index 0000000..1744ecc
--- /dev/null
+++ b/src/third_party/crashpad/util/win/handle.cc
@@ -0,0 +1,37 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/handle.h"
+
+#include <stdint.h>
+
+#include "base/numerics/safe_conversions.h"
+#include "util/misc/from_pointer_cast.h"
+
+namespace crashpad {
+
+// These functions use “int” for the 32-bit integer handle type because
+// sign-extension needs to work correctly. INVALID_HANDLE_VALUE is defined as
+// ((HANDLE)(LONG_PTR)-1), and this needs to round-trip through an integer and
+// back to the same HANDLE value.
+
+int HandleToInt(HANDLE handle) {
+  return FromPointerCast<int>(handle);
+}
+
+HANDLE IntToHandle(int handle_int) {
+  return reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle_int));
+}
+
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/handle.h b/src/third_party/crashpad/util/win/handle.h
new file mode 100644
index 0000000..951302e
--- /dev/null
+++ b/src/third_party/crashpad/util/win/handle.h
@@ -0,0 +1,65 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_HANDLE_TO_INT_H_
+#define CRASHPAD_UTIL_WIN_HANDLE_TO_INT_H_
+
+#include <windows.h>
+
+namespace crashpad {
+
+//! \brief Converts a `HANDLE` to an `int`.
+//!
+//! `HANDLE` is a `typedef` for `void *`, but kernel `HANDLE` values aren’t
+//! pointers to anything. Only 32 bits of kernel `HANDLE`s are significant, even
+//! in 64-bit processes on 64-bit operating systems. See <a
+//! href="https://msdn.microsoft.com/library/aa384203.aspx">Interprocess
+//! Communication Between 32-bit and 64-bit Applications</a>.
+//!
+//! This function safely converts a kernel `HANDLE` to an `int` similarly to a
+//! cast operation. It checks that the operation can be performed safely, and
+//! aborts execution if it cannot.
+//!
+//! \param[in] handle The kernel `HANDLE` to convert.
+//!
+//! \return An equivalent `int`, truncated (if necessary) from \a handle. If
+//!     truncation would have resulted in an `int` that could not be converted
+//!     back to \a handle, aborts execution.
+//!
+//! \sa IntToHandle()
+int HandleToInt(HANDLE handle);
+
+//! \brief Converts an `int` to an `HANDLE`.
+//!
+//! `HANDLE` is a `typedef` for `void *`, but kernel `HANDLE` values aren’t
+//! pointers to anything. Only 32 bits of kernel `HANDLE`s are significant, even
+//! in 64-bit processes on 64-bit operating systems. See <a
+//! href="https://msdn.microsoft.com/library/aa384203.aspx">Interprocess
+//! Communication Between 32-bit and 64-bit Applications</a>.
+//!
+//! This function safely convert an `int` to a kernel `HANDLE` similarly to a
+//! cast operation.
+//!
+//! \param[in] handle_int The `int` to convert. This must have been produced by
+//!     HandleToInt(), possibly in a different process.
+//!
+//! \return An equivalent kernel `HANDLE`, sign-extended (if necessary) from \a
+//!     handle_int.
+//!
+//! \sa HandleToInt()
+HANDLE IntToHandle(int handle_int);
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_HANDLE_TO_INT_H_
diff --git a/src/third_party/crashpad/util/win/handle_test.cc b/src/third_party/crashpad/util/win/handle_test.cc
new file mode 100644
index 0000000..5898943
--- /dev/null
+++ b/src/third_party/crashpad/util/win/handle_test.cc
@@ -0,0 +1,53 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/handle.h"
+
+#include <stdint.h>
+
+#include <limits>
+
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(Handle, HandleToInt) {
+  EXPECT_EQ(HandleToInt(nullptr), 0);
+  EXPECT_EQ(HandleToInt(INVALID_HANDLE_VALUE), -1);
+  EXPECT_EQ(HandleToInt(reinterpret_cast<HANDLE>(1)), 1);
+  EXPECT_EQ(HandleToInt(reinterpret_cast<HANDLE>(
+                static_cast<intptr_t>(std::numeric_limits<int>::max()))),
+            std::numeric_limits<int>::max());
+  EXPECT_EQ(HandleToInt(reinterpret_cast<HANDLE>(
+                static_cast<intptr_t>(std::numeric_limits<int>::min()))),
+            std::numeric_limits<int>::min());
+}
+
+TEST(Handle, IntToHandle) {
+  EXPECT_EQ(IntToHandle(0), nullptr);
+  EXPECT_EQ(IntToHandle(-1), INVALID_HANDLE_VALUE);
+  EXPECT_EQ(IntToHandle(1), reinterpret_cast<HANDLE>(1));
+  EXPECT_EQ(IntToHandle(std::numeric_limits<int>::max()),
+            reinterpret_cast<HANDLE>(
+                static_cast<intptr_t>(std::numeric_limits<int>::max())));
+  EXPECT_EQ(IntToHandle(std::numeric_limits<int>::min()),
+            reinterpret_cast<HANDLE>(
+                static_cast<intptr_t>(std::numeric_limits<int>::min())));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/initial_client_data.cc b/src/third_party/crashpad/util/win/initial_client_data.cc
new file mode 100644
index 0000000..cbeecf1
--- /dev/null
+++ b/src/third_party/crashpad/util/win/initial_client_data.cc
@@ -0,0 +1,113 @@
+// Copyright 2016 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/initial_client_data.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "util/stdlib/string_number_conversion.h"
+#include "util/string/split_string.h"
+#include "util/win/handle.h"
+
+namespace crashpad {
+
+namespace {
+
+bool HandleFromString(const std::string& str, HANDLE* handle) {
+  unsigned int handle_uint;
+  if (!StringToNumber(str, &handle_uint) ||
+      (*handle = IntToHandle(handle_uint)) == INVALID_HANDLE_VALUE) {
+    LOG(ERROR) << "could not convert '" << str << "' to HANDLE";
+    return false;
+  }
+  return true;
+}
+
+bool AddressFromString(const std::string& str, WinVMAddress* address) {
+  if (!StringToNumber(str, address)) {
+    LOG(ERROR) << "could not convert '" << str << "' to WinVMAddress";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+InitialClientData::InitialClientData()
+    : crash_exception_information_(0),
+      non_crash_exception_information_(0),
+      debug_critical_section_address_(0),
+      request_crash_dump_(nullptr),
+      request_non_crash_dump_(nullptr),
+      non_crash_dump_completed_(nullptr),
+      first_pipe_instance_(INVALID_HANDLE_VALUE),
+      client_process_(nullptr),
+      is_valid_(false) {}
+
+InitialClientData::InitialClientData(
+    HANDLE request_crash_dump,
+    HANDLE request_non_crash_dump,
+    HANDLE non_crash_dump_completed,
+    HANDLE first_pipe_instance,
+    HANDLE client_process,
+    WinVMAddress crash_exception_information,
+    WinVMAddress non_crash_exception_information,
+    WinVMAddress debug_critical_section_address)
+    : crash_exception_information_(crash_exception_information),
+      non_crash_exception_information_(non_crash_exception_information),
+      debug_critical_section_address_(debug_critical_section_address),
+      request_crash_dump_(request_crash_dump),
+      request_non_crash_dump_(request_non_crash_dump),
+      non_crash_dump_completed_(non_crash_dump_completed),
+      first_pipe_instance_(first_pipe_instance),
+      client_process_(client_process),
+      is_valid_(true) {}
+
+bool InitialClientData::InitializeFromString(const std::string& str) {
+  std::vector<std::string> parts(SplitString(str, ','));
+  if (parts.size() != 8) {
+    LOG(ERROR) << "expected 8 comma separated arguments";
+    return false;
+  }
+
+  if (!HandleFromString(parts[0], &request_crash_dump_) ||
+      !HandleFromString(parts[1], &request_non_crash_dump_) ||
+      !HandleFromString(parts[2], &non_crash_dump_completed_) ||
+      !HandleFromString(parts[3], &first_pipe_instance_) ||
+      !HandleFromString(parts[4], &client_process_) ||
+      !AddressFromString(parts[5], &crash_exception_information_) ||
+      !AddressFromString(parts[6], &non_crash_exception_information_) ||
+      !AddressFromString(parts[7], &debug_critical_section_address_)) {
+    return false;
+  }
+
+  is_valid_ = true;
+  return true;
+}
+
+std::string InitialClientData::StringRepresentation() const {
+  return base::StringPrintf("0x%x,0x%x,0x%x,0x%x,0x%x,0x%I64x,0x%I64x,0x%I64x",
+                            HandleToInt(request_crash_dump_),
+                            HandleToInt(request_non_crash_dump_),
+                            HandleToInt(non_crash_dump_completed_),
+                            HandleToInt(first_pipe_instance_),
+                            HandleToInt(client_process_),
+                            crash_exception_information_,
+                            non_crash_exception_information_,
+                            debug_critical_section_address_);
+}
+
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/initial_client_data.h b/src/third_party/crashpad/util/win/initial_client_data.h
new file mode 100644
index 0000000..f686959
--- /dev/null
+++ b/src/third_party/crashpad/util/win/initial_client_data.h
@@ -0,0 +1,116 @@
+// Copyright 2016 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_INITIAL_CLIENT_DATA_H_
+#define CRASHPAD_UTIL_WIN_INITIAL_CLIENT_DATA_H_
+
+#include <windows.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "util/win/address_types.h"
+
+namespace crashpad {
+
+//! \brief A container for the data associated with the `--initial-client-data`
+//!     method for initializing the handler process on Windows.
+class InitialClientData {
+ public:
+  //! \brief Constructs an unintialized instance to be used with
+  //!     InitializeFromString().
+  InitialClientData();
+
+  //! \brief Constructs an instance of InitialClientData. This object does not
+  //!     take ownership of any of the referenced HANDLEs.
+  //!
+  //! \param[in] request_crash_dump An event signalled from the client on crash.
+  //! \param[in] request_non_crash_dump An event signalled from the client when
+  //!     it would like a dump to be taken, but allowed to continue afterwards.
+  //! \param[in] non_crash_dump_completed An event signalled from the handler to
+  //!     tell the client that the non-crash dump has completed, and it can
+  //!     continue execution.
+  //! \param[in] first_pipe_instance The server end and first instance of a pipe
+  //!     that will be used for communication with all other clients after this
+  //!     initial one.
+  //! \param[in] client_process A process handle for the client being
+  //!     registered.
+  //! \param[in] crash_exception_information The address, in the client's
+  //!     address space, of an ExceptionInformation structure, used when
+  //!     handling a crash dump request.
+  //! \param[in] non_crash_exception_information The address, in the client's
+  //!     address space, of an ExceptionInformation structure, used when
+  //!     handling a non-crashing dump request.
+  //! \param[in] debug_critical_section_address The address, in the client
+  //!     process's address space, of a `CRITICAL_SECTION` allocated with a
+  //!     valid .DebugInfo field. This can be accomplished by using
+  //!     InitializeCriticalSectionWithDebugInfoIfPossible() or equivalent. This
+  //!     value can be `0`, however then limited lock data will be available in
+  //!     minidumps.
+  InitialClientData(HANDLE request_crash_dump,
+                    HANDLE request_non_crash_dump,
+                    HANDLE non_crash_dump_completed,
+                    HANDLE first_pipe_instance,
+                    HANDLE client_process,
+                    WinVMAddress crash_exception_information,
+                    WinVMAddress non_crash_exception_information,
+                    WinVMAddress debug_critical_section_address);
+
+  //! \brief Returns whether the object has been initialized successfully.
+  bool IsValid() const { return is_valid_; }
+
+  //! Initializes this object from a string representation presumed to have been
+  //!     created by StringRepresentation().
+  //!
+  //! \param[in] str The output of StringRepresentation().
+  //!
+  //! \return `true` on success, or `false` with a message logged on failure.
+  bool InitializeFromString(const std::string& str);
+
+  //! \brief Returns a string representation of the data of this object,
+  //!     suitable for passing on the command line.
+  std::string StringRepresentation() const;
+
+  HANDLE request_crash_dump() const { return request_crash_dump_; }
+  HANDLE request_non_crash_dump() const { return request_non_crash_dump_; }
+  HANDLE non_crash_dump_completed() const { return non_crash_dump_completed_; }
+  HANDLE first_pipe_instance() const { return first_pipe_instance_; }
+  HANDLE client_process() const { return client_process_; }
+  WinVMAddress crash_exception_information() const {
+    return crash_exception_information_;
+  }
+  WinVMAddress non_crash_exception_information() const {
+    return non_crash_exception_information_;
+  }
+  WinVMAddress debug_critical_section_address() const {
+    return debug_critical_section_address_;
+  }
+
+ private:
+  WinVMAddress crash_exception_information_;
+  WinVMAddress non_crash_exception_information_;
+  WinVMAddress debug_critical_section_address_;
+  HANDLE request_crash_dump_;
+  HANDLE request_non_crash_dump_;
+  HANDLE non_crash_dump_completed_;
+  HANDLE first_pipe_instance_;
+  HANDLE client_process_;
+  bool is_valid_;
+
+  DISALLOW_COPY_AND_ASSIGN(InitialClientData);
+};
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_INITIAL_CLIENT_DATA_H_
diff --git a/src/third_party/crashpad/util/win/initial_client_data_test.cc b/src/third_party/crashpad/util/win/initial_client_data_test.cc
new file mode 100644
index 0000000..2121249
--- /dev/null
+++ b/src/third_party/crashpad/util/win/initial_client_data_test.cc
@@ -0,0 +1,73 @@
+// Copyright 2016 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/initial_client_data.h"
+
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(InitialClientData, Validity) {
+  InitialClientData icd1;
+  EXPECT_FALSE(icd1.IsValid());
+
+  InitialClientData icd2(
+      reinterpret_cast<HANDLE>(0x123),
+      reinterpret_cast<HANDLE>(0x456),
+      reinterpret_cast<HANDLE>(0x789),
+      reinterpret_cast<HANDLE>(0xabc),
+      reinterpret_cast<HANDLE>(0xdef),
+      0x7fff000012345678ull,
+      0x100000ull,
+      0xccccddddeeeeffffull);
+  EXPECT_TRUE(icd2.IsValid());
+}
+
+TEST(InitialClientData, RoundTrip) {
+  InitialClientData first(
+      reinterpret_cast<HANDLE>(0x123),
+      reinterpret_cast<HANDLE>(0x456),
+      reinterpret_cast<HANDLE>(0x789),
+      reinterpret_cast<HANDLE>(0xabc),
+      reinterpret_cast<HANDLE>(0xdef),
+      0x7fff000012345678ull,
+      0x100000ull,
+      0xccccddddeeeeffffull);
+
+  std::string as_string = first.StringRepresentation();
+  EXPECT_EQ(as_string,
+            "0x123,0x456,0x789,0xabc,0xdef,"
+            "0x7fff000012345678,0x100000,0xccccddddeeeeffff");
+
+  InitialClientData second;
+  ASSERT_TRUE(second.InitializeFromString(as_string));
+  EXPECT_EQ(second.request_crash_dump(), first.request_crash_dump());
+  EXPECT_EQ(second.request_non_crash_dump(), first.request_non_crash_dump());
+  EXPECT_EQ(second.non_crash_dump_completed(),
+            first.non_crash_dump_completed());
+  EXPECT_EQ(second.first_pipe_instance(), first.first_pipe_instance());
+  EXPECT_EQ(second.client_process(), first.client_process());
+  EXPECT_EQ(second.crash_exception_information(),
+            first.crash_exception_information());
+  EXPECT_EQ(second.non_crash_exception_information(),
+            first.non_crash_exception_information());
+  EXPECT_EQ(second.debug_critical_section_address(),
+            first.debug_critical_section_address());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/loader_lock.cc b/src/third_party/crashpad/util/win/loader_lock.cc
new file mode 100644
index 0000000..187ef84
--- /dev/null
+++ b/src/third_party/crashpad/util/win/loader_lock.cc
@@ -0,0 +1,52 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/loader_lock.h"
+
+#include <windows.h>
+
+#include "build/build_config.h"
+#include "util/win/process_structs.h"
+
+namespace crashpad {
+
+namespace {
+
+#ifdef ARCH_CPU_64_BITS
+using NativeTraits = process_types::internal::Traits64;
+#else
+using NativeTraits = process_types::internal::Traits32;
+#endif  // ARCH_CPU_64_BITS
+
+using PEB = process_types::PEB<NativeTraits>;
+using TEB = process_types::TEB<NativeTraits>;
+using RTL_CRITICAL_SECTION = process_types::RTL_CRITICAL_SECTION<NativeTraits>;
+
+TEB* GetTeb() {
+  return reinterpret_cast<TEB*>(NtCurrentTeb());
+}
+
+PEB* GetPeb() {
+  return reinterpret_cast<PEB*>(GetTeb()->ProcessEnvironmentBlock);
+}
+
+}  // namespace
+
+bool IsThreadInLoaderLock() {
+  RTL_CRITICAL_SECTION* loader_lock =
+      reinterpret_cast<RTL_CRITICAL_SECTION*>(GetPeb()->LoaderLock);
+  return loader_lock->OwningThread == GetTeb()->ClientId.UniqueThread;
+}
+
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/loader_lock.h b/src/third_party/crashpad/util/win/loader_lock.h
new file mode 100644
index 0000000..6c6b311
--- /dev/null
+++ b/src/third_party/crashpad/util/win/loader_lock.h
@@ -0,0 +1,25 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_LOADER_LOCK_H_
+#define CRASHPAD_UTIL_WIN_LOADER_LOCK_H_
+
+namespace crashpad {
+
+//! \return `true` if the current thread holds the loader lock.
+bool IsThreadInLoaderLock();
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_LOADER_LOCK_H_
diff --git a/src/third_party/crashpad/util/win/loader_lock_test.cc b/src/third_party/crashpad/util/win/loader_lock_test.cc
new file mode 100644
index 0000000..d33ea5a
--- /dev/null
+++ b/src/third_party/crashpad/util/win/loader_lock_test.cc
@@ -0,0 +1,36 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/loader_lock.h"
+
+#include "gtest/gtest.h"
+#include "util/win/get_function.h"
+
+extern "C" bool LoaderLockDetected();
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(LoaderLock, Detected) {
+  EXPECT_FALSE(IsThreadInLoaderLock());
+  auto* loader_lock_detected = GET_FUNCTION_REQUIRED(
+      L"crashpad_util_test_loader_lock_test.dll", LoaderLockDetected);
+  EXPECT_TRUE(loader_lock_detected());
+  EXPECT_FALSE(IsThreadInLoaderLock());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/loader_lock_test_dll.cc b/src/third_party/crashpad/util/win/loader_lock_test_dll.cc
new file mode 100644
index 0000000..b673ab3
--- /dev/null
+++ b/src/third_party/crashpad/util/win/loader_lock_test_dll.cc
@@ -0,0 +1,41 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <windows.h>
+
+#include "util/win/loader_lock.h"
+
+namespace {
+
+bool g_loader_lock_detected = false;
+
+}  // namespace
+
+extern "C" {
+
+__declspec(dllexport) bool LoaderLockDetected() {
+  return g_loader_lock_detected;
+}
+
+}  // extern "C"
+
+BOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID) {
+  switch (reason) {
+    case DLL_PROCESS_ATTACH:
+      g_loader_lock_detected = crashpad::IsThreadInLoaderLock();
+      break;
+  }
+
+  return TRUE;
+}
diff --git a/src/third_party/crashpad/util/win/module_version.cc b/src/third_party/crashpad/util/win/module_version.cc
new file mode 100644
index 0000000..bd83538
--- /dev/null
+++ b/src/third_party/crashpad/util/win/module_version.cc
@@ -0,0 +1,58 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/module_version.h"
+
+#include <windows.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace crashpad {
+
+bool GetModuleVersionAndType(const base::FilePath& path,
+                             VS_FIXEDFILEINFO* vs_fixedfileinfo) {
+  DWORD size = GetFileVersionInfoSize(path.value().c_str(), nullptr);
+  if (!size) {
+    PLOG_IF(WARNING, GetLastError() != ERROR_RESOURCE_TYPE_NOT_FOUND)
+        << "GetFileVersionInfoSize: " << base::UTF16ToUTF8(path.value());
+    return false;
+  }
+
+  std::unique_ptr<uint8_t[]> data(new uint8_t[size]);
+  if (!GetFileVersionInfo(path.value().c_str(), 0, size, data.get())) {
+    PLOG(WARNING) << "GetFileVersionInfo: "
+                  << base::UTF16ToUTF8(path.value());
+    return false;
+  }
+
+  VS_FIXEDFILEINFO* fixed_file_info;
+  UINT ffi_size;
+  if (!VerQueryValue(data.get(),
+                     L"\\",
+                     reinterpret_cast<void**>(&fixed_file_info),
+                     &ffi_size)) {
+    PLOG(WARNING) << "VerQueryValue";
+    return false;
+  }
+
+  *vs_fixedfileinfo = *fixed_file_info;
+  vs_fixedfileinfo->dwFileFlags &= vs_fixedfileinfo->dwFileFlagsMask;
+  return true;
+}
+
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/module_version.h b/src/third_party/crashpad/util/win/module_version.h
new file mode 100644
index 0000000..afc2a50
--- /dev/null
+++ b/src/third_party/crashpad/util/win/module_version.h
@@ -0,0 +1,45 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_MODULE_VERSION_H_
+#define CRASHPAD_UTIL_WIN_MODULE_VERSION_H_
+
+#include <windows.h>
+
+#include "base/files/file_path.h"
+
+namespace crashpad {
+
+//! \brief Retrieve the type and version information from a given module (exe,
+//!     dll, etc.)
+//!
+//! This function calls `GetFileVersionInfo()`, which can implicitly call
+//! `LoadLibrary()` to load \a path into the calling process. Do not call this
+//! function on an untrusted module, because there is a risk of executing the
+//! module’s code.
+//!
+//! \param[in] path The path to the module to be inspected.
+//! \param[out] vs_fixedfileinfo The VS_FIXEDFILEINFO on success.
+//!     VS_FIXEDFILEINFO::dwFileFlags will have been masked with
+//!     VS_FIXEDFILEINFO::dwFileFlagsMask already.
+//!
+//! \return `true` on success, or `false` on failure with a message logged. If
+//!     the module has no `VERSIONINFO` resource, `false` will be returned
+//!     without any messages logged.
+bool GetModuleVersionAndType(const base::FilePath& path,
+                             VS_FIXEDFILEINFO* vs_fixedfileinfo);
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_MODULE_VERSION_H_
diff --git a/src/third_party/crashpad/util/win/nt_internals.cc b/src/third_party/crashpad/util/win/nt_internals.cc
new file mode 100644
index 0000000..df8288a
--- /dev/null
+++ b/src/third_party/crashpad/util/win/nt_internals.cc
@@ -0,0 +1,176 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/nt_internals.h"
+
+#include "base/logging.h"
+#include "util/win/get_function.h"
+
+// Declarations that the system headers should provide but don’t.
+
+extern "C" {
+
+NTSTATUS NTAPI NtCreateThreadEx(PHANDLE ThreadHandle,
+                                ACCESS_MASK DesiredAccess,
+                                POBJECT_ATTRIBUTES ObjectAttributes,
+                                HANDLE ProcessHandle,
+                                PVOID StartRoutine,
+                                PVOID Argument,
+                                ULONG CreateFlags,
+                                SIZE_T ZeroBits,
+                                SIZE_T StackSize,
+                                SIZE_T MaximumStackSize,
+                                PVOID /*PPS_ATTRIBUTE_LIST*/ AttributeList);
+
+NTSTATUS NTAPI NtOpenThread(HANDLE* ThreadHandle,
+                            ACCESS_MASK DesiredAccess,
+                            OBJECT_ATTRIBUTES* ObjectAttributes,
+                            CLIENT_ID* ClientId);
+
+NTSTATUS NTAPI NtSuspendProcess(HANDLE);
+
+NTSTATUS NTAPI NtResumeProcess(HANDLE);
+
+VOID NTAPI RtlGetUnloadEventTraceEx(PULONG* ElementSize,
+                                    PULONG* ElementCount,
+                                    PVOID* EventTrace);
+
+}  // extern "C"
+
+namespace crashpad {
+
+NTSTATUS NtClose(HANDLE handle) {
+  static const auto nt_close = GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtClose);
+  return nt_close(handle);
+}
+
+NTSTATUS
+NtCreateThreadEx(PHANDLE thread_handle,
+                 ACCESS_MASK desired_access,
+                 POBJECT_ATTRIBUTES object_attributes,
+                 HANDLE process_handle,
+                 PVOID start_routine,
+                 PVOID argument,
+                 ULONG create_flags,
+                 SIZE_T zero_bits,
+                 SIZE_T stack_size,
+                 SIZE_T maximum_stack_size,
+                 PVOID attribute_list) {
+  static const auto nt_create_thread_ex =
+      GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtCreateThreadEx);
+  return nt_create_thread_ex(thread_handle,
+                             desired_access,
+                             object_attributes,
+                             process_handle,
+                             start_routine,
+                             argument,
+                             create_flags,
+                             zero_bits,
+                             stack_size,
+                             maximum_stack_size,
+                             attribute_list);
+}
+
+NTSTATUS NtQuerySystemInformation(
+    SYSTEM_INFORMATION_CLASS system_information_class,
+    PVOID system_information,
+    ULONG system_information_length,
+    PULONG return_length) {
+  static const auto nt_query_system_information =
+      GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtQuerySystemInformation);
+  return nt_query_system_information(system_information_class,
+                                     system_information,
+                                     system_information_length,
+                                     return_length);
+}
+
+NTSTATUS NtQueryInformationThread(HANDLE thread_handle,
+                                  THREADINFOCLASS thread_information_class,
+                                  PVOID thread_information,
+                                  ULONG thread_information_length,
+                                  PULONG return_length) {
+  static const auto nt_query_information_thread =
+      GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtQueryInformationThread);
+  return nt_query_information_thread(thread_handle,
+                                     thread_information_class,
+                                     thread_information,
+                                     thread_information_length,
+                                     return_length);
+}
+
+template <class Traits>
+NTSTATUS NtOpenThread(PHANDLE thread_handle,
+                      ACCESS_MASK desired_access,
+                      POBJECT_ATTRIBUTES object_attributes,
+                      const process_types::CLIENT_ID<Traits>* client_id) {
+  static const auto nt_open_thread =
+      GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtOpenThread);
+  return nt_open_thread(
+      thread_handle,
+      desired_access,
+      object_attributes,
+      const_cast<CLIENT_ID*>(reinterpret_cast<const CLIENT_ID*>(client_id)));
+}
+
+NTSTATUS NtQueryObject(HANDLE handle,
+                       OBJECT_INFORMATION_CLASS object_information_class,
+                       void* object_information,
+                       ULONG object_information_length,
+                       ULONG* return_length) {
+  static const auto nt_query_object =
+      GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtQueryObject);
+  return nt_query_object(handle,
+                         object_information_class,
+                         object_information,
+                         object_information_length,
+                         return_length);
+}
+
+NTSTATUS NtSuspendProcess(HANDLE handle) {
+  static const auto nt_suspend_process =
+      GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtSuspendProcess);
+  return nt_suspend_process(handle);
+}
+
+NTSTATUS NtResumeProcess(HANDLE handle) {
+  static const auto nt_resume_process =
+      GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtResumeProcess);
+  return nt_resume_process(handle);
+}
+
+void RtlGetUnloadEventTraceEx(ULONG** element_size,
+                              ULONG** element_count,
+                              void** event_trace) {
+  static const auto rtl_get_unload_event_trace_ex =
+      GET_FUNCTION_REQUIRED(L"ntdll.dll", ::RtlGetUnloadEventTraceEx);
+  rtl_get_unload_event_trace_ex(element_size, element_count, event_trace);
+}
+
+// Explicit instantiations with the only 2 valid template arguments to avoid
+// putting the body of the function in the header.
+template NTSTATUS NtOpenThread<process_types::internal::Traits32>(
+    PHANDLE thread_handle,
+    ACCESS_MASK desired_access,
+    POBJECT_ATTRIBUTES object_attributes,
+    const process_types::CLIENT_ID<process_types::internal::Traits32>*
+        client_id);
+
+template NTSTATUS NtOpenThread<process_types::internal::Traits64>(
+    PHANDLE thread_handle,
+    ACCESS_MASK desired_access,
+    POBJECT_ATTRIBUTES object_attributes,
+    const process_types::CLIENT_ID<process_types::internal::Traits64>*
+        client_id);
+
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/nt_internals.h b/src/third_party/crashpad/util/win/nt_internals.h
new file mode 100644
index 0000000..dad0405
--- /dev/null
+++ b/src/third_party/crashpad/util/win/nt_internals.h
@@ -0,0 +1,99 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_NT_INTERNALS_H_
+#define CRASHPAD_UTIL_WIN_NT_INTERNALS_H_
+
+#include <windows.h>
+#include <winternl.h>
+
+#include "util/win/process_structs.h"
+
+// Copied from ntstatus.h because um/winnt.h conflicts with general inclusion of
+// ntstatus.h.
+#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
+#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
+#define STATUS_PROCESS_IS_TERMINATING ((NTSTATUS)0xC000010AL)
+
+namespace crashpad {
+
+NTSTATUS NtClose(HANDLE handle);
+
+// http://processhacker.sourceforge.net/doc/ntpsapi_8h_source.html
+#define THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH 0x00000002
+NTSTATUS
+NtCreateThreadEx(PHANDLE thread_handle,
+                 ACCESS_MASK desired_access,
+                 POBJECT_ATTRIBUTES object_attributes,
+                 HANDLE process_handle,
+                 PVOID start_routine,
+                 PVOID argument,
+                 ULONG create_flags,
+                 SIZE_T zero_bits,
+                 SIZE_T stack_size,
+                 SIZE_T maximum_stack_size,
+                 PVOID /*PPS_ATTRIBUTE_LIST*/ attribute_list);
+
+// winternal.h defines THREADINFOCLASS, but not all members.
+enum { ThreadBasicInformation = 0 };
+
+// winternal.h defines SYSTEM_INFORMATION_CLASS, but not all members.
+enum { SystemExtendedHandleInformation = 64 };
+
+NTSTATUS NtQuerySystemInformation(
+    SYSTEM_INFORMATION_CLASS system_information_class,
+    PVOID system_information,
+    ULONG system_information_length,
+    PULONG return_length);
+
+NTSTATUS NtQueryInformationThread(HANDLE thread_handle,
+                                  THREADINFOCLASS thread_information_class,
+                                  PVOID thread_information,
+                                  ULONG thread_information_length,
+                                  PULONG return_length);
+
+template <class Traits>
+NTSTATUS NtOpenThread(PHANDLE thread_handle,
+                      ACCESS_MASK desired_access,
+                      POBJECT_ATTRIBUTES object_attributes,
+                      const process_types::CLIENT_ID<Traits>* client_id);
+
+NTSTATUS NtQueryObject(HANDLE handle,
+                       OBJECT_INFORMATION_CLASS object_information_class,
+                       void* object_information,
+                       ULONG object_information_length,
+                       ULONG* return_length);
+
+NTSTATUS NtSuspendProcess(HANDLE handle);
+
+NTSTATUS NtResumeProcess(HANDLE handle);
+
+// From https://msdn.microsoft.com/library/cc678403.aspx.
+template <class Traits>
+struct RTL_UNLOAD_EVENT_TRACE {
+  typename Traits::Pointer BaseAddress;
+  typename Traits::UnsignedIntegral SizeOfImage;
+  ULONG Sequence;
+  ULONG TimeDateStamp;
+  ULONG CheckSum;
+  WCHAR ImageName[32];
+};
+
+void RtlGetUnloadEventTraceEx(ULONG** element_size,
+                              ULONG** element_count,
+                              void** event_trace);
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_NT_INTERNALS_H_
diff --git a/src/third_party/crashpad/util/win/ntstatus_logging.cc b/src/third_party/crashpad/util/win/ntstatus_logging.cc
new file mode 100644
index 0000000..e9a9b61
--- /dev/null
+++ b/src/third_party/crashpad/util/win/ntstatus_logging.cc
@@ -0,0 +1,75 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/ntstatus_logging.h"
+
+#include <string>
+
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+
+namespace {
+
+std::string FormatNtstatus(DWORD ntstatus) {
+  char msgbuf[256];
+  DWORD len = FormatMessageA(
+      FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
+          FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_HMODULE,
+      GetModuleHandle(L"ntdll.dll"),
+      ntstatus,
+      0,
+      msgbuf,
+      static_cast<DWORD>(base::size(msgbuf)),
+      nullptr);
+  if (len) {
+    // Most system messages end in a period and a space. Remove the space if
+    // it’s there, because ~NtstatusLogMessage() includes one.
+    if (len >= 1 && msgbuf[len - 1] == ' ') {
+      msgbuf[len - 1] = '\0';
+    }
+    return msgbuf;
+  } else {
+    return base::StringPrintf("<failed to retrieve error message (0x%lx)>",
+                              GetLastError());
+  }
+}
+
+}  // namespace
+
+namespace logging {
+
+NtstatusLogMessage::NtstatusLogMessage(
+#if defined(MINI_CHROMIUM_BASE_LOGGING_H_)
+    const char* function,
+#endif
+    const char* file_path,
+    int line,
+    LogSeverity severity,
+    DWORD ntstatus)
+    : LogMessage(
+#if defined(MINI_CHROMIUM_BASE_LOGGING_H_)
+          function,
+#endif
+          file_path,
+          line,
+          severity),
+      ntstatus_(ntstatus) {
+}
+
+NtstatusLogMessage::~NtstatusLogMessage() {
+  stream() << ": " << FormatNtstatus(ntstatus_)
+           << base::StringPrintf(" (0x%08lx)", ntstatus_);
+}
+
+}  // namespace logging
diff --git a/src/third_party/crashpad/util/win/ntstatus_logging.h b/src/third_party/crashpad/util/win/ntstatus_logging.h
new file mode 100644
index 0000000..7eececc
--- /dev/null
+++ b/src/third_party/crashpad/util/win/ntstatus_logging.h
@@ -0,0 +1,98 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_NTSTATUS_LOGGING_H_
+#define CRASHPAD_UTIL_WIN_NTSTATUS_LOGGING_H_
+
+#include <windows.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace logging {
+
+class NtstatusLogMessage : public logging::LogMessage {
+ public:
+  NtstatusLogMessage(
+#if defined(MINI_CHROMIUM_BASE_LOGGING_H_)
+      const char* function,
+#endif
+      const char* file_path,
+      int line,
+      LogSeverity severity,
+      DWORD ntstatus);
+  ~NtstatusLogMessage();
+
+ private:
+  DWORD ntstatus_;
+
+  DISALLOW_COPY_AND_ASSIGN(NtstatusLogMessage);
+};
+
+}  // namespace logging
+
+#define NTSTATUS_LOG_STREAM(severity, ntstatus) \
+  COMPACT_GOOGLE_LOG_EX_##severity(NtstatusLogMessage, ntstatus).stream()
+
+#if defined(MINI_CHROMIUM_BASE_LOGGING_H_)
+
+#define NTSTATUS_VLOG_STREAM(verbose_level, ntstatus)                    \
+  logging::NtstatusLogMessage(                                           \
+      __PRETTY_FUNCTION__, __FILE__, __LINE__, -verbose_level, ntstatus) \
+      .stream()
+
+#else
+
+#define NTSTATUS_VLOG_STREAM(verbose_level, ntstatus)                       \
+  logging::NtstatusLogMessage(__FILE__, __LINE__, -verbose_level, ntstatus) \
+      .stream()
+
+#endif  // MINI_CHROMIUM_BASE_LOGGING_H_
+
+#define NTSTATUS_LOG(severity, ntstatus) \
+  LAZY_STREAM(NTSTATUS_LOG_STREAM(severity, ntstatus), LOG_IS_ON(severity))
+#define NTSTATUS_LOG_IF(severity, condition, ntstatus) \
+  LAZY_STREAM(NTSTATUS_LOG_STREAM(severity, ntstatus), \
+              LOG_IS_ON(severity) && (condition))
+
+#define NTSTATUS_VLOG(verbose_level, ntstatus)               \
+  LAZY_STREAM(NTSTATUS_VLOG_STREAM(verbose_level, ntstatus), \
+              VLOG_IS_ON(verbose_level))
+#define NTSTATUS_VLOG_IF(verbose_level, condition, ntstatus) \
+  LAZY_STREAM(NTSTATUS_VLOG_STREAM(verbose_level, ntstatus), \
+              VLOG_IS_ON(verbose_level) && (condition))
+
+#define NTSTATUS_CHECK(condition, ntstatus)                       \
+  LAZY_STREAM(NTSTATUS_LOG_STREAM(FATAL, ntstatus), !(condition)) \
+      << "Check failed: " #condition << ". "
+
+#define NTSTATUS_DLOG(severity, ntstatus) \
+  LAZY_STREAM(NTSTATUS_LOG_STREAM(severity, ntstatus), DLOG_IS_ON(severity))
+#define NTSTATUS_DLOG_IF(severity, condition, ntstatus) \
+  LAZY_STREAM(NTSTATUS_LOG_STREAM(severity, ntstatus),  \
+              DLOG_IS_ON(severity) && (condition))
+
+#define NTSTATUS_DVLOG(verbose_level, ntstatus)              \
+  LAZY_STREAM(NTSTATUS_VLOG_STREAM(verbose_level, ntstatus), \
+              DVLOG_IS_ON(verbose_level))
+#define NTSTATUS_DVLOG_IF(verbose_level, condition, ntstatus) \
+  LAZY_STREAM(NTSTATUS_VLOG_STREAM(verbose_level, ntstatus),  \
+              DVLOG_IS_ON(verbose_level) && (condition))
+
+#define NTSTATUS_DCHECK(condition, ntstatus)        \
+  LAZY_STREAM(NTSTATUS_LOG_STREAM(FATAL, ntstatus), \
+              DCHECK_IS_ON && !(condition))         \
+      << "Check failed: " #condition << ". "
+
+#endif  // CRASHPAD_UTIL_WIN_NTSTATUS_LOGGING_H_
diff --git a/src/third_party/crashpad/util/win/process_info.cc b/src/third_party/crashpad/util/win/process_info.cc
new file mode 100644
index 0000000..49a6f27
--- /dev/null
+++ b/src/third_party/crashpad/util/win/process_info.cc
@@ -0,0 +1,718 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/process_info.h"
+
+#include <stddef.h>
+#include <winternl.h>
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <new>
+#include <type_traits>
+
+#include "base/logging.h"
+#include "base/memory/free_deleter.h"
+#include "base/process/memory.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "util/misc/from_pointer_cast.h"
+#include "util/numeric/safe_assignment.h"
+#include "util/win/get_function.h"
+#include "util/win/handle.h"
+#include "util/win/nt_internals.h"
+#include "util/win/ntstatus_logging.h"
+#include "util/win/process_structs.h"
+#include "util/win/scoped_handle.h"
+
+namespace crashpad {
+
+namespace {
+
+using UniqueMallocPtr = std::unique_ptr<uint8_t[], base::FreeDeleter>;
+
+UniqueMallocPtr UncheckedAllocate(size_t size) {
+  void* raw_ptr = nullptr;
+  if (!base::UncheckedMalloc(size, &raw_ptr))
+    return UniqueMallocPtr();
+
+  return UniqueMallocPtr(new (raw_ptr) uint8_t[size]);
+}
+
+NTSTATUS NtQueryInformationProcess(HANDLE process_handle,
+                                   PROCESSINFOCLASS process_information_class,
+                                   PVOID process_information,
+                                   ULONG process_information_length,
+                                   PULONG return_length) {
+  static const auto nt_query_information_process =
+      GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtQueryInformationProcess);
+  return nt_query_information_process(process_handle,
+                                      process_information_class,
+                                      process_information,
+                                      process_information_length,
+                                      return_length);
+}
+
+bool IsProcessWow64(HANDLE process_handle) {
+  static const auto is_wow64_process =
+      GET_FUNCTION(L"kernel32.dll", ::IsWow64Process);
+  if (!is_wow64_process)
+    return false;
+  BOOL is_wow64;
+  if (!is_wow64_process(process_handle, &is_wow64)) {
+    PLOG(ERROR) << "IsWow64Process";
+    return false;
+  }
+  return !!is_wow64;
+}
+
+template <class T>
+bool ReadUnicodeString(HANDLE process,
+                       const process_types::UNICODE_STRING<T>& us,
+                       std::wstring* result) {
+  if (us.Length == 0) {
+    result->clear();
+    return true;
+  }
+  DCHECK_EQ(us.Length % sizeof(wchar_t), 0u);
+  result->resize(us.Length / sizeof(wchar_t));
+  SIZE_T bytes_read;
+  if (!ReadProcessMemory(
+          process,
+          reinterpret_cast<const void*>(static_cast<uintptr_t>(us.Buffer)),
+          &result->operator[](0),
+          us.Length,
+          &bytes_read)) {
+    PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING";
+    return false;
+  }
+  if (bytes_read != us.Length) {
+    LOG(ERROR) << "ReadProcessMemory UNICODE_STRING incorrect size";
+    return false;
+  }
+  return true;
+}
+
+template <class T>
+bool ReadStruct(HANDLE process, WinVMAddress at, T* into) {
+  SIZE_T bytes_read;
+  if (!ReadProcessMemory(process,
+                         reinterpret_cast<const void*>(at),
+                         into,
+                         sizeof(T),
+                         &bytes_read)) {
+    // We don't have a name for the type we're reading, so include the signature
+    // to get the type of T.
+    PLOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__;
+    return false;
+  }
+  if (bytes_read != sizeof(T)) {
+    LOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__ << " incorrect size";
+    return false;
+  }
+  return true;
+}
+
+bool RegionIsAccessible(const MEMORY_BASIC_INFORMATION64& memory_info) {
+  return memory_info.State == MEM_COMMIT &&
+         (memory_info.Protect & PAGE_NOACCESS) == 0 &&
+         (memory_info.Protect & PAGE_GUARD) == 0;
+}
+
+MEMORY_BASIC_INFORMATION64 MemoryBasicInformationToMemoryBasicInformation64(
+    const MEMORY_BASIC_INFORMATION& mbi) {
+  MEMORY_BASIC_INFORMATION64 mbi64 = {0};
+  mbi64.BaseAddress = FromPointerCast<ULONGLONG>(mbi.BaseAddress);
+  mbi64.AllocationBase = reinterpret_cast<ULONGLONG>(mbi.AllocationBase);
+  mbi64.AllocationProtect = mbi.AllocationProtect;
+  mbi64.RegionSize = mbi.RegionSize;
+  mbi64.State = mbi.State;
+  mbi64.Protect = mbi.Protect;
+  mbi64.Type = mbi.Type;
+  return mbi64;
+}
+
+// NtQueryObject with a retry for size mismatch as well as a minimum size to
+// retrieve (and expect).
+std::unique_ptr<uint8_t[]> QueryObject(
+    HANDLE handle,
+    OBJECT_INFORMATION_CLASS object_information_class,
+    ULONG minimum_size) {
+  ULONG size = minimum_size;
+  ULONG return_length;
+  std::unique_ptr<uint8_t[]> buffer(new uint8_t[size]);
+  NTSTATUS status = crashpad::NtQueryObject(
+      handle, object_information_class, buffer.get(), size, &return_length);
+  if (status == STATUS_INFO_LENGTH_MISMATCH) {
+    DCHECK_GT(return_length, size);
+    size = return_length;
+
+    // Free the old buffer before attempting to allocate a new one.
+    buffer.reset();
+
+    buffer.reset(new uint8_t[size]);
+    status = crashpad::NtQueryObject(
+        handle, object_information_class, buffer.get(), size, &return_length);
+  }
+
+  if (!NT_SUCCESS(status)) {
+    NTSTATUS_LOG(ERROR, status) << "NtQueryObject";
+    return nullptr;
+  }
+
+  DCHECK_LE(return_length, size);
+  DCHECK_GE(return_length, minimum_size);
+  return buffer;
+}
+
+}  // namespace
+
+template <class Traits>
+bool GetProcessBasicInformation(HANDLE process,
+                                bool is_wow64,
+                                ProcessInfo* process_info,
+                                WinVMAddress* peb_address,
+                                WinVMSize* peb_size) {
+  ULONG bytes_returned;
+  process_types::PROCESS_BASIC_INFORMATION<Traits> process_basic_information;
+  NTSTATUS status =
+      crashpad::NtQueryInformationProcess(process,
+                                          ProcessBasicInformation,
+                                          &process_basic_information,
+                                          sizeof(process_basic_information),
+                                          &bytes_returned);
+  if (!NT_SUCCESS(status)) {
+    NTSTATUS_LOG(ERROR, status) << "NtQueryInformationProcess";
+    return false;
+  }
+  if (bytes_returned != sizeof(process_basic_information)) {
+    LOG(ERROR) << "NtQueryInformationProcess incorrect size";
+    return false;
+  }
+
+  // API functions (e.g. OpenProcess) take only a DWORD, so there's no sense in
+  // maintaining the top bits.
+  process_info->process_id_ =
+      static_cast<DWORD>(process_basic_information.UniqueProcessId);
+  process_info->inherited_from_process_id_ = static_cast<DWORD>(
+      process_basic_information.InheritedFromUniqueProcessId);
+
+  // We now want to read the PEB to gather the rest of our information. The
+  // PebBaseAddress as returned above is what we want for 64-on-64 and 32-on-32,
+  // but for Wow64, we want to read the 32 bit PEB (a Wow64 process has both).
+  // The address of this is found by a second call to NtQueryInformationProcess.
+  if (!is_wow64) {
+    *peb_address = process_basic_information.PebBaseAddress;
+    *peb_size = sizeof(process_types::PEB<Traits>);
+  } else {
+    ULONG_PTR wow64_peb_address;
+    status = crashpad::NtQueryInformationProcess(process,
+                                                 ProcessWow64Information,
+                                                 &wow64_peb_address,
+                                                 sizeof(wow64_peb_address),
+                                                 &bytes_returned);
+    if (!NT_SUCCESS(status)) {
+      NTSTATUS_LOG(ERROR, status) << "NtQueryInformationProcess";
+      return false;
+    }
+    if (bytes_returned != sizeof(wow64_peb_address)) {
+      LOG(ERROR) << "NtQueryInformationProcess incorrect size";
+      return false;
+    }
+    *peb_address = wow64_peb_address;
+    *peb_size = sizeof(process_types::PEB<process_types::internal::Traits32>);
+  }
+
+  return true;
+}
+
+template <class Traits>
+bool ReadProcessData(HANDLE process,
+                     WinVMAddress peb_address_vmaddr,
+                     ProcessInfo* process_info) {
+  typename Traits::Pointer peb_address;
+  if (!AssignIfInRange(&peb_address, peb_address_vmaddr)) {
+    LOG(ERROR) << base::StringPrintf("peb address 0x%llx out of range",
+                                     peb_address_vmaddr);
+    return false;
+  }
+
+  // Try to read the process environment block.
+  process_types::PEB<Traits> peb;
+  if (!ReadStruct(process, peb_address, &peb))
+    return false;
+
+  process_types::RTL_USER_PROCESS_PARAMETERS<Traits> process_parameters;
+  if (!ReadStruct(process, peb.ProcessParameters, &process_parameters))
+    return false;
+
+  if (!ReadUnicodeString(process,
+                         process_parameters.CommandLine,
+                         &process_info->command_line_)) {
+    return false;
+  }
+
+  process_types::PEB_LDR_DATA<Traits> peb_ldr_data;
+  if (!ReadStruct(process, peb.Ldr, &peb_ldr_data))
+    return false;
+
+  process_types::LDR_DATA_TABLE_ENTRY<Traits> ldr_data_table_entry;
+  ProcessInfo::Module module;
+
+  // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded
+  // modules. We use this method rather than EnumProcessModules to get the
+  // modules in load order rather than memory order. Notably, this includes the
+  // main executable as the first element.
+  typename Traits::Pointer last = peb_ldr_data.InLoadOrderModuleList.Blink;
+  for (typename Traits::Pointer cur = peb_ldr_data.InLoadOrderModuleList.Flink;;
+       cur = ldr_data_table_entry.InLoadOrderLinks.Flink) {
+    // |cur| is the pointer to the LIST_ENTRY embedded in the
+    // LDR_DATA_TABLE_ENTRY, in the target process's address space. So we need
+    // to read from the target, and also offset back to the beginning of the
+    // structure.
+    if (!ReadStruct(process,
+                    static_cast<WinVMAddress>(cur) -
+                        offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
+                                 InLoadOrderLinks),
+                    &ldr_data_table_entry)) {
+      break;
+    }
+    // TODO(scottmg): Capture Checksum, etc. too?
+    if (!ReadUnicodeString(
+            process, ldr_data_table_entry.FullDllName, &module.name)) {
+      module.name = L"???";
+    }
+    module.dll_base = ldr_data_table_entry.DllBase;
+    module.size = ldr_data_table_entry.SizeOfImage;
+    module.timestamp = ldr_data_table_entry.TimeDateStamp;
+    process_info->modules_.push_back(module);
+    if (cur == last)
+      break;
+  }
+
+  return true;
+}
+
+bool ReadMemoryInfo(HANDLE process, bool is_64_bit, ProcessInfo* process_info) {
+  DCHECK(process_info->memory_info_.empty());
+
+  constexpr WinVMAddress min_address = 0;
+  // We can't use GetSystemInfo() to get the address space range for another
+  // process. VirtualQueryEx() will fail with ERROR_INVALID_PARAMETER if the
+  // address is above the highest memory address accessible to the process, so
+  // we just probe the entire potential range (2^32 for x86, or 2^64 for x64).
+  const WinVMAddress max_address = is_64_bit
+                                       ? std::numeric_limits<uint64_t>::max()
+                                       : std::numeric_limits<uint32_t>::max();
+  MEMORY_BASIC_INFORMATION memory_basic_information;
+  for (WinVMAddress address = min_address; address <= max_address;
+       address += memory_basic_information.RegionSize) {
+    size_t result = VirtualQueryEx(process,
+                                   reinterpret_cast<void*>(address),
+                                   &memory_basic_information,
+                                   sizeof(memory_basic_information));
+    if (result == 0) {
+      if (GetLastError() == ERROR_INVALID_PARAMETER)
+        break;
+      PLOG(ERROR) << "VirtualQueryEx";
+      return false;
+    }
+
+    process_info->memory_info_.push_back(
+        MemoryBasicInformationToMemoryBasicInformation64(
+            memory_basic_information));
+
+    if (memory_basic_information.RegionSize == 0) {
+      LOG(ERROR) << "RegionSize == 0";
+      return false;
+    }
+  }
+
+  return true;
+}
+
+std::vector<ProcessInfo::Handle> ProcessInfo::BuildHandleVector(
+    HANDLE process) const {
+  ULONG buffer_size = 2 * 1024 * 1024;
+  // Typically if the buffer were too small, STATUS_INFO_LENGTH_MISMATCH would
+  // return the correct size in the final argument, but it does not for
+  // SystemExtendedHandleInformation, so we loop and attempt larger sizes.
+  NTSTATUS status;
+  ULONG returned_length;
+  UniqueMallocPtr buffer;
+  for (int tries = 0; tries < 5; ++tries) {
+    buffer.reset();
+    buffer = UncheckedAllocate(buffer_size);
+    if (!buffer) {
+      LOG(ERROR) << "UncheckedAllocate";
+      return std::vector<Handle>();
+    }
+
+    status = crashpad::NtQuerySystemInformation(
+        static_cast<SYSTEM_INFORMATION_CLASS>(SystemExtendedHandleInformation),
+        buffer.get(),
+        buffer_size,
+        &returned_length);
+    if (NT_SUCCESS(status) || status != STATUS_INFO_LENGTH_MISMATCH)
+      break;
+
+    buffer_size *= 2;
+  }
+
+  if (!NT_SUCCESS(status)) {
+    NTSTATUS_LOG(ERROR, status)
+        << "NtQuerySystemInformation SystemExtendedHandleInformation";
+    return std::vector<Handle>();
+  }
+
+  const auto& system_handle_information_ex =
+      *reinterpret_cast<process_types::SYSTEM_HANDLE_INFORMATION_EX*>(
+          buffer.get());
+
+  DCHECK_LE(offsetof(process_types::SYSTEM_HANDLE_INFORMATION_EX, Handles) +
+                system_handle_information_ex.NumberOfHandles *
+                    sizeof(system_handle_information_ex.Handles[0]),
+            returned_length);
+
+  std::vector<Handle> handles;
+
+  for (size_t i = 0; i < system_handle_information_ex.NumberOfHandles; ++i) {
+    const auto& handle = system_handle_information_ex.Handles[i];
+    if (handle.UniqueProcessId != process_id_)
+      continue;
+
+    Handle result_handle;
+    result_handle.handle = HandleToInt(handle.HandleValue);
+    result_handle.attributes = handle.HandleAttributes;
+    result_handle.granted_access = handle.GrantedAccess;
+
+    // TODO(scottmg): Could special case for self.
+    HANDLE dup_handle;
+    if (DuplicateHandle(process,
+                        handle.HandleValue,
+                        GetCurrentProcess(),
+                        &dup_handle,
+                        0,
+                        false,
+                        DUPLICATE_SAME_ACCESS)) {
+      // Some handles cannot be duplicated, for example, handles of type
+      // EtwRegistration. If we fail to duplicate, then we can't gather any more
+      // information, but include the information that we do have already.
+      ScopedKernelHANDLE scoped_dup_handle(dup_handle);
+
+      std::unique_ptr<uint8_t[]> object_basic_information_buffer =
+          QueryObject(dup_handle,
+                      ObjectBasicInformation,
+                      sizeof(PUBLIC_OBJECT_BASIC_INFORMATION));
+      if (object_basic_information_buffer) {
+        PUBLIC_OBJECT_BASIC_INFORMATION* object_basic_information =
+            reinterpret_cast<PUBLIC_OBJECT_BASIC_INFORMATION*>(
+                object_basic_information_buffer.get());
+        // The Attributes and GrantedAccess sometimes differ slightly between
+        // the data retrieved in SYSTEM_HANDLE_INFORMATION_EX and
+        // PUBLIC_OBJECT_TYPE_INFORMATION. We prefer the values in
+        // SYSTEM_HANDLE_INFORMATION_EX because they were retrieved from the
+        // target process, rather than on the duplicated handle, so don't use
+        // them here.
+
+        // Subtract one to account for our DuplicateHandle() and another for
+        // NtQueryObject() while the query was being executed.
+        DCHECK_GT(object_basic_information->PointerCount, 2u);
+        result_handle.pointer_count =
+            object_basic_information->PointerCount - 2;
+
+        // Subtract one to account for our DuplicateHandle().
+        DCHECK_GT(object_basic_information->HandleCount, 1u);
+        result_handle.handle_count = object_basic_information->HandleCount - 1;
+      }
+
+      std::unique_ptr<uint8_t[]> object_type_information_buffer =
+          QueryObject(dup_handle,
+                      ObjectTypeInformation,
+                      sizeof(PUBLIC_OBJECT_TYPE_INFORMATION));
+      if (object_type_information_buffer) {
+        PUBLIC_OBJECT_TYPE_INFORMATION* object_type_information =
+            reinterpret_cast<PUBLIC_OBJECT_TYPE_INFORMATION*>(
+                object_type_information_buffer.get());
+
+        DCHECK_EQ(object_type_information->TypeName.Length %
+                      sizeof(result_handle.type_name[0]),
+                  0u);
+        result_handle.type_name =
+            std::wstring(object_type_information->TypeName.Buffer,
+                         object_type_information->TypeName.Length /
+                             sizeof(result_handle.type_name[0]));
+      }
+    }
+
+    handles.push_back(result_handle);
+  }
+  return handles;
+}
+
+ProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() {
+}
+
+ProcessInfo::Module::~Module() {
+}
+
+ProcessInfo::Handle::Handle()
+    : type_name(),
+      handle(0),
+      attributes(0),
+      granted_access(0),
+      pointer_count(0),
+      handle_count(0) {
+}
+
+ProcessInfo::Handle::~Handle() {
+}
+
+ProcessInfo::ProcessInfo()
+    : process_id_(),
+      inherited_from_process_id_(),
+      process_(),
+      command_line_(),
+      peb_address_(0),
+      peb_size_(0),
+      modules_(),
+      memory_info_(),
+      handles_(),
+      is_64_bit_(false),
+      is_wow64_(false),
+      initialized_() {
+}
+
+ProcessInfo::~ProcessInfo() {
+}
+
+bool ProcessInfo::Initialize(HANDLE process) {
+  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+  process_ = process;
+
+  is_wow64_ = IsProcessWow64(process);
+
+  if (is_wow64_) {
+    // If it's WoW64, then it's 32-on-64.
+    is_64_bit_ = false;
+  } else {
+    // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to
+    // distinguish between these two cases.
+    SYSTEM_INFO system_info;
+    GetSystemInfo(&system_info);
+
+#if defined(ARCH_CPU_X86_FAMILY)
+    constexpr uint16_t kNative64BitArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
+#elif defined(ARCH_CPU_ARM_FAMILY)
+    constexpr uint16_t kNative64BitArchitecture = PROCESSOR_ARCHITECTURE_ARM64;
+#endif
+
+    is_64_bit_ = system_info.wProcessorArchitecture == kNative64BitArchitecture;
+  }
+
+#if defined(ARCH_CPU_32_BITS)
+  if (is_64_bit_) {
+    LOG(ERROR) << "Reading x64 process from x86 process not supported";
+    return false;
+  }
+#endif  // ARCH_CPU_32_BITS
+
+#if defined(ARCH_CPU_64_BITS)
+  bool result = GetProcessBasicInformation<process_types::internal::Traits64>(
+      process, is_wow64_, this, &peb_address_, &peb_size_);
+#else
+  bool result = GetProcessBasicInformation<process_types::internal::Traits32>(
+      process, false, this, &peb_address_, &peb_size_);
+#endif  // ARCH_CPU_64_BITS
+
+  if (!result) {
+    LOG(ERROR) << "GetProcessBasicInformation failed";
+    return false;
+  }
+
+  result = is_64_bit_ ? ReadProcessData<process_types::internal::Traits64>(
+                            process, peb_address_, this)
+                      : ReadProcessData<process_types::internal::Traits32>(
+                            process, peb_address_, this);
+  if (!result) {
+    LOG(ERROR) << "ReadProcessData failed";
+    return false;
+  }
+
+  if (!ReadMemoryInfo(process, is_64_bit_, this)) {
+    LOG(ERROR) << "ReadMemoryInfo failed";
+    return false;
+  }
+
+  INITIALIZATION_STATE_SET_VALID(initialized_);
+  return true;
+}
+
+bool ProcessInfo::Is64Bit() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return is_64_bit_;
+}
+
+bool ProcessInfo::IsWow64() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return is_wow64_;
+}
+
+crashpad::ProcessID ProcessInfo::ProcessID() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return process_id_;
+}
+
+crashpad::ProcessID ProcessInfo::ParentProcessID() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return inherited_from_process_id_;
+}
+
+bool ProcessInfo::CommandLine(std::wstring* command_line) const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  *command_line = command_line_;
+  return true;
+}
+
+void ProcessInfo::Peb(WinVMAddress* peb_address, WinVMSize* peb_size) const {
+  *peb_address = peb_address_;
+  *peb_size = peb_size_;
+}
+
+bool ProcessInfo::Modules(std::vector<Module>* modules) const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  *modules = modules_;
+  return true;
+}
+
+const ProcessInfo::MemoryBasicInformation64Vector& ProcessInfo::MemoryInfo()
+    const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return memory_info_;
+}
+
+std::vector<CheckedRange<WinVMAddress, WinVMSize>>
+ProcessInfo::GetReadableRanges(
+    const CheckedRange<WinVMAddress, WinVMSize>& range) const {
+  return GetReadableRangesOfMemoryMap(range, MemoryInfo());
+}
+
+bool ProcessInfo::LoggingRangeIsFullyReadable(
+    const CheckedRange<WinVMAddress, WinVMSize>& range) const {
+  const auto ranges = GetReadableRanges(range);
+  if (ranges.empty()) {
+    LOG(ERROR) << base::StringPrintf(
+        "range at 0x%llx, size 0x%llx fully unreadable",
+        range.base(),
+        range.size());
+    return false;
+  }
+
+  if (ranges.size() != 1 ||
+      ranges[0].base() != range.base() || ranges[0].size() != range.size()) {
+    LOG(ERROR) << base::StringPrintf(
+        "range at 0x%llx, size 0x%llx partially unreadable",
+        range.base(),
+        range.size());
+    return false;
+  }
+
+  return true;
+}
+
+const std::vector<ProcessInfo::Handle>& ProcessInfo::Handles() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  if (handles_.empty())
+    handles_ = BuildHandleVector(process_);
+  return handles_;
+}
+
+std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRangesOfMemoryMap(
+    const CheckedRange<WinVMAddress, WinVMSize>& range,
+    const ProcessInfo::MemoryBasicInformation64Vector& memory_info) {
+  using Range = CheckedRange<WinVMAddress, WinVMSize>;
+
+  // Constructing Ranges and using OverlapsRange() is very, very slow in Debug
+  // builds, so do a manual check in this loop. The ranges are still validated
+  // by a CheckedRange before being returned.
+  WinVMAddress range_base = range.base();
+  WinVMAddress range_end = range.end();
+
+  // Find all the ranges that overlap the target range, maintaining their order.
+  ProcessInfo::MemoryBasicInformation64Vector overlapping;
+  const size_t size = memory_info.size();
+
+  // This loop is written in an ugly fashion to make Debug performance
+  // reasonable.
+  const MEMORY_BASIC_INFORMATION64* begin = &memory_info[0];
+  for (size_t i = 0; i < size; ++i) {
+    const MEMORY_BASIC_INFORMATION64& mi = *(begin + i);
+    static_assert(std::is_same<decltype(mi.BaseAddress), WinVMAddress>::value,
+                  "expected range address to be WinVMAddress");
+    static_assert(std::is_same<decltype(mi.RegionSize), WinVMSize>::value,
+                  "expected range size to be WinVMSize");
+    WinVMAddress mi_end = mi.BaseAddress + mi.RegionSize;
+    if (range_base < mi_end && mi.BaseAddress < range_end)
+      overlapping.push_back(mi);
+  }
+  if (overlapping.empty())
+    return std::vector<Range>();
+
+  // For the first and last, trim to the boundary of the incoming range.
+  MEMORY_BASIC_INFORMATION64& front = overlapping.front();
+  WinVMAddress original_front_base_address = front.BaseAddress;
+  front.BaseAddress = std::max(front.BaseAddress, range.base());
+  front.RegionSize =
+      (original_front_base_address + front.RegionSize) - front.BaseAddress;
+
+  MEMORY_BASIC_INFORMATION64& back = overlapping.back();
+  WinVMAddress back_end = back.BaseAddress + back.RegionSize;
+  back.RegionSize = std::min(range.end(), back_end) - back.BaseAddress;
+
+  // Discard all non-accessible.
+  overlapping.erase(std::remove_if(overlapping.begin(),
+                                   overlapping.end(),
+                                   [](const MEMORY_BASIC_INFORMATION64& mbi) {
+                                     return !RegionIsAccessible(mbi);
+                                   }),
+                    overlapping.end());
+  if (overlapping.empty())
+    return std::vector<Range>();
+
+  // Convert to return type.
+  std::vector<Range> as_ranges;
+  for (const auto& mi : overlapping) {
+    as_ranges.push_back(Range(mi.BaseAddress, mi.RegionSize));
+    DCHECK(as_ranges.back().IsValid());
+  }
+
+  // Coalesce remaining regions.
+  std::vector<Range> result;
+  result.push_back(as_ranges[0]);
+  for (size_t i = 1; i < as_ranges.size(); ++i) {
+    if (result.back().end() == as_ranges[i].base()) {
+      result.back().SetRange(result.back().base(),
+                             result.back().size() + as_ranges[i].size());
+    } else {
+      result.push_back(as_ranges[i]);
+    }
+    DCHECK(result.back().IsValid());
+  }
+
+  return result;
+}
+
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/process_info.h b/src/third_party/crashpad/util/win/process_info.h
new file mode 100644
index 0000000..afbe146
--- /dev/null
+++ b/src/third_party/crashpad/util/win/process_info.h
@@ -0,0 +1,221 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_PROCESS_INFO_H_
+#define CRASHPAD_UTIL_WIN_PROCESS_INFO_H_
+
+#include <windows.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/numeric/checked_range.h"
+#include "util/process/process_id.h"
+#include "util/stdlib/aligned_allocator.h"
+#include "util/win/address_types.h"
+
+namespace crashpad {
+
+//! \brief Gathers information about a process given its `HANDLE`. This consists
+//!     primarily of information stored in the Process Environment Block.
+class ProcessInfo {
+ public:
+  //! \brief The return type of MemoryInfo(), for convenience.
+  using MemoryBasicInformation64Vector =
+      AlignedVector<MEMORY_BASIC_INFORMATION64>;
+
+  //! \brief Contains information about a module loaded into a process.
+  struct Module {
+    Module();
+    ~Module();
+
+    //! \brief The pathname used to load the module from disk.
+    std::wstring name;
+
+    //! \brief The base address of the loaded DLL.
+    WinVMAddress dll_base;
+
+    //! \brief The size of the module.
+    WinVMSize size;
+
+    //! \brief The module's timestamp.
+    time_t timestamp;
+  };
+
+  struct Handle {
+    Handle();
+    ~Handle();
+
+    //! \brief A string representation of the handle's type.
+    std::wstring type_name;
+
+    //! \brief The handle's value.
+    int handle;
+
+    //! \brief The attributes for the handle, e.g. `OBJ_INHERIT`,
+    //!     `OBJ_CASE_INSENSITIVE`, etc.
+    uint32_t attributes;
+
+    //! \brief The `ACCESS_MASK` for the handle in this process.
+    //!
+    //! See
+    //! https://blogs.msdn.microsoft.com/openspecification/2010/04/01/about-the-access_mask-structure/
+    //! for more information.
+    uint32_t granted_access;
+
+    //! \brief The number of kernel references to the object that this handle
+    //!     refers to.
+    uint32_t pointer_count;
+
+    //! \brief The number of open handles to the object that this handle refers
+    //!     to.
+    uint32_t handle_count;
+  };
+
+  ProcessInfo();
+  ~ProcessInfo();
+
+  //! \brief Initializes this object with information about the given
+  //!     \a process.
+  //!
+  //! This method must be called successfully prior to calling any other
+  //! method in this class. This method may only be called once.
+  //!
+  //! \return `true` on success, `false` on failure with a message logged.
+  bool Initialize(HANDLE process);
+
+  //! \return `true` if the target process is a 64-bit process.
+  bool Is64Bit() const;
+
+  //! \return `true` if the target process is running on the Win32-on-Win64
+  //!     subsystem.
+  bool IsWow64() const;
+
+  //! \return The target process's process ID.
+  crashpad::ProcessID ProcessID() const;
+
+  //! \return The target process's parent process ID.
+  crashpad::ProcessID ParentProcessID() const;
+
+  //! \return The command line from the target process's Process Environment
+  //!     Block.
+  bool CommandLine(std::wstring* command_line) const;
+
+  //! \brief Gets the address and size of the process's Process Environment
+  //!     Block.
+  //!
+  //! \param[out] peb_address The address of the Process Environment Block.
+  //! \param[out] peb_size The size of the Process Environment Block.
+  void Peb(WinVMAddress* peb_address, WinVMSize* peb_size) const;
+
+  //! \brief Retrieves the modules loaded into the target process.
+  //!
+  //! The modules are enumerated in initialization order as detailed in the
+  //!     Process Environment Block. The main executable will always be the
+  //!     first element.
+  bool Modules(std::vector<Module>* modules) const;
+
+  //! \brief Retrieves information about all pages mapped into the process.
+  const MemoryBasicInformation64Vector& MemoryInfo() const;
+
+  //! \brief Given a range to be read from the target process, returns a vector
+  //!     of ranges, representing the readable portions of the original range.
+  //!
+  //! \param[in] range The range being identified.
+  //!
+  //! \return A vector of ranges corresponding to the portion of \a range that
+  //!     is readable based on the memory map.
+  std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRanges(
+      const CheckedRange<WinVMAddress, WinVMSize>& range) const;
+
+  //! \brief Given a range in the target process, determines if the entire range
+  //!     is readable.
+  //!
+  //! \param[in] range The range being inspected.
+  //!
+  //! \return `true` if the range is fully readable, otherwise `false` with a
+  //!     message logged.
+  bool LoggingRangeIsFullyReadable(
+      const CheckedRange<WinVMAddress, WinVMSize>& range) const;
+
+  //! \brief Retrieves information about open handles in the target process.
+  const std::vector<Handle>& Handles() const;
+
+ private:
+  template <class Traits>
+  friend bool GetProcessBasicInformation(HANDLE process,
+                                         bool is_wow64,
+                                         ProcessInfo* process_info,
+                                         WinVMAddress* peb_address,
+                                         WinVMSize* peb_size);
+  template <class Traits>
+  friend bool ReadProcessData(HANDLE process,
+                              WinVMAddress peb_address_vmaddr,
+                              ProcessInfo* process_info);
+
+  friend bool ReadMemoryInfo(HANDLE process,
+                             bool is_64_bit,
+                             ProcessInfo* process_info);
+
+  // This function is best-effort under low memory conditions.
+  std::vector<Handle> BuildHandleVector(HANDLE process) const;
+
+  crashpad::ProcessID process_id_;
+  crashpad::ProcessID inherited_from_process_id_;
+  HANDLE process_;
+  std::wstring command_line_;
+  WinVMAddress peb_address_;
+  WinVMSize peb_size_;
+  std::vector<Module> modules_;
+
+  // memory_info_ is a MemoryBasicInformation64Vector instead of a
+  // std::vector<MEMORY_BASIC_INFORMATION64> because MEMORY_BASIC_INFORMATION64
+  // is declared with __declspec(align(16)), but std::vector<> does not maintain
+  // this alignment on 32-bit x86. clang-cl (but not MSVC cl) takes advantage of
+  // the presumed alignment and emits SSE instructions that require aligned
+  // storage. clang-cl should relax (unfortunately), but in the mean time, this
+  // provides aligned storage. See https://crbug.com/564691 and
+  // https://llvm.org/PR25779.
+  //
+  // TODO(mark): Remove this workaround when https://llvm.org/PR25779 is fixed
+  // and the fix is present in the clang-cl that compiles this code.
+  MemoryBasicInformation64Vector memory_info_;
+
+  // Handles() is logically const, but updates this member on first retrieval.
+  // See https://crashpad.chromium.org/bug/9.
+  mutable std::vector<Handle> handles_;
+
+  bool is_64_bit_;
+  bool is_wow64_;
+  InitializationStateDcheck initialized_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProcessInfo);
+};
+
+//! \brief Given a memory map of a process, and a range to be read from the
+//!     target process, returns a vector of ranges, representing the readable
+//!     portions of the original range.
+//!
+//! This is a free function for testing, but prefer
+//! ProcessInfo::GetReadableRanges().
+std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRangesOfMemoryMap(
+    const CheckedRange<WinVMAddress, WinVMSize>& range,
+    const ProcessInfo::MemoryBasicInformation64Vector& memory_info);
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_PROCESS_INFO_H_
diff --git a/src/third_party/crashpad/util/win/process_info_test.cc b/src/third_party/crashpad/util/win/process_info_test.cc
new file mode 100644
index 0000000..a43358d
--- /dev/null
+++ b/src/third_party/crashpad/util/win/process_info_test.cc
@@ -0,0 +1,650 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/process_info.h"
+
+#include <dbghelp.h>
+#include <intrin.h>
+#include <wchar.h>
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "gtest/gtest.h"
+#include "test/errors.h"
+#include "test/scoped_temp_dir.h"
+#include "test/test_paths.h"
+#include "test/win/child_launcher.h"
+#include "util/file/file_io.h"
+#include "util/misc/from_pointer_cast.h"
+#include "util/misc/random_string.h"
+#include "util/misc/uuid.h"
+#include "util/win/command_line.h"
+#include "util/win/get_function.h"
+#include "util/win/handle.h"
+#include "util/win/scoped_handle.h"
+#include "util/win/scoped_registry_key.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+constexpr wchar_t kNtdllName[] = L"\\ntdll.dll";
+
+#if !defined(ARCH_CPU_64_BITS)
+bool IsProcessWow64(HANDLE process_handle) {
+  static const auto is_wow64_process =
+      GET_FUNCTION(L"kernel32.dll", ::IsWow64Process);
+  if (!is_wow64_process)
+    return false;
+  BOOL is_wow64;
+  if (!is_wow64_process(process_handle, &is_wow64)) {
+    PLOG(ERROR) << "IsWow64Process";
+    return false;
+  }
+  return !!is_wow64;
+}
+#endif
+
+void VerifyAddressInInCodePage(const ProcessInfo& process_info,
+                               WinVMAddress code_address) {
+  // Make sure the child code address is an code page address with the right
+  // information.
+  const ProcessInfo::MemoryBasicInformation64Vector& memory_info =
+      process_info.MemoryInfo();
+  bool found_region = false;
+  for (const auto& mi : memory_info) {
+    if (mi.BaseAddress <= code_address &&
+        mi.BaseAddress + mi.RegionSize > code_address) {
+      EXPECT_EQ(mi.State, static_cast<DWORD>(MEM_COMMIT));
+      EXPECT_EQ(mi.Protect, static_cast<DWORD>(PAGE_EXECUTE_READ));
+      EXPECT_EQ(mi.Type, static_cast<DWORD>(MEM_IMAGE));
+      EXPECT_FALSE(found_region);
+      found_region = true;
+    }
+  }
+  EXPECT_TRUE(found_region);
+}
+
+TEST(ProcessInfo, Self) {
+  ProcessInfo process_info;
+  ASSERT_TRUE(process_info.Initialize(GetCurrentProcess()));
+  EXPECT_EQ(process_info.ProcessID(), GetCurrentProcessId());
+  EXPECT_GT(process_info.ParentProcessID(), 0u);
+
+#if defined(ARCH_CPU_64_BITS)
+  EXPECT_TRUE(process_info.Is64Bit());
+  EXPECT_FALSE(process_info.IsWow64());
+#else
+  EXPECT_FALSE(process_info.Is64Bit());
+  if (IsProcessWow64(GetCurrentProcess()))
+    EXPECT_TRUE(process_info.IsWow64());
+  else
+    EXPECT_FALSE(process_info.IsWow64());
+#endif
+
+  std::wstring command_line;
+  EXPECT_TRUE(process_info.CommandLine(&command_line));
+  EXPECT_EQ(command_line, std::wstring(GetCommandLine()));
+
+  std::vector<ProcessInfo::Module> modules;
+  EXPECT_TRUE(process_info.Modules(&modules));
+  ASSERT_GE(modules.size(), 2u);
+  std::wstring self_name =
+      std::wstring(1, '\\') +
+      TestPaths::ExpectedExecutableBasename(L"crashpad_util_test").value();
+  ASSERT_GE(modules[0].name.size(), self_name.size());
+  EXPECT_EQ(modules[0].name.substr(modules[0].name.size() - self_name.size()),
+            self_name);
+  ASSERT_GE(modules[1].name.size(), wcslen(kNtdllName));
+  EXPECT_EQ(modules[1].name.substr(modules[1].name.size() - wcslen(kNtdllName)),
+            kNtdllName);
+
+  EXPECT_EQ(modules[0].dll_base,
+            reinterpret_cast<uintptr_t>(GetModuleHandle(nullptr)));
+  EXPECT_EQ(modules[1].dll_base,
+            reinterpret_cast<uintptr_t>(GetModuleHandle(L"ntdll.dll")));
+
+  EXPECT_GT(modules[0].size, 0u);
+  EXPECT_GT(modules[1].size, 0u);
+
+  EXPECT_EQ(modules[0].timestamp,
+            GetTimestampForLoadedLibrary(GetModuleHandle(nullptr)));
+  // System modules are forced to particular stamps and the file header values
+  // don't match the on-disk times. Just make sure we got some data here.
+  EXPECT_GT(modules[1].timestamp, 0);
+
+  // Find something we know is a code address and confirm expected memory
+  // information settings.
+  VerifyAddressInInCodePage(process_info,
+                            FromPointerCast<WinVMAddress>(_ReturnAddress()));
+}
+
+void TestOtherProcess(TestPaths::Architecture architecture) {
+  ProcessInfo process_info;
+
+  UUID done_uuid;
+  done_uuid.InitializeWithNew();
+
+  ScopedKernelHANDLE done(
+      CreateEvent(nullptr, true, false, done_uuid.ToString16().c_str()));
+  ASSERT_TRUE(done.get()) << ErrorMessage("CreateEvent");
+
+  base::FilePath child_test_executable =
+      TestPaths::BuildArtifact(L"util",
+                               L"process_info_test_child",
+                               TestPaths::FileType::kExecutable,
+                               architecture);
+  std::wstring args;
+  AppendCommandLineArgument(done_uuid.ToString16(), &args);
+
+  ChildLauncher child(child_test_executable, args);
+  ASSERT_NO_FATAL_FAILURE(child.Start());
+
+  // The child sends us a code address we can look up in the memory map.
+  WinVMAddress code_address;
+  CheckedReadFileExactly(
+      child.stdout_read_handle(), &code_address, sizeof(code_address));
+
+  ASSERT_TRUE(process_info.Initialize(child.process_handle()));
+
+  // Tell the test it's OK to shut down now that we've read our data.
+  EXPECT_TRUE(SetEvent(done.get())) << ErrorMessage("SetEvent");
+
+  EXPECT_EQ(child.WaitForExit(), 0u);
+
+  std::vector<ProcessInfo::Module> modules;
+  EXPECT_TRUE(process_info.Modules(&modules));
+  ASSERT_GE(modules.size(), 3u);
+  std::wstring child_name = L"\\crashpad_util_test_process_info_test_child.exe";
+  ASSERT_GE(modules[0].name.size(), child_name.size());
+  EXPECT_EQ(modules[0].name.substr(modules[0].name.size() - child_name.size()),
+            child_name);
+  ASSERT_GE(modules[1].name.size(), wcslen(kNtdllName));
+  EXPECT_EQ(modules[1].name.substr(modules[1].name.size() - wcslen(kNtdllName)),
+            kNtdllName);
+  // lz32.dll is an uncommonly-used-but-always-available module that the test
+  // binary manually loads.
+  static constexpr wchar_t kLz32dllName[] = L"\\lz32.dll";
+  auto& lz32 = modules[modules.size() - 2];
+  ASSERT_GE(lz32.name.size(), wcslen(kLz32dllName));
+  EXPECT_EQ(lz32.name.substr(lz32.name.size() - wcslen(kLz32dllName)),
+            kLz32dllName);
+
+  // Note that the test code corrupts the PEB MemoryOrder list, whereas
+  // ProcessInfo::Modules() retrieves the module names via the PEB LoadOrder
+  // list. These are expected to point to the same strings, but theoretically
+  // could be separate.
+  auto& corrupted = modules.back();
+  EXPECT_EQ(corrupted.name, L"???");
+
+  VerifyAddressInInCodePage(process_info, code_address);
+}
+
+TEST(ProcessInfo, OtherProcess) {
+  TestOtherProcess(TestPaths::Architecture::kDefault);
+}
+
+#if defined(ARCH_CPU_64_BITS)
+TEST(ProcessInfo, OtherProcessWOW64) {
+  if (!TestPaths::Has32BitBuildArtifacts()) {
+    GTEST_SKIP();
+  }
+
+  TestOtherProcess(TestPaths::Architecture::k32Bit);
+}
+#endif  // ARCH_CPU_64_BITS
+
+TEST(ProcessInfo, AccessibleRangesNone) {
+  ProcessInfo::MemoryBasicInformation64Vector memory_info;
+  MEMORY_BASIC_INFORMATION64 mbi = {0};
+
+  mbi.BaseAddress = 0;
+  mbi.RegionSize = 10;
+  mbi.State = MEM_FREE;
+  memory_info.push_back(mbi);
+
+  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
+      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(2, 4),
+                                   memory_info);
+
+  EXPECT_TRUE(result.empty());
+}
+
+TEST(ProcessInfo, AccessibleRangesOneInside) {
+  ProcessInfo::MemoryBasicInformation64Vector memory_info;
+  MEMORY_BASIC_INFORMATION64 mbi = {0};
+
+  mbi.BaseAddress = 0;
+  mbi.RegionSize = 10;
+  mbi.State = MEM_COMMIT;
+  memory_info.push_back(mbi);
+
+  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
+      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(2, 4),
+                                   memory_info);
+
+  ASSERT_EQ(result.size(), 1u);
+  EXPECT_EQ(result[0].base(), 2u);
+  EXPECT_EQ(result[0].size(), 4u);
+}
+
+TEST(ProcessInfo, AccessibleRangesOneTruncatedSize) {
+  ProcessInfo::MemoryBasicInformation64Vector memory_info;
+  MEMORY_BASIC_INFORMATION64 mbi = {0};
+
+  mbi.BaseAddress = 0;
+  mbi.RegionSize = 10;
+  mbi.State = MEM_COMMIT;
+  memory_info.push_back(mbi);
+
+  mbi.BaseAddress = 10;
+  mbi.RegionSize = 20;
+  mbi.State = MEM_FREE;
+  memory_info.push_back(mbi);
+
+  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
+      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),
+                                   memory_info);
+
+  ASSERT_EQ(result.size(), 1u);
+  EXPECT_EQ(result[0].base(), 5u);
+  EXPECT_EQ(result[0].size(), 5u);
+}
+
+TEST(ProcessInfo, AccessibleRangesOneMovedStart) {
+  ProcessInfo::MemoryBasicInformation64Vector memory_info;
+  MEMORY_BASIC_INFORMATION64 mbi = {0};
+
+  mbi.BaseAddress = 0;
+  mbi.RegionSize = 10;
+  mbi.State = MEM_FREE;
+  memory_info.push_back(mbi);
+
+  mbi.BaseAddress = 10;
+  mbi.RegionSize = 20;
+  mbi.State = MEM_COMMIT;
+  memory_info.push_back(mbi);
+
+  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
+      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),
+                                   memory_info);
+
+  ASSERT_EQ(result.size(), 1u);
+  EXPECT_EQ(result[0].base(), 10u);
+  EXPECT_EQ(result[0].size(), 5u);
+}
+
+TEST(ProcessInfo, ReserveIsInaccessible) {
+  ProcessInfo::MemoryBasicInformation64Vector memory_info;
+  MEMORY_BASIC_INFORMATION64 mbi = {0};
+
+  mbi.BaseAddress = 0;
+  mbi.RegionSize = 10;
+  mbi.State = MEM_RESERVE;
+  memory_info.push_back(mbi);
+
+  mbi.BaseAddress = 10;
+  mbi.RegionSize = 20;
+  mbi.State = MEM_COMMIT;
+  memory_info.push_back(mbi);
+
+  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
+      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),
+                                   memory_info);
+
+  ASSERT_EQ(result.size(), 1u);
+  EXPECT_EQ(result[0].base(), 10u);
+  EXPECT_EQ(result[0].size(), 5u);
+}
+
+TEST(ProcessInfo, PageGuardIsInaccessible) {
+  ProcessInfo::MemoryBasicInformation64Vector memory_info;
+  MEMORY_BASIC_INFORMATION64 mbi = {0};
+
+  mbi.BaseAddress = 0;
+  mbi.RegionSize = 10;
+  mbi.State = MEM_COMMIT;
+  mbi.Protect = PAGE_GUARD;
+  memory_info.push_back(mbi);
+
+  mbi.BaseAddress = 10;
+  mbi.RegionSize = 20;
+  mbi.State = MEM_COMMIT;
+  mbi.Protect = 0;
+  memory_info.push_back(mbi);
+
+  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
+      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),
+                                   memory_info);
+
+  ASSERT_EQ(result.size(), 1u);
+  EXPECT_EQ(result[0].base(), 10u);
+  EXPECT_EQ(result[0].size(), 5u);
+}
+
+TEST(ProcessInfo, PageNoAccessIsInaccessible) {
+  ProcessInfo::MemoryBasicInformation64Vector memory_info;
+  MEMORY_BASIC_INFORMATION64 mbi = {0};
+
+  mbi.BaseAddress = 0;
+  mbi.RegionSize = 10;
+  mbi.State = MEM_COMMIT;
+  mbi.Protect = PAGE_NOACCESS;
+  memory_info.push_back(mbi);
+
+  mbi.BaseAddress = 10;
+  mbi.RegionSize = 20;
+  mbi.State = MEM_COMMIT;
+  mbi.Protect = 0;
+  memory_info.push_back(mbi);
+
+  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
+      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),
+                                   memory_info);
+
+  ASSERT_EQ(result.size(), 1u);
+  EXPECT_EQ(result[0].base(), 10u);
+  EXPECT_EQ(result[0].size(), 5u);
+}
+
+TEST(ProcessInfo, AccessibleRangesCoalesced) {
+  ProcessInfo::MemoryBasicInformation64Vector memory_info;
+  MEMORY_BASIC_INFORMATION64 mbi = {0};
+
+  mbi.BaseAddress = 0;
+  mbi.RegionSize = 10;
+  mbi.State = MEM_FREE;
+  memory_info.push_back(mbi);
+
+  mbi.BaseAddress = 10;
+  mbi.RegionSize = 2;
+  mbi.State = MEM_COMMIT;
+  memory_info.push_back(mbi);
+
+  mbi.BaseAddress = 12;
+  mbi.RegionSize = 5;
+  mbi.State = MEM_COMMIT;
+  memory_info.push_back(mbi);
+
+  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
+      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(11, 4),
+                                   memory_info);
+
+  ASSERT_EQ(result.size(), 1u);
+  EXPECT_EQ(result[0].base(), 11u);
+  EXPECT_EQ(result[0].size(), 4u);
+}
+
+TEST(ProcessInfo, AccessibleRangesMiddleUnavailable) {
+  ProcessInfo::MemoryBasicInformation64Vector memory_info;
+  MEMORY_BASIC_INFORMATION64 mbi = {0};
+
+  mbi.BaseAddress = 0;
+  mbi.RegionSize = 10;
+  mbi.State = MEM_COMMIT;
+  memory_info.push_back(mbi);
+
+  mbi.BaseAddress = 10;
+  mbi.RegionSize = 5;
+  mbi.State = MEM_FREE;
+  memory_info.push_back(mbi);
+
+  mbi.BaseAddress = 15;
+  mbi.RegionSize = 100;
+  mbi.State = MEM_COMMIT;
+  memory_info.push_back(mbi);
+
+  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
+      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 45),
+                                   memory_info);
+
+  ASSERT_EQ(result.size(), 2u);
+  EXPECT_EQ(result[0].base(), 5u);
+  EXPECT_EQ(result[0].size(), 5u);
+  EXPECT_EQ(result[1].base(), 15u);
+  EXPECT_EQ(result[1].size(), 35u);
+}
+
+TEST(ProcessInfo, RequestedBeforeMap) {
+  ProcessInfo::MemoryBasicInformation64Vector memory_info;
+  MEMORY_BASIC_INFORMATION64 mbi = {0};
+
+  mbi.BaseAddress = 10;
+  mbi.RegionSize = 10;
+  mbi.State = MEM_COMMIT;
+  memory_info.push_back(mbi);
+
+  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
+      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),
+                                   memory_info);
+
+  ASSERT_EQ(result.size(), 1u);
+  EXPECT_EQ(result[0].base(), 10u);
+  EXPECT_EQ(result[0].size(), 5u);
+}
+
+TEST(ProcessInfo, RequestedAfterMap) {
+  ProcessInfo::MemoryBasicInformation64Vector memory_info;
+  MEMORY_BASIC_INFORMATION64 mbi = {0};
+
+  mbi.BaseAddress = 10;
+  mbi.RegionSize = 10;
+  mbi.State = MEM_COMMIT;
+  memory_info.push_back(mbi);
+
+  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
+      GetReadableRangesOfMemoryMap(
+          CheckedRange<WinVMAddress, WinVMSize>(15, 100), memory_info);
+
+  ASSERT_EQ(result.size(), 1u);
+  EXPECT_EQ(result[0].base(), 15u);
+  EXPECT_EQ(result[0].size(), 5u);
+}
+
+TEST(ProcessInfo, ReadableRanges) {
+  SYSTEM_INFO system_info;
+  GetSystemInfo(&system_info);
+
+  const size_t kBlockSize = system_info.dwPageSize;
+
+  // Allocate 6 pages, and then commit the second, fourth, and fifth, and mark
+  // two as committed, but PAGE_NOACCESS, so we have a setup like this:
+  // 0       1       2       3       4       5
+  // +-----------------------------------------------+
+  // | ????? |       | xxxxx |       |       | ????? |
+  // +-----------------------------------------------+
+  void* reserve_region =
+      VirtualAlloc(nullptr, kBlockSize * 6, MEM_RESERVE, PAGE_READWRITE);
+  ASSERT_TRUE(reserve_region);
+  uintptr_t reserved_as_int = reinterpret_cast<uintptr_t>(reserve_region);
+  void* readable1 =
+      VirtualAlloc(reinterpret_cast<void*>(reserved_as_int + kBlockSize),
+                   kBlockSize,
+                   MEM_COMMIT,
+                   PAGE_READWRITE);
+  ASSERT_TRUE(readable1);
+  void* readable2 =
+      VirtualAlloc(reinterpret_cast<void*>(reserved_as_int + (kBlockSize * 3)),
+                   kBlockSize * 2,
+                   MEM_COMMIT,
+                   PAGE_READWRITE);
+  ASSERT_TRUE(readable2);
+
+  void* no_access =
+      VirtualAlloc(reinterpret_cast<void*>(reserved_as_int + (kBlockSize * 2)),
+                   kBlockSize,
+                   MEM_COMMIT,
+                   PAGE_NOACCESS);
+  ASSERT_TRUE(no_access);
+
+  HANDLE current_process = GetCurrentProcess();
+  ProcessInfo info;
+  info.Initialize(current_process);
+  auto ranges = info.GetReadableRanges(
+      CheckedRange<WinVMAddress, WinVMSize>(reserved_as_int, kBlockSize * 6));
+
+  ASSERT_EQ(ranges.size(), 2u);
+  EXPECT_EQ(ranges[0].base(), reserved_as_int + kBlockSize);
+  EXPECT_EQ(ranges[0].size(), kBlockSize);
+  EXPECT_EQ(ranges[1].base(), reserved_as_int + (kBlockSize * 3));
+  EXPECT_EQ(ranges[1].size(), kBlockSize * 2);
+
+  // Also make sure what we think we can read corresponds with what we can
+  // actually read.
+  std::unique_ptr<unsigned char[]> into(new unsigned char[kBlockSize * 6]);
+  SIZE_T bytes_read;
+
+  EXPECT_TRUE(ReadProcessMemory(
+      current_process, readable1, into.get(), kBlockSize, &bytes_read));
+  EXPECT_EQ(bytes_read, kBlockSize);
+
+  EXPECT_TRUE(ReadProcessMemory(
+      current_process, readable2, into.get(), kBlockSize * 2, &bytes_read));
+  EXPECT_EQ(bytes_read, kBlockSize * 2);
+
+  EXPECT_FALSE(ReadProcessMemory(
+      current_process, no_access, into.get(), kBlockSize, &bytes_read));
+  EXPECT_FALSE(ReadProcessMemory(
+      current_process, reserve_region, into.get(), kBlockSize, &bytes_read));
+  EXPECT_FALSE(ReadProcessMemory(current_process,
+                                 reserve_region,
+                                 into.get(),
+                                 kBlockSize * 6,
+                                 &bytes_read));
+}
+
+TEST(ProcessInfo, Handles) {
+  ScopedTempDir temp_dir;
+
+  ScopedFileHandle file(LoggingOpenFileForWrite(
+      temp_dir.path().Append(FILE_PATH_LITERAL("test_file")),
+      FileWriteMode::kTruncateOrCreate,
+      FilePermissions::kWorldReadable));
+  ASSERT_TRUE(file.is_valid());
+
+  SECURITY_ATTRIBUTES security_attributes = {0};
+  security_attributes.nLength = sizeof(security_attributes);
+  security_attributes.bInheritHandle = true;
+  ScopedFileHandle inherited_file(CreateFile(
+      temp_dir.path().Append(FILE_PATH_LITERAL("inheritable")).value().c_str(),
+      GENERIC_WRITE,
+      0,
+      &security_attributes,
+      CREATE_NEW,
+      FILE_ATTRIBUTE_NORMAL,
+      nullptr));
+  ASSERT_TRUE(inherited_file.is_valid());
+
+  HKEY key;
+  ASSERT_EQ(RegOpenKeyEx(
+                HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft", 0, KEY_READ, &key),
+            ERROR_SUCCESS);
+  ScopedRegistryKey scoped_key(key);
+  ASSERT_TRUE(scoped_key.is_valid());
+
+  std::wstring mapping_name =
+      base::UTF8ToUTF16(base::StringPrintf("Local\\test_mapping_%lu_%s",
+                                           GetCurrentProcessId(),
+                                           RandomString().c_str()));
+  ScopedKernelHANDLE mapping(CreateFileMapping(INVALID_HANDLE_VALUE,
+                                               nullptr,
+                                               PAGE_READWRITE,
+                                               0,
+                                               1024,
+                                               mapping_name.c_str()));
+  ASSERT_TRUE(mapping.is_valid()) << ErrorMessage("CreateFileMapping");
+
+  ProcessInfo info;
+  info.Initialize(GetCurrentProcess());
+  bool found_file_handle = false;
+  bool found_inherited_file_handle = false;
+  bool found_key_handle = false;
+  bool found_mapping_handle = false;
+  for (auto handle : info.Handles()) {
+    if (handle.handle == HandleToInt(file.get())) {
+      EXPECT_FALSE(found_file_handle);
+      found_file_handle = true;
+      EXPECT_EQ(handle.type_name, L"File");
+      EXPECT_EQ(handle.handle_count, 1u);
+      EXPECT_NE(handle.pointer_count, 0u);
+      EXPECT_EQ(handle.granted_access & STANDARD_RIGHTS_ALL,
+                static_cast<uint32_t>(STANDARD_RIGHTS_READ |
+                                      STANDARD_RIGHTS_WRITE | SYNCHRONIZE));
+      EXPECT_EQ(handle.attributes, 0u);
+    }
+    if (handle.handle == HandleToInt(inherited_file.get())) {
+      EXPECT_FALSE(found_inherited_file_handle);
+      found_inherited_file_handle = true;
+      EXPECT_EQ(handle.type_name, L"File");
+      EXPECT_EQ(handle.handle_count, 1u);
+      EXPECT_NE(handle.pointer_count, 0u);
+      EXPECT_EQ(handle.granted_access & STANDARD_RIGHTS_ALL,
+                static_cast<uint32_t>(STANDARD_RIGHTS_READ |
+                                      STANDARD_RIGHTS_WRITE | SYNCHRONIZE));
+
+      // OBJ_INHERIT from ntdef.h, but including that conflicts with other
+      // headers.
+      constexpr uint32_t kObjInherit = 0x2;
+      EXPECT_EQ(handle.attributes, kObjInherit);
+    }
+    if (handle.handle == HandleToInt(scoped_key.get())) {
+      EXPECT_FALSE(found_key_handle);
+      found_key_handle = true;
+      EXPECT_EQ(handle.type_name, L"Key");
+      EXPECT_EQ(handle.handle_count, 1u);
+      EXPECT_NE(handle.pointer_count, 0u);
+      EXPECT_EQ(handle.granted_access & STANDARD_RIGHTS_ALL,
+                static_cast<uint32_t>(STANDARD_RIGHTS_READ));
+      EXPECT_EQ(handle.attributes, 0u);
+    }
+    if (handle.handle == HandleToInt(mapping.get())) {
+      EXPECT_FALSE(found_mapping_handle);
+      found_mapping_handle = true;
+      EXPECT_EQ(handle.type_name, L"Section");
+      EXPECT_EQ(handle.handle_count, 1u);
+      EXPECT_NE(handle.pointer_count, 0u);
+      EXPECT_EQ(handle.granted_access & STANDARD_RIGHTS_ALL,
+                static_cast<uint32_t>(DELETE | READ_CONTROL | WRITE_DAC |
+                                      WRITE_OWNER | STANDARD_RIGHTS_READ |
+                                      STANDARD_RIGHTS_WRITE));
+      EXPECT_EQ(handle.attributes, 0u);
+    }
+  }
+  EXPECT_TRUE(found_file_handle);
+  EXPECT_TRUE(found_inherited_file_handle);
+  EXPECT_TRUE(found_key_handle);
+  EXPECT_TRUE(found_mapping_handle);
+}
+
+TEST(ProcessInfo, OutOfRangeCheck) {
+  constexpr size_t kAllocationSize = 12345;
+  std::unique_ptr<char[]> safe_memory(new char[kAllocationSize]);
+
+  ProcessInfo info;
+  info.Initialize(GetCurrentProcess());
+
+  EXPECT_TRUE(
+      info.LoggingRangeIsFullyReadable(CheckedRange<WinVMAddress, WinVMSize>(
+          FromPointerCast<WinVMAddress>(safe_memory.get()), kAllocationSize)));
+  EXPECT_FALSE(info.LoggingRangeIsFullyReadable(
+      CheckedRange<WinVMAddress, WinVMSize>(0, 1024)));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/process_info_test_child.cc b/src/third_party/crashpad/util/win/process_info_test_child.cc
new file mode 100644
index 0000000..1de1e8b
--- /dev/null
+++ b/src/third_party/crashpad/util/win/process_info_test_child.cc
@@ -0,0 +1,109 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <intrin.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <windows.h>
+#include <winternl.h>
+
+namespace {
+
+bool UnicodeStringEndsWithCaseInsensitive(const UNICODE_STRING& us,
+                                          const wchar_t* ends_with) {
+  const size_t len = wcslen(ends_with);
+  // Recall that UNICODE_STRING.Length is in bytes, not characters.
+  const size_t us_len_in_chars = us.Length / sizeof(wchar_t);
+  if (us_len_in_chars < len)
+    return false;
+  return _wcsnicmp(&us.Buffer[us_len_in_chars - len], ends_with, len) == 0;
+}
+
+}  // namespace
+
+// A simple binary to be loaded and inspected by ProcessInfo.
+int wmain(int argc, wchar_t** argv) {
+  if (argc != 2)
+    abort();
+
+  // Get a handle to the event we use to communicate with our parent.
+  HANDLE done_event = CreateEvent(nullptr, true, false, argv[1]);
+  if (!done_event)
+    abort();
+
+  // Load an unusual module (that we don't depend upon) so we can do an
+  // existence check. It's also important that these DLLs don't depend on
+  // any other DLLs, otherwise there'll be additional modules in the list, which
+  // the test expects not to be there.
+  if (!LoadLibrary(L"lz32.dll"))
+    abort();
+
+  // Load another unusual module so we can destroy its FullDllName field in the
+  // PEB to test corrupted name reads.
+  static constexpr wchar_t kCorruptableDll[] = L"kbdurdu.dll";
+  if (!LoadLibrary(kCorruptableDll))
+    abort();
+
+  // Find and corrupt the buffer pointer to the name in the PEB.
+  HINSTANCE ntdll = GetModuleHandle(L"ntdll.dll");
+  decltype(NtQueryInformationProcess)* nt_query_information_process =
+      reinterpret_cast<decltype(NtQueryInformationProcess)*>(
+          GetProcAddress(ntdll, "NtQueryInformationProcess"));
+  if (!nt_query_information_process)
+    abort();
+
+  PROCESS_BASIC_INFORMATION pbi;
+  if (nt_query_information_process(GetCurrentProcess(),
+                                   ProcessBasicInformation,
+                                   &pbi,
+                                   sizeof(pbi),
+                                   nullptr) < 0) {
+    abort();
+  }
+
+  PEB_LDR_DATA* ldr = pbi.PebBaseAddress->Ldr;
+  LIST_ENTRY* head = &ldr->InMemoryOrderModuleList;
+  LIST_ENTRY* next = head->Flink;
+  while (next != head) {
+    LDR_DATA_TABLE_ENTRY* entry =
+        CONTAINING_RECORD(next, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
+    if (UnicodeStringEndsWithCaseInsensitive(entry->FullDllName,
+                                             kCorruptableDll)) {
+      // Corrupt the pointer to the name.
+      entry->FullDllName.Buffer = 0;
+    }
+    next = next->Flink;
+  }
+
+  HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
+  if (out == INVALID_HANDLE_VALUE)
+    abort();
+  // We just want any valid address that's known to be code.
+  uint64_t code_address = reinterpret_cast<uint64_t>(_ReturnAddress());
+  DWORD bytes_written;
+  if (!WriteFile(
+          out, &code_address, sizeof(code_address), &bytes_written, nullptr) ||
+      bytes_written != sizeof(code_address)) {
+    abort();
+  }
+
+  if (WaitForSingleObject(done_event, INFINITE) != WAIT_OBJECT_0)
+    abort();
+
+  CloseHandle(done_event);
+
+  return EXIT_SUCCESS;
+}
diff --git a/src/third_party/crashpad/util/win/process_structs.h b/src/third_party/crashpad/util/win/process_structs.h
new file mode 100644
index 0000000..d9b5b2f
--- /dev/null
+++ b/src/third_party/crashpad/util/win/process_structs.h
@@ -0,0 +1,515 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_PROCESS_STRUCTS_H_
+#define CRASHPAD_UTIL_WIN_PROCESS_STRUCTS_H_
+
+#include <windows.h>
+
+namespace crashpad {
+namespace process_types {
+
+namespace internal {
+
+struct Traits32 {
+  using Pad = DWORD;
+  using UnsignedIntegral = DWORD;
+  using Pointer = DWORD;
+};
+
+struct Traits64 {
+  using Pad = DWORD64;
+  using UnsignedIntegral = DWORD64;
+  using Pointer = DWORD64;
+};
+
+}  // namespace internal
+
+//! \{
+
+//! \brief Selected structures from winternl.h, ntddk.h, and `dt ntdll!xxx`,
+//!     customized to have both x86 and x64 sizes available.
+//!
+//! The structure and field names follow the Windows names for clarity. We do,
+//! however, use plain integral types rather than pointer types. This is both
+//! easier to define, and avoids accidentally treating them as pointers into the
+//! current address space.
+//!
+//! The templates below should be instantiated with either internal::Traits32
+//! for structures targeting x86, or internal::Traits64 for x64.
+
+// We set packing to 1 so that we can explicitly control the layout to make it
+// match the OS defined structures.
+#pragma pack(push, 1)
+
+template <class Traits>
+struct PROCESS_BASIC_INFORMATION {
+  union {
+    DWORD ExitStatus;
+    typename Traits::Pad padding_for_x64_0;
+  };
+  typename Traits::Pointer PebBaseAddress;
+  typename Traits::UnsignedIntegral AffinityMask;
+  union {
+    DWORD BasePriority;
+    typename Traits::Pad padding_for_x64_1;
+  };
+  typename Traits::UnsignedIntegral UniqueProcessId;
+  typename Traits::UnsignedIntegral InheritedFromUniqueProcessId;
+};
+
+template <class Traits>
+struct LIST_ENTRY {
+  typename Traits::Pointer Flink;
+  typename Traits::Pointer Blink;
+};
+
+template <class Traits>
+struct UNICODE_STRING {
+  union {
+    struct {
+      USHORT Length;
+      USHORT MaximumLength;
+    };
+    typename Traits::Pad padding_for_x64;
+  };
+  typename Traits::Pointer Buffer;
+};
+
+template <class Traits>
+struct PEB_LDR_DATA {
+  ULONG Length;
+  DWORD Initialized;
+  typename Traits::Pointer SsHandle;
+  LIST_ENTRY<Traits> InLoadOrderModuleList;
+  LIST_ENTRY<Traits> InMemoryOrderModuleList;
+  LIST_ENTRY<Traits> InInitializationOrderModuleList;
+};
+
+template <class Traits>
+struct LDR_DATA_TABLE_ENTRY {
+  LIST_ENTRY<Traits> InLoadOrderLinks;
+  LIST_ENTRY<Traits> InMemoryOrderLinks;
+  LIST_ENTRY<Traits> InInitializationOrderLinks;
+  typename Traits::Pointer DllBase;
+  typename Traits::Pointer EntryPoint;
+  union {
+    ULONG SizeOfImage;
+    typename Traits::Pad padding_for_x64;
+  };
+  UNICODE_STRING<Traits> FullDllName;
+  UNICODE_STRING<Traits> BaseDllName;
+  ULONG Flags;
+  USHORT ObsoleteLoadCount;
+  USHORT TlsIndex;
+  LIST_ENTRY<Traits> HashLinks;
+  ULONG TimeDateStamp;
+};
+
+template <class Traits>
+struct CURDIR {
+  UNICODE_STRING<Traits> DosPath;
+  typename Traits::Pointer Handle;
+};
+
+template <class Traits>
+struct STRING {
+  union {
+    struct {
+      USHORT Length;
+      USHORT MaximumLength;
+    };
+    typename Traits::Pad padding_for_x64;
+  };
+  typename Traits::Pointer Buffer;
+};
+
+template <class Traits>
+struct RTL_DRIVE_LETTER_CURDIR {
+  WORD Flags;
+  WORD Length;
+  DWORD TimeStamp;
+  STRING<Traits> DosPath;
+};
+
+template <class Traits>
+struct RTL_USER_PROCESS_PARAMETERS {
+  DWORD MaximumLength;
+  DWORD Length;
+  DWORD Flags;
+  DWORD DebugFlags;
+  typename Traits::Pointer ConsoleHandle;
+  union {
+    DWORD ConsoleFlags;
+    typename Traits::Pad padding_for_x64_0;
+  };
+  typename Traits::Pointer StandardInput;
+  typename Traits::Pointer StandardOutput;
+  typename Traits::Pointer StandardError;
+  CURDIR<Traits> CurrentDirectory;
+  UNICODE_STRING<Traits> DllPath;
+  UNICODE_STRING<Traits> ImagePathName;
+  UNICODE_STRING<Traits> CommandLine;
+  typename Traits::Pointer Environment;
+  DWORD StartingX;
+  DWORD StartingY;
+  DWORD CountX;
+  DWORD CountY;
+  DWORD CountCharsX;
+  DWORD CountCharsY;
+  DWORD FillAttribute;
+  DWORD WindowFlags;
+  union {
+    DWORD ShowWindowFlags;
+    typename Traits::Pad padding_for_x64_1;
+  };
+  UNICODE_STRING<Traits> WindowTitle;
+  UNICODE_STRING<Traits> DesktopInfo;
+  UNICODE_STRING<Traits> ShellInfo;
+  UNICODE_STRING<Traits> RuntimeData;
+  RTL_DRIVE_LETTER_CURDIR<Traits> CurrentDirectores[32];  // sic.
+  ULONG EnvironmentSize;
+};
+
+template <class T>
+struct GdiHandleBufferCountForBitness;
+
+template <>
+struct GdiHandleBufferCountForBitness<internal::Traits32> {
+  enum { value = 34 };
+};
+template <>
+struct GdiHandleBufferCountForBitness<internal::Traits64> {
+  enum { value = 60 };
+};
+
+template <class Traits>
+struct PEB {
+  union {
+    struct {
+      BYTE InheritedAddressSpace;
+      BYTE ReadImageFileExecOptions;
+      BYTE BeingDebugged;
+      BYTE BitField;
+    };
+    typename Traits::Pad padding_for_x64_0;
+  };
+  typename Traits::Pointer Mutant;
+  typename Traits::Pointer ImageBaseAddress;
+  typename Traits::Pointer Ldr;
+  typename Traits::Pointer ProcessParameters;
+  typename Traits::Pointer SubSystemData;
+  typename Traits::Pointer ProcessHeap;
+  typename Traits::Pointer FastPebLock;
+  typename Traits::Pointer AtlThunkSListPtr;
+  typename Traits::Pointer IFEOKey;
+  union {
+    DWORD CrossProcessFlags;
+    typename Traits::Pad padding_for_x64_1;
+  };
+  typename Traits::Pointer KernelCallbackTable;
+  DWORD SystemReserved;
+  DWORD AtlThunkSListPtr32;
+  typename Traits::Pointer ApiSetMap;
+  union {
+    DWORD TlsExpansionCounter;
+    typename Traits::Pad padding_for_x64_2;
+  };
+  typename Traits::Pointer TlsBitmap;
+  DWORD TlsBitmapBits[2];
+  typename Traits::Pointer ReadOnlySharedMemoryBase;
+  typename Traits::Pointer SparePvoid0;
+  typename Traits::Pointer ReadOnlyStaticServerData;
+  typename Traits::Pointer AnsiCodePageData;
+  typename Traits::Pointer OemCodePageData;
+  typename Traits::Pointer UnicodeCaseTableData;
+  DWORD NumberOfProcessors;
+  DWORD NtGlobalFlag;
+  DWORD alignment_for_x86;
+  LARGE_INTEGER CriticalSectionTimeout;
+  typename Traits::UnsignedIntegral HeapSegmentReserve;
+  typename Traits::UnsignedIntegral HeapSegmentCommit;
+  typename Traits::UnsignedIntegral HeapDeCommitTotalFreeThreshold;
+  typename Traits::UnsignedIntegral HeapDeCommitFreeBlockThreshold;
+  DWORD NumberOfHeaps;
+  DWORD MaximumNumberOfHeaps;
+  typename Traits::Pointer ProcessHeaps;
+  typename Traits::Pointer GdiSharedHandleTable;
+  typename Traits::Pointer ProcessStarterHelper;
+  DWORD GdiDCAttributeList;
+  typename Traits::Pointer LoaderLock;
+  DWORD OSMajorVersion;
+  DWORD OSMinorVersion;
+  WORD OSBuildNumber;
+  WORD OSCSDVersion;
+  DWORD OSPlatformId;
+  DWORD ImageSubsystem;
+  DWORD ImageSubsystemMajorVersion;
+  union {
+    DWORD ImageSubsystemMinorVersion;
+    typename Traits::Pad padding_for_x64_3;
+  };
+  typename Traits::UnsignedIntegral ActiveProcessAffinityMask;
+  DWORD GdiHandleBuffer[GdiHandleBufferCountForBitness<Traits>::value];
+  typename Traits::Pointer PostProcessInitRoutine;
+  typename Traits::Pointer TlsExpansionBitmap;
+  DWORD TlsExpansionBitmapBits[32];
+  union {
+    DWORD SessionId;
+    typename Traits::Pad padding_for_x64_4;
+  };
+  ULARGE_INTEGER AppCompatFlags;
+  ULARGE_INTEGER AppCompatFlagsUser;
+  typename Traits::Pointer pShimData;
+  typename Traits::Pointer AppCompatInfo;
+  UNICODE_STRING<Traits> CSDVersion;
+  typename Traits::Pointer ActivationContextData;
+  typename Traits::Pointer ProcessAssemblyStorageMap;
+  typename Traits::Pointer SystemDefaultActivationContextData;
+  typename Traits::Pointer SystemAssemblyStorageMap;
+  typename Traits::UnsignedIntegral MinimumStackCommit;
+  typename Traits::Pointer FlsCallback;
+  LIST_ENTRY<Traits> FlsListHead;
+  typename Traits::Pointer FlsBitmap;
+  DWORD FlsBitmapBits[4];
+  DWORD FlsHighIndex;
+};
+
+template <class Traits>
+struct NT_TIB {
+  union {
+    // See https://msdn.microsoft.com/library/dn424783.aspx.
+    typename Traits::Pointer Wow64Teb;
+    struct {
+      typename Traits::Pointer ExceptionList;
+      typename Traits::Pointer StackBase;
+      typename Traits::Pointer StackLimit;
+      typename Traits::Pointer SubSystemTib;
+      union {
+        typename Traits::Pointer FiberData;
+        BYTE Version[4];
+      };
+      typename Traits::Pointer ArbitraryUserPointer;
+      typename Traits::Pointer Self;
+    };
+  };
+};
+
+// See https://msdn.microsoft.com/library/gg750647.aspx.
+template <class Traits>
+struct CLIENT_ID {
+  typename Traits::Pointer UniqueProcess;
+  typename Traits::Pointer UniqueThread;
+};
+
+// This is a partial definition of the TEB, as we do not currently use many
+// fields of it. See https://nirsoft.net/kernel_struct/vista/TEB.html, and the
+// (arch-specific) definition of _TEB in winternl.h.
+template <class Traits>
+struct TEB {
+  NT_TIB<Traits> NtTib;
+  typename Traits::Pointer EnvironmentPointer;
+  CLIENT_ID<Traits> ClientId;
+  typename Traits::Pointer ActiveRpcHandle;
+  typename Traits::Pointer ThreadLocalStoragePointer;
+  typename Traits::Pointer ProcessEnvironmentBlock;
+  typename Traits::Pointer RemainderOfReserved2[399];
+  BYTE Reserved3[1952];
+  typename Traits::Pointer TlsSlots[64];
+  BYTE Reserved4[8];
+  typename Traits::Pointer Reserved5[26];
+  typename Traits::Pointer ReservedForOle;
+  typename Traits::Pointer Reserved6[4];
+  typename Traits::Pointer TlsExpansionSlots;
+};
+
+// See https://msdn.microsoft.com/library/gg750724.aspx.
+template <class Traits>
+struct SYSTEM_THREAD_INFORMATION {
+  union {
+    struct {
+      LARGE_INTEGER KernelTime;
+      LARGE_INTEGER UserTime;
+      LARGE_INTEGER CreateTime;
+      union {
+        ULONG WaitTime;
+        typename Traits::Pad padding_for_x64_0;
+      };
+      typename Traits::Pointer StartAddress;
+      CLIENT_ID<Traits> ClientId;
+      LONG Priority;
+      LONG BasePriority;
+      ULONG ContextSwitches;
+      ULONG ThreadState;
+      union {
+        ULONG WaitReason;
+        typename Traits::Pad padding_for_x64_1;
+      };
+    };
+    LARGE_INTEGER alignment_for_x86[8];
+  };
+};
+
+// There's an extra field in the x64 VM_COUNTERS (or maybe it's VM_COUNTERS_EX,
+// it's not clear), so we just make separate specializations for 32/64.
+template <class Traits>
+struct VM_COUNTERS {};
+
+template <>
+struct VM_COUNTERS<internal::Traits32> {
+  SIZE_T PeakVirtualSize;
+  SIZE_T VirtualSize;
+  ULONG PageFaultCount;
+  SIZE_T PeakWorkingSetSize;
+  SIZE_T WorkingSetSize;
+  SIZE_T QuotaPeakPagedPoolUsage;
+  SIZE_T QuotaPagedPoolUsage;
+  SIZE_T QuotaPeakNonPagedPoolUsage;
+  SIZE_T QuotaNonPagedPoolUsage;
+  SIZE_T PagefileUsage;
+  SIZE_T PeakPagefileUsage;
+};
+
+template <>
+struct VM_COUNTERS<internal::Traits64> {
+  SIZE_T PeakVirtualSize;
+  SIZE_T VirtualSize;
+  union {
+    ULONG PageFaultCount;
+    internal::Traits64::Pad padding_for_x64;
+  };
+  SIZE_T PeakWorkingSetSize;
+  SIZE_T WorkingSetSize;
+  SIZE_T QuotaPeakPagedPoolUsage;
+  SIZE_T QuotaPagedPoolUsage;
+  SIZE_T QuotaPeakNonPagedPoolUsage;
+  SIZE_T QuotaNonPagedPoolUsage;
+  SIZE_T PagefileUsage;
+  SIZE_T PeakPagefileUsage;
+  SIZE_T PrivateUsage;
+};
+
+// https://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/System%20Information/Structures/SYSTEM_PROCESS_INFORMATION.html
+template <class Traits>
+struct SYSTEM_PROCESS_INFORMATION {
+  ULONG NextEntryOffset;
+  ULONG NumberOfThreads;
+  LARGE_INTEGER WorkingSetPrivateSize;
+  ULONG HardFaultCount;
+  ULONG NumberOfThreadsHighWatermark;
+  ULONGLONG CycleTime;
+  LARGE_INTEGER CreateTime;
+  LARGE_INTEGER UserTime;
+  LARGE_INTEGER KernelTime;
+  UNICODE_STRING<Traits> ImageName;
+  union {
+    LONG BasePriority;
+    typename Traits::Pad padding_for_x64_0;
+  };
+  union {
+    DWORD UniqueProcessId;
+    typename Traits::Pad padding_for_x64_1;
+  };
+  union {
+    DWORD InheritedFromUniqueProcessId;
+    typename Traits::Pad padding_for_x64_2;
+  };
+  ULONG HandleCount;
+  ULONG SessionId;
+  typename Traits::Pointer UniqueProcessKey;
+  union {
+    VM_COUNTERS<Traits> VirtualMemoryCounters;
+    LARGE_INTEGER alignment_for_x86[6];
+  };
+  IO_COUNTERS IoCounters;
+  SYSTEM_THREAD_INFORMATION<Traits> Threads[1];
+};
+
+// https://undocumented.ntinternals.net/source/usermode/structures/THREAD_BASIC_INFORMATION.html
+template <class Traits>
+struct THREAD_BASIC_INFORMATION {
+  union {
+    LONG ExitStatus;
+    typename Traits::Pad padding_for_x64_0;
+  };
+  typename Traits::Pointer TebBaseAddress;
+  CLIENT_ID<Traits> ClientId;
+  typename Traits::Pointer AffinityMask;
+  ULONG Priority;
+  LONG BasePriority;
+};
+
+template <class Traits>
+struct EXCEPTION_POINTERS {
+  typename Traits::Pointer ExceptionRecord;
+  typename Traits::Pointer ContextRecord;
+};
+
+using EXCEPTION_POINTERS32 = EXCEPTION_POINTERS<internal::Traits32>;
+using EXCEPTION_POINTERS64 = EXCEPTION_POINTERS<internal::Traits64>;
+
+// This is defined in winnt.h, but not for cross-bitness.
+template <class Traits>
+struct RTL_CRITICAL_SECTION {
+  typename Traits::Pointer DebugInfo;
+  LONG LockCount;
+  LONG RecursionCount;
+  typename Traits::Pointer OwningThread;
+  typename Traits::Pointer LockSemaphore;
+  typename Traits::UnsignedIntegral SpinCount;
+};
+
+template <class Traits>
+struct RTL_CRITICAL_SECTION_DEBUG {
+  union {
+    struct {
+      WORD Type;
+      WORD CreatorBackTraceIndex;
+    };
+    typename Traits::Pad alignment_for_x64;
+  };
+  typename Traits::Pointer CriticalSection;
+  LIST_ENTRY<Traits> ProcessLocksList;
+  DWORD EntryCount;
+  DWORD ContentionCount;
+  DWORD Flags;
+  WORD CreatorBackTraceIndexHigh;
+  WORD SpareWORD;
+};
+
+struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
+  void* Object;
+  ULONG_PTR UniqueProcessId;
+  HANDLE HandleValue;
+  ULONG GrantedAccess;
+  USHORT CreatorBackTraceIndex;
+  USHORT ObjectTypeIndex;
+  ULONG HandleAttributes;
+  ULONG Reserved;
+};
+
+struct SYSTEM_HANDLE_INFORMATION_EX {
+  ULONG_PTR NumberOfHandles;
+  ULONG_PTR Reserved;
+  SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
+};
+
+#pragma pack(pop)
+
+//! \}
+
+}  // namespace process_types
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_PROCESS_STRUCTS_H_
diff --git a/src/third_party/crashpad/util/win/registration_protocol_win.cc b/src/third_party/crashpad/util/win/registration_protocol_win.cc
new file mode 100644
index 0000000..64ed518
--- /dev/null
+++ b/src/third_party/crashpad/util/win/registration_protocol_win.cc
@@ -0,0 +1,271 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/registration_protocol_win.h"
+
+#include <windows.h>
+#include <aclapi.h>
+#include <sddl.h>
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "util/win/exception_handler_server.h"
+#include "util/win/loader_lock.h"
+#include "util/win/scoped_handle.h"
+#include "util/win/scoped_local_alloc.h"
+
+namespace crashpad {
+
+namespace {
+
+void* GetSecurityDescriptorWithUser(const base::char16* sddl_string,
+                                    size_t* size) {
+  if (size)
+    *size = 0;
+
+  PSECURITY_DESCRIPTOR base_sec_desc;
+  if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
+          sddl_string, SDDL_REVISION_1, &base_sec_desc, nullptr)) {
+    PLOG(ERROR) << "ConvertStringSecurityDescriptorToSecurityDescriptor";
+    return nullptr;
+  }
+
+  ScopedLocalAlloc base_sec_desc_owner(base_sec_desc);
+  EXPLICIT_ACCESS access;
+  wchar_t username[] = L"CURRENT_USER";
+  BuildExplicitAccessWithName(
+      &access, username, GENERIC_ALL, GRANT_ACCESS, NO_INHERITANCE);
+
+  PSECURITY_DESCRIPTOR user_sec_desc;
+  ULONG user_sec_desc_size;
+  DWORD error = BuildSecurityDescriptor(nullptr,
+                                        nullptr,
+                                        1,
+                                        &access,
+                                        0,
+                                        nullptr,
+                                        base_sec_desc,
+                                        &user_sec_desc_size,
+                                        &user_sec_desc);
+  if (error != ERROR_SUCCESS) {
+    SetLastError(error);
+    PLOG(ERROR) << "BuildSecurityDescriptor";
+    return nullptr;
+  }
+
+  *size = user_sec_desc_size;
+  return user_sec_desc;
+}
+
+}  // namespace
+
+bool SendToCrashHandlerServer(const base::string16& pipe_name,
+                              const ClientToServerMessage& message,
+                              ServerToClientMessage* response) {
+  // Retry CreateFile() in a loop. If the handler isn’t actively waiting in
+  // ConnectNamedPipe() on a pipe instance because it’s busy doing something
+  // else, CreateFile() will fail with ERROR_PIPE_BUSY. WaitNamedPipe() waits
+  // until a pipe instance is ready, but there’s no way to wait for this
+  // condition and atomically open the client side of the pipe in a single
+  // operation. CallNamedPipe() implements similar retry logic to this, also in
+  // user-mode code.
+  //
+  // This loop is only intended to retry on ERROR_PIPE_BUSY. Notably, if the
+  // handler is so lazy that it hasn’t even called CreateNamedPipe() yet,
+  // CreateFile() will fail with ERROR_FILE_NOT_FOUND, and this function is
+  // expected to fail without retrying anything. If the handler is started at
+  // around the same time as its client, something external to this code must be
+  // done to guarantee correct ordering. When the client starts the handler
+  // itself, CrashpadClient::StartHandler() provides this synchronization.
+  for (;;) {
+    ScopedFileHANDLE pipe(
+        CreateFile(pipe_name.c_str(),
+                   GENERIC_READ | GENERIC_WRITE,
+                   0,
+                   nullptr,
+                   OPEN_EXISTING,
+                   SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION,
+                   nullptr));
+    if (!pipe.is_valid()) {
+      if (GetLastError() != ERROR_PIPE_BUSY) {
+        PLOG(ERROR) << "CreateFile";
+        return false;
+      }
+
+      if (!WaitNamedPipe(pipe_name.c_str(), NMPWAIT_WAIT_FOREVER)) {
+        PLOG(ERROR) << "WaitNamedPipe";
+        return false;
+      }
+
+      continue;
+    }
+
+    DWORD mode = PIPE_READMODE_MESSAGE;
+    if (!SetNamedPipeHandleState(pipe.get(), &mode, nullptr, nullptr)) {
+      PLOG(ERROR) << "SetNamedPipeHandleState";
+      return false;
+    }
+    DWORD bytes_read = 0;
+    BOOL result = TransactNamedPipe(
+        pipe.get(),
+        // This is [in], but is incorrectly declared non-const.
+        const_cast<ClientToServerMessage*>(&message),
+        sizeof(message),
+        response,
+        sizeof(*response),
+        &bytes_read,
+        nullptr);
+    if (!result) {
+      PLOG(ERROR) << "TransactNamedPipe";
+      return false;
+    }
+    if (bytes_read != sizeof(*response)) {
+      LOG(ERROR) << "TransactNamedPipe: expected " << sizeof(*response)
+                 << ", observed " << bytes_read;
+      return false;
+    }
+    return true;
+  }
+}
+
+HANDLE CreateNamedPipeInstance(const std::wstring& pipe_name,
+                               bool first_instance) {
+  SECURITY_ATTRIBUTES security_attributes;
+  SECURITY_ATTRIBUTES* security_attributes_pointer = nullptr;
+
+  if (first_instance) {
+    // Pre-Vista does not have integrity levels.
+    const DWORD version = GetVersion();
+    const DWORD major_version = LOBYTE(LOWORD(version));
+    const bool is_vista_or_later = major_version >= 6;
+    if (is_vista_or_later) {
+      memset(&security_attributes, 0, sizeof(security_attributes));
+      security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+      security_attributes.lpSecurityDescriptor =
+          const_cast<void*>(GetSecurityDescriptorForNamedPipeInstance(nullptr));
+      security_attributes.bInheritHandle = TRUE;
+      security_attributes_pointer = &security_attributes;
+    }
+  }
+
+  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,
+      ExceptionHandlerServer::kPipeInstances,
+      512,
+      512,
+      0,
+      security_attributes_pointer);
+}
+
+const void* GetFallbackSecurityDescriptorForNamedPipeInstance(size_t* size) {
+  // Mandatory Label, no ACE flags, no ObjectType, integrity level untrusted is
+  // "S:(ML;;;;;S-1-16-0)". This static security descriptor is used as a
+  // fallback if GetSecurityDescriptorWithUser fails, to avoid losing crashes
+  // from non-AppContainer sandboxed applications.
+
+#pragma pack(push, 1)
+  static constexpr struct SecurityDescriptorBlob {
+    // See https://msdn.microsoft.com/library/cc230366.aspx.
+    SECURITY_DESCRIPTOR_RELATIVE sd_rel;
+    struct {
+      ACL acl;
+      struct {
+        // This is equivalent to SYSTEM_MANDATORY_LABEL_ACE, but there's no
+        // DWORD offset to the SID, instead it's inline.
+        ACE_HEADER header;
+        ACCESS_MASK mask;
+        SID sid;
+      } ace[1];
+    } sacl;
+  } kSecDescBlob = {
+      // sd_rel.
+      {
+          SECURITY_DESCRIPTOR_REVISION1,  // Revision.
+          0x00,  // Sbz1.
+          SE_SELF_RELATIVE | SE_SACL_PRESENT,  // Control.
+          0,  // OffsetOwner.
+          0,  // OffsetGroup.
+          offsetof(SecurityDescriptorBlob, sacl),  // OffsetSacl.
+          0,  // OffsetDacl.
+      },
+
+      // sacl.
+      {
+          // acl.
+          {
+              ACL_REVISION,  // AclRevision.
+              0,  // Sbz1.
+              sizeof(kSecDescBlob.sacl),  // AclSize.
+              static_cast<WORD>(
+                  base::size(kSecDescBlob.sacl.ace)),  // AceCount.
+              0,  // Sbz2.
+          },
+
+          // ace[0].
+          {
+              {
+                  // header.
+                  {
+                      SYSTEM_MANDATORY_LABEL_ACE_TYPE,  // AceType.
+                      0,  // AceFlags.
+                      sizeof(kSecDescBlob.sacl.ace[0]),  // AceSize.
+                  },
+
+                  // mask.
+                  0,
+
+                  // sid.
+                  {
+                      SID_REVISION,  // Revision.
+                                     // SubAuthorityCount.
+                      static_cast<BYTE>(base::size(
+                          kSecDescBlob.sacl.ace[0].sid.SubAuthority)),
+                      // IdentifierAuthority.
+                      {SECURITY_MANDATORY_LABEL_AUTHORITY},
+                      {SECURITY_MANDATORY_UNTRUSTED_RID},  // SubAuthority.
+                  },
+              },
+          },
+      },
+  };
+#pragma pack(pop)
+
+  if (size)
+    *size = sizeof(kSecDescBlob);
+  return reinterpret_cast<const void*>(&kSecDescBlob);
+}
+
+const void* GetSecurityDescriptorForNamedPipeInstance(size_t* size) {
+  CHECK(!IsThreadInLoaderLock());
+
+  // Get a security descriptor which grants the current user and SYSTEM full
+  // access to the named pipe. Also grant AppContainer RW access through the ALL
+  // APPLICATION PACKAGES SID (S-1-15-2-1). Finally add an Untrusted Mandatory
+  // Label for non-AppContainer sandboxed users.
+  static size_t sd_size;
+  static void* sec_desc = GetSecurityDescriptorWithUser(
+      L"D:(A;;GA;;;SY)(A;;GWGR;;;S-1-15-2-1)S:(ML;;;;;S-1-16-0)", &sd_size);
+
+  if (!sec_desc)
+    return GetFallbackSecurityDescriptorForNamedPipeInstance(size);
+
+  if (size)
+    *size = sd_size;
+  return sec_desc;
+}
+
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/registration_protocol_win.h b/src/third_party/crashpad/util/win/registration_protocol_win.h
new file mode 100644
index 0000000..ef8bebc
--- /dev/null
+++ b/src/third_party/crashpad/util/win/registration_protocol_win.h
@@ -0,0 +1,175 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_H_
+#define CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_H_
+
+#include <windows.h>
+#include <stdint.h>
+
+#include "base/strings/string16.h"
+#include "util/win/address_types.h"
+
+namespace crashpad {
+
+#pragma pack(push, 1)
+
+//! \brief Structure read out of the client process by the crash handler when an
+//!     exception occurs.
+struct ExceptionInformation {
+  //! \brief The address of an EXCEPTION_POINTERS structure in the client
+  //!     process that describes the exception.
+  WinVMAddress exception_pointers;
+
+  //! \brief The thread on which the exception happened.
+  DWORD thread_id;
+};
+
+//! \brief A client registration request.
+struct RegistrationRequest {
+  //! \brief The expected value of `version`. This should be changed whenever
+  //!     the messages or ExceptionInformation are modified incompatibly.
+  enum { kMessageVersion = 1 };
+
+  //! \brief Version field to detect skew between client and server. Should be
+  //!     set to kMessageVersion.
+  int version;
+
+  //! \brief The PID of the client process.
+  DWORD client_process_id;
+
+  //! \brief The address, in the client process's address space, of an
+  //!     ExceptionInformation structure, used when handling a crash dump
+  //!     request.
+  WinVMAddress crash_exception_information;
+
+  //! \brief The address, in the client process's address space, of an
+  //!     ExceptionInformation structure, used when handling a non-crashing dump
+  //!     request.
+  WinVMAddress non_crash_exception_information;
+
+  //! \brief The address, in the client process's address space, of a
+  //!     `CRITICAL_SECTION` allocated with a valid .DebugInfo field. This can
+  //!     be accomplished by using
+  //!     InitializeCriticalSectionWithDebugInfoIfPossible() or equivalent. This
+  //!     value can be `0`, however then limited lock data will be available in
+  //!     minidumps.
+  WinVMAddress critical_section_address;
+};
+
+//! \brief A message only sent to the server by itself to trigger shutdown.
+struct ShutdownRequest {
+  //! \brief A randomly generated token used to validate the the shutdown
+  //!     request was not sent from another process.
+  uint64_t token;
+};
+
+//! \brief The message passed from client to server by
+//!     SendToCrashHandlerServer().
+struct ClientToServerMessage {
+  //! \brief Indicates which field of the union is in use.
+  enum Type : uint32_t {
+    //! \brief For RegistrationRequest.
+    kRegister,
+
+    //! \brief For ShutdownRequest.
+    kShutdown,
+
+    //! \brief An empty message sent by the initial client in asynchronous mode.
+    //!     No data is required, this just confirms that the server is ready to
+    //!     accept client registrations.
+    kPing,
+  } type;
+
+  union {
+    RegistrationRequest registration;
+    ShutdownRequest shutdown;
+  };
+};
+
+//! \brief A client registration response.
+struct RegistrationResponse {
+  //! \brief An event `HANDLE`, valid in the client process, that should be
+  //!     signaled to request a crash report. Clients should convert the value
+  //!     to a `HANDLE` by calling IntToHandle().
+  int request_crash_dump_event;
+
+  //! \brief An event `HANDLE`, valid in the client process, that should be
+  //!     signaled to request a non-crashing dump be taken. Clients should
+  //!     convert the value to a `HANDLE` by calling IntToHandle().
+  int request_non_crash_dump_event;
+
+  //! \brief An event `HANDLE`, valid in the client process, that will be
+  //!     signaled by the server when the non-crashing dump is complete. Clients
+  //!     should convert the value to a `HANDLE` by calling IntToHandle().
+  int non_crash_dump_completed_event;
+};
+
+//! \brief The response sent back to the client via SendToCrashHandlerServer().
+union ServerToClientMessage {
+  RegistrationResponse registration;
+};
+
+#pragma pack(pop)
+
+//! \brief Connect over the given \a pipe_name, passing \a message to the
+//!     server, storing the server's reply into \a response.
+//!
+//! Typically clients will not use this directly, instead using
+//! CrashpadClient::SetHandler().
+//!
+//! \sa CrashpadClient::SetHandler()
+bool SendToCrashHandlerServer(const base::string16& pipe_name,
+                              const ClientToServerMessage& message,
+                              ServerToClientMessage* response);
+
+//! \brief Wraps CreateNamedPipe() to create a single named pipe instance.
+//!
+//! \param[in] pipe_name The name to use for the pipe.
+//! \param[in] first_instance If `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. The first instance will be
+//!     created with an untrusted integrity SACL so instances of this pipe can
+//!     be connected to by processes of any integrity level.
+HANDLE CreateNamedPipeInstance(const std::wstring& pipe_name,
+                               bool first_instance);
+
+//! \brief Returns the `SECURITY_DESCRIPTOR` blob that will be used for creating
+//!     the connection pipe in CreateNamedPipeInstance().
+//!
+//! This function is only exposed for testing.
+//!
+//! \param[out] size The size of the returned blob. May be `nullptr` if not
+//!     required.
+//!
+//! \return A pointer to a self-relative `SECURITY_DESCRIPTOR`. Ownership is not
+//!     transferred to the caller.
+const void* GetSecurityDescriptorForNamedPipeInstance(size_t* size);
+
+//! \brief Returns the `SECURITY_DESCRIPTOR` blob that will be used for creating
+//!     the connection pipe in CreateNamedPipeInstance() if the full descriptor
+//!     can't be created.
+//!
+//! This function is only exposed for testing.
+//!
+//! \param[out] size The size of the returned blob. May be `nullptr` if not
+//!     required.
+//!
+//! \return A pointer to a self-relative `SECURITY_DESCRIPTOR`. Ownership is not
+//!     transferred to the caller.
+const void* GetFallbackSecurityDescriptorForNamedPipeInstance(size_t* size);
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_H_
diff --git a/src/third_party/crashpad/util/win/registration_protocol_win_test.cc b/src/third_party/crashpad/util/win/registration_protocol_win_test.cc
new file mode 100644
index 0000000..334d7d8
--- /dev/null
+++ b/src/third_party/crashpad/util/win/registration_protocol_win_test.cc
@@ -0,0 +1,157 @@
+// Copyright 2016 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/registration_protocol_win.h"
+
+#include <aclapi.h>
+#include <sddl.h>
+#include <string.h>
+#include <windows.h>
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/string16.h"
+#include "gtest/gtest.h"
+#include "test/errors.h"
+#include "util/win/scoped_handle.h"
+#include "util/win/scoped_local_alloc.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+base::string16 GetStringFromSid(PSID sid) {
+  LPWSTR sid_str;
+  if (!ConvertSidToStringSid(sid, &sid_str)) {
+    PLOG(ERROR) << "ConvertSidToStringSid";
+    return base::string16();
+  }
+  ScopedLocalAlloc sid_str_ptr(sid_str);
+  return sid_str;
+}
+
+base::string16 GetUserSidString() {
+  HANDLE token_handle;
+  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token_handle)) {
+    PLOG(ERROR) << "OpenProcessToken";
+    return base::string16();
+  }
+
+  ScopedKernelHANDLE token(token_handle);
+  DWORD user_size = 0;
+  GetTokenInformation(token.get(), TokenUser, nullptr, 0, &user_size);
+  if (user_size == 0) {
+    PLOG(ERROR) << "GetTokenInformation Size";
+    return base::string16();
+  }
+
+  std::vector<char> user(user_size);
+  if (!GetTokenInformation(
+          token.get(), TokenUser, user.data(), user_size, &user_size)) {
+    PLOG(ERROR) << "GetTokenInformation";
+    return base::string16();
+  }
+
+  TOKEN_USER* user_ptr = reinterpret_cast<TOKEN_USER*>(user.data());
+  return GetStringFromSid(user_ptr->User.Sid);
+}
+
+void CheckAce(PACL acl,
+              DWORD index,
+              BYTE check_ace_type,
+              ACCESS_MASK check_mask,
+              const base::string16& check_sid) {
+  ASSERT_FALSE(check_sid.empty());
+  void* ace_ptr;
+  ASSERT_TRUE(GetAce(acl, index, &ace_ptr));
+
+  ACE_HEADER* header = static_cast<ACE_HEADER*>(ace_ptr);
+  ASSERT_EQ(check_ace_type, header->AceType);
+  ASSERT_EQ(0, header->AceFlags);
+
+  PSID sid = nullptr;
+  ACCESS_MASK mask = 0;
+  switch (header->AceType) {
+    case ACCESS_ALLOWED_ACE_TYPE: {
+      ACCESS_ALLOWED_ACE* allowed_ace =
+          static_cast<ACCESS_ALLOWED_ACE*>(ace_ptr);
+      sid = &allowed_ace->SidStart;
+      mask = allowed_ace->Mask;
+    } break;
+    case SYSTEM_MANDATORY_LABEL_ACE_TYPE: {
+      SYSTEM_MANDATORY_LABEL_ACE* label_ace =
+          static_cast<SYSTEM_MANDATORY_LABEL_ACE*>(ace_ptr);
+      sid = &label_ace->SidStart;
+      mask = label_ace->Mask;
+    } break;
+    default:
+      NOTREACHED();
+      break;
+  }
+
+  ASSERT_EQ(check_mask, mask);
+  ASSERT_EQ(check_sid, GetStringFromSid(sid));
+}
+
+TEST(SecurityDescriptor, NamedPipeDefault) {
+  const void* sec_desc = GetSecurityDescriptorForNamedPipeInstance(nullptr);
+
+  PACL acl;
+  BOOL acl_present;
+  BOOL acl_defaulted;
+  ASSERT_TRUE(GetSecurityDescriptorDacl(
+      const_cast<void*>(sec_desc), &acl_present, &acl, &acl_defaulted));
+  ASSERT_EQ(3, acl->AceCount);
+  CheckAce(acl, 0, ACCESS_ALLOWED_ACE_TYPE, GENERIC_ALL, GetUserSidString());
+  // Check SYSTEM user SID.
+  CheckAce(acl, 1, ACCESS_ALLOWED_ACE_TYPE, GENERIC_ALL, L"S-1-5-18");
+  // Check ALL APPLICATION PACKAGES group SID.
+  CheckAce(acl,
+           2,
+           ACCESS_ALLOWED_ACE_TYPE,
+           GENERIC_READ | GENERIC_WRITE,
+           L"S-1-15-2-1");
+
+  ASSERT_TRUE(GetSecurityDescriptorSacl(
+      const_cast<void*>(sec_desc), &acl_present, &acl, &acl_defaulted));
+  ASSERT_EQ(1, acl->AceCount);
+  CheckAce(acl, 0, SYSTEM_MANDATORY_LABEL_ACE_TYPE, 0, L"S-1-16-0");
+}
+
+TEST(SecurityDescriptor, MatchesAdvapi32) {
+  // This security descriptor is built manually in the connection code to avoid
+  // calling the advapi32 functions. Verify that it returns the same thing as
+  // ConvertStringSecurityDescriptorToSecurityDescriptor() would.
+
+  // Mandatory Label, no ACE flags, no ObjectType, integrity level
+  // untrusted.
+  static constexpr wchar_t kSddl[] = L"S:(ML;;;;;S-1-16-0)";
+  PSECURITY_DESCRIPTOR sec_desc;
+  ULONG sec_desc_len;
+  ASSERT_TRUE(ConvertStringSecurityDescriptorToSecurityDescriptor(
+      kSddl, SDDL_REVISION_1, &sec_desc, &sec_desc_len))
+      << ErrorMessage("ConvertStringSecurityDescriptorToSecurityDescriptor");
+  ScopedLocalAlloc sec_desc_owner(sec_desc);
+
+  size_t created_len;
+  const void* const created =
+      GetFallbackSecurityDescriptorForNamedPipeInstance(&created_len);
+  ASSERT_EQ(created_len, sec_desc_len);
+  EXPECT_EQ(memcmp(sec_desc, created, sec_desc_len), 0);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/safe_terminate_process.asm b/src/third_party/crashpad/util/win/safe_terminate_process.asm
new file mode 100644
index 0000000..b219a9e
--- /dev/null
+++ b/src/third_party/crashpad/util/win/safe_terminate_process.asm
@@ -0,0 +1,74 @@
+; Copyright 2017 The Crashpad Authors. All rights reserved.
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+; Detect ml64 assembling for x86_64 by checking for rax.
+ifdef rax
+_M_X64 equ 1
+else
+_M_IX86 equ 1
+endif
+
+ifdef _M_IX86
+.586
+.xmm
+.model flat
+
+includelib kernel32.lib
+
+extern __imp__TerminateProcess@8:proc
+
+; namespace crashpad {
+; bool SafeTerminateProcess(HANDLE process, UINT exit_code);
+; }  // namespace crashpad
+SAFETERMINATEPROCESS_SYMBOL equ ?SafeTerminateProcess@crashpad@@YA_NPAXI@Z
+
+_TEXT segment
+public SAFETERMINATEPROCESS_SYMBOL
+
+SAFETERMINATEPROCESS_SYMBOL proc
+
+  ; This function is written in assembler source because it’s important for it
+  ; to not be inlined, for it to allocate a stack frame, and most critically,
+  ; for it to not trust esp on return from TerminateProcess().
+  ; __declspec(noinline) can prevent inlining and #pragma optimize("y", off) can
+  ; disable frame pointer omission, but there’s no way to force a C compiler to
+  ; distrust esp, and even if there was a way, it’d probably be fragile.
+
+  push ebp
+  mov ebp, esp
+
+  push [ebp+12]
+  push [ebp+8]
+  call dword ptr [__imp__TerminateProcess@8]
+
+  ; Convert from BOOL to bool.
+  test eax, eax
+  setne al
+
+  ; TerminateProcess() is supposed to be stdcall (callee clean-up), and esp and
+  ; ebp are expected to already be equal. But if it’s been patched badly by
+  ; something that’s cdecl (caller clean-up), this next move will get things
+  ; back on track.
+  mov esp, ebp
+  pop ebp
+
+  ret
+
+SAFETERMINATEPROCESS_SYMBOL endp
+
+_TEXT ends
+
+endif
+
+end
diff --git a/src/third_party/crashpad/util/win/safe_terminate_process.h b/src/third_party/crashpad/util/win/safe_terminate_process.h
new file mode 100644
index 0000000..7079eb2
--- /dev/null
+++ b/src/third_party/crashpad/util/win/safe_terminate_process.h
@@ -0,0 +1,55 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_SAFE_TERMINATE_PROCESS_H_
+#define CRASHPAD_UTIL_WIN_SAFE_TERMINATE_PROCESS_H_
+
+#include <windows.h>
+
+#include "build/build_config.h"
+
+namespace crashpad {
+
+//! \brief Calls `TerminateProcess()`.
+//!
+//! `TerminateProcess()` has been observed in the wild as being patched badly on
+//! 32-bit x86: it’s patched with code adhering to the `cdecl` (caller clean-up)
+//! convention, although it’s supposed to be `stdcall` (callee clean-up). The
+//! mix-up means that neither caller nor callee perform parameter clean-up from
+//! the stack, causing the stack pointer to have an unexpected value on return
+//! from the patched function. This typically results in a crash shortly
+//! thereafter. See <a href="https://crashpad.chromium.org/bug/179">Crashpad bug
+//! 179</a>.
+//!
+//! On 32-bit x86, this replacement function calls `TerminateProcess()` without
+//! making any assumptions about the stack pointer on its return. As such, it’s
+//! compatible with the badly patched `cdecl` version as well as the native
+//! `stdcall` version (and other less badly patched versions).
+//!
+//! Elsewhere, this function calls `TerminateProcess()` directly without any
+//! additional fanfare.
+//!
+//! Call this function instead of `TerminateProcess()` anywhere that
+//! `TerminateProcess()` would normally be called.
+bool SafeTerminateProcess(HANDLE process, UINT exit_code);
+
+#if !defined(ARCH_CPU_X86)
+inline bool SafeTerminateProcess(HANDLE process, UINT exit_code) {
+  return TerminateProcess(process, exit_code) != FALSE;
+}
+#endif  // !ARCH_CPU_X86
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_SAFE_TERMINATE_PROCESS_H_
diff --git a/src/third_party/crashpad/util/win/safe_terminate_process_broken.cc b/src/third_party/crashpad/util/win/safe_terminate_process_broken.cc
new file mode 100644
index 0000000..2429c72
--- /dev/null
+++ b/src/third_party/crashpad/util/win/safe_terminate_process_broken.cc
@@ -0,0 +1,34 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/safe_terminate_process.h"
+
+#include "base/logging.h"
+
+#if defined(ARCH_CPU_X86)
+
+namespace crashpad {
+
+bool SafeTerminateProcess(HANDLE process, UINT exit_code) {
+  // Third-party software that hooks TerminateProcess() incorrectly has been
+  // encountered in the wild. This version of SafeTerminateProcess() lacks
+  // protection against that, so don't use it in production.
+  LOG(WARNING)
+      << "Don't use this! For cross builds only. See https://crbug.com/777924.";
+  return TerminateProcess(process, exit_code) != FALSE;
+}
+
+}  // namespace crashpad
+
+#endif  // defined(ARCH_CPU_X86)
diff --git a/src/third_party/crashpad/util/win/safe_terminate_process_test.cc b/src/third_party/crashpad/util/win/safe_terminate_process_test.cc
new file mode 100644
index 0000000..d2e4b6d
--- /dev/null
+++ b/src/third_party/crashpad/util/win/safe_terminate_process_test.cc
@@ -0,0 +1,186 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/safe_terminate_process.h"
+
+#include <string.h>
+
+#include <string>
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "build/build_config.h"
+#include "gtest/gtest.h"
+#include "test/errors.h"
+#include "test/test_paths.h"
+#include "test/win/child_launcher.h"
+#include "util/win/scoped_handle.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// Patches executable code, saving a copy of the original code so that it can be
+// restored on destruction.
+class ScopedExecutablePatch {
+ public:
+  ScopedExecutablePatch(void* target, const void* source, size_t size)
+      : original_(new uint8_t[size]), target_(target), size_(size) {
+    memcpy(original_.get(), target_, size_);
+
+    ScopedVirtualProtectRWX protect_rwx(target_, size_);
+    memcpy(target_, source, size_);
+  }
+
+  ~ScopedExecutablePatch() {
+    ScopedVirtualProtectRWX protect_rwx(target_, size_);
+    memcpy(target_, original_.get(), size_);
+  }
+
+ private:
+  // Sets the protection on (address, size) to PAGE_EXECUTE_READWRITE by calling
+  // VirtualProtect(), and restores the original protection on destruction. Note
+  // that the region may span multiple pages, but the first page’s original
+  // protection will be applied to the entire region on destruction. This
+  // shouldn’t be a problem in practice for patching a function for this test’s
+  // purposes.
+  class ScopedVirtualProtectRWX {
+   public:
+    // If either the constructor or destructor fails, PCHECK() to terminate
+    // immediately, because the process will be in a weird and untrustworthy
+    // state, and gtest error handling isn’t worthwhile at that point.
+
+    ScopedVirtualProtectRWX(void* address, size_t size)
+        : address_(address), size_(size) {
+      PCHECK(VirtualProtect(
+          address_, size_, PAGE_EXECUTE_READWRITE, &old_protect_))
+          << "VirtualProtect";
+    }
+
+    ~ScopedVirtualProtectRWX() {
+      DWORD last_protect_;
+      PCHECK(VirtualProtect(address_, size_, old_protect_, &last_protect_))
+          << "VirtualProtect";
+    }
+
+   private:
+    void* address_;
+    size_t size_;
+    DWORD old_protect_;
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedVirtualProtectRWX);
+  };
+
+  std::unique_ptr<uint8_t[]> original_;
+  void* target_;
+  size_t size_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedExecutablePatch);
+};
+
+// SafeTerminateProcess is calling convention specific only for x86.
+#if defined(ARCH_CPU_X86_FAMILY)
+TEST(SafeTerminateProcess, PatchBadly) {
+  // This is a test of SafeTerminateProcess(), but it doesn’t actually terminate
+  // anything. Instead, it works with a process handle for the current process
+  // that doesn’t have PROCESS_TERMINATE access. The whole point of this test is
+  // to patch the real TerminateProcess() badly with a cdecl implementation to
+  // ensure that SafeTerminateProcess() can recover from such gross misconduct.
+  // The actual termination isn’t relevant to this test.
+  //
+  // Notably, don’t duplicate the process handle with PROCESS_TERMINATE access
+  // or with the DUPLICATE_SAME_ACCESS option. The SafeTerminateProcess() calls
+  // that follow operate on a duplicate of the current process’ process handle,
+  // and they’re supposed to fail, not terminate this process.
+  HANDLE process;
+  ASSERT_TRUE(DuplicateHandle(GetCurrentProcess(),
+                              GetCurrentProcess(),
+                              GetCurrentProcess(),
+                              &process,
+                              PROCESS_QUERY_INFORMATION,
+                              false,
+                              0))
+      << ErrorMessage("DuplicateHandle");
+  ScopedKernelHANDLE process_owner(process);
+
+  // Make sure that TerminateProcess() works as a baseline.
+  SetLastError(ERROR_SUCCESS);
+  EXPECT_FALSE(TerminateProcess(process, 0));
+  EXPECT_EQ(GetLastError(), static_cast<DWORD>(ERROR_ACCESS_DENIED));
+
+  // Make sure that SafeTerminateProcess() works, calling through to
+  // TerminateProcess() properly.
+  SetLastError(ERROR_SUCCESS);
+  EXPECT_FALSE(SafeTerminateProcess(process, 0));
+  EXPECT_EQ(GetLastError(), static_cast<DWORD>(ERROR_ACCESS_DENIED));
+
+  {
+    // Patch TerminateProcess() badly. This turns it into a no-op that returns 0
+    // without cleaning up arguments from the stack, as a stdcall function is
+    // expected to do.
+    //
+    // This simulates the unexpected cdecl-patched TerminateProcess() as seen at
+    // https://crashpad.chromium.org/bug/179. In reality, this only affects
+    // 32-bit x86, as there’s no calling convention confusion on x86_64. It
+    // doesn’t hurt to run this test in the 64-bit environment, though.
+    static constexpr uint8_t patch[] = {
+#if defined(ARCH_CPU_X86)
+        0x31, 0xc0,  // xor eax, eax
+#elif defined(ARCH_CPU_X86_64)
+        0x48, 0x31, 0xc0,  // xor rax, rax
+#else
+#error Port
+#endif
+        0xc3,  // ret
+    };
+
+    void* target = reinterpret_cast<void*>(TerminateProcess);
+    ScopedExecutablePatch executable_patch(target, patch, base::size(patch));
+
+    // Make sure that SafeTerminateProcess() can be called. Since it’s been
+    // patched with a no-op stub, GetLastError() shouldn’t be modified.
+    SetLastError(ERROR_SUCCESS);
+    EXPECT_FALSE(SafeTerminateProcess(process, 0));
+    EXPECT_EQ(GetLastError(), static_cast<DWORD>(ERROR_SUCCESS));
+  }
+
+  // Now that the real TerminateProcess() has been restored, verify that it
+  // still works properly.
+  SetLastError(ERROR_SUCCESS);
+  EXPECT_FALSE(SafeTerminateProcess(process, 0));
+  EXPECT_EQ(GetLastError(), static_cast<DWORD>(ERROR_ACCESS_DENIED));
+}
+#endif  // ARCH_CPU_X86_FAMILY
+
+TEST(SafeTerminateProcess, TerminateChild) {
+  base::FilePath child_executable =
+      TestPaths::BuildArtifact(L"util",
+                               L"safe_terminate_process_test_child",
+                               TestPaths::FileType::kExecutable);
+  ChildLauncher child(child_executable, L"");
+  ASSERT_NO_FATAL_FAILURE(child.Start());
+
+  constexpr DWORD kExitCode = 0x51ee9d1e;  // Sort of like “sleep and die.”
+
+  ASSERT_TRUE(SafeTerminateProcess(child.process_handle(), kExitCode))
+      << ErrorMessage("TerminateProcess");
+  EXPECT_EQ(child.WaitForExit(), kExitCode);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/safe_terminate_process_test_child.cc b/src/third_party/crashpad/util/win/safe_terminate_process_test_child.cc
new file mode 100644
index 0000000..605778b
--- /dev/null
+++ b/src/third_party/crashpad/util/win/safe_terminate_process_test_child.cc
@@ -0,0 +1,22 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdlib.h>
+#include <wchar.h>
+#include <windows.h>
+
+int wmain(int argc, wchar_t** argv) {
+  Sleep(INFINITE);
+  return EXIT_FAILURE;
+}
diff --git a/src/third_party/crashpad/util/win/scoped_handle.cc b/src/third_party/crashpad/util/win/scoped_handle.cc
new file mode 100644
index 0000000..5e79e57
--- /dev/null
+++ b/src/third_party/crashpad/util/win/scoped_handle.cc
@@ -0,0 +1,36 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/scoped_handle.h"
+
+#include "base/logging.h"
+#include "util/file/file_io.h"
+
+namespace crashpad {
+namespace internal {
+
+void ScopedFileHANDLECloseTraits::Free(HANDLE handle) {
+  CheckedCloseFile(handle);
+}
+
+void ScopedKernelHANDLECloseTraits::Free(HANDLE handle) {
+  PCHECK(CloseHandle(handle)) << "CloseHandle";
+}
+
+void ScopedSearchHANDLECloseTraits::Free(HANDLE handle) {
+  PCHECK(FindClose(handle)) << "FindClose";
+}
+
+}  // namespace internal
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/scoped_handle.h b/src/third_party/crashpad/util/win/scoped_handle.h
new file mode 100644
index 0000000..a5893b8
--- /dev/null
+++ b/src/third_party/crashpad/util/win/scoped_handle.h
@@ -0,0 +1,52 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_SCOPED_HANDLE_H_
+#define CRASHPAD_UTIL_WIN_SCOPED_HANDLE_H_
+
+#include <windows.h>
+
+#include "base/scoped_generic.h"
+
+namespace crashpad {
+
+namespace internal {
+
+struct ScopedFileHANDLECloseTraits {
+  static HANDLE InvalidValue() { return INVALID_HANDLE_VALUE; }
+  static void Free(HANDLE handle);
+};
+
+struct ScopedKernelHANDLECloseTraits {
+  static HANDLE InvalidValue() { return nullptr; }
+  static void Free(HANDLE handle);
+};
+
+struct ScopedSearchHANDLECloseTraits {
+  static HANDLE InvalidValue() { return INVALID_HANDLE_VALUE; }
+  static void Free(HANDLE handle);
+};
+
+}  // namespace internal
+
+using ScopedFileHANDLE =
+    base::ScopedGeneric<HANDLE, internal::ScopedFileHANDLECloseTraits>;
+using ScopedKernelHANDLE =
+    base::ScopedGeneric<HANDLE, internal::ScopedKernelHANDLECloseTraits>;
+using ScopedSearchHANDLE =
+    base::ScopedGeneric<HANDLE, internal::ScopedSearchHANDLECloseTraits>;
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_SCOPED_HANDLE_H_
diff --git a/src/third_party/crashpad/util/win/scoped_local_alloc.cc b/src/third_party/crashpad/util/win/scoped_local_alloc.cc
new file mode 100644
index 0000000..75838bc
--- /dev/null
+++ b/src/third_party/crashpad/util/win/scoped_local_alloc.cc
@@ -0,0 +1,28 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/scoped_local_alloc.h"
+
+#include "base/logging.h"
+
+namespace crashpad {
+namespace internal {
+
+// static
+void LocalAllocTraits::Free(HLOCAL memory) {
+  PLOG_IF(ERROR, LocalFree(memory) != nullptr) << "LocalFree";
+}
+
+}  // namespace internal
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/scoped_local_alloc.h b/src/third_party/crashpad/util/win/scoped_local_alloc.h
new file mode 100644
index 0000000..b460785
--- /dev/null
+++ b/src/third_party/crashpad/util/win/scoped_local_alloc.h
@@ -0,0 +1,38 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_SCOPED_LOCAL_ALLOC_H_
+#define CRASHPAD_UTIL_WIN_SCOPED_LOCAL_ALLOC_H_
+
+#include <windows.h>
+
+#include "base/scoped_generic.h"
+
+namespace crashpad {
+
+namespace internal {
+
+struct LocalAllocTraits {
+  static HLOCAL InvalidValue() { return nullptr; }
+  static void Free(HLOCAL mem);
+};
+
+}  // namespace internal
+
+using ScopedLocalAlloc =
+    base::ScopedGeneric<HLOCAL, internal::LocalAllocTraits>;
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_SCOPED_LOCAL_ALLOC_H_
diff --git a/src/third_party/crashpad/util/win/scoped_process_suspend.cc b/src/third_party/crashpad/util/win/scoped_process_suspend.cc
new file mode 100644
index 0000000..721eb7f
--- /dev/null
+++ b/src/third_party/crashpad/util/win/scoped_process_suspend.cc
@@ -0,0 +1,49 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/scoped_process_suspend.h"
+
+#include <stddef.h>
+#include <winternl.h>
+
+#include "util/win/nt_internals.h"
+#include "util/win/ntstatus_logging.h"
+
+namespace crashpad {
+
+ScopedProcessSuspend::ScopedProcessSuspend(HANDLE process) {
+  NTSTATUS status = NtSuspendProcess(process);
+  if (NT_SUCCESS(status)) {
+    process_ = process;
+  } else {
+    process_ = nullptr;
+    NTSTATUS_LOG(ERROR, status) << "NtSuspendProcess";
+  }
+}
+
+ScopedProcessSuspend::~ScopedProcessSuspend() {
+  if (process_) {
+    NTSTATUS status = NtResumeProcess(process_);
+    if (!NT_SUCCESS(status) &&
+        (!tolerate_termination_ || status != STATUS_PROCESS_IS_TERMINATING)) {
+      NTSTATUS_LOG(ERROR, status) << "NtResumeProcess";
+    }
+  }
+}
+
+void ScopedProcessSuspend::TolerateTermination() {
+  tolerate_termination_ = true;
+}
+
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/scoped_process_suspend.h b/src/third_party/crashpad/util/win/scoped_process_suspend.h
new file mode 100644
index 0000000..913886e
--- /dev/null
+++ b/src/third_party/crashpad/util/win/scoped_process_suspend.h
@@ -0,0 +1,56 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_SCOPED_PROCESS_SUSPEND_H_
+#define CRASHPAD_UTIL_WIN_SCOPED_PROCESS_SUSPEND_H_
+
+#include <windows.h>
+
+#include "base/macros.h"
+
+namespace crashpad {
+
+//! \brief Manages the suspension of another process.
+//!
+//! While an object of this class exists, the other process will be suspended.
+//! Once the object is destroyed, the other process will become eligible for
+//! resumption.
+//!
+//! If this process crashes while this object exists, there is no guarantee that
+//! the other process will be resumed.
+class ScopedProcessSuspend {
+ public:
+  //! Does not take ownership of \a process.
+  explicit ScopedProcessSuspend(HANDLE process);
+  ~ScopedProcessSuspend();
+
+  //! \brief Informs the object that the suspended process may be terminating,
+  //!     and that this should not be treated as an error.
+  //!
+  //! Normally, attempting to resume a terminating process during destruction
+  //! results in an error message being logged for
+  //! `STATUS_PROCESS_IS_TERMINATING`. When it is known that a process may be
+  //! terminating, this method may be called to suppress that error message.
+  void TolerateTermination();
+
+ private:
+  HANDLE process_;
+  bool tolerate_termination_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedProcessSuspend);
+};
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_SCOPED_PROCESS_SUSPEND_H_
diff --git a/src/third_party/crashpad/util/win/scoped_process_suspend_test.cc b/src/third_party/crashpad/util/win/scoped_process_suspend_test.cc
new file mode 100644
index 0000000..2d0f5a0
--- /dev/null
+++ b/src/third_party/crashpad/util/win/scoped_process_suspend_test.cc
@@ -0,0 +1,119 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/scoped_process_suspend.h"
+
+#include <stddef.h>
+#include <tlhelp32.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "test/errors.h"
+#include "test/win/win_child_process.h"
+#include "util/win/xp_compat.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// There is no per-process suspend count on Windows, only a per-thread suspend
+// count. NtSuspendProcess just suspends all threads of a given process. So,
+// verify that all thread's suspend counts match the desired suspend count.
+bool SuspendCountMatches(HANDLE process, DWORD desired_suspend_count) {
+  DWORD process_id = GetProcessId(process);
+
+  ScopedKernelHANDLE snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));
+  if (!snapshot.is_valid()) {
+    ADD_FAILURE() << ErrorMessage("CreateToolhelp32Snapshot");
+    return false;
+  }
+
+  THREADENTRY32 te;
+  te.dwSize = sizeof(te);
+
+  BOOL ret = Thread32First(snapshot.get(), &te);
+  if (!ret) {
+    ADD_FAILURE() << ErrorMessage("Thread32First");
+    return false;
+  }
+  do {
+    if (te.dwSize >= offsetof(THREADENTRY32, th32OwnerProcessID) +
+                         sizeof(te.th32OwnerProcessID) &&
+        te.th32OwnerProcessID == process_id) {
+      ScopedKernelHANDLE thread(
+          OpenThread(kXPThreadAllAccess, false, te.th32ThreadID));
+      EXPECT_TRUE(thread.is_valid()) << ErrorMessage("OpenThread");
+      DWORD result = SuspendThread(thread.get());
+      EXPECT_NE(result, static_cast<DWORD>(-1))
+          << ErrorMessage("SuspendThread");
+      if (result != static_cast<DWORD>(-1)) {
+        EXPECT_NE(ResumeThread(thread.get()), static_cast<DWORD>(-1))
+            << ErrorMessage("ResumeThread");
+      }
+      if (result != desired_suspend_count)
+        return false;
+    }
+    te.dwSize = sizeof(te);
+  } while (Thread32Next(snapshot.get(), &te));
+
+  return true;
+}
+
+class ScopedProcessSuspendTest final : public WinChildProcess {
+ public:
+  ScopedProcessSuspendTest() : WinChildProcess() {}
+  ~ScopedProcessSuspendTest() {}
+
+ private:
+  int Run() override {
+    char c;
+    // Wait for notification from parent.
+    EXPECT_TRUE(LoggingReadFileExactly(ReadPipeHandle(), &c, sizeof(c)));
+    EXPECT_EQ(c, ' ');
+    return EXIT_SUCCESS;
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedProcessSuspendTest);
+};
+
+TEST(ScopedProcessSuspend, ScopedProcessSuspend) {
+  WinChildProcess::EntryPoint<ScopedProcessSuspendTest>();
+  std::unique_ptr<WinChildProcess::Handles> handles = WinChildProcess::Launch();
+
+  EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 0));
+
+  {
+    ScopedProcessSuspend suspend0(handles->process.get());
+    EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 1));
+
+    {
+      ScopedProcessSuspend suspend1(handles->process.get());
+      EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 2));
+    }
+
+    EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 1));
+  }
+
+  EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 0));
+
+  // Tell the child it's OK to terminate.
+  char c = ' ';
+  EXPECT_TRUE(WriteFile(handles->write.get(), &c, sizeof(c)));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/scoped_registry_key.h b/src/third_party/crashpad/util/win/scoped_registry_key.h
new file mode 100644
index 0000000..4393dc2
--- /dev/null
+++ b/src/third_party/crashpad/util/win/scoped_registry_key.h
@@ -0,0 +1,34 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_SCOPED_REGISTRY_KEY_H_
+#define CRASHPAD_UTIL_WIN_SCOPED_REGISTRY_KEY_H_
+
+#include <windows.h>
+
+#include "base/scoped_generic.h"
+
+namespace crashpad {
+
+struct ScopedRegistryKeyCloseTraits {
+  static HKEY InvalidValue() { return nullptr; }
+  static void Free(HKEY key) { RegCloseKey(key); }
+};
+
+using ScopedRegistryKey =
+    base::ScopedGeneric<HKEY, ScopedRegistryKeyCloseTraits>;
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_SCOPED_REGISTRY_KEY_H_
\ No newline at end of file
diff --git a/src/third_party/crashpad/util/win/scoped_set_event.cc b/src/third_party/crashpad/util/win/scoped_set_event.cc
new file mode 100644
index 0000000..7fb1647
--- /dev/null
+++ b/src/third_party/crashpad/util/win/scoped_set_event.cc
@@ -0,0 +1,40 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/scoped_set_event.h"
+
+#include "base/logging.h"
+
+namespace crashpad {
+
+ScopedSetEvent::ScopedSetEvent(HANDLE event) : event_(event) {
+  DCHECK(event_);
+}
+
+ScopedSetEvent::~ScopedSetEvent() {
+  if (event_) {
+    Set();
+  }
+}
+
+bool ScopedSetEvent::Set() {
+  bool rv = !!SetEvent(event_);
+  if (!rv) {
+    PLOG(ERROR) << "SetEvent";
+  }
+  event_ = nullptr;
+  return rv;
+}
+
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/scoped_set_event.h b/src/third_party/crashpad/util/win/scoped_set_event.h
new file mode 100644
index 0000000..82a1b31
--- /dev/null
+++ b/src/third_party/crashpad/util/win/scoped_set_event.h
@@ -0,0 +1,48 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_SCOPED_SET_EVENT_H_
+#define CRASHPAD_UTIL_WIN_SCOPED_SET_EVENT_H_
+
+#include <windows.h>
+
+#include "base/macros.h"
+
+namespace crashpad {
+
+//! \brief Calls `SetEvent()` on destruction at latest.
+//!
+//! Does not assume ownership of the event handle. Use ScopedKernelHANDLE for
+//! ownership.
+class ScopedSetEvent {
+ public:
+  explicit ScopedSetEvent(HANDLE event);
+  ~ScopedSetEvent();
+
+  //! \brief Calls `SetEvent()` immediately.
+  //!
+  //! `SetEvent()` will not be called on destruction.
+  //!
+  //! \return `true` on success, `false` on failure with a message logged.
+  bool Set();
+
+ private:
+  HANDLE event_;  // weak
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedSetEvent);
+};
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_SCOPED_SET_EVENT_H_
diff --git a/src/third_party/crashpad/util/win/session_end_watcher.cc b/src/third_party/crashpad/util/win/session_end_watcher.cc
new file mode 100644
index 0000000..1d47089
--- /dev/null
+++ b/src/third_party/crashpad/util/win/session_end_watcher.cc
@@ -0,0 +1,229 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/session_end_watcher.h"
+
+#include "base/logging.h"
+#include "base/scoped_generic.h"
+#include "util/win/scoped_set_event.h"
+
+extern "C" {
+extern IMAGE_DOS_HEADER __ImageBase;
+}  // extern "C"
+
+namespace crashpad {
+
+namespace {
+
+// ScopedWindowClass and ScopedWindow operate on ATOM* and HWND*, respectively,
+// instead of ATOM and HWND, so that the actual storage can exist as a local
+// variable or a member variable, and the scoper can be responsible for
+// releasing things only if the actual storage hasn’t been released and zeroed
+// already by something else.
+struct ScopedWindowClassTraits {
+  static ATOM* InvalidValue() { return nullptr; }
+  static void Free(ATOM* window_class) {
+    if (*window_class) {
+      if (!UnregisterClass(MAKEINTATOM(*window_class), 0)) {
+        PLOG(ERROR) << "UnregisterClass";
+      } else {
+        *window_class = 0;
+      }
+    }
+  }
+};
+using ScopedWindowClass = base::ScopedGeneric<ATOM*, ScopedWindowClassTraits>;
+
+struct ScopedWindowTraits {
+  static HWND* InvalidValue() { return nullptr; }
+  static void Free(HWND* window) {
+    if (*window) {
+      if (!DestroyWindow(*window)) {
+        PLOG(ERROR) << "DestroyWindow";
+      } else {
+        *window = nullptr;
+      }
+    }
+  }
+};
+using ScopedWindow = base::ScopedGeneric<HWND*, ScopedWindowTraits>;
+
+// GetWindowLongPtr()’s return value doesn’t unambiguously indicate whether it
+// was successful, because 0 could either represent successful retrieval of the
+// value 0, or failure. This wrapper is more convenient to use.
+bool GetWindowLongPtrAndSuccess(HWND window, int index, LONG_PTR* value) {
+  SetLastError(ERROR_SUCCESS);
+  *value = GetWindowLongPtr(window, index);
+  return *value || GetLastError() == ERROR_SUCCESS;
+}
+
+// SetWindowLongPtr() has the same problem as GetWindowLongPtr(). Use this
+// wrapper instead.
+bool SetWindowLongPtrAndGetSuccess(HWND window, int index, LONG_PTR value) {
+  SetLastError(ERROR_SUCCESS);
+  LONG_PTR previous = SetWindowLongPtr(window, index, value);
+  return previous || GetLastError() == ERROR_SUCCESS;
+}
+
+}  // namespace
+
+SessionEndWatcher::SessionEndWatcher()
+    : Thread(),
+      window_(nullptr),
+      started_(nullptr),
+      stopped_(nullptr) {
+  // Set bManualReset for these events so that WaitForStart() and WaitForStop()
+  // can be called multiple times.
+
+  started_.reset(CreateEvent(nullptr, true, false, nullptr));
+  PLOG_IF(ERROR, !started_.get()) << "CreateEvent";
+
+  stopped_.reset(CreateEvent(nullptr, true, false, nullptr));
+  PLOG_IF(ERROR, !stopped_.get()) << "CreateEvent";
+
+  Start();
+}
+
+SessionEndWatcher::~SessionEndWatcher() {
+  // Tear everything down by posting a WM_CLOSE to the window. This obviously
+  // can’t work until the window has been created, and that happens on a
+  // different thread, so wait for the start event to be signaled first.
+  WaitForStart();
+  if (window_) {
+    if (!PostMessage(window_, WM_CLOSE, 0, 0)) {
+      PLOG(ERROR) << "PostMessage";
+    }
+  }
+
+  Join();
+  DCHECK(!window_);
+}
+
+void SessionEndWatcher::WaitForStart() {
+  if (WaitForSingleObject(started_.get(), INFINITE) != WAIT_OBJECT_0) {
+    PLOG(ERROR) << "WaitForSingleObject";
+  }
+}
+
+void SessionEndWatcher::WaitForStop() {
+  if (WaitForSingleObject(stopped_.get(), INFINITE) != WAIT_OBJECT_0) {
+    PLOG(ERROR) << "WaitForSingleObject";
+  }
+}
+
+void SessionEndWatcher::ThreadMain() {
+  ATOM atom = 0;
+  ScopedWindowClass window_class(&atom);
+  ScopedWindow window(&window_);
+
+  ScopedSetEvent call_set_stop(stopped_.get());
+
+  {
+    ScopedSetEvent call_set_start(started_.get());
+
+    WNDCLASS wndclass = {};
+    wndclass.lpfnWndProc = WindowProc;
+    wndclass.hInstance = reinterpret_cast<HMODULE>(&__ImageBase);
+    wndclass.lpszClassName = L"crashpad_SessionEndWatcher";
+    atom = RegisterClass(&wndclass);
+    if (!atom) {
+      PLOG(ERROR) << "RegisterClass";
+      return;
+    }
+
+    window_ = CreateWindow(MAKEINTATOM(atom),  // lpClassName
+                           nullptr,  // lpWindowName
+                           0,  // dwStyle
+                           0,  // x
+                           0,  // y
+                           0,  // nWidth
+                           0,  // nHeight
+                           nullptr,  // hWndParent
+                           nullptr,  // hMenu
+                           nullptr,  // hInstance
+                           this);  // lpParam
+    if (!window_) {
+      PLOG(ERROR) << "CreateWindow";
+      return;
+    }
+  }
+
+  MSG message;
+  BOOL rv = 0;
+  while (window_ && (rv = GetMessage(&message, window_, 0, 0)) > 0) {
+    TranslateMessage(&message);
+    DispatchMessage(&message);
+  }
+  if (window_ && rv == -1) {
+    PLOG(ERROR) << "GetMessage";
+    return;
+  }
+}
+
+// static
+LRESULT CALLBACK SessionEndWatcher::WindowProc(HWND window,
+                                               UINT message,
+                                               WPARAM w_param,
+                                               LPARAM l_param) {
+  // Figure out which object this is. A pointer to it is stuffed into the last
+  // parameter of CreateWindow(), which shows up as CREATESTRUCT::lpCreateParams
+  // in a WM_CREATE message. That should be processed before any of the other
+  // messages of interest to this function. Once the object is known, save a
+  // pointer to it in the GWLP_USERDATA slot for later retrieval when processing
+  // other messages.
+  SessionEndWatcher* self;
+  if (!GetWindowLongPtrAndSuccess(
+          window, GWLP_USERDATA, reinterpret_cast<LONG_PTR*>(&self))) {
+    PLOG(ERROR) << "GetWindowLongPtr";
+  }
+  if (!self && message == WM_CREATE) {
+    CREATESTRUCT* create = reinterpret_cast<CREATESTRUCT*>(l_param);
+    self = reinterpret_cast<SessionEndWatcher*>(create->lpCreateParams);
+    if (!SetWindowLongPtrAndGetSuccess(
+            window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(self))) {
+      PLOG(ERROR) << "SetWindowLongPtr";
+    }
+  }
+
+  if (self) {
+    if (message == WM_ENDSESSION) {
+      // If w_param is false, this WM_ENDSESSION message cancels a previous
+      // WM_QUERYENDSESSION.
+      if (w_param) {
+        self->SessionEnding();
+
+        // If the session is ending, post a close message which will kick off
+        // window destruction and cause the message loop thread to terminate.
+        if (!PostMessage(self->window_, WM_CLOSE, 0, 0)) {
+          PLOG(ERROR) << "PostMessage";
+        }
+      }
+    } else if (message == WM_DESTROY) {
+      // The window is being destroyed. Clear GWLP_USERDATA so that |self| won’t
+      // be found during a subsequent call into this function for this window.
+      // Clear self->window_ too, because it refers to an object that soon won’t
+      // exist. That signals the message loop to stop processing messages.
+      if (!SetWindowLongPtrAndGetSuccess(window, GWLP_USERDATA, 0)) {
+        PLOG(ERROR) << "SetWindowLongPtr";
+      }
+      self->window_ = nullptr;
+    }
+  }
+
+  // If the message is WM_CLOSE, DefWindowProc() will call DestroyWindow(), and
+  // this function will be called again with a WM_DESTROY message.
+  return DefWindowProc(window, message, w_param, l_param);
+}
+
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/session_end_watcher.h b/src/third_party/crashpad/util/win/session_end_watcher.h
new file mode 100644
index 0000000..b23d391
--- /dev/null
+++ b/src/third_party/crashpad/util/win/session_end_watcher.h
@@ -0,0 +1,79 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_SESSION_END_WATCHER_H_
+#define CRASHPAD_UTIL_WIN_SESSION_END_WATCHER_H_
+
+#include <windows.h>
+
+#include "base/macros.h"
+#include "util/thread/thread.h"
+#include "util/win/scoped_handle.h"
+
+namespace crashpad {
+
+//! \brief Creates a hidden window and waits for a `WM_ENDSESSION` message,
+//!     indicating that the session is ending and the application should
+//!     terminate.
+//!
+//! A dedicated thread will be created to run the `GetMessage()`-based message
+//! loop required to monitor for this message.
+//!
+//! Users should subclass this class and receive notifications by implementing
+//! the SessionEndWatcherEvent() method.
+class SessionEndWatcher : public Thread {
+ public:
+  SessionEndWatcher();
+
+  //! \note The destructor waits for the thread that runs the message loop to
+  //!     terminate.
+  ~SessionEndWatcher() override;
+
+ protected:
+  // Exposed for testing.
+  HWND GetWindow() const { return window_; }
+
+  // Exposed for testing. Blocks until window_ has been created. May be called
+  // multiple times if necessary.
+  void WaitForStart();
+
+  // Exposed for testing. Blocks until the message loop ends. May be called
+  // multiple times if necessary.
+  void WaitForStop();
+
+ private:
+  // Thread:
+  void ThreadMain() override;
+
+  static LRESULT CALLBACK WindowProc(HWND window,
+                                     UINT message,
+                                     WPARAM w_param,
+                                     LPARAM l_param);
+
+  //! \brief A `WM_ENDSESSION` message was received and it indicates that the
+  //!     user session will be ending imminently.
+  //!
+  //! This method is called on the thread that runs the message loop.
+  virtual void SessionEnding() = 0;
+
+  HWND window_;  // Conceptually strong, but ownership managed in ThreadMain()
+  ScopedKernelHANDLE started_;
+  ScopedKernelHANDLE stopped_;
+
+  DISALLOW_COPY_AND_ASSIGN(SessionEndWatcher);
+};
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_SESSION_END_WATCHER_H_
diff --git a/src/third_party/crashpad/util/win/session_end_watcher_test.cc b/src/third_party/crashpad/util/win/session_end_watcher_test.cc
new file mode 100644
index 0000000..692d76e
--- /dev/null
+++ b/src/third_party/crashpad/util/win/session_end_watcher_test.cc
@@ -0,0 +1,62 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/session_end_watcher.h"
+
+#include "gtest/gtest.h"
+#include "test/errors.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+class SessionEndWatcherTest final : public SessionEndWatcher {
+ public:
+  SessionEndWatcherTest() : SessionEndWatcher(), called_(false) {}
+
+  ~SessionEndWatcherTest() override {}
+
+  void Run() {
+    WaitForStart();
+
+    HWND window = GetWindow();
+    ASSERT_TRUE(window);
+    EXPECT_TRUE(PostMessage(window, WM_ENDSESSION, 1, 0));
+
+    WaitForStop();
+
+    EXPECT_TRUE(called_);
+  }
+
+ private:
+  // SessionEndWatcher:
+  void SessionEnding() override { called_ = true; }
+
+  bool called_;
+
+  DISALLOW_COPY_AND_ASSIGN(SessionEndWatcherTest);
+};
+
+TEST(SessionEndWatcher, SessionEndWatcher) {
+  SessionEndWatcherTest test;
+  test.Run();
+}
+
+TEST(SessionEndWatcher, DoNothing) {
+  SessionEndWatcherTest test;
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/src/third_party/crashpad/util/win/termination_codes.h b/src/third_party/crashpad/util/win/termination_codes.h
new file mode 100644
index 0000000..ce7fdba
--- /dev/null
+++ b/src/third_party/crashpad/util/win/termination_codes.h
@@ -0,0 +1,37 @@
+// Copyright 2016 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_TERMINATION_CODES_H_
+#define CRASHPAD_UTIL_WIN_TERMINATION_CODES_H_
+
+namespace crashpad {
+
+//! \brief Crashpad-specific codes that are used as arguments to
+//!     SafeTerminateProcess() or `TerminateProcess()` in unusual circumstances.
+enum TerminationCodes : unsigned int {
+  //! \brief The crash handler did not respond, and the client self-terminated.
+  kTerminationCodeCrashNoDump = 0xffff7001,
+
+  //! \brief The initial process snapshot failed, so the correct client
+  //!     termination code could not be retrieved.
+  kTerminationCodeSnapshotFailed = 0xffff7002,
+
+  //! \brief A dump was requested for a client that was never registered with
+  //!     the crash handler.
+  kTerminationCodeNotConnectedToHandler = 0xffff7003,
+};
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_TERMINATION_CODES_H_
diff --git a/src/third_party/crashpad/util/win/xp_compat.h b/src/third_party/crashpad/util/win/xp_compat.h
new file mode 100644
index 0000000..1d4d4a0
--- /dev/null
+++ b/src/third_party/crashpad/util/win/xp_compat.h
@@ -0,0 +1,40 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_XP_COMPAT_H_
+#define CRASHPAD_UTIL_WIN_XP_COMPAT_H_
+
+#include <windows.h>
+
+namespace crashpad {
+
+enum {
+  //! \brief This is the XP-suitable value of `PROCESS_ALL_ACCESS`.
+  //!
+  //! Requesting `PROCESS_ALL_ACCESS` with the value defined when building
+  //! against a Vista+ SDK results in `ERROR_ACCESS_DENIED` when running on XP.
+  //! See https://msdn.microsoft.com/library/ms684880.aspx.
+  kXPProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF,
+
+  //! \brief This is the XP-suitable value of `THREAD_ALL_ACCESS`.
+  //!
+  //! Requesting `THREAD_ALL_ACCESS` with the value defined when building
+  //! against a Vista+ SDK results in `ERROR_ACCESS_DENIED` when running on XP.
+  //! See https://msdn.microsoft.com/library/ms686769.aspx.
+  kXPThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3FF,
+};
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_WIN_XP_COMPAT_H_