Daniel Cheng | ed0471b | 2019-05-10 11:43:36 | [diff] [blame^] | 1 | // 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 | |
| 19 | namespace 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. |
| 28 | TEST(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 |