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