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