blob: 6b202784d67ae67c1b3ff28fc7e8be9d44d9f5fb [file] [log] [blame]
Daniel Chenged0471b2019-05-10 11:43:361// Copyright 2019 The Chromium Authors. All rights reserved.
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/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"
Elly Fong-Jonese32032202020-09-25 02:08:1511#include "base/compiler_specific.h"
Daniel Chenged0471b2019-05-10 11:43:3612#include "base/containers/span.h"
13#include "base/files/file_path.h"
Daniel Chenged0471b2019-05-10 11:43:3614#include "base/optional.h"
Daniel Chengf355d632019-05-14 17:25:3015#include "base/path_service.h"
Elly Fong-Jonese32032202020-09-25 02:08:1516#include "base/scoped_native_library.h"
Daniel Chenged0471b2019-05-10 11:43:3617#include "base/strings/string_number_conversions.h"
18#include "build/build_config.h"
Elly Fong-Jones664e25f2020-10-13 21:35:3519#include "build/buildflag.h"
Daniel Chenged0471b2019-05-10 11:43:3620#include "testing/gtest/include/gtest/gtest.h"
21
22namespace base {
23
Elly Fong-Jonese32032202020-09-25 02:08:1524namespace {
25
Daniel Cheng69359e92019-06-20 23:43:0226// Compile test.
Elly Fong-Jonese32032202020-09-25 02:08:1527int ALLOW_UNUSED_TYPE TestImmediateCrashTreatedAsNoReturn() {
Daniel Cheng69359e92019-06-20 23:43:0228 IMMEDIATE_CRASH();
29}
30
Elly Fong-Jonese32032202020-09-25 02:08:1531#if defined(ARCH_CPU_X86_FAMILY)
32// This is tricksy and false, since x86 instructions are not all one byte long,
33// but there is no better alternative short of implementing an x86 instruction
34// decoder.
35using Instruction = uint8_t;
Daniel Chenged0471b2019-05-10 11:43:3636
Elly Fong-Jonese32032202020-09-25 02:08:1537// 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
38// Look for RET opcode (0xc3). Note that 0xC3 is a substring of several
39// other opcodes (VMRESUME, MOVNTI), and can also be encoded as part of an
40// argument to another opcode. None of these other cases are expected to be
41// present, so a simple byte scan should be Good Enoughâ„¢.
42constexpr Instruction kRet = 0xc3;
43// INT3 ; UD2
44constexpr Instruction kRequiredBody[] = {0xcc, 0x0f, 0x0b};
45constexpr Instruction kOptionalFooter[] = {};
46
47#elif defined(ARCH_CPU_ARMEL)
48using Instruction = uint16_t;
49
50// T32 opcode reference: https://developer.arm.com/docs/ddi0487/latest
51// Actually BX LR, canonical encoding:
52constexpr Instruction kRet = 0x4770;
53// BKPT #0; UDF #0
54constexpr Instruction kRequiredBody[] = {0xbe00, 0xde00};
55constexpr Instruction kOptionalFooter[] = {};
56
57#elif defined(ARCH_CPU_ARM64)
58using Instruction = uint32_t;
59
60// A64 opcode reference: https://developer.arm.com/docs/ddi0487/latest
61// Use an enum here rather than separate constexpr vars because otherwise some
62// of the vars will end up unused on each platform, upsetting
63// -Wunused-const-variable.
64enum {
65 // There are multiple valid encodings of return (which is really a special
66 // form of branch). This is the one clang seems to use:
67 kRet = 0xd65f03c0,
68 kBrk0 = 0xd4200000,
69 kBrk1 = 0xd4200020,
70 kBrkF000 = 0xd43e0000,
71 kHlt0 = 0xd4400000,
72};
73
74#if defined(OS_WIN)
75
76constexpr Instruction kRequiredBody[] = {kBrkF000, kBrk1};
77constexpr Instruction kOptionalFooter[] = {};
78
79#elif defined(OS_MAC)
80
81constexpr Instruction kRequiredBody[] = {kBrk0, kHlt0};
82// Some clangs emit a BRK #1 for __builtin_unreachable(), but some do not, so
83// it is allowed but not required to occur.
84constexpr Instruction kOptionalFooter[] = {kBrk1};
85
86#else
87
88constexpr Instruction kRequiredBody[] = {kBrk0, kHlt0};
89constexpr Instruction kOptionalFooter[] = {};
90
91#endif
92
93#endif
94
95// This function loads a shared library that defines two functions,
96// TestFunction1 and TestFunction2. It then returns the bytes of the body of
97// whichever of those functions happens to come first in the library.
98void GetTestFunctionInstructions(std::vector<Instruction>* body) {
Daniel Chengf355d632019-05-14 17:25:3099 FilePath helper_library_path;
100#if !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
Reid Klecknerc55cd142019-07-23 00:38:17101 // On Android M, DIR_EXE == /system/bin when running base_unittests.
102 // On Fuchsia, NativeLibrary understands the native convention that libraries
103 // are not colocated with the binary.
Daniel Chengf355d632019-05-14 17:25:30104 ASSERT_TRUE(PathService::Get(DIR_EXE, &helper_library_path));
105#endif
106 helper_library_path = helper_library_path.AppendASCII(
107 GetNativeLibraryName("immediate_crash_test_helper"));
Daniel Cheng79bb3cf22019-05-16 19:15:29108#if defined(OS_ANDROID) && defined(COMPONENT_BUILD)
109 helper_library_path = helper_library_path.ReplaceExtension(".cr.so");
110#endif
Elly Fong-Jonese32032202020-09-25 02:08:15111 ScopedNativeLibrary helper_library(helper_library_path);
112 ASSERT_TRUE(helper_library.is_valid())
113 << "shared library load failed: "
114 << helper_library.GetError()->ToString();
Daniel Chenged0471b2019-05-10 11:43:36115
Elly Fong-Jonese32032202020-09-25 02:08:15116 void* a = helper_library.GetFunctionPointer("TestFunction1");
Daniel Chenged0471b2019-05-10 11:43:36117 ASSERT_TRUE(a);
Elly Fong-Jonese32032202020-09-25 02:08:15118 void* b = helper_library.GetFunctionPointer("TestFunction2");
Daniel Chenged0471b2019-05-10 11:43:36119 ASSERT_TRUE(b);
120
Elly Fong-Jonese32032202020-09-25 02:08:15121#if defined(ARCH_CPU_ARMEL)
Daniel Chenged0471b2019-05-10 11:43:36122 // Routines loaded from a shared library will have the LSB in the pointer set
123 // if encoded as T32 instructions. The rest of this test assumes T32.
124 ASSERT_TRUE(reinterpret_cast<uintptr_t>(a) & 0x1)
125 << "Expected T32 opcodes but found A32 opcodes instead.";
126 ASSERT_TRUE(reinterpret_cast<uintptr_t>(b) & 0x1)
127 << "Expected T32 opcodes but found A32 opcodes instead.";
128
129 // Mask off the lowest bit.
130 a = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(a) & ~uintptr_t{0x1});
131 b = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(b) & ~uintptr_t{0x1});
Elly Fong-Jonese32032202020-09-25 02:08:15132#endif
Daniel Chenged0471b2019-05-10 11:43:36133
Elly Fong-Jonese32032202020-09-25 02:08:15134 // There are two identical test functions starting at a and b, which may
135 // occur in the library in either order. Grab whichever one comes first,
136 // and use the address of the other to figure out where it ends.
137 const Instruction* const start = static_cast<Instruction*>(std::min(a, b));
138 const Instruction* const end = static_cast<Instruction*>(std::max(a, b));
Daniel Chenged0471b2019-05-10 11:43:36139
Elly Fong-Jonese32032202020-09-25 02:08:15140 for (const Instruction& instruction : make_span(start, end))
141 body->push_back(instruction);
Daniel Chenged0471b2019-05-10 11:43:36142}
143
Elly Fong-Jonese32032202020-09-25 02:08:15144base::Optional<std::vector<Instruction>> ExpectImmediateCrashInvocation(
145 std::vector<Instruction> instructions) {
146 auto iter = instructions.begin();
147 for (const auto inst : kRequiredBody) {
148 if (iter == instructions.end())
149 return base::nullopt;
150 EXPECT_EQ(inst, *iter);
151 iter++;
152 }
153 return base::make_optional(
154 std::vector<Instruction>(iter, instructions.end()));
155}
156
157std::vector<Instruction> MaybeSkipOptionalFooter(
158 std::vector<Instruction> instructions) {
159 auto iter = instructions.begin();
160 for (const auto inst : kOptionalFooter) {
161 if (iter == instructions.end() || *iter != inst)
162 break;
163 iter++;
164 }
165 return std::vector<Instruction>(iter, instructions.end());
166}
167
Elly Fong-Jones664e25f2020-10-13 21:35:35168#if BUILDFLAG(USE_CLANG_COVERAGE)
169bool MatchPrefix(const std::vector<Instruction>& haystack,
170 const base::span<const Instruction>& needle) {
171 for (size_t i = 0; i < needle.size(); i++) {
172 if (i >= haystack.size() || needle[i] != haystack[i])
173 return false;
174 }
175 return true;
176}
177
178std::vector<Instruction> DropUntilMatch(
179 std::vector<Instruction> haystack,
180 const base::span<const Instruction>& needle) {
181 while (!haystack.empty() && !MatchPrefix(haystack, needle))
182 haystack.erase(haystack.begin());
183 return haystack;
184}
185#endif // USE_CLANG_COVERAGE
186
187std::vector<Instruction> MaybeSkipCoverageHook(
188 std::vector<Instruction> instructions) {
189#if BUILDFLAG(USE_CLANG_COVERAGE)
190 // Warning: it is not illegal for the entirety of the expected crash sequence
191 // to appear as a subsequence of the coverage hook code. If that happens, this
192 // code will falsely exit early, having not found the real expected crash
193 // sequence, so this may not adequately ensure that the immediate crash
194 // sequence is present. We do check when not under coverage, at least.
195 return DropUntilMatch(instructions, base::make_span(kRequiredBody));
196#else
197 return instructions;
198#endif // USE_CLANG_COVERAGE
199}
200
Elly Fong-Jonese32032202020-09-25 02:08:15201} // namespace
202
203// Checks that the IMMEDIATE_CRASH() macro produces specific instructions; see
204// comments in immediate_crash.h for the requirements.
205TEST(ImmediateCrashTest, ExpectedOpcodeSequence) {
206 std::vector<Instruction> body;
207 ASSERT_NO_FATAL_FAILURE(GetTestFunctionInstructions(&body));
208 SCOPED_TRACE(HexEncode(body.data(), body.size() * sizeof(Instruction)));
209
210 auto it = std::find(body.begin(), body.end(), kRet);
211 ASSERT_NE(body.end(), it) << "Failed to find return opcode";
212 it++;
213
214 body = std::vector<Instruction>(it, body.end());
Elly Fong-Jones664e25f2020-10-13 21:35:35215 base::Optional<std::vector<Instruction>> result = MaybeSkipCoverageHook(body);
216 result = ExpectImmediateCrashInvocation(result.value());
Elly Fong-Jonese32032202020-09-25 02:08:15217 result = MaybeSkipOptionalFooter(result.value());
Elly Fong-Jones664e25f2020-10-13 21:35:35218 result = MaybeSkipCoverageHook(result.value());
Elly Fong-Jonese32032202020-09-25 02:08:15219 result = ExpectImmediateCrashInvocation(result.value());
220 ASSERT_TRUE(result);
221}
Daniel Chenged0471b2019-05-10 11:43:36222
223} // namespace base