// Copyright 2017 The Crashpad Authors. All rights reserved. // // 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 #include #include #include #include "base/bit_cast.h" #include "base/macros.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 !defined(OS_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 !defined(OS_ANDROID) LinuxVMAddress entry_addr; ASSERT_TRUE(aux.GetValue(AT_ENTRY, &entry_addr)); EXPECT_EQ(entry_addr, FromPointerCast(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); LinuxVMAddress platform_addr; ASSERT_TRUE(aux.GetValue(AT_PLATFORM, &platform_addr)); std::string platform; ASSERT_TRUE(memory.ReadCStringSizeLimited(platform_addr, 10, &platform)); #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() {} private: void MultiprocessParent() override { TestAgainstCloneOrSelf(ChildPID()); } void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); } DISALLOW_COPY_AND_ASSIGN(ReadChildTest); }; 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, bit_cast(neg1_32)); int32_t outval32s; ASSERT_TRUE(aux.GetValue(type, &outval32s)); EXPECT_EQ(outval32s, neg1_32); constexpr int32_t int32_max = std::numeric_limits::max(); aux.Insert(type, bit_cast(int32_max)); ASSERT_TRUE(aux.GetValue(type, &outval32s)); EXPECT_EQ(outval32s, int32_max); constexpr uint32_t uint32_max = std::numeric_limits::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, bit_cast(neg1_64)); int64_t outval64s; ASSERT_TRUE(aux.GetValue(type, &outval64s)); EXPECT_EQ(outval64s, neg1_64); constexpr int64_t int64_max = std::numeric_limits::max(); aux.Insert(type, bit_cast(int64_max)); ASSERT_TRUE(aux.GetValue(type, &outval64s)); EXPECT_EQ(outval64s, int64_max); constexpr uint64_t uint64_max = std::numeric_limits::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