-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathimmediate_crash_unittest.cc
159 lines (132 loc) · 6.16 KB
/
immediate_crash_unittest.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/immediate_crash.h"
#include <stdint.h>
#include <algorithm>
#include "base/base_paths.h"
#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/native_library.h"
#include "base/optional.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
// Compile test.
int TestImmediateCrashTreatedAsNoReturn() {
IMMEDIATE_CRASH();
}
// iOS is excluded, since it doesn't support loading shared libraries.
#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \
defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_CHROMEOS) || \
defined(OS_FUCHSIA)
// Checks that the IMMEDIATE_CRASH() macro produces specific instructions; see
// comments in immediate_crash.h for the requirements.
TEST(ImmediateCrashTest, ExpectedOpcodeSequence) {
// TestFunction1() and TestFunction2() are defined in a shared library in an
// attempt to guarantee that they are located next to each other.
NativeLibraryLoadError load_error;
FilePath helper_library_path;
#if !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
// On Android M, DIR_EXE == /system/bin when running base_unittests.
// On Fuchsia, NativeLibrary understands the native convention that libraries
// are not colocated with the binary.
ASSERT_TRUE(PathService::Get(DIR_EXE, &helper_library_path));
#endif
helper_library_path = helper_library_path.AppendASCII(
GetNativeLibraryName("immediate_crash_test_helper"));
#if defined(OS_ANDROID) && defined(COMPONENT_BUILD)
helper_library_path = helper_library_path.ReplaceExtension(".cr.so");
#endif
// TODO(dcheng): Shouldn't GetNativeLibraryName just return a FilePath?
NativeLibrary helper_library =
LoadNativeLibrary(helper_library_path, &load_error);
ASSERT_TRUE(helper_library)
<< "shared library load failed: " << load_error.ToString();
// TestFunction1() and TestFunction2() each contain two IMMEDIATE_CRASH()
// invocations. IMMEDIATE_CRASH() should be treated as a noreturn sequence and
// optimized into the function epilogue. The general strategy is to find the
// return opcode, then scan the following bytes for the opcodes for two
// consecutive IMMEDIATE_CRASH() sequences.
void* a =
GetFunctionPointerFromNativeLibrary(helper_library, "TestFunction1");
ASSERT_TRUE(a);
void* b =
GetFunctionPointerFromNativeLibrary(helper_library, "TestFunction2");
ASSERT_TRUE(b);
#if defined(ARCH_CPU_X86_FAMILY)
// X86 opcode reference:
// 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
span<const uint8_t> function_body =
a < b ? make_span(static_cast<const uint8_t*>(a),
static_cast<const uint8_t*>(b))
: make_span(static_cast<const uint8_t*>(b),
static_cast<const uint8_t*>(a));
SCOPED_TRACE(HexEncode(function_body.data(), function_body.size_bytes()));
// Look for RETN opcode (0xC3). Note that 0xC3 is a substring of several
// other opcodes (VMRESUME, MOVNTI), and can also be encoded as part of an
// argument to another opcode. None of these other cases are expected to be
// present, so a simple byte scan should be Good Enough™.
auto it = std::find(function_body.begin(), function_body.end(), 0xC3);
ASSERT_NE(function_body.end(), it) << "Failed to find return! ";
// Look for two IMMEDIATE_CRASH() opcode sequences.
for (int i = 0; i < 2; ++i) {
// INT 3
EXPECT_EQ(0xCC, *++it);
// UD2
EXPECT_EQ(0x0F, *++it);
EXPECT_EQ(0x0B, *++it);
}
#elif defined(ARCH_CPU_ARMEL)
// Routines loaded from a shared library will have the LSB in the pointer set
// if encoded as T32 instructions. The rest of this test assumes T32.
ASSERT_TRUE(reinterpret_cast<uintptr_t>(a) & 0x1)
<< "Expected T32 opcodes but found A32 opcodes instead.";
ASSERT_TRUE(reinterpret_cast<uintptr_t>(b) & 0x1)
<< "Expected T32 opcodes but found A32 opcodes instead.";
// Mask off the lowest bit.
a = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(a) & ~uintptr_t{0x1});
b = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(b) & ~uintptr_t{0x1});
// T32 opcode reference: https://developer.arm.com/docs/ddi0487/latest
span<const uint16_t> function_body =
a < b ? make_span(static_cast<const uint16_t*>(a),
static_cast<const uint16_t*>(b))
: make_span(static_cast<const uint16_t*>(b),
static_cast<const uint16_t*>(a));
SCOPED_TRACE(HexEncode(function_body.data(), function_body.size_bytes()));
// Look for the standard return opcode sequence (BX LR).
auto it = std::find(function_body.begin(), function_body.end(), 0x4770);
ASSERT_NE(function_body.end(), it) << "Failed to find return! ";
// Look for two IMMEDIATE_CRASH() opcode sequences.
for (int i = 0; i < 2; ++i) {
// BKPT #0
EXPECT_EQ(0xBE00, *++it);
// UDF #0
EXPECT_EQ(0xDE00, *++it);
}
#elif defined(ARCH_CPU_ARM64)
// A64 opcode reference: https://developer.arm.com/docs/ddi0487/latest
span<const uint32_t> function_body =
a < b ? make_span(static_cast<const uint32_t*>(a),
static_cast<const uint32_t*>(b))
: make_span(static_cast<const uint32_t*>(b),
static_cast<const uint32_t*>(a));
SCOPED_TRACE(HexEncode(function_body.data(), function_body.size_bytes()));
// Look for RET. There appears to be multiple valid encodings, so this is
// hardcoded to whatever clang currently emits...
auto it = std::find(function_body.begin(), function_body.end(), 0XD65F03C0);
ASSERT_NE(function_body.end(), it) << "Failed to find return! ";
// Look for two IMMEDIATE_CRASH() opcode sequences.
for (int i = 0; i < 2; ++i) {
// BRK #0
EXPECT_EQ(0XD4200000, *++it);
// HLT #0
EXPECT_EQ(0xD4400000, *++it);
}
#endif // defined(ARCH_CPU_X86_FAMILY)
UnloadNativeLibrary(helper_library);
}
#endif
} // namespace base