| // 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 "util/linux/auxiliary_vector.h" |
| |
| #include <linux/auxvec.h> |
| #include <sys/utsname.h> |
| #include <unistd.h> |
| |
| #include <limits> |
| |
| #include "base/bit_cast.h" |
| #include "build/build_config.h" |
| #include "gtest/gtest.h" |
| #include "test/errors.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/memory_map.h" |
| #include "util/misc/from_pointer_cast.h" |
| #include "util/numeric/int128.h" |
| #include "util/process/process_memory_linux.h" |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| // TODO(jperaza): This symbol isn't defined when building in chromium for |
| // Android. There may be another symbol to use. |
| extern "C" { |
| #if defined(ARCH_CPU_MIPS_FAMILY) |
| #define START_SYMBOL __start |
| #else |
| #define START_SYMBOL _start |
| #endif |
| extern void START_SYMBOL(); |
| } // extern "C" |
| #endif |
| |
| namespace crashpad { |
| namespace test { |
| namespace { |
| |
| void TestAgainstCloneOrSelf(pid_t pid) { |
| FakePtraceConnection connection; |
| ASSERT_TRUE(connection.Initialize(pid)); |
| |
| AuxiliaryVector aux; |
| ASSERT_TRUE(aux.Initialize(&connection)); |
| |
| MemoryMap mappings; |
| ASSERT_TRUE(mappings.Initialize(&connection)); |
| |
| LinuxVMAddress phdrs; |
| ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs)); |
| EXPECT_TRUE(mappings.FindMapping(phdrs)); |
| |
| int pagesize; |
| ASSERT_TRUE(aux.GetValue(AT_PAGESZ, &pagesize)); |
| EXPECT_EQ(pagesize, getpagesize()); |
| |
| LinuxVMAddress interp_base; |
| ASSERT_TRUE(aux.GetValue(AT_BASE, &interp_base)); |
| EXPECT_TRUE(mappings.FindMapping(interp_base)); |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| LinuxVMAddress entry_addr; |
| ASSERT_TRUE(aux.GetValue(AT_ENTRY, &entry_addr)); |
| EXPECT_EQ(entry_addr, FromPointerCast<LinuxVMAddress>(START_SYMBOL)); |
| #endif |
| |
| uid_t uid; |
| ASSERT_TRUE(aux.GetValue(AT_UID, &uid)); |
| EXPECT_EQ(uid, getuid()); |
| |
| uid_t euid; |
| ASSERT_TRUE(aux.GetValue(AT_EUID, &euid)); |
| EXPECT_EQ(euid, geteuid()); |
| |
| gid_t gid; |
| ASSERT_TRUE(aux.GetValue(AT_GID, &gid)); |
| EXPECT_EQ(gid, getgid()); |
| |
| gid_t egid; |
| ASSERT_TRUE(aux.GetValue(AT_EGID, &egid)); |
| EXPECT_EQ(egid, getegid()); |
| |
| ProcessMemoryLinux memory(&connection); |
| |
| // AT_PLATFORM is null for RISC-V: |
| // https://elixir.bootlin.com/linux/v6.4-rc4/C/ident/ELF_PLATFORM |
| #if !defined(ARCH_CPU_RISCV64) |
| LinuxVMAddress platform_addr; |
| ASSERT_TRUE(aux.GetValue(AT_PLATFORM, &platform_addr)); |
| std::string platform; |
| ASSERT_TRUE(memory.ReadCStringSizeLimited(platform_addr, 10, &platform)); |
| #endif // ARCH_CPU_RISCV64 |
| |
| #if defined(ARCH_CPU_X86) |
| EXPECT_STREQ(platform.c_str(), "i686"); |
| #elif defined(ARCH_CPU_X86_64) |
| EXPECT_STREQ(platform.c_str(), "x86_64"); |
| #elif defined(ARCH_CPU_ARMEL) |
| // Machine name and platform are set in Linux:/arch/arm/kernel/setup.c |
| // Machine typically looks like "armv7l". |
| // Platform typically looks like "v7l". |
| utsname sys_names; |
| ASSERT_EQ(uname(&sys_names), 0); |
| std::string machine_name(sys_names.machine); |
| EXPECT_NE(machine_name.find(platform), std::string::npos); |
| #elif defined(ARCH_CPU_ARM64) |
| EXPECT_STREQ(platform.c_str(), "aarch64"); |
| #endif // ARCH_CPU_X86 |
| |
| #if defined(AT_SYSINFO_EHDR) |
| LinuxVMAddress vdso_addr; |
| if (aux.GetValue(AT_SYSINFO_EHDR, &vdso_addr)) { |
| EXPECT_TRUE(mappings.FindMapping(vdso_addr)); |
| } |
| #endif // AT_SYSINFO_EHDR |
| |
| #if defined(AT_EXECFN) |
| LinuxVMAddress filename_addr; |
| ASSERT_TRUE(aux.GetValue(AT_EXECFN, &filename_addr)); |
| std::string filename; |
| ASSERT_TRUE(memory.ReadCStringSizeLimited(filename_addr, 4096, &filename)); |
| EXPECT_TRUE(filename.find(GetMainArguments()[0]) != std::string::npos); |
| #endif // AT_EXECFN |
| |
| int ignore; |
| EXPECT_FALSE(aux.GetValue(AT_NULL, &ignore)); |
| |
| char too_small; |
| EXPECT_FALSE(aux.GetValue(AT_PAGESZ, &too_small)); |
| |
| uint128_struct big_dest; |
| memset(&big_dest, 0xf, sizeof(big_dest)); |
| ASSERT_TRUE(aux.GetValue(AT_PHDR, &big_dest)); |
| EXPECT_EQ(big_dest.lo, phdrs); |
| } |
| |
| TEST(AuxiliaryVector, ReadSelf) { |
| TestAgainstCloneOrSelf(getpid()); |
| } |
| |
| class ReadChildTest : public Multiprocess { |
| public: |
| ReadChildTest() : Multiprocess() {} |
| |
| ReadChildTest(const ReadChildTest&) = delete; |
| ReadChildTest& operator=(const ReadChildTest&) = delete; |
| |
| ~ReadChildTest() {} |
| |
| private: |
| void MultiprocessParent() override { TestAgainstCloneOrSelf(ChildPID()); } |
| |
| void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); } |
| }; |
| |
| TEST(AuxiliaryVector, ReadChild) { |
| ReadChildTest test; |
| test.Run(); |
| } |
| |
| class AuxVecTester : public AuxiliaryVector { |
| public: |
| AuxVecTester() : AuxiliaryVector() {} |
| void Insert(uint64_t type, uint64_t value) { values_[type] = value; } |
| }; |
| |
| TEST(AuxiliaryVector, SignedBit) { |
| FakePtraceConnection connection; |
| ASSERT_TRUE(connection.Initialize(getpid())); |
| |
| AuxVecTester aux; |
| ASSERT_TRUE(&connection); |
| constexpr uint64_t type = 0x0000000012345678; |
| |
| constexpr int32_t neg1_32 = -1; |
| aux.Insert(type, base::bit_cast<uint32_t>(neg1_32)); |
| int32_t outval32s; |
| ASSERT_TRUE(aux.GetValue(type, &outval32s)); |
| EXPECT_EQ(outval32s, neg1_32); |
| |
| constexpr int32_t int32_max = std::numeric_limits<int32_t>::max(); |
| aux.Insert(type, base::bit_cast<uint32_t>(int32_max)); |
| ASSERT_TRUE(aux.GetValue(type, &outval32s)); |
| EXPECT_EQ(outval32s, int32_max); |
| |
| constexpr uint32_t uint32_max = std::numeric_limits<uint32_t>::max(); |
| aux.Insert(type, uint32_max); |
| uint32_t outval32u; |
| ASSERT_TRUE(aux.GetValue(type, &outval32u)); |
| EXPECT_EQ(outval32u, uint32_max); |
| |
| constexpr int64_t neg1_64 = -1; |
| aux.Insert(type, base::bit_cast<uint64_t>(neg1_64)); |
| int64_t outval64s; |
| ASSERT_TRUE(aux.GetValue(type, &outval64s)); |
| EXPECT_EQ(outval64s, neg1_64); |
| |
| constexpr int64_t int64_max = std::numeric_limits<int64_t>::max(); |
| aux.Insert(type, base::bit_cast<uint64_t>(int64_max)); |
| ASSERT_TRUE(aux.GetValue(type, &outval64s)); |
| EXPECT_EQ(outval64s, int64_max); |
| |
| constexpr uint64_t uint64_max = std::numeric_limits<uint64_t>::max(); |
| aux.Insert(type, uint64_max); |
| uint64_t outval64u; |
| ASSERT_TRUE(aux.GetValue(type, &outval64u)); |
| EXPECT_EQ(outval64u, uint64_max); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace crashpad |