1 // Copyright 2019 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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "base/immediate_crash.h"
11
12 #include <stdint.h>
13
14 #include <optional>
15
16 #include "base/base_paths.h"
17 #include "base/clang_profiling_buildflags.h"
18 #include "base/containers/span.h"
19 #include "base/files/file_path.h"
20 #include "base/path_service.h"
21 #include "base/ranges/algorithm.h"
22 #include "base/scoped_native_library.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "build/build_config.h"
25 #include "build/buildflag.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27
28 namespace base {
29
30 namespace {
31
32 // If ImmediateCrash() is not treated as noreturn by the compiler, the compiler
33 // will complain that not all paths through this function return a value.
TestImmediateCrashTreatedAsNoReturn()34 [[maybe_unused]] int TestImmediateCrashTreatedAsNoReturn() {
35 ImmediateCrash();
36 }
37
38 #if defined(ARCH_CPU_X86_FAMILY)
39 // This is tricksy and false, since x86 instructions are not all one byte long,
40 // but there is no better alternative short of implementing an x86 instruction
41 // decoder.
42 using Instruction = uint8_t;
43
44 #if defined(OFFICIAL_BUILD)
45 // 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
46 // Look for RET opcode (0xc3). Note that 0xC3 is a substring of several
47 // other opcodes (VMRESUME, MOVNTI), and can also be encoded as part of an
48 // argument to another opcode. None of these other cases are expected to be
49 // present, so a simple byte scan should be Good Enough™.
50 constexpr Instruction kRet = 0xc3;
51 // INT3 ; UD2
52
53 constexpr Instruction kRequiredBody[] = {0xcc, 0x0f, 0x0b};
54 constexpr Instruction kOptionalFooter[] = {};
55 #endif // defined(OFFICIAL_BUILD)
56
57 #elif defined(ARCH_CPU_ARMEL)
58 using Instruction = uint16_t;
59
60 #if defined(OFFICIAL_BUILD)
61 // T32 opcode reference: https://developer.arm.com/docs/ddi0487/latest
62 // Actually BX LR, canonical encoding:
63 constexpr Instruction kRet = 0x4770;
64
65 // BKPT #0; UDF #0
66 constexpr Instruction kRequiredBody[] = {0xbe00, 0xde00};
67 constexpr Instruction kOptionalFooter[] = {};
68 #endif // defined(OFFICIAL_BUILD)
69
70 #elif defined(ARCH_CPU_ARM64)
71 using Instruction = uint32_t;
72
73 #if defined(OFFICIAL_BUILD)
74 // A64 opcode reference: https://developer.arm.com/docs/ddi0487/latest
75 // Use an enum here rather than separate constexpr vars because otherwise some
76 // of the vars will end up unused on each platform, upsetting
77 // -Wunused-const-variable.
78 enum : Instruction {
79 // There are multiple valid encodings of return (which is really a special
80 // form of branch). This is the one clang seems to use:
81 kRet = 0xd65f03c0,
82 kBrk0 = 0xd4200000,
83 kBrk1 = 0xd4200020,
84 kBrkF000 = 0xd43e0000,
85 kHlt0 = 0xd4400000,
86 };
87
88 #if BUILDFLAG(IS_WIN)
89
90 constexpr Instruction kRequiredBody[] = {kBrkF000, kBrk1};
91 constexpr Instruction kOptionalFooter[] = {};
92
93 #elif BUILDFLAG(IS_MAC)
94
95 constexpr Instruction kRequiredBody[] = {kBrk0, kHlt0};
96 // Some clangs emit a BRK #1 for __builtin_unreachable(), but some do not, so
97 // it is allowed but not required to occur.
98 constexpr Instruction kOptionalFooter[] = {kBrk1};
99
100 #else
101
102 constexpr Instruction kRequiredBody[] = {kBrk0, kHlt0};
103 constexpr Instruction kOptionalFooter[] = {};
104
105 #endif
106
107 #endif // defined(OFFICIAL_BUILD)
108
109 #endif
110
111 // This function loads a shared library that defines two functions,
112 // TestFunction1 and TestFunction2. It then returns the bytes of the body of
113 // whichever of those functions happens to come first in the library.
GetTestFunctionInstructions(std::vector<Instruction> * body)114 void GetTestFunctionInstructions(std::vector<Instruction>* body) {
115 FilePath helper_library_path;
116 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
117 // On Android M, DIR_EXE == /system/bin when running base_unittests.
118 // On Fuchsia, NativeLibrary understands the native convention that libraries
119 // are not colocated with the binary.
120 ASSERT_TRUE(PathService::Get(DIR_EXE, &helper_library_path));
121 #endif
122 helper_library_path = helper_library_path.AppendASCII(
123 GetNativeLibraryName("immediate_crash_test_helper"));
124 ScopedNativeLibrary helper_library(helper_library_path);
125 ASSERT_TRUE(helper_library.is_valid())
126 << "shared library load failed: "
127 << helper_library.GetError()->ToString();
128
129 void* a = helper_library.GetFunctionPointer("TestFunction1");
130 ASSERT_TRUE(a);
131 void* b = helper_library.GetFunctionPointer("TestFunction2");
132 ASSERT_TRUE(b);
133
134 #if defined(ARCH_CPU_ARMEL)
135 // Routines loaded from a shared library will have the LSB in the pointer set
136 // if encoded as T32 instructions. The rest of this test assumes T32.
137 ASSERT_TRUE(reinterpret_cast<uintptr_t>(a) & 0x1)
138 << "Expected T32 opcodes but found A32 opcodes instead.";
139 ASSERT_TRUE(reinterpret_cast<uintptr_t>(b) & 0x1)
140 << "Expected T32 opcodes but found A32 opcodes instead.";
141
142 // Mask off the lowest bit.
143 a = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(a) & ~uintptr_t{0x1});
144 b = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(b) & ~uintptr_t{0x1});
145 #endif
146
147 // There are two identical test functions starting at a and b, which may
148 // occur in the library in either order. Grab whichever one comes first,
149 // and use the address of the other to figure out where it ends.
150 const Instruction* const start = static_cast<Instruction*>(std::min(a, b));
151 const Instruction* const end = static_cast<Instruction*>(std::max(a, b));
152
153 for (const Instruction& instruction : span(start, end)) {
154 body->push_back(instruction);
155 }
156 }
157
158 #if defined(OFFICIAL_BUILD)
159
ExpectImmediateCrashInvocation(std::vector<Instruction> instructions)160 std::optional<std::vector<Instruction>> ExpectImmediateCrashInvocation(
161 std::vector<Instruction> instructions) {
162 auto iter = instructions.begin();
163 for (const auto inst : kRequiredBody) {
164 if (iter == instructions.end())
165 return std::nullopt;
166 EXPECT_EQ(inst, *iter);
167 iter++;
168 }
169 return std::make_optional(std::vector<Instruction>(iter, instructions.end()));
170 }
171
MaybeSkipOptionalFooter(std::vector<Instruction> instructions)172 std::vector<Instruction> MaybeSkipOptionalFooter(
173 std::vector<Instruction> instructions) {
174 auto iter = instructions.begin();
175 for (const auto inst : kOptionalFooter) {
176 if (iter == instructions.end() || *iter != inst)
177 break;
178 iter++;
179 }
180 return std::vector<Instruction>(iter, instructions.end());
181 }
182
183 #if BUILDFLAG(USE_CLANG_COVERAGE) || BUILDFLAG(CLANG_PROFILING)
MatchPrefix(const std::vector<Instruction> & haystack,const base::span<const Instruction> & needle)184 bool MatchPrefix(const std::vector<Instruction>& haystack,
185 const base::span<const Instruction>& needle) {
186 for (size_t i = 0; i < needle.size(); i++) {
187 if (i >= haystack.size() || needle[i] != haystack[i])
188 return false;
189 }
190 return true;
191 }
192
DropUntilMatch(std::vector<Instruction> haystack,const base::span<const Instruction> & needle)193 std::vector<Instruction> DropUntilMatch(
194 std::vector<Instruction> haystack,
195 const base::span<const Instruction>& needle) {
196 while (!haystack.empty() && !MatchPrefix(haystack, needle))
197 haystack.erase(haystack.begin());
198 return haystack;
199 }
200
201 #endif // USE_CLANG_COVERAGE || BUILDFLAG(CLANG_PROFILING)
202
MaybeSkipCoverageHook(std::vector<Instruction> instructions)203 std::vector<Instruction> MaybeSkipCoverageHook(
204 std::vector<Instruction> instructions) {
205 #if BUILDFLAG(USE_CLANG_COVERAGE) || BUILDFLAG(CLANG_PROFILING)
206 // Warning: it is not illegal for the entirety of the expected crash sequence
207 // to appear as a subsequence of the coverage hook code. If that happens, this
208 // code will falsely exit early, having not found the real expected crash
209 // sequence, so this may not adequately ensure that the immediate crash
210 // sequence is present. We do check when not under coverage, at least.
211 return DropUntilMatch(instructions, span(kRequiredBody));
212 #else
213 return instructions;
214 #endif // USE_CLANG_COVERAGE || BUILDFLAG(CLANG_PROFILING)
215 }
216
217 #endif // defined(OFFICIAL_BUILD)
218
219 } // namespace
220
221 // Attempts to verify the actual instructions emitted by ImmediateCrash().
222 // While the test results are highly implementation-specific, this allows macro
223 // changes (e.g. CLs like https://crrev.com/671123) to be verified using the
224 // trybots/waterfall, without having to build and disassemble Chrome on
225 // multiple platforms. This makes it easier to evaluate changes to
226 // ImmediateCrash() against its requirements (e.g. size of emitted sequence,
227 // whether or not multiple ImmediateCrash sequences can be folded together, et
228 // cetera). Please see immediate_crash.h for more details about the
229 // requirements.
230 //
231 // Note that C++ provides no way to get the size of a function. Instead, the
232 // test relies on a shared library which defines only two functions and assumes
233 // the two functions will be laid out contiguously as a heuristic for finding
234 // the size of the function.
TEST(ImmediateCrashTest,ExpectedOpcodeSequence)235 TEST(ImmediateCrashTest, ExpectedOpcodeSequence) {
236 std::vector<Instruction> body;
237 ASSERT_NO_FATAL_FAILURE(GetTestFunctionInstructions(&body));
238 SCOPED_TRACE(HexEncode(body.data(), body.size() * sizeof(Instruction)));
239
240 // In non-official builds, we std::abort instead, so the result will be
241 // false - but let's still go through the motions above so we spot any
242 // problems in this _test code_ in as many build permutations as possible.
243 #if defined(OFFICIAL_BUILD)
244 auto it = ranges::find(body, kRet);
245 ASSERT_NE(body.end(), it) << "Failed to find return opcode";
246 it++;
247
248 body = std::vector<Instruction>(it, body.end());
249 std::optional<std::vector<Instruction>> result = MaybeSkipCoverageHook(body);
250 result = ExpectImmediateCrashInvocation(result.value());
251 result = MaybeSkipOptionalFooter(result.value());
252 result = MaybeSkipCoverageHook(result.value());
253 result = ExpectImmediateCrashInvocation(result.value());
254 ASSERT_TRUE(result);
255 #endif // defined(OFFICIAL_BUILD)
256 }
257
258 } // namespace base
259