blob: d7330133303f103836742d737e82ce76473739c2 [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
9#include <algorithm>
10
11#include "base/containers/span.h"
12#include "base/files/file_path.h"
13#include "base/native_library.h"
14#include "base/optional.h"
15#include "base/strings/string_number_conversions.h"
16#include "build/build_config.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
19namespace base {
20
21// iOS is excluded, since it doesn't support loading shared libraries.
22#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \
23 defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_CHROMEOS) || \
24 defined(OS_FUCHSIA)
25
26// Checks that the IMMEDIATE_CRASH() macro produces specific instructions; see
27// comments in immediate_crash.h for the requirements.
28TEST(ImmediateCrashTest, ExpectedOpcodeSequence) {
29 // TestFunction1() and TestFunction2() are defined in a shared library in an
30 // attempt to guarantee that they are located next to each other.
31 NativeLibraryLoadError load_error;
32 // TODO(dcheng): Shouldn't GetNativeLibraryName just return a FilePath?
33 NativeLibrary helper_library = LoadNativeLibrary(
34 FilePath::FromUTF8Unsafe(
35 GetNativeLibraryName("immediate_crash_test_helper")),
36 &load_error);
37 ASSERT_TRUE(helper_library)
38 << "shared library load failed: " << load_error.ToString();
39
40 // TestFunction1() and TestFunction2() each contain two IMMEDIATE_CRASH()
41 // invocations. IMMEDIATE_CRASH() should be treated as a noreturn sequence and
42 // optimized into the function epilogue. The general strategy is to find the
43 // return opcode, then scan the following bytes for the opcodes for two
44 // consecutive IMMEDIATE_CRASH() sequences.
45 void* a =
46 GetFunctionPointerFromNativeLibrary(helper_library, "TestFunction1");
47 ASSERT_TRUE(a);
48 void* b =
49 GetFunctionPointerFromNativeLibrary(helper_library, "TestFunction2");
50 ASSERT_TRUE(b);
51
52#if defined(ARCH_CPU_X86_FAMILY)
53
54 // X86 opcode reference:
55 // 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
56 span<const uint8_t> function_body =
57 a < b ? make_span(static_cast<const uint8_t*>(a),
58 static_cast<const uint8_t*>(b))
59 : make_span(static_cast<const uint8_t*>(b),
60 static_cast<const uint8_t*>(a));
61 SCOPED_TRACE(HexEncode(function_body.data(), function_body.size_bytes()));
62
63 // Look for RETN opcode (0xC3). Note that 0xC3 is a substring of several
64 // other opcodes (VMRESUME, MOVNTI), and can also be encoded as part of an
65 // argument to another opcode. None of these other cases are expected to be
66 // present, so a simple byte scan should be Good Enoughâ„¢.
67 auto it = std::find(function_body.begin(), function_body.end(), 0xC3);
68 ASSERT_NE(function_body.end(), it) << "Failed to find return! ";
69
70 // Look for two IMMEDIATE_CRASH() opcode sequences.
71 base::Optional<uint8_t> nonce;
72 for (int i = 0; i < 2; ++i) {
73 // INT 3
74 EXPECT_EQ(0xCC, *++it);
75 // UD2
76 EXPECT_EQ(0x0F, *++it);
77 EXPECT_EQ(0x0B, *++it);
78 // PUSH
79 EXPECT_EQ(0x6A, *++it);
80 // Immediate nonce argument to PUSH
81 if (!nonce) {
82 nonce = *++it;
83 } else {
84 EXPECT_NE(*nonce, *++it);
85 }
86#if (defined(OS_WIN) && defined(ARCH_CPU_64_BITS)) || defined(OS_MACOSX)
87 // On Windows x64 and Mac, __builtin_unreachable() generates UD2. See
88 // https://crbug.com/958373.
89 EXPECT_EQ(0x0F, *++it);
90 EXPECT_EQ(0x0B, *++it);
91#endif // defined(OS_WIN) || defined(OS_MACOSX)
92 }
93
94#elif defined(ARCH_CPU_ARMEL)
95
96 // Routines loaded from a shared library will have the LSB in the pointer set
97 // if encoded as T32 instructions. The rest of this test assumes T32.
98 ASSERT_TRUE(reinterpret_cast<uintptr_t>(a) & 0x1)
99 << "Expected T32 opcodes but found A32 opcodes instead.";
100 ASSERT_TRUE(reinterpret_cast<uintptr_t>(b) & 0x1)
101 << "Expected T32 opcodes but found A32 opcodes instead.";
102
103 // Mask off the lowest bit.
104 a = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(a) & ~uintptr_t{0x1});
105 b = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(b) & ~uintptr_t{0x1});
106
107 // T32 opcode reference: https://developer.arm.com/docs/ddi0487/latest
108 span<const uint16_t> function_body =
109 a < b ? make_span(static_cast<const uint16_t*>(a),
110 static_cast<const uint16_t*>(b))
111 : make_span(static_cast<const uint16_t*>(b),
112 static_cast<const uint16_t*>(a));
113 SCOPED_TRACE(HexEncode(function_body.data(), function_body.size_bytes()));
114
115 // Look for the standard return opcode sequence (BX LR).
116 auto it = std::find(function_body.begin(), function_body.end(), 0x4770);
117 ASSERT_NE(function_body.end(), it) << "Failed to find return! ";
118
119 // Look for two IMMEDIATE_CRASH() opcode sequences.
120 base::Optional<uint8_t> nonce;
121 for (int i = 0; i < 2; ++i) {
122 // BKPT #0
123 EXPECT_EQ(0xBE00, *++it);
124 // UDF #<nonce>
125 EXPECT_EQ(0xDE00, *++it & 0xFF00);
126 if (!nonce) {
127 nonce = *it & 0x00FF;
128 } else {
129 EXPECT_NE(*nonce, *it & 0x00FF);
130 }
131 }
132
133#elif defined(ARCH_CPU_ARM64)
134
135 // A64 opcode reference: https://developer.arm.com/docs/ddi0487/latest
136 span<const uint32_t> function_body =
137 a < b ? make_span(static_cast<const uint32_t*>(a),
138 static_cast<const uint32_t*>(b))
139 : make_span(static_cast<const uint32_t*>(b),
140 static_cast<const uint32_t*>(a));
141 SCOPED_TRACE(HexEncode(function_body.data(), function_body.size_bytes()));
142
143 // Look for RET. There appears to be multiple valid encodings, so this is
144 // hardcoded to whatever clang currently emits...
145 auto it = std::find(function_body.begin(), function_body.end(), 0XD65F03C0);
146 ASSERT_NE(function_body.end(), it) << "Failed to find return! ";
147
148 // Look for two IMMEDIATE_CRASH() opcode sequences.
149 base::Optional<uint16_t> nonce;
150 for (int i = 0; i < 2; ++i) {
151 // BRK #0
152 EXPECT_EQ(0XD4200000, *++it);
153 // HLT #<nonce>
154 EXPECT_EQ(0xD4400000, *++it & 0xFFE00000);
155 if (!nonce) {
156 nonce = (*it >> 5) & 0xFFFF;
157 } else {
158 EXPECT_NE(*nonce, (*it >> 5) & 0xFFFF);
159 }
160 }
161
162#endif // defined(ARCH_CPU_X86_FAMILY)
163
164 UnloadNativeLibrary(helper_library);
165}
166
167#endif
168
169} // namespace base