mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-10 06:36:02 +00:00
This change was partially scripted and partially done manually with vim regex + manually placing the deleted constructors. The script change looked for destructors in the public: section of a class, if that existed the deleted constructors would go before the destructor. For manual placement I looked for any constructor in the public: section of the corresponding class. If there wasn't one, then it would ideally have gone as the first entry except below enums, classes and typedefs. This may not have been perfect, but is hopefully good enough. Fingers crossed. #include "base/macros.h" is removed from files that don't use ignore_result, which is the only other thing defined in base/macros.h. Bug: chromium:1010217 Change-Id: I099526255a40b1ac1264904b4ece2f3f503c9418 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3171034 Reviewed-by: Mark Mentovai <mark@chromium.org> Commit-Queue: Peter Boström <pbos@chromium.org>
405 lines
13 KiB
C++
405 lines
13 KiB
C++
// 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/memory_map.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/sysmacros.h>
|
|
|
|
#include "base/bit_cast.h"
|
|
#include "base/files/file_path.h"
|
|
#include "base/logging.h"
|
|
#include "build/build_config.h"
|
|
#include "util/file/delimited_file_reader.h"
|
|
#include "util/file/file_io.h"
|
|
#include "util/file/string_file.h"
|
|
#include "util/stdlib/string_number_conversion.h"
|
|
|
|
namespace crashpad {
|
|
|
|
namespace {
|
|
|
|
template <typename Type>
|
|
bool HexStringToNumber(const std::string& string, Type* number) {
|
|
return StringToNumber("0x" + string, number);
|
|
}
|
|
|
|
// The result from parsing a line from the maps file.
|
|
enum class ParseResult {
|
|
// A line was successfully parsed.
|
|
kSuccess = 0,
|
|
|
|
// The end of the file was successfully reached.
|
|
kEndOfFile,
|
|
|
|
// There was an error in the file, likely because it was read non-atmoically.
|
|
// We should try to read it again.
|
|
kRetry,
|
|
|
|
// An error with a message logged.
|
|
kError
|
|
};
|
|
|
|
// Reads a line from a maps file being read by maps_file_reader and extends
|
|
// mappings with a new MemoryMap::Mapping describing the line.
|
|
ParseResult ParseMapsLine(DelimitedFileReader* maps_file_reader,
|
|
std::vector<MemoryMap::Mapping>* mappings) {
|
|
std::string field;
|
|
LinuxVMAddress start_address;
|
|
switch (maps_file_reader->GetDelim('-', &field)) {
|
|
case DelimitedFileReader::Result::kError:
|
|
return ParseResult::kError;
|
|
case DelimitedFileReader::Result::kEndOfFile:
|
|
return ParseResult::kEndOfFile;
|
|
case DelimitedFileReader::Result::kSuccess:
|
|
field.pop_back();
|
|
if (!HexStringToNumber(field, &start_address)) {
|
|
LOG(ERROR) << "format error";
|
|
return ParseResult::kError;
|
|
}
|
|
if (!mappings->empty() && start_address < mappings->back().range.End()) {
|
|
return ParseResult::kRetry;
|
|
}
|
|
}
|
|
|
|
LinuxVMAddress end_address;
|
|
if (maps_file_reader->GetDelim(' ', &field) !=
|
|
DelimitedFileReader::Result::kSuccess ||
|
|
(field.pop_back(), !HexStringToNumber(field, &end_address))) {
|
|
LOG(ERROR) << "format error";
|
|
return ParseResult::kError;
|
|
}
|
|
if (end_address < start_address) {
|
|
LOG(ERROR) << "format error";
|
|
return ParseResult::kError;
|
|
}
|
|
// Skip zero-length mappings.
|
|
if (end_address == start_address) {
|
|
std::string rest_of_line;
|
|
if (maps_file_reader->GetLine(&rest_of_line) !=
|
|
DelimitedFileReader::Result::kSuccess) {
|
|
LOG(ERROR) << "format error";
|
|
return ParseResult::kError;
|
|
}
|
|
return ParseResult::kSuccess;
|
|
}
|
|
|
|
// TODO(jperaza): set bitness properly
|
|
#if defined(ARCH_CPU_64_BITS)
|
|
constexpr bool is_64_bit = true;
|
|
#else
|
|
constexpr bool is_64_bit = false;
|
|
#endif
|
|
|
|
MemoryMap::Mapping mapping;
|
|
mapping.range.SetRange(is_64_bit, start_address, end_address - start_address);
|
|
|
|
if (maps_file_reader->GetDelim(' ', &field) !=
|
|
DelimitedFileReader::Result::kSuccess ||
|
|
(field.pop_back(), field.size() != 4)) {
|
|
LOG(ERROR) << "format error";
|
|
return ParseResult::kError;
|
|
}
|
|
#define SET_FIELD(actual_c, outval, true_chars, false_chars) \
|
|
do { \
|
|
if (strchr(true_chars, actual_c)) { \
|
|
*outval = true; \
|
|
} else if (strchr(false_chars, actual_c)) { \
|
|
*outval = false; \
|
|
} else { \
|
|
LOG(ERROR) << "format error"; \
|
|
return ParseResult::kError; \
|
|
} \
|
|
} while (false)
|
|
SET_FIELD(field[0], &mapping.readable, "r", "-");
|
|
SET_FIELD(field[1], &mapping.writable, "w", "-");
|
|
SET_FIELD(field[2], &mapping.executable, "x", "-");
|
|
SET_FIELD(field[3], &mapping.shareable, "sS", "p");
|
|
#undef SET_FIELD
|
|
|
|
if (maps_file_reader->GetDelim(' ', &field) !=
|
|
DelimitedFileReader::Result::kSuccess ||
|
|
(field.pop_back(), !HexStringToNumber(field, &mapping.offset))) {
|
|
LOG(ERROR) << "format error";
|
|
return ParseResult::kError;
|
|
}
|
|
|
|
uint32_t major;
|
|
if (maps_file_reader->GetDelim(':', &field) !=
|
|
DelimitedFileReader::Result::kSuccess ||
|
|
(field.pop_back(), field.size()) < 2 ||
|
|
!HexStringToNumber(field, &major)) {
|
|
LOG(ERROR) << "format error";
|
|
return ParseResult::kError;
|
|
}
|
|
|
|
uint32_t minor;
|
|
if (maps_file_reader->GetDelim(' ', &field) !=
|
|
DelimitedFileReader::Result::kSuccess ||
|
|
(field.pop_back(), field.size()) < 2 ||
|
|
!HexStringToNumber(field, &minor)) {
|
|
LOG(ERROR) << "format error";
|
|
return ParseResult::kError;
|
|
}
|
|
|
|
mapping.device = makedev(major, minor);
|
|
|
|
if (maps_file_reader->GetDelim(' ', &field) !=
|
|
DelimitedFileReader::Result::kSuccess ||
|
|
(field.pop_back(), !StringToNumber(field, &mapping.inode))) {
|
|
LOG(ERROR) << "format error";
|
|
return ParseResult::kError;
|
|
}
|
|
|
|
if (maps_file_reader->GetDelim('\n', &field) !=
|
|
DelimitedFileReader::Result::kSuccess) {
|
|
LOG(ERROR) << "format error";
|
|
return ParseResult::kError;
|
|
}
|
|
if (field.back() != '\n') {
|
|
LOG(ERROR) << "format error";
|
|
return ParseResult::kError;
|
|
}
|
|
field.pop_back();
|
|
|
|
mappings->push_back(mapping);
|
|
|
|
size_t path_start = field.find_first_not_of(' ');
|
|
if (path_start != std::string::npos) {
|
|
mappings->back().name = field.substr(path_start);
|
|
}
|
|
return ParseResult::kSuccess;
|
|
}
|
|
|
|
class SparseReverseIterator : public MemoryMap::Iterator {
|
|
public:
|
|
SparseReverseIterator(const std::vector<const MemoryMap::Mapping*>& mappings)
|
|
: mappings_(mappings), riter_(mappings_.rbegin()) {}
|
|
|
|
SparseReverseIterator() : mappings_(), riter_(mappings_.rend()) {}
|
|
|
|
SparseReverseIterator(const SparseReverseIterator&) = delete;
|
|
SparseReverseIterator& operator=(const SparseReverseIterator&) = delete;
|
|
|
|
// Iterator:
|
|
const MemoryMap::Mapping* Next() override {
|
|
return riter_ == mappings_.rend() ? nullptr : *(riter_++);
|
|
}
|
|
|
|
unsigned int Count() override { return mappings_.rend() - riter_; }
|
|
|
|
private:
|
|
std::vector<const MemoryMap::Mapping*> mappings_;
|
|
std::vector<const MemoryMap::Mapping*>::reverse_iterator riter_;
|
|
};
|
|
|
|
class FullReverseIterator : public MemoryMap::Iterator {
|
|
public:
|
|
FullReverseIterator(
|
|
std::vector<MemoryMap::Mapping>::const_reverse_iterator rbegin,
|
|
std::vector<MemoryMap::Mapping>::const_reverse_iterator rend)
|
|
: riter_(rbegin), rend_(rend) {}
|
|
|
|
FullReverseIterator(const FullReverseIterator&) = delete;
|
|
FullReverseIterator& operator=(const FullReverseIterator&) = delete;
|
|
|
|
// Iterator:
|
|
const MemoryMap::Mapping* Next() override {
|
|
return riter_ == rend_ ? nullptr : &*riter_++;
|
|
}
|
|
|
|
unsigned int Count() override { return rend_ - riter_; }
|
|
|
|
private:
|
|
std::vector<MemoryMap::Mapping>::const_reverse_iterator riter_;
|
|
std::vector<MemoryMap::Mapping>::const_reverse_iterator rend_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
MemoryMap::Mapping::Mapping()
|
|
: name(),
|
|
range(false, 0, 0),
|
|
offset(0),
|
|
device(0),
|
|
inode(0),
|
|
readable(false),
|
|
writable(false),
|
|
executable(false),
|
|
shareable(false) {}
|
|
|
|
MemoryMap::MemoryMap() : mappings_(), initialized_() {}
|
|
|
|
MemoryMap::~MemoryMap() {}
|
|
|
|
bool MemoryMap::Mapping::Equals(const Mapping& other) const {
|
|
DCHECK_EQ(range.Is64Bit(), other.range.Is64Bit());
|
|
return range.Base() == other.range.Base() &&
|
|
range.Size() == other.range.Size() && name == other.name &&
|
|
offset == other.offset && device == other.device &&
|
|
inode == other.inode && readable == other.readable &&
|
|
writable == other.writable && executable == other.executable &&
|
|
shareable == other.shareable;
|
|
}
|
|
|
|
bool MemoryMap::Initialize(PtraceConnection* connection) {
|
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
|
|
|
// If the maps file is not read atomically, entries can be read multiple times
|
|
// or missed entirely. The kernel reads entries from this file into a page
|
|
// sized buffer, so maps files larger than a page require multiple reads.
|
|
// Attempt to reduce the time between reads by reading the entire file into a
|
|
// StringFile before attempting to parse it. If ParseMapsLine detects
|
|
// duplicate, overlapping, or out-of-order entries, it will trigger restarting
|
|
// the read up to |attempts| times.
|
|
int attempts = 3;
|
|
do {
|
|
std::string contents;
|
|
char path[32];
|
|
snprintf(path, sizeof(path), "/proc/%d/maps", connection->GetProcessID());
|
|
if (!connection->ReadFileContents(base::FilePath(path), &contents)) {
|
|
return false;
|
|
}
|
|
|
|
StringFile maps_file;
|
|
maps_file.SetString(contents);
|
|
DelimitedFileReader maps_file_reader(&maps_file);
|
|
|
|
ParseResult result;
|
|
while ((result = ParseMapsLine(&maps_file_reader, &mappings_)) ==
|
|
ParseResult::kSuccess) {
|
|
}
|
|
if (result == ParseResult::kEndOfFile) {
|
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
|
return true;
|
|
}
|
|
if (result == ParseResult::kError) {
|
|
return false;
|
|
}
|
|
|
|
DCHECK(result == ParseResult::kRetry);
|
|
} while (--attempts > 0);
|
|
|
|
LOG(ERROR) << "retry count exceeded";
|
|
return false;
|
|
}
|
|
|
|
const MemoryMap::Mapping* MemoryMap::FindMapping(LinuxVMAddress address) const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
for (const auto& mapping : mappings_) {
|
|
if (mapping.range.Base() <= address && mapping.range.End() > address) {
|
|
return &mapping;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const MemoryMap::Mapping* MemoryMap::FindMappingWithName(
|
|
const std::string& name) const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
for (const auto& mapping : mappings_) {
|
|
if (mapping.name == name) {
|
|
return &mapping;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
std::unique_ptr<MemoryMap::Iterator> MemoryMap::FindFilePossibleMmapStarts(
|
|
const Mapping& mapping) const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
std::vector<const Mapping*> possible_starts;
|
|
|
|
// If the mapping is anonymous, as is for the VDSO, there is no mapped file to
|
|
// find the start of, so just return the input mapping.
|
|
if (mapping.device == 0 && mapping.inode == 0) {
|
|
for (const auto& candidate : mappings_) {
|
|
if (mapping.Equals(candidate)) {
|
|
possible_starts.push_back(&candidate);
|
|
return std::make_unique<SparseReverseIterator>(possible_starts);
|
|
}
|
|
}
|
|
|
|
LOG(ERROR) << "mapping not found";
|
|
return std::make_unique<SparseReverseIterator>();
|
|
}
|
|
|
|
#if defined(OS_ANDROID)
|
|
// The Android Chromium linker uses ashmem to share RELRO segments between
|
|
// processes. The original RELRO segment has been unmapped and replaced with a
|
|
// mapping named "/dev/ashmem/RELRO:<libname>" where <libname> is the base
|
|
// library name (e.g. libchrome.so) sans any preceding path that may be
|
|
// present in other mappings for the library.
|
|
// https://crashpad.chromium.org/bug/253
|
|
static constexpr char kRelro[] = "/dev/ashmem/RELRO:";
|
|
if (mapping.name.compare(0, strlen(kRelro), kRelro, 0, strlen(kRelro)) == 0) {
|
|
// The kernel appends "(deleted)" to ashmem mappings because there isn't
|
|
// any corresponding file on the filesystem.
|
|
static constexpr char kDeleted[] = " (deleted)";
|
|
size_t libname_end = mapping.name.rfind(kDeleted);
|
|
DCHECK_NE(libname_end, std::string::npos);
|
|
if (libname_end == std::string::npos) {
|
|
libname_end = mapping.name.size();
|
|
}
|
|
|
|
std::string libname =
|
|
mapping.name.substr(strlen(kRelro), libname_end - strlen(kRelro));
|
|
for (const auto& candidate : mappings_) {
|
|
if (candidate.name.rfind(libname) != std::string::npos) {
|
|
possible_starts.push_back(&candidate);
|
|
}
|
|
if (mapping.Equals(candidate)) {
|
|
return std::make_unique<SparseReverseIterator>(possible_starts);
|
|
}
|
|
}
|
|
}
|
|
#endif // OS_ANDROID
|
|
|
|
for (const auto& candidate : mappings_) {
|
|
if (candidate.device == mapping.device &&
|
|
candidate.inode == mapping.inode
|
|
#if !defined(OS_ANDROID)
|
|
// Libraries on Android may be mapped from zipfiles (APKs), in which
|
|
// case the offset is not 0.
|
|
&& candidate.offset == 0
|
|
#endif // !defined(OS_ANDROID)
|
|
) {
|
|
possible_starts.push_back(&candidate);
|
|
}
|
|
if (mapping.Equals(candidate)) {
|
|
return std::make_unique<SparseReverseIterator>(possible_starts);
|
|
}
|
|
}
|
|
|
|
LOG(ERROR) << "mapping not found";
|
|
return std::make_unique<SparseReverseIterator>();
|
|
}
|
|
|
|
std::unique_ptr<MemoryMap::Iterator> MemoryMap::ReverseIteratorFrom(
|
|
const Mapping& target) const {
|
|
for (auto riter = mappings_.crbegin(); riter != mappings_.rend(); ++riter) {
|
|
if (riter->Equals(target)) {
|
|
return std::make_unique<FullReverseIterator>(riter, mappings_.rend());
|
|
}
|
|
}
|
|
return std::make_unique<FullReverseIterator>(mappings_.rend(),
|
|
mappings_.rend());
|
|
}
|
|
|
|
} // namespace crashpad
|