mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-28 15:50:26 +08:00
linux: Add AuxiliaryVector for reading other process' aux vectors
Bug: crashpad:30 Change-Id: Ief19be7d60decb17f159b3d740ac9d15a034b807 Reviewed-on: https://chromium-review.googlesource.com/526533 Commit-Queue: Joshua Peraza <jperaza@chromium.org> Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
dbc229a2d7
commit
8e2e805fa5
104
util/linux/auxiliary_vector.cc
Normal file
104
util/linux/auxiliary_vector.cc
Normal file
@ -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 <linux/auxvec.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#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<uint64_t>(pid) : Read<uint32_t>(pid);
|
||||
}
|
||||
|
||||
template <typename ULong>
|
||||
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<const char*>(&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
|
76
util/linux/auxiliary_vector.h
Normal file
76
util/linux/auxiliary_vector.h
Normal file
@ -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 <sys/types.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#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 `<linux/auxvec.h>`.
|
||||
//! \param[out] value The value, casted to an appropriate type, if found.
|
||||
//! \return `true` if the value is found.
|
||||
template <typename V>
|
||||
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<char*>(value), sizeof(V));
|
||||
}
|
||||
|
||||
protected:
|
||||
std::map<uint64_t, uint64_t> values_;
|
||||
|
||||
private:
|
||||
template <typename ULong>
|
||||
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_
|
209
util/linux/auxiliary_vector_test.cc
Normal file
209
util/linux/auxiliary_vector_test.cc
Normal file
@ -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 <linux/auxvec.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#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<LinuxVMAddress>(_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<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, 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, 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, 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
|
@ -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',
|
||||
|
@ -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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user