1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/check.h"
6
7 #include <optional>
8
9 #include "base/check_op.h"
10 #include "base/check_version_internal.h"
11 #include "base/debug/alias.h"
12 #include "base/debug/dump_without_crashing.h"
13 #include "base/logging.h"
14 #include "base/thread_annotations.h"
15 #include "base/types/cxx23_to_underlying.h"
16 #include "build/build_config.h"
17
18 #if BUILDFLAG(IS_NACL)
19 // Forward declaring this ptr for code simplicity below, we'll never dereference
20 // it under NaCl.
21 namespace base::debug {
22 class CrashKeyString;
23 } // namespace base::debug
24 #else
25 #include "base/debug/crash_logging.h"
26 #endif // !BUILDFLAG(IS_NACL)
27
28 namespace logging {
29
30 namespace {
31
GetDumpSeverity()32 LogSeverity GetDumpSeverity() {
33 #if defined(OFFICIAL_BUILD)
34 return DCHECK_IS_ON() ? LOGGING_DCHECK : LOGGING_ERROR;
35 #else
36 // Crash outside official builds (outside user-facing builds) to detect
37 // invariant violations early in release-build testing like fuzzing, etc.
38 // These should eventually be migrated to fatal CHECKs.
39 return LOGGING_FATAL;
40 #endif
41 }
42
GetNotFatalUntilSeverity(base::NotFatalUntil fatal_milestone)43 LogSeverity GetNotFatalUntilSeverity(base::NotFatalUntil fatal_milestone) {
44 if (fatal_milestone != base::NotFatalUntil::NoSpecifiedMilestoneInternal &&
45 base::to_underlying(fatal_milestone) <= BASE_CHECK_VERSION_INTERNAL) {
46 return LOGGING_FATAL;
47 }
48 return GetDumpSeverity();
49 }
50
GetCheckSeverity(base::NotFatalUntil fatal_milestone)51 LogSeverity GetCheckSeverity(base::NotFatalUntil fatal_milestone) {
52 // CHECKs are fatal unless `fatal_milestone` overrides it.
53 if (fatal_milestone == base::NotFatalUntil::NoSpecifiedMilestoneInternal) {
54 return LOGGING_FATAL;
55 }
56 return GetNotFatalUntilSeverity(fatal_milestone);
57 }
58
GetNotReachedCrashKey()59 base::debug::CrashKeyString* GetNotReachedCrashKey() {
60 #if BUILDFLAG(IS_NACL)
61 return nullptr;
62 #else
63 static auto* const key = ::base::debug::AllocateCrashKeyString(
64 "Logging-NOTREACHED_MESSAGE", base::debug::CrashKeySize::Size1024);
65 return key;
66 #endif // BUILDFLAG(IS_NACL)
67 }
68
GetDCheckCrashKey()69 base::debug::CrashKeyString* GetDCheckCrashKey() {
70 #if BUILDFLAG(IS_NACL)
71 return nullptr;
72 #else
73 static auto* const key = ::base::debug::AllocateCrashKeyString(
74 "Logging-DCHECK_MESSAGE", base::debug::CrashKeySize::Size1024);
75 return key;
76 #endif // BUILDFLAG(IS_NACL)
77 }
78
GetDumpWillBeCheckCrashKey()79 base::debug::CrashKeyString* GetDumpWillBeCheckCrashKey() {
80 #if BUILDFLAG(IS_NACL)
81 return nullptr;
82 #else
83 static auto* const key = ::base::debug::AllocateCrashKeyString(
84 "Logging-DUMP_WILL_BE_CHECK_MESSAGE",
85 base::debug::CrashKeySize::Size1024);
86 return key;
87 #endif // BUILDFLAG(IS_NACL)
88 }
89
90 #if !BUILDFLAG(IS_NACL)
GetFatalMilestoneCrashKey()91 base::debug::CrashKeyString* GetFatalMilestoneCrashKey() {
92 static auto* const key = ::base::debug::AllocateCrashKeyString(
93 "Logging-FATAL_MILESTONE", base::debug::CrashKeySize::Size32);
94 return key;
95 }
96 #endif // BUILDFLAG(IS_NACL)
97
MaybeSetFatalMilestoneCrashKey(base::NotFatalUntil fatal_milestone)98 void MaybeSetFatalMilestoneCrashKey(base::NotFatalUntil fatal_milestone) {
99 #if !BUILDFLAG(IS_NACL)
100 if (fatal_milestone == base::NotFatalUntil::NoSpecifiedMilestoneInternal) {
101 return;
102 }
103 base::debug::SetCrashKeyString(
104 GetFatalMilestoneCrashKey(),
105 base::NumberToString(base::to_underlying(fatal_milestone)));
106 #endif // BUILDFLAG(IS_NACL)
107 }
108
DumpWithoutCrashing(base::debug::CrashKeyString * message_key,const logging::LogMessage * log_message,const base::Location & location,base::NotFatalUntil fatal_milestone)109 void DumpWithoutCrashing(base::debug::CrashKeyString* message_key,
110 const logging::LogMessage* log_message,
111 const base::Location& location,
112 base::NotFatalUntil fatal_milestone) {
113 const std::string crash_string = log_message->BuildCrashString();
114 #if !BUILDFLAG(IS_NACL)
115 base::debug::ScopedCrashKeyString scoped_message_key(message_key,
116 crash_string);
117
118 MaybeSetFatalMilestoneCrashKey(fatal_milestone);
119 #endif // !BUILDFLAG(IS_NACL)
120 // Copy the crash message to stack memory to make sure it can be recovered in
121 // crash dumps. This is easier to recover in minidumps than crash keys during
122 // local debugging.
123 DEBUG_ALIAS_FOR_CSTR(log_message_str, crash_string.c_str(), 1024);
124
125 // Report from the same location at most once every 30 days (unless the
126 // process has died). This attempts to prevent us from flooding ourselves with
127 // repeat reports for the same bug.
128 base::debug::DumpWithoutCrashing(location, base::Days(30));
129
130 #if !BUILDFLAG(IS_NACL)
131 base::debug::ClearCrashKeyString(GetFatalMilestoneCrashKey());
132 #endif // !BUILDFLAG(IS_NACL)
133 }
134
HandleCheckErrorLogMessage(base::debug::CrashKeyString * message_key,const logging::LogMessage * log_message,const base::Location & location,base::NotFatalUntil fatal_milestone)135 void HandleCheckErrorLogMessage(base::debug::CrashKeyString* message_key,
136 const logging::LogMessage* log_message,
137 const base::Location& location,
138 base::NotFatalUntil fatal_milestone) {
139 if (log_message->severity() == logging::LOGGING_FATAL) {
140 // Set NotFatalUntil key if applicable for when we die in ~LogMessage.
141 MaybeSetFatalMilestoneCrashKey(fatal_milestone);
142 } else {
143 DumpWithoutCrashing(message_key, log_message, location, fatal_milestone);
144 }
145 }
146
147 class NotReachedLogMessage : public LogMessage {
148 public:
NotReachedLogMessage(const base::Location & location,LogSeverity severity,base::NotFatalUntil fatal_milestone)149 NotReachedLogMessage(const base::Location& location,
150 LogSeverity severity,
151 base::NotFatalUntil fatal_milestone)
152 : LogMessage(location.file_name(), location.line_number(), severity),
153 location_(location),
154 fatal_milestone_(fatal_milestone) {}
~NotReachedLogMessage()155 ~NotReachedLogMessage() override {
156 HandleCheckErrorLogMessage(GetNotReachedCrashKey(), this, location_,
157 fatal_milestone_);
158 }
159
160 private:
161 const base::Location location_;
162 const base::NotFatalUntil fatal_milestone_;
163 };
164
165 class DCheckLogMessage : public LogMessage {
166 public:
DCheckLogMessage(const base::Location & location)167 DCheckLogMessage(const base::Location& location)
168 : LogMessage(location.file_name(),
169 location.line_number(),
170 LOGGING_DCHECK),
171 location_(location) {}
~DCheckLogMessage()172 ~DCheckLogMessage() override {
173 HandleCheckErrorLogMessage(
174 GetDCheckCrashKey(), this, location_,
175 base::NotFatalUntil::NoSpecifiedMilestoneInternal);
176 }
177
178 private:
179 const base::Location location_;
180 };
181
182 class CheckLogMessage : public LogMessage {
183 public:
CheckLogMessage(const base::Location & location,LogSeverity severity,base::NotFatalUntil fatal_milestone)184 CheckLogMessage(const base::Location& location,
185 LogSeverity severity,
186 base::NotFatalUntil fatal_milestone)
187 : LogMessage(location.file_name(), location.line_number(), severity),
188 location_(location),
189 fatal_milestone_(fatal_milestone) {}
~CheckLogMessage()190 ~CheckLogMessage() override {
191 HandleCheckErrorLogMessage(GetDumpWillBeCheckCrashKey(), this, location_,
192 fatal_milestone_);
193 }
194
195 private:
196 const base::Location location_;
197 const base::NotFatalUntil fatal_milestone_;
198 };
199
200 #if BUILDFLAG(IS_WIN)
201 class DCheckWin32ErrorLogMessage : public Win32ErrorLogMessage {
202 public:
DCheckWin32ErrorLogMessage(const base::Location & location,SystemErrorCode err)203 DCheckWin32ErrorLogMessage(const base::Location& location,
204 SystemErrorCode err)
205 : Win32ErrorLogMessage(location.file_name(),
206 location.line_number(),
207 LOGGING_DCHECK,
208 err),
209 location_(location) {}
~DCheckWin32ErrorLogMessage()210 ~DCheckWin32ErrorLogMessage() override {
211 HandleCheckErrorLogMessage(
212 GetDCheckCrashKey(), this, location_,
213 base::NotFatalUntil::NoSpecifiedMilestoneInternal);
214 }
215
216 private:
217 const base::Location location_;
218 };
219 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
220 class DCheckErrnoLogMessage : public ErrnoLogMessage {
221 public:
DCheckErrnoLogMessage(const base::Location & location,SystemErrorCode err)222 DCheckErrnoLogMessage(const base::Location& location, SystemErrorCode err)
223 : ErrnoLogMessage(location.file_name(),
224 location.line_number(),
225 LOGGING_DCHECK,
226 err),
227 location_(location) {}
~DCheckErrnoLogMessage()228 ~DCheckErrnoLogMessage() override {
229 HandleCheckErrorLogMessage(
230 GetDCheckCrashKey(), this, location_,
231 base::NotFatalUntil::NoSpecifiedMilestoneInternal);
232 }
233
234 private:
235 const base::Location location_;
236 };
237 #endif // BUILDFLAG(IS_WIN)
238
239 } // namespace
240
Check(const char * condition,base::NotFatalUntil fatal_milestone,const base::Location & location)241 CheckError CheckError::Check(const char* condition,
242 base::NotFatalUntil fatal_milestone,
243 const base::Location& location) {
244 auto* const log_message = new CheckLogMessage(
245 location, GetCheckSeverity(fatal_milestone), fatal_milestone);
246 log_message->stream() << "Check failed: " << condition << ". ";
247 return CheckError(log_message);
248 }
249
CheckOp(char * log_message_str,base::NotFatalUntil fatal_milestone,const base::Location & location)250 CheckError CheckError::CheckOp(char* log_message_str,
251 base::NotFatalUntil fatal_milestone,
252 const base::Location& location) {
253 auto* const log_message = new CheckLogMessage(
254 location, GetCheckSeverity(fatal_milestone), fatal_milestone);
255 log_message->stream() << log_message_str;
256 free(log_message_str);
257 return CheckError(log_message);
258 }
259
DCheck(const char * condition,const base::Location & location)260 CheckError CheckError::DCheck(const char* condition,
261 const base::Location& location) {
262 auto* const log_message = new DCheckLogMessage(location);
263 log_message->stream() << "Check failed: " << condition << ". ";
264 return CheckError(log_message);
265 }
266
DCheckOp(char * log_message_str,const base::Location & location)267 CheckError CheckError::DCheckOp(char* log_message_str,
268 const base::Location& location) {
269 auto* const log_message = new DCheckLogMessage(location);
270 log_message->stream() << log_message_str;
271 free(log_message_str);
272 return CheckError(log_message);
273 }
274
DumpWillBeCheck(const char * condition,const base::Location & location)275 CheckError CheckError::DumpWillBeCheck(const char* condition,
276 const base::Location& location) {
277 auto* const log_message =
278 new CheckLogMessage(location, GetDumpSeverity(),
279 base::NotFatalUntil::NoSpecifiedMilestoneInternal);
280 log_message->stream() << "Check failed: " << condition << ". ";
281 return CheckError(log_message);
282 }
283
DumpWillBeCheckOp(char * log_message_str,const base::Location & location)284 CheckError CheckError::DumpWillBeCheckOp(char* log_message_str,
285 const base::Location& location) {
286 auto* const log_message =
287 new CheckLogMessage(location, GetDumpSeverity(),
288 base::NotFatalUntil::NoSpecifiedMilestoneInternal);
289 log_message->stream() << log_message_str;
290 free(log_message_str);
291 return CheckError(log_message);
292 }
293
DPCheck(const char * condition,const base::Location & location)294 CheckError CheckError::DPCheck(const char* condition,
295 const base::Location& location) {
296 SystemErrorCode err_code = logging::GetLastSystemErrorCode();
297 #if BUILDFLAG(IS_WIN)
298 auto* const log_message = new DCheckWin32ErrorLogMessage(location, err_code);
299 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
300 auto* const log_message = new DCheckErrnoLogMessage(location, err_code);
301 #endif
302 log_message->stream() << "Check failed: " << condition << ". ";
303 return CheckError(log_message);
304 }
305
NotImplemented(const char * function,const base::Location & location)306 CheckError CheckError::NotImplemented(const char* function,
307 const base::Location& location) {
308 auto* const log_message = new LogMessage(
309 location.file_name(), location.line_number(), LOGGING_ERROR);
310 log_message->stream() << "Not implemented reached in " << function;
311 return CheckError(log_message);
312 }
313
stream()314 std::ostream& CheckError::stream() {
315 return log_message_->stream();
316 }
317
~CheckError()318 CheckError::~CheckError() {
319 // TODO(crbug.com/40254046): Consider splitting out CHECK from DCHECK so that
320 // the destructor can be marked [[noreturn]] and we don't need to check
321 // severity in the destructor.
322 const bool is_fatal = log_message_->severity() == LOGGING_FATAL;
323 // Note: This function ends up in crash stack traces. If its full name
324 // changes, the crash server's magic signature logic needs to be updated.
325 // See cl/306632920.
326
327 // Reset before `ImmediateCrash()` to ensure the message is flushed.
328 log_message_.reset();
329
330 // Make sure we crash even if LOG(FATAL) has been overridden.
331 // TODO(crbug.com/40254046): Remove severity checking in the destructor when
332 // LOG(FATAL) is [[noreturn]] and can't be overridden.
333 if (is_fatal) {
334 base::ImmediateCrash();
335 }
336 }
337
CheckError(LogMessage * log_message)338 CheckError::CheckError(LogMessage* log_message) : log_message_(log_message) {}
339
340 // Note: This function ends up in crash stack traces. If its full name changes,
341 // the crash server's magic signature logic needs to be updated. See
342 // cl/306632920.
~CheckNoreturnError()343 CheckNoreturnError::~CheckNoreturnError() {
344 // Reset before `ImmediateCrash()` to ensure the message is flushed.
345 log_message_.reset();
346
347 // Make sure we die if we haven't.
348 // TODO(crbug.com/40254046): Replace this with NOTREACHED() once LOG(FATAL) is
349 // [[noreturn]].
350 base::ImmediateCrash();
351 }
352
Check(const char * condition,const base::Location & location)353 CheckNoreturnError CheckNoreturnError::Check(const char* condition,
354 const base::Location& location) {
355 auto* const log_message =
356 new CheckLogMessage(location, LOGGING_FATAL,
357 base::NotFatalUntil::NoSpecifiedMilestoneInternal);
358 log_message->stream() << "Check failed: " << condition << ". ";
359 return CheckNoreturnError(log_message);
360 }
361
CheckOp(char * log_message_str,const base::Location & location)362 CheckNoreturnError CheckNoreturnError::CheckOp(char* log_message_str,
363 const base::Location& location) {
364 auto* const log_message =
365 new CheckLogMessage(location, LOGGING_FATAL,
366 base::NotFatalUntil::NoSpecifiedMilestoneInternal);
367 log_message->stream() << log_message_str;
368 free(log_message_str);
369 return CheckNoreturnError(log_message);
370 }
371
PCheck(const char * condition,const base::Location & location)372 CheckNoreturnError CheckNoreturnError::PCheck(const char* condition,
373 const base::Location& location) {
374 SystemErrorCode err_code = logging::GetLastSystemErrorCode();
375 #if BUILDFLAG(IS_WIN)
376 auto* const log_message = new Win32ErrorLogMessage(
377 location.file_name(), location.line_number(), LOGGING_FATAL, err_code);
378 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
379 auto* const log_message = new ErrnoLogMessage(
380 location.file_name(), location.line_number(), LOGGING_FATAL, err_code);
381 #endif
382 log_message->stream() << "Check failed: " << condition << ". ";
383 return CheckNoreturnError(log_message);
384 }
385
PCheck(const base::Location & location)386 CheckNoreturnError CheckNoreturnError::PCheck(const base::Location& location) {
387 return PCheck("", location);
388 }
389
NotReached(base::NotFatalUntil fatal_milestone,const base::Location & location)390 NotReachedError NotReachedError::NotReached(base::NotFatalUntil fatal_milestone,
391 const base::Location& location) {
392 auto* const log_message = new NotReachedLogMessage(
393 location, GetCheckSeverity(fatal_milestone), fatal_milestone);
394
395 // TODO(pbos): Consider a better message for NotReached(), this is here to
396 // match existing behavior + test expectations.
397 log_message->stream() << "Check failed: false. ";
398 return NotReachedError(log_message);
399 }
400
DumpWillBeNotReached(const base::Location & location)401 NotReachedError NotReachedError::DumpWillBeNotReached(
402 const base::Location& location) {
403 auto* const log_message = new NotReachedLogMessage(
404 location, GetDumpSeverity(),
405 base::NotFatalUntil::NoSpecifiedMilestoneInternal);
406 log_message->stream() << "NOTREACHED hit. ";
407 return NotReachedError(log_message);
408 }
409
410 NotReachedError::~NotReachedError() = default;
411
NotReachedNoreturnError(const base::Location & location)412 NotReachedNoreturnError::NotReachedNoreturnError(const base::Location& location)
413 : CheckError([location] {
414 auto* const log_message = new NotReachedLogMessage(
415 location, LOGGING_FATAL,
416 base::NotFatalUntil::NoSpecifiedMilestoneInternal);
417 log_message->stream() << "NOTREACHED hit. ";
418 return log_message;
419 }()) {}
420
421 // Note: This function ends up in crash stack traces. If its full name changes,
422 // the crash server's magic signature logic needs to be updated. See
423 // cl/306632920.
~NotReachedNoreturnError()424 NotReachedNoreturnError::~NotReachedNoreturnError() {
425 // Reset before `ImmediateCrash()` to ensure the message is flushed.
426 log_message_.reset();
427
428 // Make sure we die if we haven't.
429 // TODO(crbug.com/40254046): Replace this with NOTREACHED() once LOG(FATAL) is
430 // [[noreturn]].
431 base::ImmediateCrash();
432 }
433
RawCheckFailure(const char * message)434 void RawCheckFailure(const char* message) {
435 RawLog(LOGGING_FATAL, message);
436 __builtin_unreachable();
437 }
438
439 } // namespace logging
440