• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
2 // 2016 The Chromium Authors. All rights reserved. Use of this source code is
3 // governed by a BSD-style license that can be found in the LICENSE file.
4 
5 #include "libcef/common/crash_reporting.h"
6 
7 #include "include/cef_crash_util.h"
8 #include "libcef/common/cef_switches.h"
9 #include "libcef/features/runtime.h"
10 
11 #include "base/base_switches.h"
12 #include "base/command_line.h"
13 #include "base/debug/crash_logging.h"
14 #include "base/logging.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_piece.h"
17 #include "base/strings/string_util.h"
18 #include "chrome/common/crash_keys.h"
19 #include "components/crash/core/common/crash_key.h"
20 #include "components/crash/core/common/crash_keys.h"
21 #include "content/public/common/content_switches.h"
22 
23 #if BUILDFLAG(IS_MAC)
24 #include "base/mac/foundation_util.h"
25 #include "components/crash/core/app/crashpad.h"
26 #include "components/crash/core/common/crash_keys.h"
27 #include "content/public/common/content_paths.h"
28 #endif
29 
30 #if BUILDFLAG(IS_POSIX)
31 #include "base/lazy_instance.h"
32 #include "libcef/common/crash_reporter_client.h"
33 #endif
34 
35 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC)
36 #include "components/crash/core/app/breakpad_linux.h"
37 #include "v8/include/v8-wasm-trap-handler-posix.h"
38 #endif
39 
40 namespace crash_reporting {
41 
42 namespace {
43 
44 #if BUILDFLAG(IS_WIN)
45 
46 const base::FilePath::CharType kChromeElfDllName[] =
47     FILE_PATH_LITERAL("chrome_elf.dll");
48 
49 // exported in crash_reporter_client.cc:
50 //    int __declspec(dllexport) __cdecl SetCrashKeyValueImpl.
51 typedef int(__cdecl* SetCrashKeyValue)(const char*,
52                                        size_t,
53                                        const char*,
54                                        size_t);
55 
56 //    int __declspec(dllexport) __cdecl IsCrashReportingEnabledImpl.
57 typedef int(__cdecl* IsCrashReportingEnabled)();
58 
SetCrashKeyValueTrampoline(const base::StringPiece & key,const base::StringPiece & value)59 bool SetCrashKeyValueTrampoline(const base::StringPiece& key,
60                                 const base::StringPiece& value) {
61   static SetCrashKeyValue set_crash_key = []() {
62     HMODULE elf_module = GetModuleHandle(kChromeElfDllName);
63     return reinterpret_cast<SetCrashKeyValue>(
64         elf_module ? GetProcAddress(elf_module, "SetCrashKeyValueImpl")
65                    : nullptr);
66   }();
67   if (set_crash_key) {
68     return !!(set_crash_key)(key.data(), key.size(), value.data(),
69                              value.size());
70   }
71   return false;
72 }
73 
IsCrashReportingEnabledTrampoline()74 bool IsCrashReportingEnabledTrampoline() {
75   static IsCrashReportingEnabled is_crash_reporting_enabled = []() {
76     HMODULE elf_module = GetModuleHandle(kChromeElfDllName);
77     return reinterpret_cast<IsCrashReportingEnabled>(
78         elf_module ? GetProcAddress(elf_module, "IsCrashReportingEnabledImpl")
79                    : nullptr);
80   }();
81   if (is_crash_reporting_enabled) {
82     return !!(is_crash_reporting_enabled)();
83   }
84   return false;
85 }
86 
87 #endif  // BUILDFLAG(IS_WIN)
88 
89 bool g_crash_reporting_enabled = false;
90 
91 #if BUILDFLAG(IS_POSIX)
92 base::LazyInstance<CefCrashReporterClient>::Leaky g_crash_reporter_client =
93     LAZY_INSTANCE_INITIALIZER;
94 
InitCrashReporter(const base::CommandLine & command_line,const std::string & process_type)95 void InitCrashReporter(const base::CommandLine& command_line,
96                        const std::string& process_type) {
97   CefCrashReporterClient* crash_client = g_crash_reporter_client.Pointer();
98   if (!crash_client->HasCrashConfigFile())
99     return;
100 
101   crash_reporter::SetCrashReporterClient(crash_client);
102 
103 #if BUILDFLAG(IS_MAC)
104   // TODO(mark): Right now, InitializeCrashpad() needs to be called after
105   // CommandLine::Init() and configuration of chrome::DIR_CRASH_DUMPS. Ideally,
106   // Crashpad initialization could occur sooner, preferably even before the
107   // framework dylib is even loaded, to catch potential early crashes.
108   crash_reporter::InitializeCrashpad(process_type.empty(), process_type);
109 
110   if (base::mac::AmIBundled()) {
111     // Mac Chrome is packaged with a main app bundle and a helper app bundle.
112     // The main app bundle should only be used for the browser process, so it
113     // should never see a --type switch (switches::kProcessType).  Likewise,
114     // the helper should always have a --type switch.
115     //
116     // This check is done this late so there is already a call to
117     // base::mac::IsBackgroundOnlyProcess(), so there is no change in
118     // startup/initialization order.
119 
120     // The helper's Info.plist marks it as a background only app.
121     if (base::mac::IsBackgroundOnlyProcess()) {
122       CHECK(command_line.HasSwitch(switches::kProcessType) &&
123             !process_type.empty())
124           << "Helper application requires --type.";
125     } else {
126       CHECK(!command_line.HasSwitch(switches::kProcessType) &&
127             process_type.empty())
128           << "Main application forbids --type, saw " << process_type;
129     }
130   }
131 
132   g_crash_reporting_enabled = true;
133 #else   // !BUILDFLAG(IS_MAC)
134 
135   if (process_type != switches::kZygoteProcess) {
136     // Crash reporting for subprocesses created using the zygote will be
137     // initialized in ZygoteForked.
138     breakpad::InitCrashReporter(process_type);
139 
140     g_crash_reporting_enabled = true;
141   }
142 #endif  // !BUILDFLAG(IS_MAC)
143 }
144 #endif  // BUILDFLAG(IS_POSIX)
145 
146 // Used to exclude command-line flags from crash reporting.
IsBoringCEFSwitch(const std::string & flag)147 bool IsBoringCEFSwitch(const std::string& flag) {
148   if (crash_keys::IsBoringChromeSwitch(flag))
149     return true;
150 
151   static const char* const kIgnoreSwitches[] = {
152       // CEF internals.
153       switches::kLogFile,
154 
155       // Chromium internals.
156       "content-image-texture-target",
157       "mojo-platform-channel-handle",
158       "primordial-pipe-token",
159       "service-pipe-token",
160       "service-request-channel-token",
161   };
162 
163   if (!base::StartsWith(flag, "--", base::CompareCase::SENSITIVE))
164     return false;
165 
166   size_t end = flag.find("=");
167   size_t len = (end == std::string::npos) ? flag.length() - 2 : end - 2;
168   for (size_t i = 0; i < base::size(kIgnoreSwitches); ++i) {
169     if (flag.compare(2, len, kIgnoreSwitches[i]) == 0)
170       return true;
171   }
172   return false;
173 }
174 
175 }  // namespace
176 
Enabled()177 bool Enabled() {
178   return g_crash_reporting_enabled;
179 }
180 
SetCrashKeyValue(const base::StringPiece & key,const base::StringPiece & value)181 bool SetCrashKeyValue(const base::StringPiece& key,
182                       const base::StringPiece& value) {
183   if (!g_crash_reporting_enabled)
184     return false;
185 
186 #if BUILDFLAG(IS_WIN)
187   return SetCrashKeyValueTrampoline(key, value);
188 #else
189   return g_crash_reporter_client.Pointer()->SetCrashKeyValue(key, value);
190 #endif
191 }
192 
193 #if BUILDFLAG(IS_POSIX)
194 // Be aware that logging is not initialized at the time this method is called.
BasicStartupComplete(base::CommandLine * command_line)195 void BasicStartupComplete(base::CommandLine* command_line) {
196   CefCrashReporterClient* crash_client = g_crash_reporter_client.Pointer();
197   if (crash_client->ReadCrashConfigFile()) {
198 #if !BUILDFLAG(IS_MAC)
199     // Breakpad requires this switch.
200     command_line->AppendSwitch(switches::kEnableCrashReporter);
201 
202     breakpad::SetFirstChanceExceptionHandler(v8::TryHandleWebAssemblyTrapPosix);
203 #endif
204   }
205 }
206 #endif
207 
PreSandboxStartup(const base::CommandLine & command_line,const std::string & process_type)208 void PreSandboxStartup(const base::CommandLine& command_line,
209                        const std::string& process_type) {
210 #if BUILDFLAG(IS_POSIX)
211   // Initialize crash reporting here on macOS and Linux. Crash reporting on
212   // Windows is initialized from context.cc.
213   InitCrashReporter(command_line, process_type);
214 #elif BUILDFLAG(IS_WIN)
215   g_crash_reporting_enabled = IsCrashReportingEnabledTrampoline();
216 #endif
217 
218   if (g_crash_reporting_enabled) {
219     LOG(INFO) << "Crash reporting enabled for process: "
220               << (process_type.empty() ? "browser" : process_type.c_str());
221   }
222 
223   crash_reporter::InitializeCrashKeys();
224 
225   // After platform crash reporting have been initialized, store the command
226   // line for crash reporting.
227   crash_keys::SetSwitchesFromCommandLine(command_line, &IsBoringCEFSwitch);
228 }
229 
230 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_MAC)
ZygoteForked(base::CommandLine * command_line,const std::string & process_type)231 void ZygoteForked(base::CommandLine* command_line,
232                   const std::string& process_type) {
233   CefCrashReporterClient* crash_client = g_crash_reporter_client.Pointer();
234   if (crash_client->HasCrashConfigFile()) {
235     // Breakpad requires this switch.
236     command_line->AppendSwitch(switches::kEnableCrashReporter);
237   }
238 
239   InitCrashReporter(*command_line, process_type);
240 
241   if (g_crash_reporting_enabled) {
242     LOG(INFO) << "Crash reporting enabled for process: " << process_type;
243   }
244 
245   // Reset the command line for the newly spawned process.
246   crash_keys::SetSwitchesFromCommandLine(*command_line, &IsBoringCEFSwitch);
247 }
248 #endif
249 
250 }  // namespace crash_reporting
251 
CefCrashReportingEnabled()252 bool CefCrashReportingEnabled() {
253   return crash_reporting::Enabled();
254 }
255 
CefSetCrashKeyValue(const CefString & key,const CefString & value)256 void CefSetCrashKeyValue(const CefString& key, const CefString& value) {
257   if (!crash_reporting::SetCrashKeyValue(key.ToString(), value.ToString())) {
258     LOG(WARNING) << "Failed to set crash key: " << key.ToString()
259                  << " with value: " << value.ToString();
260   }
261 }
262 
263 // From libcef/features/runtime.h:
264 namespace cef {
265 
IsCrashReportingEnabled()266 bool IsCrashReportingEnabled() {
267   return crash_reporting::Enabled();
268 }
269 
270 }  // namespace cef
271