// Copyright 2017 The Crashpad Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "snapshot/linux/debug_rendezvous.h" #include #include #include #include #include "base/format_macros.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" #include "build/build_config.h" #include "gtest/gtest.h" #include "snapshot/elf/elf_image_reader.h" #include "snapshot/linux/test_modules.h" #include "test/linux/fake_ptrace_connection.h" #include "test/main_arguments.h" #include "test/multiprocess.h" #include "util/linux/address_types.h" #include "util/linux/auxiliary_vector.h" #include "util/linux/direct_ptrace_connection.h" #include "util/linux/memory_map.h" #include "util/misc/address_sanitizer.h" #include "util/misc/memory_sanitizer.h" #include "util/numeric/safe_assignment.h" #include "util/process/process_memory_linux.h" #include "util/process/process_memory_range.h" #if BUILDFLAG(IS_ANDROID) #include #endif namespace crashpad { namespace test { namespace { void ExpectLoadBias(bool is_64_bit, VMAddress unsigned_bias, VMOffset signed_bias) { if (is_64_bit) { EXPECT_EQ(unsigned_bias, static_cast(signed_bias)); } else { uint32_t unsigned_bias32; ASSERT_TRUE(AssignIfInRange(&unsigned_bias32, unsigned_bias)); uint32_t casted_bias32 = static_cast(signed_bias); EXPECT_EQ(unsigned_bias32, casted_bias32); } } void TestAgainstTarget(PtraceConnection* connection) { // Use ElfImageReader on the main executable which can tell us the debug // address. glibc declares the symbol _r_debug in link.h which we can use to // get the address, but Android does not. AuxiliaryVector aux; ASSERT_TRUE(aux.Initialize(connection)); LinuxVMAddress phdrs; ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs)); MemoryMap mappings; ASSERT_TRUE(mappings.Initialize(connection)); const MemoryMap::Mapping* phdr_mapping = mappings.FindMapping(phdrs); ASSERT_TRUE(phdr_mapping); auto exe_mappings = mappings.FindFilePossibleMmapStarts(*phdr_mapping); ASSERT_EQ(exe_mappings->Count(), 1u); LinuxVMAddress elf_address = exe_mappings->Next()->range.Base(); ProcessMemoryLinux memory(connection); ProcessMemoryRange range; ASSERT_TRUE(range.Initialize(&memory, connection->Is64Bit())); ElfImageReader exe_reader; ASSERT_TRUE(exe_reader.Initialize(range, elf_address)); LinuxVMAddress debug_address; ASSERT_TRUE(exe_reader.GetDebugAddress(&debug_address)); VMAddress exe_dynamic_address = 0; if (exe_reader.GetDynamicArrayAddress(&exe_dynamic_address)) { CheckedLinuxAddressRange exe_range( connection->Is64Bit(), exe_reader.Address(), exe_reader.Size()); EXPECT_TRUE(exe_range.ContainsValue(exe_dynamic_address)); } // start the actual tests DebugRendezvous debug; ASSERT_TRUE(debug.Initialize(range, debug_address)); #if BUILDFLAG(IS_ANDROID) const int android_runtime_api = android_get_device_api_level(); ASSERT_GE(android_runtime_api, 1); base::FilePath exe_name(base::FilePath(GetMainArguments()[0]).BaseName()); EXPECT_NE(debug.Executable()->name.find(exe_name.value()), std::string::npos); // Android's loader doesn't set the dynamic array for the executable in the // link map until Android 10.0 (API 29). if (android_runtime_api >= 29) { EXPECT_EQ(debug.Executable()->dynamic_array, exe_dynamic_address); } else { EXPECT_EQ(debug.Executable()->dynamic_array, 0u); } #else // glibc's loader implements most of the tested features that Android's was // missing but has since gained. const int android_runtime_api = std::numeric_limits::max(); // glibc's loader does not set the name for the executable. EXPECT_TRUE(debug.Executable()->name.empty()); EXPECT_EQ(debug.Executable()->dynamic_array, exe_dynamic_address); #endif // BUILDFLAG(IS_ANDROID) // Android's loader doesn't set the load bias until Android 4.3 (API 18). if (android_runtime_api >= 18) { ExpectLoadBias(connection->Is64Bit(), debug.Executable()->load_bias, exe_reader.GetLoadBias()); } else { EXPECT_EQ(debug.Executable()->load_bias, 0u); } for (const DebugRendezvous::LinkEntry& module : debug.Modules()) { SCOPED_TRACE(base::StringPrintf("name %s, load_bias 0x%" PRIx64 ", dynamic_array 0x%" PRIx64, module.name.c_str(), module.load_bias, module.dynamic_array)); const bool is_android_loader = (module.name == "/system/bin/linker" || module.name == "/system/bin/linker64"); // Android's loader doesn't set its own dynamic array until Android 4.2 // (API 17). if (is_android_loader && android_runtime_api < 17) { EXPECT_EQ(module.dynamic_array, 0u); EXPECT_EQ(module.load_bias, 0u); continue; } ASSERT_TRUE(module.dynamic_array); const MemoryMap::Mapping* dyn_mapping = mappings.FindMapping(module.dynamic_array); ASSERT_TRUE(dyn_mapping); auto possible_mappings = mappings.FindFilePossibleMmapStarts(*dyn_mapping); ASSERT_GE(possible_mappings->Count(), 1u); std::unique_ptr module_reader; #if !BUILDFLAG(IS_ANDROID) const MemoryMap::Mapping* module_mapping = nullptr; #endif const MemoryMap::Mapping* mapping = nullptr; while ((mapping = possible_mappings->Next())) { auto parsed_module = std::make_unique(); VMAddress dynamic_address; if (parsed_module->Initialize( range, mapping->range.Base(), possible_mappings->Count() == 0) && parsed_module->GetDynamicArrayAddress(&dynamic_address) && dynamic_address == module.dynamic_array) { module_reader = std::move(parsed_module); #if !BUILDFLAG(IS_ANDROID) module_mapping = mapping; #endif break; } } ASSERT_TRUE(module_reader.get()); #if BUILDFLAG(IS_ANDROID) EXPECT_FALSE(module.name.empty()); #else // glibc's loader doesn't always set the name in the link map for the vdso. EXPECT_PRED4( [](const std::string mapping_name, int device, int inode, const std::string& module_name) { const bool is_vdso_mapping = device == 0 && inode == 0 && mapping_name == "[vdso]"; #if defined(ARCH_CPU_X86) static constexpr char kPrefix[] = "linux-gate.so."; #else static constexpr char kPrefix[] = "linux-vdso.so."; #endif return is_vdso_mapping == (module_name.empty() || module_name.compare(0, strlen(kPrefix), kPrefix) == 0); }, module_mapping->name, module_mapping->device, module_mapping->inode, module.name); #endif // BUILDFLAG(IS_ANDROID) // Android's loader stops setting its own load bias after Android 4.4.4 // (API 20) until Android 6.0 (API 23). if (is_android_loader && android_runtime_api > 20 && android_runtime_api < 23) { EXPECT_EQ(module.load_bias, 0u); } else { ExpectLoadBias(connection->Is64Bit(), module.load_bias, static_cast(module_reader->GetLoadBias())); } CheckedLinuxAddressRange module_range( connection->Is64Bit(), module_reader->Address(), module_reader->Size()); EXPECT_TRUE(module_range.ContainsValue(module.dynamic_array)); } } TEST(DebugRendezvous, Self) { #if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) const std::string module_name = "test_module.so"; const std::string module_soname = "test_module_soname"; ScopedModuleHandle empty_test_module( LoadTestModule(module_name, module_soname)); ASSERT_TRUE(empty_test_module.valid()); #endif // !ADDRESS_SANITIZER && !MEMORY_SANITIZER FakePtraceConnection connection; ASSERT_TRUE(connection.Initialize(getpid())); TestAgainstTarget(&connection); } class ChildTest : public Multiprocess { public: ChildTest() {} ChildTest(const ChildTest&) = delete; ChildTest& operator=(const ChildTest&) = delete; ~ChildTest() {} private: void MultiprocessParent() { DirectPtraceConnection connection; ASSERT_TRUE(connection.Initialize(ChildPID())); TestAgainstTarget(&connection); } void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); } }; TEST(DebugRendezvous, Child) { ChildTest test; test.Run(); } } // namespace } // namespace test } // namespace crashpad