blob: eb3850925e75cb98bf93526792d9cd36984ac337 [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#include "base/immediate_crash.h"
6
7#include <stdint.h>
8
Daniel Chengf355d632019-05-14 17:25:309#include "base/base_paths.h"
Elly Fong-Jones664e25f2020-10-13 21:35:3510#include "base/clang_profiling_buildflags.h"
Daniel Chenged0471b2019-05-10 11:43:3611#include "base/containers/span.h"
12#include "base/files/file_path.h"
Daniel Chengf355d632019-05-14 17:25:3013#include "base/path_service.h"
Anton Bikineeva61fb572020-10-18 08:54:4414#include "base/ranges/algorithm.h"
Elly Fong-Jonese32032202020-09-25 02:08:1515#include "base/scoped_native_library.h"
Daniel Chenged0471b2019-05-10 11:43:3616#include "base/strings/string_number_conversions.h"
17#include "build/build_config.h"
Elly Fong-Jones664e25f2020-10-13 21:35:3518#include "build/buildflag.h"
Daniel Chenged0471b2019-05-10 11:43:3619#include "testing/gtest/include/gtest/gtest.h"
Anton Bikineev7dd58ad2021-05-18 01:01:3920#include "third_party/abseil-cpp/absl/types/optional.h"
Daniel Chenged0471b2019-05-10 11:43:3621
22namespace base {
23
Elly Fong-Jonese32032202020-09-25 02:08:1524namespace {
25
Peter Boström25c6ec72022-11-02 23:25:1926// If ImmediateCrash() is not treated as noreturn by the compiler, the compiler
Daniel Cheng98c77232020-10-26 21:40:2727// will complain that not all paths through this function return a value.
Avi Drissmandea32052022-01-13 21:31:1828[[maybe_unused]] int TestImmediateCrashTreatedAsNoReturn() {
Peter Boström25c6ec72022-11-02 23:25:1929 ImmediateCrash();
Daniel Cheng69359e92019-06-20 23:43:0230}
31
Elly Fong-Jonese32032202020-09-25 02:08:1532#if defined(ARCH_CPU_X86_FAMILY)
33// This is tricksy and false, since x86 instructions are not all one byte long,
34// but there is no better alternative short of implementing an x86 instruction
35// decoder.
36using Instruction = uint8_t;
Daniel Chenged0471b2019-05-10 11:43:3637
Elly Fong-Jonese32032202020-09-25 02:08:1538// https://software.intel.com/en-us/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4
39// Look for RET opcode (0xc3). Note that 0xC3 is a substring of several
40// other opcodes (VMRESUME, MOVNTI), and can also be encoded as part of an
41// argument to another opcode. None of these other cases are expected to be
42// present, so a simple byte scan should be Good Enough™.
43constexpr Instruction kRet = 0xc3;
44// INT3 ; UD2
45constexpr Instruction kRequiredBody[] = {0xcc, 0x0f, 0x0b};
46constexpr Instruction kOptionalFooter[] = {};
47
48#elif defined(ARCH_CPU_ARMEL)
49using Instruction = uint16_t;
50
51// T32 opcode reference: https://developer.arm.com/docs/ddi0487/latest
52// Actually BX LR, canonical encoding:
53constexpr Instruction kRet = 0x4770;
54// BKPT #0; UDF #0
55constexpr Instruction kRequiredBody[] = {0xbe00, 0xde00};
56constexpr Instruction kOptionalFooter[] = {};
57
58#elif defined(ARCH_CPU_ARM64)
59using Instruction = uint32_t;
60
61// A64 opcode reference: https://developer.arm.com/docs/ddi0487/latest
62// Use an enum here rather than separate constexpr vars because otherwise some
63// of the vars will end up unused on each platform, upsetting
64// -Wunused-const-variable.
Richard Townsende94604ad72020-10-22 23:39:2065enum : Instruction {
Elly Fong-Jonese32032202020-09-25 02:08:1566 // There are multiple valid encodings of return (which is really a special
67 // form of branch). This is the one clang seems to use:
68 kRet = 0xd65f03c0,
69 kBrk0 = 0xd4200000,
70 kBrk1 = 0xd4200020,
71 kBrkF000 = 0xd43e0000,
72 kHlt0 = 0xd4400000,
73};
74
Xiaohan Wang38e4ebb2022-01-19 06:57:4375#if BUILDFLAG(IS_WIN)
Elly Fong-Jonese32032202020-09-25 02:08:1576
77constexpr Instruction kRequiredBody[] = {kBrkF000, kBrk1};
78constexpr Instruction kOptionalFooter[] = {};
79
Xiaohan Wang38e4ebb2022-01-19 06:57:4380#elif BUILDFLAG(IS_MAC)
Elly Fong-Jonese32032202020-09-25 02:08:1581
82constexpr Instruction kRequiredBody[] = {kBrk0, kHlt0};
83// Some clangs emit a BRK #1 for __builtin_unreachable(), but some do not, so
84// it is allowed but not required to occur.
85constexpr Instruction kOptionalFooter[] = {kBrk1};
86
87#else
88
89constexpr Instruction kRequiredBody[] = {kBrk0, kHlt0};
90constexpr Instruction kOptionalFooter[] = {};
91
92#endif
93
94#endif
95
96// This function loads a shared library that defines two functions,
97// TestFunction1 and TestFunction2. It then returns the bytes of the body of
98// whichever of those functions happens to come first in the library.
99void GetTestFunctionInstructions(std::vector<Instruction>* body) {
Daniel Chengf355d632019-05-14 17:25:30100 FilePath helper_library_path;
Xiaohan Wang38e4ebb2022-01-19 06:57:43101#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
Reid Klecknerc55cd142019-07-23 00:38:17102 // On Android M, DIR_EXE == /system/bin when running base_unittests.
103 // On Fuchsia, NativeLibrary understands the native convention that libraries
104 // are not colocated with the binary.
Daniel Chengf355d632019-05-14 17:25:30105 ASSERT_TRUE(PathService::Get(DIR_EXE, &helper_library_path));
106#endif
107 helper_library_path = helper_library_path.AppendASCII(
108 GetNativeLibraryName("immediate_crash_test_helper"));
Xiaohan Wang38e4ebb2022-01-19 06:57:43109#if BUILDFLAG(IS_ANDROID) && defined(COMPONENT_BUILD)
Daniel Cheng79bb3cf22019-05-16 19:15:29110 helper_library_path = helper_library_path.ReplaceExtension(".cr.so");
111#endif
Elly Fong-Jonese32032202020-09-25 02:08:15112 ScopedNativeLibrary helper_library(helper_library_path);
113 ASSERT_TRUE(helper_library.is_valid())
114 << "shared library load failed: "
115 << helper_library.GetError()->ToString();
Daniel Chenged0471b2019-05-10 11:43:36116
Elly Fong-Jonese32032202020-09-25 02:08:15117 void* a = helper_library.GetFunctionPointer("TestFunction1");
Daniel Chenged0471b2019-05-10 11:43:36118 ASSERT_TRUE(a);
Elly Fong-Jonese32032202020-09-25 02:08:15119 void* b = helper_library.GetFunctionPointer("TestFunction2");
Daniel Chenged0471b2019-05-10 11:43:36120 ASSERT_TRUE(b);
121
Elly Fong-Jonese32032202020-09-25 02:08:15122#if defined(ARCH_CPU_ARMEL)
Daniel Chenged0471b2019-05-10 11:43:36123 // Routines loaded from a shared library will have the LSB in the pointer set
124 // if encoded as T32 instructions. The rest of this test assumes T32.
125 ASSERT_TRUE(reinterpret_cast<uintptr_t>(a) & 0x1)
126 << "Expected T32 opcodes but found A32 opcodes instead.";
127 ASSERT_TRUE(reinterpret_cast<uintptr_t>(b) & 0x1)
128 << "Expected T32 opcodes but found A32 opcodes instead.";
129
130 // Mask off the lowest bit.
131 a = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(a) & ~uintptr_t{0x1});
132 b = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(b) & ~uintptr_t{0x1});
Elly Fong-Jonese32032202020-09-25 02:08:15133#endif
Daniel Chenged0471b2019-05-10 11:43:36134
Elly Fong-Jonese32032202020-09-25 02:08:15135 // There are two identical test functions starting at a and b, which may
136 // occur in the library in either order. Grab whichever one comes first,
137 // and use the address of the other to figure out where it ends.
138 const Instruction* const start = static_cast<Instruction*>(std::min(a, b));
139 const Instruction* const end = static_cast<Instruction*>(std::max(a, b));
Daniel Chenged0471b2019-05-10 11:43:36140
Elly Fong-Jonese32032202020-09-25 02:08:15141 for (const Instruction& instruction : make_span(start, end))
142 body->push_back(instruction);
Daniel Chenged0471b2019-05-10 11:43:36143}
144
Anton Bikineev7dd58ad2021-05-18 01:01:39145absl::optional<std::vector<Instruction>> ExpectImmediateCrashInvocation(
Elly Fong-Jonese32032202020-09-25 02:08:15146 std::vector<Instruction> instructions) {
147 auto iter = instructions.begin();
148 for (const auto inst : kRequiredBody) {
149 if (iter == instructions.end())
Anton Bikineev7dd58ad2021-05-18 01:01:39150 return absl::nullopt;
Elly Fong-Jonese32032202020-09-25 02:08:15151 EXPECT_EQ(inst, *iter);
152 iter++;
153 }
Anton Bikineev7dd58ad2021-05-18 01:01:39154 return absl::make_optional(
Elly Fong-Jonese32032202020-09-25 02:08:15155 std::vector<Instruction>(iter, instructions.end()));
156}
157
158std::vector<Instruction> MaybeSkipOptionalFooter(
159 std::vector<Instruction> instructions) {
160 auto iter = instructions.begin();
161 for (const auto inst : kOptionalFooter) {
162 if (iter == instructions.end() || *iter != inst)
163 break;
164 iter++;
165 }
166 return std::vector<Instruction>(iter, instructions.end());
167}
168
Arthur Eubanks0d8e2b172021-08-26 15:36:23169#if BUILDFLAG(USE_CLANG_COVERAGE) || BUILDFLAG(CLANG_PROFILING)
Elly Fong-Jones664e25f2020-10-13 21:35:35170bool MatchPrefix(const std::vector<Instruction>& haystack,
171 const base::span<const Instruction>& needle) {
172 for (size_t i = 0; i < needle.size(); i++) {
173 if (i >= haystack.size() || needle[i] != haystack[i])
174 return false;
175 }
176 return true;
177}
178
179std::vector<Instruction> DropUntilMatch(
180 std::vector<Instruction> haystack,
181 const base::span<const Instruction>& needle) {
182 while (!haystack.empty() && !MatchPrefix(haystack, needle))
183 haystack.erase(haystack.begin());
184 return haystack;
185}
Arthur Eubanks0d8e2b172021-08-26 15:36:23186#endif // USE_CLANG_COVERAGE || BUILDFLAG(CLANG_PROFILING)
Elly Fong-Jones664e25f2020-10-13 21:35:35187
188std::vector<Instruction> MaybeSkipCoverageHook(
189 std::vector<Instruction> instructions) {
Arthur Eubanks0d8e2b172021-08-26 15:36:23190#if BUILDFLAG(USE_CLANG_COVERAGE) || BUILDFLAG(CLANG_PROFILING)
Elly Fong-Jones664e25f2020-10-13 21:35:35191 // Warning: it is not illegal for the entirety of the expected crash sequence
192 // to appear as a subsequence of the coverage hook code. If that happens, this
193 // code will falsely exit early, having not found the real expected crash
194 // sequence, so this may not adequately ensure that the immediate crash
195 // sequence is present. We do check when not under coverage, at least.
196 return DropUntilMatch(instructions, base::make_span(kRequiredBody));
197#else
198 return instructions;
Arthur Eubanks0d8e2b172021-08-26 15:36:23199#endif // USE_CLANG_COVERAGE || BUILDFLAG(CLANG_PROFILING)
Elly Fong-Jones664e25f2020-10-13 21:35:35200}
201
Elly Fong-Jonese32032202020-09-25 02:08:15202} // namespace
203
Peter Boström25c6ec72022-11-02 23:25:19204// Attempts to verify the actual instructions emitted by ImmediateCrash().
Daniel Cheng98c77232020-10-26 21:40:27205// While the test results are highly implementation-specific, this allows macro
206// changes (e.g. CLs like https://crrev.com/671123) to be verified using the
207// trybots/waterfall, without having to build and disassemble Chrome on
208// multiple platforms. This makes it easier to evaluate changes to
Peter Boström25c6ec72022-11-02 23:25:19209// ImmediateCrash() against its requirements (e.g. size of emitted sequence,
210// whether or not multiple ImmediateCrash sequences can be folded together, et
Daniel Cheng98c77232020-10-26 21:40:27211// cetera). Please see immediate_crash.h for more details about the
212// requirements.
213//
214// Note that C++ provides no way to get the size of a function. Instead, the
215// test relies on a shared library which defines only two functions and assumes
216// the two functions will be laid out contiguously as a heuristic for finding
217// the size of the function.
Elly Fong-Jonese32032202020-09-25 02:08:15218TEST(ImmediateCrashTest, ExpectedOpcodeSequence) {
219 std::vector<Instruction> body;
220 ASSERT_NO_FATAL_FAILURE(GetTestFunctionInstructions(&body));
221 SCOPED_TRACE(HexEncode(body.data(), body.size() * sizeof(Instruction)));
222
Anton Bikineeva61fb572020-10-18 08:54:44223 auto it = ranges::find(body, kRet);
Elly Fong-Jonese32032202020-09-25 02:08:15224 ASSERT_NE(body.end(), it) << "Failed to find return opcode";
225 it++;
226
227 body = std::vector<Instruction>(it, body.end());
Anton Bikineev7dd58ad2021-05-18 01:01:39228 absl::optional<std::vector<Instruction>> result = MaybeSkipCoverageHook(body);
Elly Fong-Jones664e25f2020-10-13 21:35:35229 result = ExpectImmediateCrashInvocation(result.value());
Elly Fong-Jonese32032202020-09-25 02:08:15230 result = MaybeSkipOptionalFooter(result.value());
Elly Fong-Jones664e25f2020-10-13 21:35:35231 result = MaybeSkipCoverageHook(result.value());
Elly Fong-Jonese32032202020-09-25 02:08:15232 result = ExpectImmediateCrashInvocation(result.value());
233 ASSERT_TRUE(result);
234}
Daniel Chenged0471b2019-05-10 11:43:36235
236} // namespace base