blob: 1ba59b08569223212f82428a2b631a6fe6af9c97 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2019 The Chromium Authors
Daniel Chenged0471b2019-05-10 11:43:362// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef BASE_IMMEDIATE_CRASH_H_
6#define BASE_IMMEDIATE_CRASH_H_
7
Adrian Taylorab8f3572023-12-01 20:57:188#include "base/fuzzing_buildflags.h"
Daniel Chenged0471b2019-05-10 11:43:369#include "build/build_config.h"
10
Brendon Tiszka254cb732024-09-27 15:17:0811#if !(defined(OFFICIAL_BUILD) || BUILDFLAG(IS_WIN))
Adrian Taylorab8f3572023-12-01 20:57:1812#include <stdlib.h>
Brendon Tiszka254cb732024-09-27 15:17:0813#endif
Adrian Taylorc54d1972023-12-05 22:10:0214
Brendon Tiszka254cb732024-09-27 15:17:0815#if BUILDFLAG(USE_FUZZING_ENGINE) && BUILDFLAG(IS_LINUX)
Adrian Taylorc54d1972023-12-05 22:10:0216// The fuzzing coverage display wants to record coverage even
17// for failure cases. It's Linux-only. So on Linux, dump coverage
18// before we immediately exit. We provide a weak symbol so that
19// this causes no link problems on configurations that don't involve
Brendon Tiszka254cb732024-09-27 15:17:0820// coverage. (This wouldn't work on Windows due to limitations of
21// weak symbol linkage.)
Paul Semel5ad5f1642023-12-12 11:15:5822extern "C" int __attribute__((weak)) __llvm_profile_write_file(void);
Brendon Tiszka254cb732024-09-27 15:17:0823#endif // BUILDFLAG(USE_FUZZING_ENGINE) && BUILDFLAG(IS_LINUX)
Adrian Taylorab8f3572023-12-01 20:57:1824
Daniel Chenged0471b2019-05-10 11:43:3625// Crashes in the fastest possible way with no attempt at logging.
Daniel Cheng69359e92019-06-20 23:43:0226// There are several constraints; see http://crbug.com/664209 for more context.
27//
28// - TRAP_SEQUENCE_() must be fatal. It should not be possible to ignore the
29// resulting exception or simply hit 'continue' to skip over it in a debugger.
30// - Different instances of TRAP_SEQUENCE_() must not be folded together, to
31// ensure crash reports are debuggable. Unlike __builtin_trap(), asm volatile
32// blocks will not be folded together.
33// Note: TRAP_SEQUENCE_() previously required an instruction with a unique
34// nonce since unlike clang, GCC folds together identical asm volatile
35// blocks.
36// - TRAP_SEQUENCE_() must produce a signal that is distinct from an invalid
37// memory access.
38// - TRAP_SEQUENCE_() must be treated as a set of noreturn instructions.
39// __builtin_unreachable() is used to provide that hint here. clang also uses
40// this as a heuristic to pack the instructions in the function epilogue to
41// improve code density.
André Kempe3566cc52023-03-30 14:10:1742// - base::ImmediateCrash() is used in allocation hooks. To prevent recursions,
43// TRAP_SEQUENCE_() must not allocate.
Daniel Cheng69359e92019-06-20 23:43:0244//
45// Additional properties that are nice to have:
46// - TRAP_SEQUENCE_() should be as compact as possible.
47// - The first instruction of TRAP_SEQUENCE_() should not change, to avoid
48// shifting crash reporting clusters. As a consequence of this, explicit
49// assembly is preferred over intrinsics.
50// Note: this last bullet point may no longer be true, and may be removed in
51// the future.
52
Reid Klecknerc55cd142019-07-23 00:38:1753// Note: TRAP_SEQUENCE Is currently split into two macro helpers due to the fact
54// that clang emits an actual instruction for __builtin_unreachable() on certain
55// platforms (see https://crbug.com/958675). In addition, the int3/bkpt/brk will
56// be removed in followups, so splitting it up like this now makes it easy to
57// land the followups.
Daniel Cheng69359e92019-06-20 23:43:0258
Daniel Chenged0471b2019-05-10 11:43:3659#if defined(COMPILER_GCC)
60
Daniel Cheng110e9da2025-06-24 05:17:0761#if defined(ARCH_CPU_X86_FAMILY)
Daniel Cheng69359e92019-06-20 23:43:0262
Alison Galed965ba02024-04-26 21:50:5463// TODO(crbug.com/40625592): In theory, it should be possible to use just
Reid Klecknerc55cd142019-07-23 00:38:1764// int3. However, there are a number of crashes with SIGILL as the exception
65// code, so it seems likely that there's a signal handler that allows execution
66// to continue after SIGTRAP.
67#define TRAP_SEQUENCE1_() asm volatile("int3")
Daniel Cheng69359e92019-06-20 23:43:0268
Xiaohan Wang38e4ebb2022-01-19 06:57:4369#if BUILDFLAG(IS_APPLE)
Daniel Cheng69359e92019-06-20 23:43:0270// Intentionally empty: __builtin_unreachable() is always part of the sequence
71// (see IMMEDIATE_CRASH below) and already emits a ud2 on Mac.
72#define TRAP_SEQUENCE2_() asm volatile("")
73#else
74#define TRAP_SEQUENCE2_() asm volatile("ud2")
Xiaohan Wang38e4ebb2022-01-19 06:57:4375#endif // BUILDFLAG(IS_APPLE)
Daniel Cheng69359e92019-06-20 23:43:0276
77#elif defined(ARCH_CPU_ARMEL)
78
Daniel Chenged0471b2019-05-10 11:43:3679// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
80// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
81// cause a SIGTRAP from userspace without using a syscall (which would be a
82// problem for sandboxing).
Alison Galed965ba02024-04-26 21:50:5483// TODO(crbug.com/40625592): Remove bkpt from this sequence.
Reid Klecknerc55cd142019-07-23 00:38:1784#define TRAP_SEQUENCE1_() asm volatile("bkpt #0")
Daniel Cheng69359e92019-06-20 23:43:0285#define TRAP_SEQUENCE2_() asm volatile("udf #0")
Daniel Chenged0471b2019-05-10 11:43:3686
Daniel Cheng69359e92019-06-20 23:43:0287#elif defined(ARCH_CPU_ARM64)
88
Daniel Chenged0471b2019-05-10 11:43:3689// This will always generate a SIGTRAP on arm64.
Alison Galed965ba02024-04-26 21:50:5490// TODO(crbug.com/40625592): Remove brk from this sequence.
Reid Klecknerc55cd142019-07-23 00:38:1791#define TRAP_SEQUENCE1_() asm volatile("brk #0")
Daniel Cheng69359e92019-06-20 23:43:0292#define TRAP_SEQUENCE2_() asm volatile("hlt #0")
Daniel Chenged0471b2019-05-10 11:43:3693
94#else
Daniel Cheng69359e92019-06-20 23:43:0295
Daniel Chenged0471b2019-05-10 11:43:3696// Crash report accuracy will not be guaranteed on other architectures, but at
97// least this will crash as expected.
Reid Klecknerc55cd142019-07-23 00:38:1798#define TRAP_SEQUENCE1_() __builtin_trap()
99#define TRAP_SEQUENCE2_() asm volatile("")
Daniel Cheng69359e92019-06-20 23:43:02100
Daniel Chenged0471b2019-05-10 11:43:36101#endif // ARCH_CPU_*
102
103#elif defined(COMPILER_MSVC)
104
Daniel Chenged0471b2019-05-10 11:43:36105#if !defined(__clang__)
Daniel Cheng69359e92019-06-20 23:43:02106
107// MSVC x64 doesn't support inline asm, so use the MSVC intrinsic.
Reid Klecknerc55cd142019-07-23 00:38:17108#define TRAP_SEQUENCE1_() __debugbreak()
109#define TRAP_SEQUENCE2_()
Daniel Cheng69359e92019-06-20 23:43:02110
Daniel Chenged0471b2019-05-10 11:43:36111#elif defined(ARCH_CPU_ARM64)
Daniel Cheng69359e92019-06-20 23:43:02112
Tom Tan9fc93d82019-10-30 09:01:25113// Windows ARM64 uses "BRK #F000" as its breakpoint instruction, and
114// __debugbreak() generates that in both VC++ and clang.
115#define TRAP_SEQUENCE1_() __debugbreak()
Daniel Cheng69359e92019-06-20 23:43:02116// Intentionally empty: __builtin_unreachable() is always part of the sequence
Nico Weber8b833cd2019-09-16 11:47:40117// (see IMMEDIATE_CRASH below) and already emits a ud2 on Win64,
118// https://crbug.com/958373
Daniel Cheng69359e92019-06-20 23:43:02119#define TRAP_SEQUENCE2_() __asm volatile("")
120
Daniel Chenged0471b2019-05-10 11:43:36121#else
Daniel Cheng69359e92019-06-20 23:43:02122
Reid Klecknerc55cd142019-07-23 00:38:17123#define TRAP_SEQUENCE1_() asm volatile("int3")
Daniel Cheng69359e92019-06-20 23:43:02124#define TRAP_SEQUENCE2_() asm volatile("ud2")
Daniel Cheng69359e92019-06-20 23:43:02125
Daniel Chenged0471b2019-05-10 11:43:36126#endif // __clang__
127
128#else
Daniel Cheng69359e92019-06-20 23:43:02129
130#error No supported trap sequence!
131
Daniel Chenged0471b2019-05-10 11:43:36132#endif // COMPILER_GCC
133
Daniel Cheng69359e92019-06-20 23:43:02134#define TRAP_SEQUENCE_() \
135 do { \
Reid Klecknerc55cd142019-07-23 00:38:17136 TRAP_SEQUENCE1_(); \
Daniel Cheng69359e92019-06-20 23:43:02137 TRAP_SEQUENCE2_(); \
138 } while (false)
139
Peter Boströmb30544d2022-10-21 00:17:58140// This version of ALWAYS_INLINE inlines even in is_debug=true.
141// TODO(pbos): See if NDEBUG can be dropped from ALWAYS_INLINE as well, and if
142// so merge. Otherwise document why it cannot inline in debug in
143// base/compiler_specific.h.
144#if defined(COMPILER_GCC)
145#define IMMEDIATE_CRASH_ALWAYS_INLINE inline __attribute__((__always_inline__))
146#elif defined(COMPILER_MSVC)
147#define IMMEDIATE_CRASH_ALWAYS_INLINE __forceinline
Hiroki Nakagawacd01d122022-10-20 02:44:20148#else
Peter Boströmb30544d2022-10-21 00:17:58149#define IMMEDIATE_CRASH_ALWAYS_INLINE inline
150#endif
Hiroki Nakagawacd01d122022-10-20 02:44:20151
Peter Boströmb30544d2022-10-21 00:17:58152namespace base {
Hiroki Nakagawacd01d122022-10-20 02:44:20153
Peter Boströmb30544d2022-10-21 00:17:58154[[noreturn]] IMMEDIATE_CRASH_ALWAYS_INLINE void ImmediateCrash() {
Brendon Tiszka254cb732024-09-27 15:17:08155#if BUILDFLAG(USE_FUZZING_ENGINE) && BUILDFLAG(IS_LINUX)
156 // A fuzzer run will often handle many successful cases then
157 // find one which crashes and dies. It's important that the
158 // coverage of those successful cases is represented when we're
159 // considering fuzzing coverage. At the moment fuzzing coverage
160 // is only measured on Linux, which is why this is Linux-
161 // specific.
162 // exit() arranges to write out coverage information because
163 // an atexit handler is registered to do so, but there is no
164 // such action in the std::abort case. Instead, manually write
165 // out such coverage.
166 // We could extend this step to all coverage builds, but
167 // at present failing tests don't get coverage reported,
168 // so we're retaining that behavior.
169 // TODO(crbug.com/40948553): consider doing this for all coverage builds
Paul Semeldf26c852023-12-18 20:36:58170 if (__llvm_profile_write_file) {
171 __llvm_profile_write_file();
172 }
Brendon Tiszka254cb732024-09-27 15:17:08173#endif // BUILDFLAG(USE_FUZZING_ENGINE) && BUILDFLAG(IS_LINUX)
174
175#if defined(OFFICIAL_BUILD) || BUILDFLAG(IS_WIN)
176 // We can't use abort() on Windows because it results in the
177 // abort/retry/ignore dialog which disrupts automated tests.
178 // TODO(crbug.com/40948553): investigate if such dialogs can
179 // be suppressed
Peter Boströmb30544d2022-10-21 00:17:58180 TRAP_SEQUENCE_();
Daniel Chenged0471b2019-05-10 11:43:36181#if defined(__clang__) || defined(COMPILER_GCC)
Peter Boströmb30544d2022-10-21 00:17:58182 __builtin_unreachable();
Daniel Cheng69359e92019-06-20 23:43:02183#endif // defined(__clang__) || defined(COMPILER_GCC)
Brendon Tiszka254cb732024-09-27 15:17:08184#else // defined(OFFICIAL_BUILD) || BUILDFLAG(IS_WIN)
185 abort();
186#endif // defined(OFFICIAL_BUILD)
Peter Boströmb30544d2022-10-21 00:17:58187}
188
189} // namespace base
190
Daniel Chenged0471b2019-05-10 11:43:36191#endif // BASE_IMMEDIATE_CRASH_H_