Add CrashpadInfoReader to read CrashpadInfo via ProcessMemory

Bug: crashpad:30
Change-Id: I295d518ee0eef1fd61e5544cd6bad25827d07a02
Reviewed-on: https://chromium-review.googlesource.com/846025
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Joshua Peraza 2018-01-08 17:17:38 -08:00 committed by Commit Bot
parent 8bbe985004
commit 70563e92f4
7 changed files with 404 additions and 1 deletions

View File

@ -100,6 +100,8 @@ static_library("snapshot") {
if (crashpad_is_linux || crashpad_is_android) {
sources += [
"crashpad_types/crashpad_info_reader.cc",
"crashpad_types/crashpad_info_reader.h",
"elf/elf_dynamic_array_reader.cc",
"elf/elf_dynamic_array_reader.h",
"elf/elf_image_reader.cc",
@ -285,6 +287,7 @@ source_set("snapshot_test") {
if (crashpad_is_linux || crashpad_is_android) {
sources += [
"crashpad_types/crashpad_info_reader_test.cc",
"elf/elf_image_reader_test.cc",
"elf/elf_image_reader_test_note.S",
"elf/test_exported_symbols.sym",

View File

@ -0,0 +1,187 @@
// 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 "snapshot/crashpad_types/crashpad_info_reader.h"
#include <type_traits>
#include "build/build_config.h"
#include "client/crashpad_info.h"
#include "util/linux/traits.h"
#include "util/misc/as_underlying_type.h"
namespace crashpad {
namespace {
void UnsetIfNotValidTriState(TriState* value) {
switch (AsUnderlyingType(*value)) {
case AsUnderlyingType(TriState::kUnset):
case AsUnderlyingType(TriState::kEnabled):
case AsUnderlyingType(TriState::kDisabled):
return;
}
LOG(WARNING) << "Unsetting invalid TriState " << AsUnderlyingType(*value);
*value = TriState::kUnset;
}
} // namespace
class CrashpadInfoReader::InfoContainer {
public:
virtual ~InfoContainer() = default;
virtual bool Read(const ProcessMemoryRange* memory, VMAddress address) = 0;
protected:
InfoContainer() = default;
};
template <class Traits>
class CrashpadInfoReader::InfoContainerSpecific : public InfoContainer {
public:
InfoContainerSpecific() : InfoContainer() {}
~InfoContainerSpecific() override = default;
bool Read(const ProcessMemoryRange* memory, VMAddress address) override {
if (!memory->Read(address,
offsetof(decltype(info), size) + sizeof(info.size),
&info)) {
return false;
}
if (info.signature != CrashpadInfo::kSignature) {
LOG(ERROR) << "invalid signature 0x" << std::hex << info.signature;
return false;
}
if (!memory->Read(address,
std::min(VMSize{info.size}, VMSize{sizeof(info)}),
&info)) {
return false;
}
if (info.size > sizeof(info)) {
LOG(INFO) << "large crashpad info size " << info.size;
}
if (info.version != 1) {
LOG(ERROR) << "unexpected version " << info.version;
return false;
}
memset(reinterpret_cast<char*>(&info), 0, sizeof(info) - info.size);
UnsetIfNotValidTriState(&info.crashpad_handler_behavior);
UnsetIfNotValidTriState(&info.system_crash_reporter_forwarding);
UnsetIfNotValidTriState(&info.gather_indirectly_referenced_memory);
return true;
}
struct {
uint32_t signature;
uint32_t size;
uint32_t version;
uint32_t indirectly_referenced_memory_cap;
uint32_t padding_0;
TriState crashpad_handler_behavior;
TriState system_crash_reporter_forwarding;
TriState gather_indirectly_referenced_memory;
uint8_t padding_1;
typename Traits::Address extra_memory_ranges;
typename Traits::Address simple_annotations;
typename Traits::Address user_data_minidump_stream_head;
typename Traits::Address annotations_list;
} info;
#if defined(ARCH_CPU_64_BITS)
#define NATIVE_TRAITS Traits64
#else
#define NATIVE_TRAITS Traits32
#endif
static_assert(!std::is_same<Traits, NATIVE_TRAITS>::value ||
sizeof(info) == sizeof(CrashpadInfo),
"CrashpadInfo size mismtach");
#undef NATIVE_TRAITS
};
CrashpadInfoReader::CrashpadInfoReader()
: container_(), is_64_bit_(false), initialized_() {}
CrashpadInfoReader::~CrashpadInfoReader() = default;
bool CrashpadInfoReader::Initialize(const ProcessMemoryRange* memory,
VMAddress address) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
is_64_bit_ = memory->Is64Bit();
std::unique_ptr<InfoContainer> new_container;
if (is_64_bit_) {
new_container = std::make_unique<InfoContainerSpecific<Traits64>>();
} else {
new_container = std::make_unique<InfoContainerSpecific<Traits32>>();
}
if (!new_container->Read(memory, address)) {
return false;
}
container_ = std::move(new_container);
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
#define GET_MEMBER(name) \
(is_64_bit_ \
? reinterpret_cast<InfoContainerSpecific<Traits64>*>(container_.get()) \
->info.name \
: reinterpret_cast<InfoContainerSpecific<Traits32>*>(container_.get()) \
->info.name)
#define DEFINE_GETTER(type, method, member) \
type CrashpadInfoReader::method() { \
INITIALIZATION_STATE_DCHECK_VALID(initialized_); \
return GET_MEMBER(member); \
}
DEFINE_GETTER(TriState, CrashpadHandlerBehavior, crashpad_handler_behavior);
DEFINE_GETTER(TriState,
SystemCrashReporterForwarding,
system_crash_reporter_forwarding);
DEFINE_GETTER(TriState,
GatherIndirectlyReferencedMemory,
gather_indirectly_referenced_memory);
DEFINE_GETTER(uint32_t,
IndirectlyReferencedMemoryCap,
indirectly_referenced_memory_cap);
DEFINE_GETTER(VMAddress, ExtraMemoryRanges, extra_memory_ranges);
DEFINE_GETTER(VMAddress, SimpleAnnotations, simple_annotations);
DEFINE_GETTER(VMAddress, AnnotationsList, annotations_list);
DEFINE_GETTER(VMAddress,
UserDataMinidumpStreamHead,
user_data_minidump_stream_head);
#undef DEFINE_GETTER
#undef GET_MEMBER
} // namespace crashpad

View File

@ -0,0 +1,75 @@
// 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_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_
#define CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_
#include <stdint.h>
#include <memory>
#include "base/macros.h"
#include "util/misc/address_types.h"
#include "util/misc/initialization_state_dcheck.h"
#include "util/misc/tri_state.h"
#include "util/process/process_memory_range.h"
namespace crashpad {
//! \brief Reads CrashpadInfo structs from another process via a
//! ProcessMemoryRange.
class CrashpadInfoReader {
public:
CrashpadInfoReader();
~CrashpadInfoReader();
//! \brief Initializes this object.
//!
//! This method must be successfully called bfore any other method in this
//! class.
//!
//! \param[in] memory The reader for the remote process.
//! \param[in] address The address in the remote process' address space of a
//! CrashpadInfo struct.
//! \return `true` on success. `false` on failure with a message logged.
bool Initialize(const ProcessMemoryRange* memory, VMAddress address);
//! \{
//! \see CrashpadInfo
TriState CrashpadHandlerBehavior();
TriState SystemCrashReporterForwarding();
TriState GatherIndirectlyReferencedMemory();
uint32_t IndirectlyReferencedMemoryCap();
VMAddress ExtraMemoryRanges();
VMAddress SimpleAnnotations();
VMAddress AnnotationsList();
VMAddress UserDataMinidumpStreamHead();
//! \}
private:
class InfoContainer;
template <typename Traits>
class InfoContainerSpecific;
std::unique_ptr<InfoContainer> container_;
bool is_64_bit_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(CrashpadInfoReader);
};
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_

View File

@ -0,0 +1,132 @@
// 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 "snapshot/crashpad_types/crashpad_info_reader.h"
#include <sys/types.h>
#include <unistd.h>
#include "build/build_config.h"
#include "client/annotation_list.h"
#include "client/crashpad_info.h"
#include "client/simple_address_range_bag.h"
#include "client/simple_string_dictionary.h"
#include "gtest/gtest.h"
#include "test/multiprocess.h"
#include "util/file/file_io.h"
#include "util/misc/from_pointer_cast.h"
#include "util/process/process_memory_linux.h"
namespace crashpad {
namespace test {
namespace {
constexpr TriState kCrashpadHandlerBehavior = TriState::kEnabled;
constexpr TriState kSystemCrashReporterForwarding = TriState::kDisabled;
constexpr TriState kGatherIndirectlyReferencedMemory = TriState::kUnset;
constexpr uint32_t kIndirectlyReferencedMemoryCap = 42;
class CrashpadInfoTest {
public:
CrashpadInfoTest()
: extra_memory_(), simple_annotations_(), annotation_list_() {
CrashpadInfo* info = CrashpadInfo::GetCrashpadInfo();
info->set_extra_memory_ranges(&extra_memory_);
info->set_simple_annotations(&simple_annotations_);
info->set_annotations_list(&annotation_list_);
info->set_crashpad_handler_behavior(kCrashpadHandlerBehavior);
info->set_system_crash_reporter_forwarding(kSystemCrashReporterForwarding);
info->set_gather_indirectly_referenced_memory(
kGatherIndirectlyReferencedMemory, kIndirectlyReferencedMemoryCap);
}
void ExpectCrashpadInfo(pid_t pid, bool is_64_bit) {
ProcessMemoryLinux memory;
ASSERT_TRUE(memory.Initialize(pid));
ProcessMemoryRange range;
ASSERT_TRUE(range.Initialize(&memory, is_64_bit));
CrashpadInfo* info = CrashpadInfo::GetCrashpadInfo();
CrashpadInfoReader reader;
ASSERT_TRUE(reader.Initialize(&range, FromPointerCast<VMAddress>(info)));
EXPECT_EQ(reader.CrashpadHandlerBehavior(), kCrashpadHandlerBehavior);
EXPECT_EQ(reader.SystemCrashReporterForwarding(),
kSystemCrashReporterForwarding);
EXPECT_EQ(reader.GatherIndirectlyReferencedMemory(),
kGatherIndirectlyReferencedMemory);
EXPECT_EQ(reader.IndirectlyReferencedMemoryCap(),
kIndirectlyReferencedMemoryCap);
EXPECT_EQ(reader.ExtraMemoryRanges(),
FromPointerCast<VMAddress>(&extra_memory_));
EXPECT_EQ(reader.SimpleAnnotations(),
FromPointerCast<VMAddress>(info->simple_annotations()));
EXPECT_EQ(reader.AnnotationsList(),
FromPointerCast<VMAddress>(info->annotations_list()));
}
private:
SimpleAddressRangeBag extra_memory_;
SimpleStringDictionary simple_annotations_;
AnnotationList annotation_list_;
DISALLOW_COPY_AND_ASSIGN(CrashpadInfoTest);
};
TEST(CrashpadInfoReader, ReadFromSelf) {
CrashpadInfoTest test;
#if defined(ARCH_CPU_64_BITS)
constexpr bool am_64_bit = true;
#else
constexpr bool am_64_bit = false;
#endif
test.ExpectCrashpadInfo(getpid(), am_64_bit);
}
class ReadFromChildTest : public Multiprocess {
public:
ReadFromChildTest() : Multiprocess(), info_test_() {}
~ReadFromChildTest() = default;
private:
void MultiprocessParent() {
#if defined(ARCH_CPU_64_BITS)
constexpr bool am_64_bit = true;
#else
constexpr bool am_64_bit = false;
#endif
info_test_.ExpectCrashpadInfo(ChildPID(), am_64_bit);
}
void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); }
CrashpadInfoTest info_test_;
DISALLOW_COPY_AND_ASSIGN(ReadFromChildTest);
};
TEST(CrashpadInfoReader, ReadFromChild) {
ReadFromChildTest test;
test.Run();
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -39,6 +39,8 @@
'cpu_context.h',
'crashpad_info_client_options.cc',
'crashpad_info_client_options.h',
'crashpad_types/crashpad_info_reader.cc',
'crashpad_types/crashpad_info_reader.h',
'elf/elf_dynamic_array_reader.cc',
'elf/elf_dynamic_array_reader.h',
'elf/elf_image_reader.cc',
@ -171,6 +173,7 @@
}, { # else: OS!="linux" and OS!="android"
'sources/': [
['exclude', '^elf/'],
['exclude', '^crashpad_types/'],
],
}],
['target_arch!="ia32" and target_arch!="x64"', {

View File

@ -72,6 +72,7 @@
'api/module_annotations_win_test.cc',
'cpu_context_test.cc',
'crashpad_info_client_options_test.cc',
'crashpad_types/crashpad_info_reader_test.cc',
'elf/elf_image_reader_test.cc',
'elf/elf_image_reader_test_note.S',
'linux/debug_rendezvous_test.cc',
@ -139,6 +140,7 @@
}, { # else: OS!="linux" and OS!="android"
'sources/': [
['exclude', '^elf/'],
['exclude', '^crashpad_types/'],
],
}],
],

View File

@ -24,7 +24,8 @@ namespace crashpad {
//! \param[in] from The value to be casted.
//! \return \a from casted to its underlying type.
template <typename From>
typename std::underlying_type<From>::type AsUnderlyingType(From from) {
constexpr typename std::underlying_type<From>::type AsUnderlyingType(
From from) {
return static_cast<typename std::underlying_type<From>::type>(from);
}