// Copyright 2019 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifdef UNSAFE_BUFFERS_BUILD // TODO(crbug.com/40284755): Remove this and spanify to fix the errors. #pragma allow_unsafe_buffers #endif #include "base/immediate_crash.h" #include <stdint.h> #include <optional> #include "base/base_paths.h" #include "base/clang_profiling_buildflags.h" #include "base/containers/span.h" #include "base/files/file_path.h" #include "base/path_service.h" #include "base/ranges/algorithm.h" #include "base/scoped_native_library.h" #include "base/strings/string_number_conversions.h" #include "build/build_config.h" #include "build/buildflag.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { namespace { // If ImmediateCrash() is not treated as noreturn by the compiler, the compiler // will complain that not all paths through this function return a value. [[maybe_unused]] int TestImmediateCrashTreatedAsNoReturn() { … } #if defined(ARCH_CPU_X86_FAMILY) // This is tricksy and false, since x86 instructions are not all one byte long, // but there is no better alternative short of implementing an x86 instruction // decoder. Instruction; // 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 // Look for RET 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™. constexpr Instruction kRet = …; // INT3 ; UD2 constexpr Instruction kRequiredBody[] = …; constexpr Instruction kOptionalFooter[] = …; #elif defined(ARCH_CPU_ARMEL) using Instruction = uint16_t; // T32 opcode reference: https://developer.arm.com/docs/ddi0487/latest // Actually BX LR, canonical encoding: constexpr Instruction kRet = 0x4770; // BKPT #0; UDF #0 constexpr Instruction kRequiredBody[] = {0xbe00, 0xde00}; constexpr Instruction kOptionalFooter[] = {}; #elif defined(ARCH_CPU_ARM64) using Instruction = uint32_t; // A64 opcode reference: https://developer.arm.com/docs/ddi0487/latest // Use an enum here rather than separate constexpr vars because otherwise some // of the vars will end up unused on each platform, upsetting // -Wunused-const-variable. enum : Instruction { // There are multiple valid encodings of return (which is really a special // form of branch). This is the one clang seems to use: kRet = 0xd65f03c0, kBrk0 = 0xd4200000, kBrk1 = 0xd4200020, kBrkF000 = 0xd43e0000, kHlt0 = 0xd4400000, }; #if BUILDFLAG(IS_WIN) constexpr Instruction kRequiredBody[] = {kBrkF000, kBrk1}; constexpr Instruction kOptionalFooter[] = {}; #elif BUILDFLAG(IS_MAC) constexpr Instruction kRequiredBody[] = {kBrk0, kHlt0}; // Some clangs emit a BRK #1 for __builtin_unreachable(), but some do not, so // it is allowed but not required to occur. constexpr Instruction kOptionalFooter[] = {kBrk1}; #else constexpr Instruction kRequiredBody[] = {kBrk0, kHlt0}; constexpr Instruction kOptionalFooter[] = {}; #endif #endif // This function loads a shared library that defines two functions, // TestFunction1 and TestFunction2. It then returns the bytes of the body of // whichever of those functions happens to come first in the library. void GetTestFunctionInstructions(std::vector<Instruction>* body) { … } std::optional<std::vector<Instruction>> ExpectImmediateCrashInvocation( std::vector<Instruction> instructions) { … } std::vector<Instruction> MaybeSkipOptionalFooter( std::vector<Instruction> instructions) { … } #if BUILDFLAG(USE_CLANG_COVERAGE) || BUILDFLAG(CLANG_PROFILING) bool MatchPrefix(const std::vector<Instruction>& haystack, const base::span<const Instruction>& needle) { for (size_t i = 0; i < needle.size(); i++) { if (i >= haystack.size() || needle[i] != haystack[i]) return false; } return true; } std::vector<Instruction> DropUntilMatch( std::vector<Instruction> haystack, const base::span<const Instruction>& needle) { while (!haystack.empty() && !MatchPrefix(haystack, needle)) haystack.erase(haystack.begin()); return haystack; } #endif // USE_CLANG_COVERAGE || BUILDFLAG(CLANG_PROFILING) std::vector<Instruction> MaybeSkipCoverageHook( std::vector<Instruction> instructions) { … } } // namespace // Attempts to verify the actual instructions emitted by ImmediateCrash(). // While the test results are highly implementation-specific, this allows macro // changes (e.g. CLs like https://crrev.com/671123) to be verified using the // trybots/waterfall, without having to build and disassemble Chrome on // multiple platforms. This makes it easier to evaluate changes to // ImmediateCrash() against its requirements (e.g. size of emitted sequence, // whether or not multiple ImmediateCrash sequences can be folded together, et // cetera). Please see immediate_crash.h for more details about the // requirements. // // Note that C++ provides no way to get the size of a function. Instead, the // test relies on a shared library which defines only two functions and assumes // the two functions will be laid out contiguously as a heuristic for finding // the size of the function. TEST(ImmediateCrashTest, ExpectedOpcodeSequence) { … } } // namespace base