diff --git a/util/linux/auxiliary_vector.cc b/util/linux/auxiliary_vector.cc new file mode 100644 index 00000000..8852e614 --- /dev/null +++ b/util/linux/auxiliary_vector.cc @@ -0,0 +1,104 @@ +// 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/files/file_path.h" +#include "base/logging.h" +#include "util/file/file_reader.h" +#include "util/stdlib/map_insert.h" + +namespace crashpad { + +AuxiliaryVector::AuxiliaryVector() : values_() {} + +AuxiliaryVector::~AuxiliaryVector() {} + +bool AuxiliaryVector::Initialize(pid_t pid, bool is_64_bit) { + return is_64_bit ? Read(pid) : Read(pid); +} + +template +bool AuxiliaryVector::Read(pid_t pid) { + char path[32]; + snprintf(path, sizeof(path), "/proc/%d/auxv", pid); + FileReader aux_file; + if (!aux_file.Open(base::FilePath(path))) { + return false; + } + + ULong type; + ULong value; + while (aux_file.ReadExactly(&type, sizeof(type)) && + aux_file.ReadExactly(&value, sizeof(value))) { + if (type == AT_NULL && value == 0) { + return true; + } + if (type == AT_IGNORE) { + continue; + } + if (!MapInsertOrReplace(&values_, type, value, nullptr)) { + LOG(ERROR) << "duplicate auxv entry"; + return false; + } + } + return false; +} + +// static +bool AuxiliaryVector::VariableSizeBitCast(uint64_t data, + char* dest, + size_t dest_size) { + auto data_p = reinterpret_cast(&data); + constexpr size_t data_size = sizeof(uint64_t); + + // Verify that any unused bytes from data are zero. + // The unused bytes are at the start of the data buffer for big-endian and the + // end of the buffer for little-endian. + if (dest_size < data_size) { + uint64_t zero = 0; + auto extra_bytes = data_p; +#if defined(ARCH_CPU_LITTLE_ENDIAN) + extra_bytes += dest_size; +#endif // ARCH_CPU_LITTLE_ENDIAN + if (memcmp(extra_bytes, &zero, data_size - dest_size) != 0) { + LOG(ERROR) << "information loss"; + return false; + } + } + + // Zero out the destination, in case it is larger than data. + memset(dest, 0, dest_size); + +#if defined(ARCH_CPU_LITTLE_ENDIAN) + // Copy a prefix of data to a prefix of dest for little-endian + memcpy(dest, data_p, std::min(dest_size, data_size)); +#else + // or the suffix of data to the suffix of dest for big-endian + if (data_size >= dest_size) { + memcpy(dest, data_p + data_size - dest_size, dest_size); + } else { + memcpy(dest + dest_size - data_size, data_p, data_size); + } +#endif // ARCH_CPU_LITTLE_ENDIAN + return true; +} + +} // namespace crashpad diff --git a/util/linux/auxiliary_vector.h b/util/linux/auxiliary_vector.h new file mode 100644 index 00000000..904eada4 --- /dev/null +++ b/util/linux/auxiliary_vector.h @@ -0,0 +1,76 @@ +// 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. + +#ifndef CRASHPAD_UTIL_LINUX_AUXILIARY_VECTOR_H_ +#define CRASHPAD_UTIL_LINUX_AUXILIARY_VECTOR_H_ + +#include + +#include + +#include "base/logging.h" +#include "base/macros.h" + +namespace crashpad { + +//! \brief Read the auxiliary vector for a target process. +class AuxiliaryVector { + public: + AuxiliaryVector(); + ~AuxiliaryVector(); + + //! \brief Initializes this object with the auxiliary vector for the process + //! with process ID \a pid. + //! + //! This method must be called successfully prior to calling any other method + //! in this class. + //! + //! \param[in] pid The process ID of a target process. + //! \param[in] is_64_bit Whether the target process is 64-bit. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool Initialize(pid_t pid, bool is_64_bit); + + //! \brief Retrieve a value from the vector. + //! + //! \param[in] type Specifies which value should be retrieved. The possible + //! values for this parameter are defined by ``. + //! \param[out] value The value, casted to an appropriate type, if found. + //! \return `true` if the value is found. + template + bool GetValue(uint64_t type, V* value) const { + auto iter = values_.find(type); + if (iter == values_.end()) { + LOG(ERROR) << "value not found"; + return false; + } + return VariableSizeBitCast( + iter->second, reinterpret_cast(value), sizeof(V)); + } + + protected: + std::map values_; + + private: + template + bool Read(pid_t pid); + + static bool VariableSizeBitCast(uint64_t data, char* dest, size_t dest_size); + + DISALLOW_COPY_AND_ASSIGN(AuxiliaryVector); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_LINUX_AUXILIARY_VECTOR_H_ diff --git a/util/linux/auxiliary_vector_test.cc b/util/linux/auxiliary_vector_test.cc new file mode 100644 index 00000000..89bc3c7e --- /dev/null +++ b/util/linux/auxiliary_vector_test.cc @@ -0,0 +1,209 @@ +// 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/macros.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/multiprocess.h" +#include "util/linux/address_types.h" +#include "util/linux/memory_map.h" +#include "util/linux/process_memory.h" +#include "util/misc/from_pointer_cast.h" +#include "util/numeric/int128.h" + +extern "C" { +extern void _start(); +} // extern "C" + +namespace crashpad { +namespace test { +namespace { + +void TestAgainstCloneOrSelf(pid_t pid) { +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif + AuxiliaryVector aux; + ASSERT_TRUE(aux.Initialize(pid, am_64_bit)); + + MemoryMap mappings; + ASSERT_TRUE(mappings.Initialize(pid)); + + 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)); + + LinuxVMAddress entry_addr; + ASSERT_TRUE(aux.GetValue(AT_ENTRY, &entry_addr)); + EXPECT_EQ(entry_addr, FromPointerCast(_start)); + + 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()); + + ProcessMemory memory; + ASSERT_TRUE(memory.Initialize(pid)); + + 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; + ASSERT_TRUE(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("crashpad_util_test") != 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) { +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif + AuxVecTester aux; + ASSERT_TRUE(aux.Initialize(getpid(), am_64_bit)); + 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 diff --git a/util/util.gyp b/util/util.gyp index aae764e5..8e5c7cfe 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -45,6 +45,8 @@ 'file/string_file.cc', 'file/string_file.h', 'linux/address_types.h', + 'linux/auxiliary_vector.cc', + 'linux/auxiliary_vector.h', 'linux/checked_address_range.h', 'linux/memory_map.cc', 'linux/memory_map.h', diff --git a/util/util_test.gyp b/util/util_test.gyp index 63829b32..ef5a681f 100644 --- a/util/util_test.gyp +++ b/util/util_test.gyp @@ -39,6 +39,7 @@ 'file/file_io_test.cc', 'file/file_reader_test.cc', 'file/string_file_test.cc', + 'linux/auxiliary_vector_test.cc', 'linux/memory_map_test.cc', 'linux/process_memory_test.cc', 'linux/thread_info_test.cc',