fuchsia: Include address space information in process snapshot

This plumbs some of the ZX_INFO_PROCESS_MAPS information out into
MINIDUMP_MEMORY_INFO. The mapping loses some information that Zircon
provides, and some of the data that Windows would provide isn't
available (for example, AllocationProtect). But this gives a general
idea of the memory layout of the process to check for bad pointers, etc.
when inspecting crashes.

Bug: fuchsia:DX-615
Change-Id: I2d7c02be0996672253cf0b1eb6a60b0a55e6033b
Reviewed-on: https://chromium-review.googlesource.com/c/1377089
Commit-Queue: Scott Graham <scottmg@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Scott Graham 2018-12-14 13:17:43 -08:00 committed by Commit Bot
parent 067f7ddebf
commit 8b6f158d20
8 changed files with 291 additions and 2 deletions

View File

@ -200,6 +200,8 @@ static_library("snapshot") {
"fuchsia/exception_snapshot_fuchsia.h",
"fuchsia/memory_map_fuchsia.cc",
"fuchsia/memory_map_fuchsia.h",
"fuchsia/memory_map_region_snapshot_fuchsia.cc",
"fuchsia/memory_map_region_snapshot_fuchsia.h",
"fuchsia/process_reader_fuchsia.cc",
"fuchsia/process_reader_fuchsia.h",
"fuchsia/process_snapshot_fuchsia.cc",
@ -384,7 +386,10 @@ source_set("snapshot_test") {
}
if (crashpad_is_fuchsia) {
sources += [ "fuchsia/process_reader_fuchsia_test.cc" ]
sources += [
"fuchsia/process_reader_fuchsia_test.cc",
"fuchsia/process_snapshot_fuchsia_test.cc",
]
}
# public_configs isnt quite right. snapshot_test_link sets ldflags, and

View File

@ -46,6 +46,10 @@ class MemoryMapFuchsia {
//! will be filled out, otherwise `false` and \a map will be unchanged.
bool FindMappingForAddress(zx_vaddr_t address, zx_info_maps_t* map) const;
//! \brief Get a vector of `zx_info_maps_t` representing the memory map for
//! this process.
const std::vector<zx_info_maps_t>& Entries() const { return map_entries_; }
private:
std::vector<zx_info_maps_t> map_entries_;
InitializationStateDcheck initialized_;

View File

@ -0,0 +1,83 @@
// Copyright 2018 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/fuchsia/memory_map_region_snapshot_fuchsia.h"
#include "base/logging.h"
namespace crashpad {
namespace internal {
namespace {
// Maps from bitwise OR of Zircon's flags to enumerated Windows version.
uint32_t MmuFlagsToProtectFlags(zx_vm_option_t flags) {
// These bits are currently the lowest 3 of zx_vm_option_t. They're used to
// index into a mapping array, so make sure that we notice if they change
// values.
static_assert(
ZX_VM_PERM_READ == 1 && ZX_VM_PERM_WRITE == 2 && ZX_VM_PERM_EXECUTE == 4,
"table below will need fixing");
// The entries set to zero don't have good corresponding Windows minidump
// names. They also aren't currently supported by the mapping syscall on
// Zircon, so DCHECK that they don't happen in practice. EXECUTE-only also
// cannot currently happen, but as that has a good mapping to the Windows
// value, leave it in place in case it's supported by the syscall in the
// future.
static constexpr uint32_t mapping[] = {
/* --- */ PAGE_NOACCESS,
/* --r */ PAGE_READONLY,
/* -w- */ 0,
/* -wr */ PAGE_READWRITE,
/* x-- */ PAGE_EXECUTE,
/* x-r */ PAGE_EXECUTE_READ,
/* xw- */ 0,
/* xwr */ PAGE_EXECUTE_READWRITE,
};
const uint32_t index =
flags & (ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE);
DCHECK_LT(index, arraysize(mapping));
const uint32_t protect_flags = mapping[index];
DCHECK_NE(protect_flags, 0u);
return protect_flags;
}
} // namespace
MemoryMapRegionSnapshotFuchsia::MemoryMapRegionSnapshotFuchsia(
const zx_info_maps_t& info_map)
: memory_info_() {
DCHECK_EQ(info_map.type, ZX_INFO_MAPS_TYPE_MAPPING);
memory_info_.BaseAddress = info_map.base;
memory_info_.AllocationBase = info_map.base;
memory_info_.RegionSize = info_map.size;
memory_info_.State = MEM_COMMIT;
memory_info_.Protect = memory_info_.AllocationProtect =
MmuFlagsToProtectFlags(info_map.u.mapping.mmu_flags);
memory_info_.Type = MEM_MAPPED;
}
MemoryMapRegionSnapshotFuchsia::~MemoryMapRegionSnapshotFuchsia() {}
const MINIDUMP_MEMORY_INFO&
MemoryMapRegionSnapshotFuchsia::AsMinidumpMemoryInfo() const {
return memory_info_;
}
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,39 @@
// Copyright 2018 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_FUCHSIA_MEMORY_MAP_REGION_SNAPSHOT_FUCHSIA_H_
#define CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_REGION_SNAPSHOT_FUCHSIA_H_
#include "snapshot/memory_map_region_snapshot.h"
#include <zircon/syscalls/object.h>
namespace crashpad {
namespace internal {
class MemoryMapRegionSnapshotFuchsia : public MemoryMapRegionSnapshot {
public:
explicit MemoryMapRegionSnapshotFuchsia(const zx_info_maps_t& info_map);
~MemoryMapRegionSnapshotFuchsia() override;
virtual const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const override;
private:
MINIDUMP_MEMORY_INFO memory_info_;
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_REGION_SNAPSHOT_FUCHSIA_H_

View File

@ -111,6 +111,9 @@ class ProcessReaderFuchsia {
//! \brief Return a memory reader for the target process.
const ProcessMemory* Memory() const { return process_memory_.get(); }
//! \brief Return a memory map for the target process.
const MemoryMapFuchsia* MemoryMap() const { return &memory_map_; }
private:
//! Performs lazy initialization of the \a modules_ vector on behalf of
//! Modules().

View File

@ -41,6 +41,13 @@ bool ProcessSnapshotFuchsia::Initialize(const zx::process& process) {
InitializeThreads();
InitializeModules();
for (const auto& entry : process_reader_.MemoryMap()->Entries()) {
if (entry.type == ZX_INFO_MAPS_TYPE_MAPPING) {
memory_map_.push_back(
std::make_unique<internal::MemoryMapRegionSnapshotFuchsia>(entry));
}
}
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
@ -175,7 +182,11 @@ const ExceptionSnapshot* ProcessSnapshotFuchsia::Exception() const {
std::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotFuchsia::MemoryMap()
const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return std::vector<const MemoryMapRegionSnapshot*>();
std::vector<const MemoryMapRegionSnapshot*> memory_map;
for (const auto& item : memory_map_) {
memory_map.push_back(item.get());
}
return memory_map;
}
std::vector<HandleSnapshot> ProcessSnapshotFuchsia::Handles() const {

View File

@ -28,6 +28,7 @@
#include "snapshot/elf/elf_image_reader.h"
#include "snapshot/elf/module_snapshot_elf.h"
#include "snapshot/fuchsia/exception_snapshot_fuchsia.h"
#include "snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h"
#include "snapshot/fuchsia/process_reader_fuchsia.h"
#include "snapshot/fuchsia/system_snapshot_fuchsia.h"
#include "snapshot/fuchsia/thread_snapshot_fuchsia.h"
@ -137,6 +138,8 @@ class ProcessSnapshotFuchsia : public ProcessSnapshot {
ProcessReaderFuchsia process_reader_;
ProcessMemoryRange memory_range_;
std::map<std::string, std::string> annotations_simple_map_;
std::vector<std::unique_ptr<internal::MemoryMapRegionSnapshotFuchsia>>
memory_map_;
UUID report_id_;
UUID client_id_;
timeval snapshot_time_;

View File

@ -0,0 +1,141 @@
// Copyright 2018 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/fuchsia/memory_map_region_snapshot_fuchsia.h"
#include <dbghelp.h>
#include <zircon/syscalls.h>
#include "base/fuchsia/fuchsia_logging.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "snapshot/fuchsia/process_snapshot_fuchsia.h"
#include "test/multiprocess_exec.h"
#include "util/fuchsia/scoped_task_suspend.h"
namespace crashpad {
namespace test {
namespace {
constexpr struct {
uint32_t zircon_perm;
size_t pages;
uint32_t minidump_perm;
} kTestMappingPermAndSizes[] = {
// Zircon doesn't currently allow write-only, execute-only, or
// write-execute-only, returning ZX_ERR_INVALID_ARGS on map.
{0, 5, PAGE_NOACCESS},
{ZX_VM_PERM_READ, 6, PAGE_READONLY},
// {ZX_VM_PERM_WRITE, 7, PAGE_WRITECOPY},
// {ZX_VM_PERM_EXECUTE, 8, PAGE_EXECUTE},
{ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 9, PAGE_READWRITE},
{ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE, 10, PAGE_EXECUTE_READ},
// {ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE, 11, PAGE_EXECUTE_WRITECOPY},
{ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE,
12,
PAGE_EXECUTE_READWRITE},
};
CRASHPAD_CHILD_TEST_MAIN(AddressSpaceChildTestMain) {
// Create specifically sized mappings/permissions and write the address in
// our address space to the parent so that the reader can check they're read
// correctly.
for (const auto& t : kTestMappingPermAndSizes) {
zx_handle_t vmo = ZX_HANDLE_INVALID;
const size_t size = t.pages * PAGE_SIZE;
zx_status_t status = zx_vmo_create(size, 0, &vmo);
ZX_CHECK(status == ZX_OK, status) << "zx_vmo_create";
uintptr_t mapping_addr = 0;
status = zx_vmar_map(
zx_vmar_root_self(), t.zircon_perm, 0, vmo, 0, size, &mapping_addr);
ZX_CHECK(status == ZX_OK, status) << "zx_vmar_map";
CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput),
&mapping_addr,
sizeof(mapping_addr));
}
CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
return 0;
}
bool HasSingleMatchingMapping(
const std::vector<const MemoryMapRegionSnapshot*>& memory_map,
uintptr_t address,
size_t size,
uint32_t perm) {
const MemoryMapRegionSnapshot* matching = nullptr;
for (const auto* region : memory_map) {
const MINIDUMP_MEMORY_INFO& mmi = region->AsMinidumpMemoryInfo();
if (mmi.BaseAddress == address) {
if (matching) {
LOG(ERROR) << "multiple mappings matching address";
return false;
}
matching = region;
}
}
if (!matching)
return false;
const MINIDUMP_MEMORY_INFO& matching_mmi = matching->AsMinidumpMemoryInfo();
return matching_mmi.Protect == perm && matching_mmi.RegionSize == size;
}
class AddressSpaceTest : public MultiprocessExec {
public:
AddressSpaceTest() : MultiprocessExec() {
SetChildTestMainFunction("AddressSpaceChildTestMain");
}
~AddressSpaceTest() {}
private:
void MultiprocessParent() override {
uintptr_t test_addresses[arraysize(kTestMappingPermAndSizes)];
for (size_t i = 0; i < arraysize(test_addresses); ++i) {
ASSERT_TRUE(ReadFileExactly(
ReadPipeHandle(), &test_addresses[i], sizeof(test_addresses[i])));
}
ScopedTaskSuspend suspend(*ChildProcess());
ProcessSnapshotFuchsia process_snapshot;
ASSERT_TRUE(process_snapshot.Initialize(*ChildProcess()));
for (size_t i = 0; i < arraysize(test_addresses); ++i) {
const auto& t = kTestMappingPermAndSizes[i];
EXPECT_TRUE(HasSingleMatchingMapping(process_snapshot.MemoryMap(),
test_addresses[i],
t.pages * PAGE_SIZE,
t.minidump_perm))
<< base::StringPrintf(
"index %zu, zircon_perm 0x%x, minidump_perm 0x%x",
i,
t.zircon_perm,
t.minidump_perm);
}
}
DISALLOW_COPY_AND_ASSIGN(AddressSpaceTest);
};
TEST(ProcessSnapshotFuchsiaTest, AddressSpaceMapping) {
AddressSpaceTest test;
test.Run();
}
} // namespace
} // namespace test
} // namespace crashpad