| // 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 <linux/auxvec.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <limits> |
| |
| #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 <android/api-level.h> |
| #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<VMAddress>(signed_bias)); |
| } else { |
| uint32_t unsigned_bias32; |
| ASSERT_TRUE(AssignIfInRange(&unsigned_bias32, unsigned_bias)); |
| |
| uint32_t casted_bias32 = static_cast<uint32_t>(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<int>::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<ElfImageReader> 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<ElfImageReader>(); |
| 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<VMAddress>(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 |