• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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