Merge master c4f6ca3c6ac5 into doc

This commit is contained in:
Mark Mentovai 2017-06-20 10:44:49 -04:00
commit a3bc746808
78 changed files with 3180 additions and 425 deletions

4
DEPS
View File

@ -25,7 +25,7 @@ deps = {
'd62d6c6556d96dda924382547c54a4b3afedb22c',
'crashpad/third_party/gyp/gyp':
Var('chromium_git') + '/external/gyp@' +
'a7055b3989c1074adca03b4b4829e7f0e57f6efd',
'ffd524cefaad622e72995e852ffb0b18e83f8054',
# TODO(scottmg): Consider pinning these. For now, we don't have any particular
# reason to do so.
@ -38,7 +38,7 @@ deps = {
'crashpad/third_party/mini_chromium/mini_chromium':
Var('chromium_git') + '/chromium/mini_chromium@' +
'dc3d480305b27a5a1fb57f51a997529e00fed00b',
'723e840a2f100a525f7feaad2e93df31d701780a',
'crashpad/third_party/zlib/zlib':
Var('chromium_git') + '/chromium/src/third_party/zlib@' +
'13dc246a58e4b72104d35f9b1809af95221ebda7',

View File

@ -35,11 +35,15 @@ namespace {
// gtest assertions.
void SanityCheckContext(const NativeCPUContext& context) {
#if defined(ARCH_CPU_X86)
ASSERT_EQ(context.tsh.flavor, x86_THREAD_STATE32);
ASSERT_EQ(context.tsh.count, implicit_cast<int>(x86_THREAD_STATE32_COUNT));
ASSERT_EQ(implicit_cast<thread_state_flavor_t>(context.tsh.flavor),
implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE32));
ASSERT_EQ(implicit_cast<uint32_t>(context.tsh.count),
implicit_cast<uint32_t>(x86_THREAD_STATE32_COUNT));
#elif defined(ARCH_CPU_X86_64)
ASSERT_EQ(context.tsh.flavor, x86_THREAD_STATE64);
ASSERT_EQ(context.tsh.count, implicit_cast<int>(x86_THREAD_STATE64_COUNT));
ASSERT_EQ(implicit_cast<thread_state_flavor_t>(context.tsh.flavor),
implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE64));
ASSERT_EQ(implicit_cast<uint32_t>(context.tsh.count),
implicit_cast<uint32_t>(x86_THREAD_STATE64_COUNT));
#endif
#if defined(ARCH_CPU_X86_FAMILY)

View File

@ -30,6 +30,7 @@
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "util/file/file_io.h"
#include "util/misc/from_pointer_cast.h"
#include "util/misc/random_string.h"
#include "util/win/address_types.h"
#include "util/win/capture_context.h"
@ -156,7 +157,7 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
// signal the crash handler.
g_crash_exception_information.thread_id = GetCurrentThreadId();
g_crash_exception_information.exception_pointers =
reinterpret_cast<WinVMAddress>(exception_pointers);
FromPointerCast<WinVMAddress>(exception_pointers);
// Now signal the crash server, which will take a dump and then terminate us
// when it's complete.
@ -390,9 +391,9 @@ bool StartHandlerProcess(
g_non_crash_dump_done,
data->ipc_pipe_handle.get(),
this_process.get(),
reinterpret_cast<WinVMAddress>(&g_crash_exception_information),
reinterpret_cast<WinVMAddress>(&g_non_crash_exception_information),
reinterpret_cast<WinVMAddress>(&g_critical_section_with_debug_info));
FromPointerCast<WinVMAddress>(&g_crash_exception_information),
FromPointerCast<WinVMAddress>(&g_non_crash_exception_information),
FromPointerCast<WinVMAddress>(&g_critical_section_with_debug_info));
AppendCommandLineArgument(
base::UTF8ToUTF16(std::string("--initial-client-data=") +
initial_client_data.StringRepresentation()),
@ -655,14 +656,14 @@ bool CrashpadClient::SetHandlerIPCPipe(const std::wstring& ipc_pipe) {
message.registration.version = RegistrationRequest::kMessageVersion;
message.registration.client_process_id = GetCurrentProcessId();
message.registration.crash_exception_information =
reinterpret_cast<WinVMAddress>(&g_crash_exception_information);
FromPointerCast<WinVMAddress>(&g_crash_exception_information);
message.registration.non_crash_exception_information =
reinterpret_cast<WinVMAddress>(&g_non_crash_exception_information);
FromPointerCast<WinVMAddress>(&g_non_crash_exception_information);
CommonInProcessInitialization();
message.registration.critical_section_address =
reinterpret_cast<WinVMAddress>(&g_critical_section_with_debug_info);
FromPointerCast<WinVMAddress>(&g_critical_section_with_debug_info);
ServerToClientMessage response = {};
@ -765,7 +766,7 @@ void CrashpadClient::DumpWithoutCrash(const CONTEXT& context) {
g_non_crash_exception_information.thread_id = GetCurrentThreadId();
g_non_crash_exception_information.exception_pointers =
reinterpret_cast<WinVMAddress>(&exception_pointers);
FromPointerCast<WinVMAddress>(&exception_pointers);
bool set_event_result = !!SetEvent(g_signal_non_crash_dump);
PLOG_IF(ERROR, !set_event_result) << "SetEvent";
@ -830,11 +831,11 @@ bool CrashpadClient::DumpAndCrashTargetProcess(HANDLE process,
const size_t kInjectBufferSize = 4 * 1024;
WinVMAddress inject_memory =
reinterpret_cast<WinVMAddress>(VirtualAllocEx(process,
nullptr,
kInjectBufferSize,
MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE));
FromPointerCast<WinVMAddress>(VirtualAllocEx(process,
nullptr,
kInjectBufferSize,
MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE));
if (!inject_memory) {
PLOG(ERROR) << "VirtualAllocEx";
return false;
@ -844,7 +845,7 @@ bool CrashpadClient::DumpAndCrashTargetProcess(HANDLE process,
// loaded at the same address in our process as the target, and just look up
// its address here.
WinVMAddress raise_exception_address =
reinterpret_cast<WinVMAddress>(&RaiseException);
FromPointerCast<WinVMAddress>(&RaiseException);
WinVMAddress code_entry_point = 0;
std::vector<unsigned char> data_to_write;

View File

@ -15,6 +15,7 @@
#include "client/crashpad_info.h"
#include "util/misc/address_sanitizer.h"
#include "util/misc/from_pointer_cast.h"
#include "util/stdlib/cxx.h"
#if defined(OS_MACOSX)
@ -131,11 +132,10 @@ void CrashpadInfo::AddUserDataMinidumpStream(uint32_t stream_type,
const void* data,
size_t size) {
auto to_be_added = new internal::UserDataMinidumpStreamListEntry();
to_be_added->next = base::checked_cast<uint64_t>(
reinterpret_cast<uintptr_t>(user_data_minidump_stream_head_));
to_be_added->next =
FromPointerCast<uint64_t>(user_data_minidump_stream_head_);
to_be_added->stream_type = stream_type;
to_be_added->base_address =
base::checked_cast<uint64_t>(reinterpret_cast<uintptr_t>(data));
to_be_added->base_address = FromPointerCast<uint64_t>(data);
to_be_added->size = base::checked_cast<uint64_t>(size);
user_data_minidump_stream_head_ = to_be_added;
}

View File

@ -14,12 +14,14 @@
#include "client/prune_crash_reports.h"
#include <stddef.h>
#include <stdlib.h>
#include <algorithm>
#include <string>
#include <vector>
#include "base/numerics/safe_conversions.h"
#include "base/rand_util.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@ -209,8 +211,8 @@ TEST(PruneCrashReports, PruneOrder) {
reports.push_back(temp);
}
// The randomness from std::rand() is not, so use a better rand() instead.
const auto random_generator = [](int rand_max) {
return base::RandInt(0, rand_max - 1);
const auto random_generator = [](ptrdiff_t rand_max) {
return base::RandInt(0, base::checked_cast<int>(rand_max) - 1);
};
std::random_shuffle(reports.begin(), reports.end(), random_generator);
std::vector<CrashReportDatabase::Report> pending_reports(

View File

@ -19,6 +19,8 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/numerics/safe_conversions.h"
#include "util/misc/from_pointer_cast.h"
#include "util/numeric/checked_range.h"
namespace crashpad {
@ -138,9 +140,8 @@ class TSimpleAddressRangeBag {
bool Insert(void* base, size_t size) {
DCHECK(base != nullptr);
DCHECK_NE(0u, size);
return Insert(CheckedRange<uint64_t>(
base::checked_cast<uint64_t>(reinterpret_cast<uintptr_t>(base)),
base::checked_cast<uint64_t>(size)));
return Insert(CheckedRange<uint64_t>(FromPointerCast<uint64_t>(base),
base::checked_cast<uint64_t>(size)));
}
//! \brief Removes the given range from the bag.
@ -175,9 +176,8 @@ class TSimpleAddressRangeBag {
bool Remove(void* base, size_t size) {
DCHECK(base != nullptr);
DCHECK_NE(0u, size);
return Remove(CheckedRange<uint64_t>(
base::checked_cast<uint64_t>(reinterpret_cast<uintptr_t>(base)),
base::checked_cast<uint64_t>(size)));
return Remove(CheckedRange<uint64_t>(FromPointerCast<uint64_t>(base),
base::checked_cast<uint64_t>(size)));
}

View File

@ -177,12 +177,12 @@ bool DeliverException(thread_t thread,
void SimulateCrash(const NativeCPUContext& cpu_context) {
#if defined(ARCH_CPU_X86)
DCHECK_EQ(cpu_context.tsh.flavor,
DCHECK_EQ(implicit_cast<thread_state_flavor_t>(cpu_context.tsh.flavor),
implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE32));
DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context.tsh.count),
x86_THREAD_STATE32_COUNT);
#elif defined(ARCH_CPU_X86_64)
DCHECK_EQ(cpu_context.tsh.flavor,
DCHECK_EQ(implicit_cast<thread_state_flavor_t>(cpu_context.tsh.flavor),
implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE64));
DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context.tsh.count),
x86_THREAD_STATE64_COUNT);

View File

@ -130,12 +130,12 @@ class TestSimulateCrashMac final : public MachMultiprocess,
reinterpret_cast<const x86_thread_state*>(old_state);
switch (state->tsh.flavor) {
case x86_THREAD_STATE32:
EXPECT_EQ(state->tsh.count,
implicit_cast<int>(x86_THREAD_STATE32_COUNT));
EXPECT_EQ(implicit_cast<uint32_t>(state->tsh.count),
implicit_cast<uint32_t>(x86_THREAD_STATE32_COUNT));
break;
case x86_THREAD_STATE64:
EXPECT_EQ(state->tsh.count,
implicit_cast<int>(x86_THREAD_STATE64_COUNT));
EXPECT_EQ(implicit_cast<uint32_t>(state->tsh.count),
implicit_cast<uint32_t>(x86_THREAD_STATE64_COUNT));
break;
default:
ADD_FAILURE() << "unexpected tsh.flavor " << state->tsh.flavor;
@ -149,12 +149,12 @@ class TestSimulateCrashMac final : public MachMultiprocess,
reinterpret_cast<const x86_float_state*>(old_state);
switch (state->fsh.flavor) {
case x86_FLOAT_STATE32:
EXPECT_EQ(state->fsh.count,
implicit_cast<int>(x86_FLOAT_STATE32_COUNT));
EXPECT_EQ(implicit_cast<uint32_t>(state->fsh.count),
implicit_cast<uint32_t>(x86_FLOAT_STATE32_COUNT));
break;
case x86_FLOAT_STATE64:
EXPECT_EQ(state->fsh.count,
implicit_cast<int>(x86_FLOAT_STATE64_COUNT));
EXPECT_EQ(implicit_cast<uint32_t>(state->fsh.count),
implicit_cast<uint32_t>(x86_FLOAT_STATE64_COUNT));
break;
default:
ADD_FAILURE() << "unexpected fsh.flavor " << state->fsh.flavor;
@ -168,12 +168,12 @@ class TestSimulateCrashMac final : public MachMultiprocess,
reinterpret_cast<const x86_debug_state*>(old_state);
switch (state->dsh.flavor) {
case x86_DEBUG_STATE32:
EXPECT_EQ(state->dsh.count,
implicit_cast<int>(x86_DEBUG_STATE32_COUNT));
EXPECT_EQ(implicit_cast<uint32_t>(state->dsh.count),
implicit_cast<uint32_t>(x86_DEBUG_STATE32_COUNT));
break;
case x86_DEBUG_STATE64:
EXPECT_EQ(state->dsh.count,
implicit_cast<int>(x86_DEBUG_STATE64_COUNT));
EXPECT_EQ(implicit_cast<uint32_t>(state->dsh.count),
implicit_cast<uint32_t>(x86_DEBUG_STATE64_COUNT));
break;
default:
ADD_FAILURE() << "unexpected dsh.flavor " << state->dsh.flavor;

View File

@ -14,7 +14,7 @@
GERRIT_HOST: True
GERRIT_SQUASH_UPLOADS: True
CODE_REVIEW_SERVER: https://canary-chromium-review.googlesource.com/
CODE_REVIEW_SERVER: https://chromium-review.googlesource.com/
VIEW_VC: https://chromium.googlesource.com/crashpad/crashpad/+/
PROJECT: crashpad
BUG_PREFIX: crashpad:

View File

@ -0,0 +1,38 @@
// 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_COMPAT_ANDROID_LINUX_ELF_H_
#define CRASHPAD_COMPAT_ANDROID_LINUX_ELF_H_
#include_next <linux/elf.h>
// Android 5.0.0 (API 21) NDK
#if defined(__i386__) || defined(__x86_64__)
#if !defined(NT_386_TLS)
#define NT_386_TLS 0x200
#endif
#endif // __i386__ || __x86_64__
#if defined(__ARMEL__) || defined(__aarch64__)
#if !defined(NT_ARM_VFP)
#define NT_ARM_VFP 0x400
#endif
#if !defined(NT_ARM_TLS)
#define NT_ARM_TLS 0x401
#endif
#endif // __ARMEL__ || __aarch64__
#endif // CRASHPAD_COMPAT_ANDROID_LINUX_ELF_H_

View File

@ -23,6 +23,7 @@
'sources': [
'mac/AvailabilityMacros.h',
'mac/kern/exc_resource.h',
'mac/mach/i386/thread_state.h',
'mac/mach/mach.h',
'mac/mach-o/getsect.cc',
'mac/mach-o/getsect.h',

View File

@ -53,4 +53,10 @@
#define MAC_OS_X_VERSION_10_12 101200
#endif
// 10.13 SDK
#ifndef MAC_OS_X_VERSION_10_13
#define MAC_OS_X_VERSION_10_13 101300
#endif
#endif // CRASHPAD_COMPAT_MAC_AVAILABILITYMACROS_H_

View File

@ -12,14 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_COMPAT_ANDROID_ELF_H_
#define CRASHPAD_COMPAT_ANDROID_ELF_H_
#ifndef CRASHPAD_COMPAT_MAC_MACH_I386_THREAD_STATE_H_
#define CRASHPAD_COMPAT_MAC_MACH_I386_THREAD_STATE_H_
#include_next <elf.h>
#include_next <mach/i386/thread_state.h>
// Android 5.0.0 (API 21) NDK
#if !defined(NT_PRSTATUS)
#define NT_PRSTATUS 1
// 10.13 SDK
//
// This was defined as 244 in the 10.7 through 10.12 SDKs, and 144 previously.
#if I386_THREAD_STATE_MAX < 614
#undef I386_THREAD_STATE_MAX
#define I386_THREAD_STATE_MAX (614)
#endif
#endif // CRASHPAD_COMPAT_ANDROID_ELF_H_
#endif // CRASHPAD_COMPAT_MAC_MACH_I386_THREAD_STATE_H_

View File

@ -93,6 +93,20 @@
#define x86_AVX_STATE 18
#endif
// 10.13 SDK
#ifndef x86_AVX512_STATE32
#define x86_AVX512_STATE32 19
#endif
#ifndef x86_AVX512_STATE64
#define x86_AVX512_STATE64 20
#endif
#ifndef x86_AVX512_STATE
#define x86_AVX512_STATE 21
#endif
#endif // defined(__i386__) || defined(__x86_64__)
// <mach/thread_status.h>

View File

@ -533,11 +533,11 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MODULE {
uint32_t CheckSum;
//! \brief The modules timestamp, in `time_t` units, seconds since the POSIX
//! epoch.
//! epoch, or `0` if unknown.
//!
//! On Windows, this field comes from the `TimeDateStamp` field of the
//! modules `IMAGE_HEADER` structure. It reflects the timestamp at the time
//! the module was linked.
//! modules `IMAGE_FILE_HEADER` structure. It reflects the timestamp at the
//! time the module was linked.
uint32_t TimeDateStamp;
//! \brief ::RVA of a MINIDUMP_STRING containing the modules path or file
@ -723,11 +723,11 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE {
uint32_t CheckSum;
//! \brief The modules timestamp, in `time_t` units, seconds since the POSIX
//! epoch.
//! epoch, or `0` if unknown.
//!
//! On Windows, this field comes from the `TimeDateStamp` field of the
//! modules `IMAGE_HEADER` structure. It reflects the timestamp at the time
//! the module was linked.
//! modules `IMAGE_FILE_HEADER` structure. It reflects the timestamp at the
//! time the module was linked.
uint32_t TimeDateStamp;
//! \brief ::RVA of a MINIDUMP_STRING containing the modules path or file

View File

@ -15,9 +15,6 @@
#ifndef CRASHPAD_COMPAT_WIN_SYS_TIME_H_
#define CRASHPAD_COMPAT_WIN_SYS_TIME_H_
struct timeval {
long tv_sec;
long tv_usec;
};
#include <winsock2.h>
#endif // CRASHPAD_COMPAT_WIN_SYS_TIME_H_

View File

@ -36,9 +36,14 @@ provides more detail.
To develop Crashpad, the following tools are necessary, and must be present in
the `$PATH` environment variable:
* Appropriate development tools. For macOS, this is
[Xcode](https://developer.apple.com/xcode/) and for Windows, its [Visual
Studio](https://www.visualstudio.com/).
* Appropriate development tools.
* On macOS, install [Xcode](https://developer.apple.com/xcode/). The latest
version is generally recommended.
* On Windows, install [Visual Studio](https://www.visualstudio.com/) with
C++ support and the Windows SDK. MSVS 2015 and MSVS 2017 are both
supported. Some tests also require the CDB debugger, installed with
[Debugging Tools for
Windows](https://msdn.microsoft.com/library/windows/hardware/ff551063.aspx).
* Chromiums
[depot_tools](https://dev.chromium.org/developers/how-tos/depottools).
* [Git](https://git-scm.com/). This is provided by Xcode on macOS and by
@ -191,6 +196,18 @@ $ cd ~/crashpad/crashpad
$ python build/run_tests.py out/Debug
```
### Windows
On Windows, `end_to_end_test.py` requires the CDB debugger, installed with
[Debugging Tools for
Windows](https://msdn.microsoft.com/library/windows/hardware/ff551063.aspx).
This can be installed either as part of the [Windows Driver
Kit](https://go.microsoft.com/fwlink/p?LinkID=239721) or the [Windows
SDK](https://go.microsoft.com/fwlink/p?LinkID=271979). If the Windows SDK has
already been installed (possibly with Visual Studio) but Debugging Tools for
Windows is not present, it can be installed from Add or remove programs→Windows
Software Development Kit.
### Android
To test on Android, use [ADB (Android Debug
@ -251,11 +268,6 @@ $ git commit
$ git cl upload
```
The [PolyGerrit interface](https://polygerrit.appspot.com/) to Gerrit,
undergoing active development, is recommended. To switch from the classic
GWT-based Gerrit UI to PolyGerrit, click the PolyGerrit link in a Gerrit review
pages footer.
Uploading a patch to Gerrit does not automatically request a review. You must
select a reviewer on the Gerrit review page after running `git cl upload`. This
action notifies your reviewer of the code review request. If you have lost track
@ -275,7 +287,8 @@ server](https://dev.chromium.org/developers/testing/try-server-usage) by running
“Commit-Queue: +1” label. This does not mean that the patch will be committed,
but the try server and commit queue share infrastructure and a Gerrit label. The
patch will be tested on try bots in a variety of configurations. Status
information will be available on Gerrit.
information will be available on Gerrit. Try server access is available to
Crashpad and Chromium committers.
### Landing Changes
@ -283,7 +296,8 @@ After code review is complete and “Code-Review: +1” has been received from a
reviewers, the patch can be submitted to Crashpads [commit
queue](https://dev.chromium.org/developers/testing/commit-queue) by clicking the
“Submit to CQ” button in Gerrit. This sets the “Commit-Queue: +2” label, which
tests the patch on the try server before landing it.
tests the patch on the try server before landing it. Commit queue access is
available to Crashpad and Chromium committers.
Although the commit queue is recommended, if needed, project members can bypass
the commit queue and land patches without testing by using the “Submit” button

View File

@ -27,8 +27,8 @@ gerrit {}
verifiers {
gerrit_cq_ability {
committer_list: "project-crashpad-committers"
dry_run_access_list: "project-crashpad-committers"
committer_list: "project-crashpad-tryjob-access"
dry_run_access_list: "project-crashpad-tryjob-access"
}
try_job {
buckets {

View File

@ -252,7 +252,7 @@ enum MinidumpContextAMD64Flags : uint32_t {
//! normally alias `dr6` and `dr7`, respectively. See Intel Software
//! Developers Manual, Volume 3B: System Programming, Part 2 (253669-052),
//! 17.2.2 “Debug Registers DR4 and DR5”.
struct ALIGNAS(16) MinidumpContextAMD64 {
struct alignas(16) MinidumpContextAMD64 {
//! \brief Register parameter home address.
//!
//! On Windows, this field may contain the “home” address (on-stack, in the

View File

@ -17,6 +17,7 @@
#include "snapshot/win/pe_image_annotations_reader.h"
#include "snapshot/win/pe_image_reader.h"
#include "snapshot/win/process_reader_win.h"
#include "util/misc/from_pointer_cast.h"
#include "util/win/get_module_information.h"
namespace crashpad {
@ -38,7 +39,7 @@ bool ReadModuleAnnotations(HANDLE process,
PEImageReader image_reader;
if (!image_reader.Initialize(
&process_reader,
reinterpret_cast<crashpad::WinVMAddress>(module_info.lpBaseOfDll),
FromPointerCast<WinVMAddress>(module_info.lpBaseOfDll),
module_info.SizeOfImage,
""))
return false;

View File

@ -21,6 +21,7 @@
#include "client/crashpad_info.h"
#include "gtest/gtest.h"
#include "test/errors.h"
#include "test/scoped_module_handle.h"
#include "test/test_paths.h"
#if defined(OS_MACOSX)
@ -138,48 +139,6 @@ TEST(CrashpadInfoClientOptions, OneModule) {
}
}
#if defined(OS_POSIX)
using DlHandle = void*;
#elif defined(OS_WIN)
using DlHandle = HMODULE;
#endif // OS_POSIX
class ScopedDlHandle {
public:
explicit ScopedDlHandle(DlHandle dl_handle)
: dl_handle_(dl_handle) {
}
~ScopedDlHandle() {
if (dl_handle_) {
#if defined(OS_POSIX)
if (dlclose(dl_handle_) != 0) {
LOG(ERROR) << "dlclose: " << dlerror();
}
#elif defined(OS_WIN)
if (!FreeLibrary(dl_handle_))
PLOG(ERROR) << "FreeLibrary";
#endif // OS_POSIX
}
}
bool valid() const { return dl_handle_ != nullptr; }
template <typename T>
T LookUpSymbol(const char* symbol_name) {
#if defined(OS_POSIX)
return reinterpret_cast<T>(dlsym(dl_handle_, symbol_name));
#elif defined(OS_WIN)
return reinterpret_cast<T>(GetProcAddress(dl_handle_, symbol_name));
#endif // OS_POSIX
}
private:
DlHandle dl_handle_;
DISALLOW_COPY_AND_ASSIGN(ScopedDlHandle);
};
TEST(CrashpadInfoClientOptions, TwoModules) {
// Open the module, which has its own CrashpadInfo structure.
#if defined(OS_MACOSX)
@ -190,15 +149,15 @@ TEST(CrashpadInfoClientOptions, TwoModules) {
base::FilePath module_path = TestPaths::Executable().DirName().Append(
FILE_PATH_LITERAL("crashpad_snapshot_test_module") + kDlExtension);
#if defined(OS_MACOSX)
ScopedDlHandle dl_handle(
ScopedModuleHandle module(
dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL));
ASSERT_TRUE(dl_handle.valid()) << "dlopen " << module_path.value() << ": "
<< dlerror();
ASSERT_TRUE(module.valid()) << "dlopen " << module_path.value() << ": "
<< dlerror();
#elif defined(OS_WIN)
ScopedDlHandle dl_handle(LoadLibrary(module_path.value().c_str()));
ASSERT_TRUE(dl_handle.valid())
<< "LoadLibrary " << base::UTF16ToUTF8(module_path.value()) << ": "
<< ErrorMessage();
ScopedModuleHandle module(LoadLibrary(module_path.value().c_str()));
ASSERT_TRUE(module.valid()) << "LoadLibrary "
<< base::UTF16ToUTF8(module_path.value()) << ": "
<< ErrorMessage();
#else
#error Port.
#endif // OS_MACOSX
@ -207,7 +166,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) {
// because it runs in the module, it returns the remote modules CrashpadInfo
// structure.
CrashpadInfo* (*TestModule_GetCrashpadInfo)() =
dl_handle.LookUpSymbol<CrashpadInfo* (*)()>("TestModule_GetCrashpadInfo");
module.LookUpSymbol<CrashpadInfo* (*)()>("TestModule_GetCrashpadInfo");
ASSERT_TRUE(TestModule_GetCrashpadInfo);
auto options = SelfProcessSnapshotAndGetCrashpadOptions();

View File

@ -555,22 +555,14 @@ bool MachOImageReader::ReadSegmentCommand(
return false;
}
mach_vm_size_t vmsize = segment->vmsize();
if (segment_name == SEG_TEXT) {
mach_vm_size_t vmsize = segment->vmsize();
if (vmsize == 0) {
LOG(WARNING) << "zero-sized " SEG_TEXT " segment" << load_command_info;
return false;
}
mach_vm_size_t fileoff = segment->fileoff();
if (fileoff != 0) {
LOG(WARNING) << base::StringPrintf(
SEG_TEXT " segment has unexpected fileoff 0x%llx",
fileoff) << load_command_info;
return false;
}
size_ = vmsize;
// The slide is computed as the difference between the __TEXT segments

View File

@ -32,6 +32,7 @@
#include "snapshot/mac/process_reader.h"
#include "snapshot/mac/process_types.h"
#include "test/mac/dyld.h"
#include "util/misc/from_pointer_cast.h"
#include "util/misc/implicit_cast.h"
#include "util/misc/uuid.h"
@ -133,7 +134,7 @@ void ExpectSegmentCommand(const SegmentCommand* expect_segment,
const uint8_t* expect_segment_data = getsegmentdata(
expect_image, segment_name.c_str(), &expect_segment_size);
mach_vm_address_t expect_segment_address =
reinterpret_cast<mach_vm_address_t>(expect_segment_data);
FromPointerCast<mach_vm_address_t>(expect_segment_data);
EXPECT_EQ(actual_segment->Address(), expect_segment_address);
EXPECT_EQ(actual_segment->vmsize(), expect_segment_size);
EXPECT_EQ(actual_segment->Size(), actual_segment->vmsize());
@ -191,7 +192,7 @@ void ExpectSegmentCommand(const SegmentCommand* expect_segment,
section_name.c_str(),
&expect_section_size);
mach_vm_address_t expect_section_address =
reinterpret_cast<mach_vm_address_t>(expect_section_data);
FromPointerCast<mach_vm_address_t>(expect_section_data);
EXPECT_EQ(actual_section_address, expect_section_address);
EXPECT_EQ(actual_section->size, expect_section_size);
} else {
@ -501,7 +502,7 @@ TEST(MachOImageReader, Self_MainExecutable) {
reinterpret_cast<MachHeader*>(dlsym(RTLD_MAIN_ONLY, MH_EXECUTE_SYM));
ASSERT_NE(mh_execute_header, nullptr);
mach_vm_address_t mh_execute_header_address =
reinterpret_cast<mach_vm_address_t>(mh_execute_header);
FromPointerCast<mach_vm_address_t>(mh_execute_header);
MachOImageReader image_reader;
ASSERT_TRUE(image_reader.Initialize(
@ -547,7 +548,7 @@ TEST(MachOImageReader, Self_DyldImages) {
const MachHeader* mach_header =
reinterpret_cast<const MachHeader*>(_dyld_get_image_header(index));
mach_vm_address_t image_address =
reinterpret_cast<mach_vm_address_t>(mach_header);
FromPointerCast<mach_vm_address_t>(mach_header);
MachOImageReader image_reader;
ASSERT_TRUE(
@ -576,8 +577,7 @@ TEST(MachOImageReader, Self_DyldImages) {
// Now that all of the modules have been verified, make sure that dyld itself
// can be read properly too.
const struct dyld_all_image_infos* dyld_image_infos =
_dyld_get_all_image_infos();
const dyld_all_image_infos* dyld_image_infos = DyldGetAllImageInfos();
ASSERT_GE(dyld_image_infos->version, 1u);
EXPECT_EQ(dyld_image_infos->infoArrayCount, count);
@ -588,7 +588,7 @@ TEST(MachOImageReader, Self_DyldImages) {
const MachHeader* mach_header = reinterpret_cast<const MachHeader*>(
dyld_image_infos->dyldImageLoadAddress);
mach_vm_address_t image_address =
reinterpret_cast<mach_vm_address_t>(mach_header);
FromPointerCast<mach_vm_address_t>(mach_header);
MachOImageReader image_reader;
ASSERT_TRUE(
@ -619,7 +619,7 @@ TEST(MachOImageReader, Self_DyldImages) {
const MachHeader* mach_header =
reinterpret_cast<const MachHeader*>(dyld_image->imageLoadAddress);
mach_vm_address_t image_address =
reinterpret_cast<mach_vm_address_t>(mach_header);
FromPointerCast<mach_vm_address_t>(mach_header);
MachOImageReader image_reader;
ASSERT_TRUE(

View File

@ -120,36 +120,29 @@ bool MachOImageSegmentReader::Initialize(ProcessReader* process_reader,
sections_.size(),
load_command_info.c_str());
if (section_segment_name != segment_name) {
// cl_kernels modules (for OpenCL) arent ld output, and theyre formatted
// incorrectly on OS X 10.10 and later. They have a single __TEXT segment,
// but one of the sections within it claims to belong to the __LD segment.
// This mismatch shouldnt happen. This errant section also has the
// S_ATTR_DEBUG flag set, which shouldnt happen unless all of the other
// sections in the segment also have this bit set (they dont). These odd
// sections are reminiscent of unwind information stored in MH_OBJECT
// images, although cl_kernels images claim to be MH_BUNDLE. Because at
// least one cl_kernels module will commonly be found in a process, and
// sometimes more will be, tolerate this quirk.
//
// https://openradar.appspot.com/20239912
bool ok = false;
if (file_type == MH_BUNDLE && module_name == "cl_kernels") {
int mac_os_x_minor_version = MacOSXMinorVersion();
if ((mac_os_x_minor_version >= 10 && mac_os_x_minor_version <= 12) &&
segment_name == SEG_TEXT &&
section_segment_name == "__LD" &&
section_name == "__compact_unwind" &&
(section.flags & S_ATTR_DEBUG)) {
ok = true;
}
}
if (!ok) {
LOG(WARNING) << "section.segname incorrect in segment " << segment_name
<< section_info;
return false;
}
// cl_kernels modules (for OpenCL) arent ld output, and theyre formatted
// incorrectly on OS X 10.10 and later. They have a single __TEXT segment,
// but one of the sections within it claims to belong to the __LD segment.
// This mismatch shouldnt happen. This errant section also has the
// S_ATTR_DEBUG flag set, which shouldnt happen unless all of the other
// sections in the segment also have this bit set (they dont). These odd
// sections are reminiscent of unwind information stored in MH_OBJECT
// images, although cl_kernels images claim to be MH_BUNDLE. Because at
// least one cl_kernels module will commonly be found in a process, and
// sometimes more will be, tolerate this quirk.
//
// https://openradar.appspot.com/20239912
if (section_segment_name != segment_name &&
!(file_type == MH_BUNDLE &&
module_name == "cl_kernels" &&
MacOSXMinorVersion() >= 10 &&
segment_name == SEG_TEXT &&
section_segment_name == "__LD" &&
section_name == "__compact_unwind" &&
(section.flags & S_ATTR_DEBUG))) {
LOG(WARNING) << "section.segname incorrect in segment " << segment_name
<< section_info;
return false;
}
CheckedMachAddressRange section_range(

View File

@ -198,6 +198,46 @@ const std::vector<ProcessReader::Module>& ProcessReader::Modules() {
return modules_;
}
mach_vm_address_t ProcessReader::DyldAllImageInfo(
mach_vm_size_t* all_image_info_size) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
task_dyld_info_data_t dyld_info;
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
kern_return_t kr = task_info(
task_, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&dyld_info), &count);
if (kr != KERN_SUCCESS) {
MACH_LOG(WARNING, kr) << "task_info";
return 0;
}
// TODO(mark): Deal with statically linked executables which dont use dyld.
// This may look for the module that matches the executable path in the same
// data set that vmmap uses.
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
// The task_dyld_info_data_t struct grew in 10.7, adding the format field.
// Dont check this field if its not present, which can happen when either
// the SDK used at compile time or the kernel at run time are too old and
// dont know about it.
if (count >= TASK_DYLD_INFO_COUNT) {
const integer_t kExpectedFormat =
!Is64Bit() ? TASK_DYLD_ALL_IMAGE_INFO_32 : TASK_DYLD_ALL_IMAGE_INFO_64;
if (dyld_info.all_image_info_format != kExpectedFormat) {
LOG(WARNING) << "unexpected task_dyld_info_data_t::all_image_info_format "
<< dyld_info.all_image_info_format;
DCHECK_EQ(dyld_info.all_image_info_format, kExpectedFormat);
return 0;
}
}
#endif
if (all_image_info_size) {
*all_image_info_size = dyld_info.all_image_info_size;
}
return dyld_info.all_image_info_addr;
}
void ProcessReader::InitializeThreads() {
DCHECK(!initialized_threads_);
DCHECK(threads_.empty());
@ -345,38 +385,12 @@ void ProcessReader::InitializeModules() {
initialized_modules_ = true;
task_dyld_info_data_t dyld_info;
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
kern_return_t kr = task_info(
task_, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&dyld_info), &count);
if (kr != KERN_SUCCESS) {
MACH_LOG(WARNING, kr) << "task_info";
return;
}
// TODO(mark): Deal with statically linked executables which dont use dyld.
// This may look for the module that matches the executable path in the same
// data set that vmmap uses.
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
// The task_dyld_info_data_t struct grew in 10.7, adding the format field.
// Dont check this field if its not present, which can happen when either
// the SDK used at compile time or the kernel at run time are too old and
// dont know about it.
if (count >= TASK_DYLD_INFO_COUNT) {
const integer_t kExpectedFormat =
!Is64Bit() ? TASK_DYLD_ALL_IMAGE_INFO_32 : TASK_DYLD_ALL_IMAGE_INFO_64;
if (dyld_info.all_image_info_format != kExpectedFormat) {
LOG(WARNING) << "unexpected task_dyld_info_data_t::all_image_info_format "
<< dyld_info.all_image_info_format;
DCHECK_EQ(dyld_info.all_image_info_format, kExpectedFormat);
return;
}
}
#endif
mach_vm_size_t all_image_info_size;
mach_vm_address_t all_image_info_addr =
DyldAllImageInfo(&all_image_info_size);
process_types::dyld_all_image_infos all_image_infos;
if (!all_image_infos.Read(this, dyld_info.all_image_info_addr)) {
if (!all_image_infos.Read(this, all_image_info_addr)) {
LOG(WARNING) << "could not read dyld_all_image_infos";
return;
}
@ -390,10 +404,10 @@ void ProcessReader::InitializeModules() {
size_t expected_size =
process_types::dyld_all_image_infos::ExpectedSizeForVersion(
this, all_image_infos.version);
if (dyld_info.all_image_info_size < expected_size) {
LOG(WARNING) << "small dyld_all_image_infos size "
<< dyld_info.all_image_info_size << " < " << expected_size
<< " for version " << all_image_infos.version;
if (all_image_info_size < expected_size) {
LOG(WARNING) << "small dyld_all_image_infos size " << all_image_info_size
<< " < " << expected_size << " for version "
<< all_image_infos.version;
return;
}

View File

@ -150,6 +150,21 @@ class ProcessReader {
//! corresponds to the dynamic loader, dyld.
const std::vector<Module>& Modules();
//! \brief Determines the location of the `dyld_all_image_infos` structure in
//! the process address space.
//!
//! This function is an internal implementation detail of Modules(), and
//! should not normally be used directly. It is exposed solely for use by test
//! code.
//!
//! \param[out] all_image_info_size The size of the `dyld_all_image_infos`
//! structure. Optional, may be `nullptr` if not required.
//!
//! \return The address of the `dyld_all_image_infos` structure in the
//! process address space, with \a all_image_info_size set appropriately.
//! On failure, returns `0` with a message logged.
mach_vm_address_t DyldAllImageInfo(mach_vm_size_t* all_image_info_size);
private:
//! Performs lazy initialization of the \a threads_ vector on behalf of
//! Threads().

View File

@ -40,15 +40,17 @@
#include "util/file/file_io.h"
#include "util/mac/mac_util.h"
#include "util/mach/mach_extensions.h"
#include "util/misc/from_pointer_cast.h"
#include "util/stdlib/pointer_container.h"
#include "util/synchronization/semaphore.h"
#if !defined(MAC_OS_X_VERSION_10_10) || \
MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
extern "C" {
// Redeclare a typedef whose availability (OSX 10.10) is newer than the
// Redeclare a typedef whose availability (OS X 10.10) is newer than the
// deployment target.
typedef struct _cl_device_id* cl_device_id;
} // extern "C"
#endif
@ -74,7 +76,7 @@ TEST(ProcessReader, SelfBasic) {
const char kTestMemory[] = "Some test memory";
char buffer[arraysize(kTestMemory)];
ASSERT_TRUE(process_reader.Memory()->Read(
reinterpret_cast<mach_vm_address_t>(kTestMemory),
FromPointerCast<mach_vm_address_t>(kTestMemory),
sizeof(kTestMemory),
&buffer));
EXPECT_STREQ(kTestMemory, buffer);
@ -115,8 +117,7 @@ class ProcessReaderChild final : public MachMultiprocess {
void MachMultiprocessChild() override {
FileHandle write_handle = WritePipeHandle();
mach_vm_address_t address =
reinterpret_cast<mach_vm_address_t>(kTestMemory);
mach_vm_address_t address = FromPointerCast<mach_vm_address_t>(kTestMemory);
CheckedWriteFile(write_handle, &address, sizeof(address));
// Wait for the parent to signal that its OK to exit by closing its end of
@ -282,7 +283,7 @@ class TestThreadPool {
ThreadInfo* thread_info = static_cast<ThreadInfo*>(argument);
thread_info->stack_address =
reinterpret_cast<mach_vm_address_t>(&thread_info);
FromPointerCast<mach_vm_address_t>(&thread_info);
thread_info->ready_semaphore.Signal();
thread_info->exit_semaphore.Wait();
@ -391,7 +392,7 @@ TEST(ProcessReader, SelfSeveralThreads) {
ThreadMap thread_map;
const uint64_t self_thread_id = PthreadToThreadID(pthread_self());
TestThreadPool::ThreadExpectation expectation;
expectation.stack_address = reinterpret_cast<mach_vm_address_t>(&thread_map);
expectation.stack_address = FromPointerCast<mach_vm_address_t>(&thread_map);
expectation.suspend_count = 0;
thread_map[self_thread_id] = expectation;
for (size_t thread_index = 0; thread_index < kChildThreads; ++thread_index) {
@ -484,7 +485,7 @@ class ProcessReaderThreadedChild final : public MachMultiprocess {
CheckedWriteFile(write_handle, &thread_id, sizeof(thread_id));
TestThreadPool::ThreadExpectation expectation;
expectation.stack_address = reinterpret_cast<mach_vm_address_t>(&thread_id);
expectation.stack_address = FromPointerCast<mach_vm_address_t>(&thread_id);
expectation.suspend_count = 0;
CheckedWriteFile(write_handle,
@ -667,7 +668,7 @@ TEST(ProcessReader, SelfModules) {
ASSERT_TRUE(modules[index].reader);
EXPECT_EQ(
modules[index].reader->Address(),
reinterpret_cast<mach_vm_address_t>(_dyld_get_image_header(index)));
FromPointerCast<mach_vm_address_t>(_dyld_get_image_header(index)));
if (index == 0) {
// dyld didnt load the main executable, so it couldnt record its
@ -698,12 +699,11 @@ TEST(ProcessReader, SelfModules) {
// is also reported as 0.
EXPECT_EQ(modules[index].timestamp, 0);
const struct dyld_all_image_infos* dyld_image_infos =
_dyld_get_all_image_infos();
const dyld_all_image_infos* dyld_image_infos = DyldGetAllImageInfos();
if (dyld_image_infos->version >= 2) {
ASSERT_TRUE(modules[index].reader);
EXPECT_EQ(modules[index].reader->Address(),
reinterpret_cast<mach_vm_address_t>(
FromPointerCast<mach_vm_address_t>(
dyld_image_infos->dyldImageLoadAddress));
}
}
@ -781,8 +781,7 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
FileHandle write_handle = WritePipeHandle();
uint32_t dyld_image_count = _dyld_image_count();
const struct dyld_all_image_infos* dyld_image_infos =
_dyld_get_all_image_infos();
const dyld_all_image_infos* dyld_image_infos = DyldGetAllImageInfos();
uint32_t write_image_count = dyld_image_count;
if (dyld_image_infos->version >= 2) {
@ -801,10 +800,10 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
if (index < dyld_image_count) {
dyld_image_name = _dyld_get_image_name(index);
dyld_image_address =
reinterpret_cast<mach_vm_address_t>(_dyld_get_image_header(index));
FromPointerCast<mach_vm_address_t>(_dyld_get_image_header(index));
} else {
dyld_image_name = kDyldPath;
dyld_image_address = reinterpret_cast<mach_vm_address_t>(
dyld_image_address = FromPointerCast<mach_vm_address_t>(
dyld_image_infos->dyldImageLoadAddress);
}

View File

@ -41,6 +41,15 @@ inline void Assign(Type* destination, const Type& source) {
memcpy(destination, &source, sizeof(source));
}
template <>
inline void Assign<process_types::internal::Reserved32_32Only64,
process_types::internal::Reserved32_32Only32>(
process_types::internal::Reserved32_32Only64* destination,
const process_types::internal::Reserved32_32Only32& source) {
// Reserved32_32Only32 carries no data and has no storage in the 64-bit
// structure.
}
template <>
inline void Assign<process_types::internal::Reserved32_64Only64,
process_types::internal::Reserved32_64Only32>(

View File

@ -37,8 +37,10 @@ using Nothing = char[0];
// Some structure definitions differ in 32-bit and 64-bit environments by having
// additional “reserved” padding fields present only in the 64-bit environment.
// These Reserved*_64Only* types allow the process_types system to replicate
// These Reserved*_*Only* types allow the process_types system to replicate
// these structures more precisely.
using Reserved32_32Only32 = uint32_t;
using Reserved32_32Only64 = Nothing;
using Reserved32_64Only32 = Nothing;
using Reserved32_64Only64 = uint32_t;
using Reserved64_64Only32 = Nothing;
@ -71,6 +73,7 @@ DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64)
using Pointer = internal::TraitsGeneric::Pointer; \
using IntPtr = internal::TraitsGeneric::IntPtr; \
using UIntPtr = internal::TraitsGeneric::UIntPtr; \
using Reserved32_32Only = internal::TraitsGeneric::Reserved32_32Only; \
using Reserved32_64Only = internal::TraitsGeneric::Reserved32_64Only; \
using Reserved64_64Only = internal::TraitsGeneric::Reserved64_64Only; \
using Nothing = internal::TraitsGeneric::Nothing; \
@ -162,6 +165,7 @@ DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64)
using Pointer = typename Traits::Pointer; \
using IntPtr = typename Traits::IntPtr; \
using UIntPtr = typename Traits::UIntPtr; \
using Reserved32_32Only = typename Traits::Reserved32_32Only; \
using Reserved32_64Only = typename Traits::Reserved32_64Only; \
using Reserved64_64Only = typename Traits::Reserved64_64Only; \
using Nothing = typename Traits::Nothing; \

View File

@ -80,7 +80,9 @@ size_t dyld_all_image_infos<Traits>::ExpectedSizeForVersion(
offsetof(dyld_all_image_infos<Traits>, sharedCacheSlide), // 11
offsetof(dyld_all_image_infos<Traits>, sharedCacheUUID), // 12
offsetof(dyld_all_image_infos<Traits>, infoArrayChangeTimestamp), // 13
offsetof(dyld_all_image_infos<Traits>, end), // 14
offsetof(dyld_all_image_infos<Traits>, end_14_15), // 14
offsetof(dyld_all_image_infos<Traits>, end_14_15), // 15
sizeof(dyld_all_image_infos<Traits>), // 16
};
if (version >= arraysize(kSizeForVersion)) {

View File

@ -116,20 +116,28 @@ PROCESS_TYPE_STRUCT_BEGIN(dyld_all_image_infos)
// Version 14 (OS X 10.9)
// As of the 10.12 SDK, this is declared as reserved[9] for 64-bit platforms
// and reserved[4] for 32-bit platforms.
// and reserved[4] for 32-bit platforms. It was expanded to reserved[5] for
// 32-bit platforms in the 10.13 SDK to provide proper padding, but because
// the runtimes that use versions 14 and 15 were built with SDKs that did not
// have this extra padding, its necessary to treat the element at index 4 on
// 32-bit systems as outside of the version 14 and 15 structure. This is why
// |reserved| is only declared a 4-element array, with a special end_14_15
// member (not present in the native definition) available to indicate the
// end of the native version 14 and 15 structure, preceding the padding in the
// 32-bit structure that would natively be addressed at index 4 of |reserved|.
// Treat reserved_4_32 as only available in version 16 of the structure.
PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, reserved, [4])
PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_4)
PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_4_64)
PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_5)
PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_6)
PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_7)
PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_8)
PROCESS_TYPE_STRUCT_MEMBER(Nothing, end_14_15)
PROCESS_TYPE_STRUCT_MEMBER(Reserved32_32Only, reserved_4_32)
// The 32-bit version of the structure will have four extra bytes of tail
// padding when built for 64-bit systems than it does natively and when built
// for 32-bit systems. Instead of using sizeof(dyld_all_image_infos), use
// offsetof(dyld_all_image_infos, end) to avoid taking this tail padding into
// account.
PROCESS_TYPE_STRUCT_MEMBER(Nothing, end)
// Version 16 (macOS 10.13)
PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, compact_dyld_image_info_addr)
PROCESS_TYPE_STRUCT_MEMBER(ULong, compact_dyld_image_info_size) // size_t
PROCESS_TYPE_STRUCT_END(dyld_all_image_infos)
#endif // ! PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO &&

View File

@ -36,6 +36,7 @@
using Pointer = uint##lp_bits##_t __VA_ARGS__; \
using IntPtr = int##lp_bits##_t __VA_ARGS__; \
using UIntPtr = uint##lp_bits##_t __VA_ARGS__; \
using Reserved32_32Only = Reserved32_32Only##lp_bits; \
using Reserved32_64Only = Reserved32_64Only##lp_bits; \
using Reserved64_64Only = Reserved64_64Only##lp_bits; \
using Nothing = Nothing; \

View File

@ -27,6 +27,7 @@
#include "snapshot/mac/process_types/internal.h"
#include "test/mac/dyld.h"
#include "util/mac/mac_util.h"
#include "util/misc/from_pointer_cast.h"
#include "util/misc/implicit_cast.h"
namespace crashpad {
@ -45,9 +46,13 @@ namespace {
TEST(ProcessTypes, DyldImagesSelf) {
// Get the in-process view of dyld_all_image_infos, and check it for sanity.
const struct dyld_all_image_infos* self_image_infos =
_dyld_get_all_image_infos();
const dyld_all_image_infos* self_image_infos = DyldGetAllImageInfos();
int mac_os_x_minor_version = MacOSXMinorVersion();
// The 10.13 SDK defines dyld_all_image_infos version 16 and says that its
// used on 10.13, but 10.13db1 17A264c uses version 15.
//
// TODO(mark): Recheck later in the beta period, up to the 10.13 release.
if (mac_os_x_minor_version >= 12) {
EXPECT_GE(self_image_infos->version, 15u);
} else if (mac_os_x_minor_version >= 9) {
@ -81,7 +86,7 @@ TEST(ProcessTypes, DyldImagesSelf) {
ASSERT_EQ(kr, KERN_SUCCESS);
EXPECT_EQ(dyld_info.all_image_info_addr,
reinterpret_cast<mach_vm_address_t>(self_image_infos));
FromPointerCast<mach_vm_address_t>(self_image_infos));
EXPECT_GT(dyld_info.all_image_info_size, 1u);
// This field is only present in the OS X 10.7 SDK (at build time) and kernel
@ -99,16 +104,18 @@ TEST(ProcessTypes, DyldImagesSelf) {
ProcessReader process_reader;
ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12
const uint32_t kDyldAllImageInfosVersionInSDK = 15;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13
constexpr uint32_t kDyldAllImageInfosVersionInSDK = 16;
#elif MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12
constexpr uint32_t kDyldAllImageInfosVersionInSDK = 15;
#elif MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
const uint32_t kDyldAllImageInfosVersionInSDK = 14;
constexpr uint32_t kDyldAllImageInfosVersionInSDK = 14;
#elif MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
const uint32_t kDyldAllImageInfosVersionInSDK = 12;
constexpr uint32_t kDyldAllImageInfosVersionInSDK = 12;
#elif MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
const uint32_t kDyldAllImageInfosVersionInSDK = 7;
constexpr uint32_t kDyldAllImageInfosVersionInSDK = 7;
#else
const uint32_t kDyldAllImageInfosVersionInSDK = 1;
constexpr uint32_t kDyldAllImageInfosVersionInSDK = 1;
#endif
// Make sure that the size of the structure as declared in the SDK matches the
@ -119,7 +126,7 @@ TEST(ProcessTypes, DyldImagesSelf) {
// Make sure that the computed sizes of various versions of this structure are
// correct at different bitnessses.
const struct {
constexpr struct {
uint32_t version;
size_t size_32;
size_t size_64;
@ -138,6 +145,7 @@ TEST(ProcessTypes, DyldImagesSelf) {
{13, 104, 184},
{14, 164, 304},
{15, 164, 304},
{16, 176, 320},
};
for (size_t index = 0; index < arraysize(kVersionsAndSizes); ++index) {
uint32_t version = kVersionsAndSizes[index].version;
@ -289,7 +297,8 @@ TEST(ProcessTypes, DyldImagesSelf) {
<< "index " << index;
}
#if defined(ARCH_CPU_64_BITS)
EXPECT_EQ(proctype_image_infos.reserved_4, self_image_infos->reserved[4]);
EXPECT_EQ(proctype_image_infos.reserved_4_64,
self_image_infos->reserved[4]);
EXPECT_EQ(proctype_image_infos.reserved_5, self_image_infos->reserved[5]);
EXPECT_EQ(proctype_image_infos.reserved_6, self_image_infos->reserved[6]);
EXPECT_EQ(proctype_image_infos.reserved_7, self_image_infos->reserved[7]);
@ -298,6 +307,15 @@ TEST(ProcessTypes, DyldImagesSelf) {
}
#endif
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13
if (proctype_image_infos.version >= 16) {
EXPECT_EQ(proctype_image_infos.compact_dyld_image_info_addr,
self_image_infos->compact_dyld_image_info_addr);
EXPECT_EQ(proctype_image_infos.compact_dyld_image_info_size,
self_image_infos->compact_dyld_image_info_size);
}
#endif
if (proctype_image_infos.version >= 1) {
std::vector<process_types::dyld_image_info> proctype_image_info_vector(
proctype_image_infos.infoArrayCount);

View File

@ -12,18 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <intrin.h>
#include <windows.h>
#include "base/files/file_path.h"
#include "base/logging.h"
#include "client/crashpad_client.h"
#include "util/file/file_io.h"
#include "util/misc/from_pointer_cast.h"
#include "util/win/address_types.h"
namespace {
__declspec(noinline) crashpad::WinVMAddress CurrentAddress() {
return reinterpret_cast<crashpad::WinVMAddress>(_ReturnAddress());
return crashpad::FromPointerCast<crashpad::WinVMAddress>(_ReturnAddress());
}
} // namespace

View File

@ -12,18 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <intrin.h>
#include <windows.h>
#include "base/logging.h"
#include "client/crashpad_client.h"
#include "client/simulate_crash.h"
#include "util/file/file_io.h"
#include "util/misc/from_pointer_cast.h"
#include "util/win/address_types.h"
namespace {
__declspec(noinline) crashpad::WinVMAddress CurrentAddress() {
return reinterpret_cast<crashpad::WinVMAddress>(_ReturnAddress());
return crashpad::FromPointerCast<crashpad::WinVMAddress>(_ReturnAddress());
}
} // namespace

View File

@ -22,6 +22,7 @@
#include "base/logging.h"
#include "client/crashpad_info.h"
#include "snapshot/win/pe_image_resource_reader.h"
#include "util/misc/from_pointer_cast.h"
#include "util/misc/pdb_structures.h"
#include "util/win/process_structs.h"
@ -202,10 +203,8 @@ bool PEImageReader::VSFixedFileInfo(
WinVMAddress address;
WinVMSize size;
const uint16_t vs_file_info_type = static_cast<uint16_t>(
reinterpret_cast<uintptr_t>(VS_FILE_INFO)); // RT_VERSION
if (!resource_reader.FindResourceByID(
vs_file_info_type,
FromPointerCast<uint16_t>(VS_FILE_INFO), // RT_VERSION
VS_VERSION_INFO,
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
&address,

View File

@ -22,6 +22,7 @@
#include "gtest/gtest.h"
#include "snapshot/win/process_reader_win.h"
#include "test/errors.h"
#include "util/misc/from_pointer_cast.h"
#include "util/win/get_module_information.h"
#include "util/win/module_version.h"
#include "util/win/process_info.h"
@ -44,7 +45,7 @@ TEST(PEImageReader, DebugDirectory) {
<< ErrorMessage("GetModuleInformation");
EXPECT_EQ(module_info.lpBaseOfDll, self);
ASSERT_TRUE(pe_image_reader.Initialize(&process_reader,
reinterpret_cast<WinVMAddress>(self),
FromPointerCast<WinVMAddress>(self),
module_info.SizeOfImage,
"self"));
UUID uuid;
@ -139,7 +140,7 @@ TEST(PEImageReader, VSFixedFileInfo_OneModule) {
ProcessInfo::Module module;
module.name = kModuleName;
module.dll_base = reinterpret_cast<WinVMAddress>(module_info.lpBaseOfDll);
module.dll_base = FromPointerCast<WinVMAddress>(module_info.lpBaseOfDll);
module.size = module_info.SizeOfImage;
TestVSFixedFileInfo(&process_reader, module, true);

View File

@ -19,6 +19,7 @@
#include "gtest/gtest.h"
#include "test/win/win_multiprocess.h"
#include "util/misc/from_pointer_cast.h"
#include "util/synchronization/semaphore.h"
#include "util/thread/thread.h"
#include "util/win/scoped_process_suspend.h"
@ -42,10 +43,8 @@ TEST(ProcessReaderWin, SelfBasic) {
const char kTestMemory[] = "Some test memory";
char buffer[arraysize(kTestMemory)];
ASSERT_TRUE(
process_reader.ReadMemory(reinterpret_cast<uintptr_t>(kTestMemory),
sizeof(kTestMemory),
&buffer));
ASSERT_TRUE(process_reader.ReadMemory(
reinterpret_cast<uintptr_t>(kTestMemory), sizeof(kTestMemory), &buffer));
EXPECT_STREQ(kTestMemory, buffer);
}
@ -78,7 +77,7 @@ class ProcessReaderChild final : public WinMultiprocess {
}
void WinMultiprocessChild() override {
WinVMAddress address = reinterpret_cast<WinVMAddress>(kTestMemory);
WinVMAddress address = FromPointerCast<WinVMAddress>(kTestMemory);
CheckedWriteFile(WritePipeHandle(), &address, sizeof(address));
// Wait for the parent to signal that it's OK to exit by closing its end of

View File

@ -26,6 +26,7 @@
#include "snapshot/win/exception_snapshot_win.h"
#include "snapshot/win/memory_snapshot_win.h"
#include "snapshot/win/module_snapshot_win.h"
#include "util/misc/from_pointer_cast.h"
#include "util/win/nt_internals.h"
#include "util/win/registration_protocol_win.h"
#include "util/win/time.h"
@ -295,7 +296,7 @@ void ProcessSnapshotWin::InitializeUnloadedModules() {
}
const WinVMAddress address_in_target_process =
reinterpret_cast<WinVMAddress>(event_trace_address);
FromPointerCast<WinVMAddress>(event_trace_address);
Traits::Pointer pointer_to_array;
if (!process_reader_.ReadMemory(address_in_target_process,

99
test/mac/dyld.cc Normal file
View File

@ -0,0 +1,99 @@
// 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 "test/mac/dyld.h"
#include <AvailabilityMacros.h>
#include <dlfcn.h>
#include <mach/mach.h>
#include <mach-o/dyld.h>
#include <stdint.h>
#include "base/logging.h"
#include "snapshot/mac/process_reader.h"
#include "test/scoped_module_handle.h"
#include "util/numeric/safe_assignment.h"
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13
extern "C" {
// A non-public dyld API, declared in 10.12.4
// dyld-433.5/include/mach-o/dyld_priv.h. The code still exists in 10.13, but
// its symbol is no longer public, so it cant be used there.
const dyld_all_image_infos* _dyld_get_all_image_infos()
__attribute__((weak_import));
} // extern "C"
#endif
namespace crashpad {
namespace test {
const dyld_all_image_infos* DyldGetAllImageInfos() {
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13
// When building with the pre-10.13 SDK, the weak_import declaration above is
// available and a symbol will be present in the SDK to link against. If the
// old interface is also available at run time (running on pre-10.13), use it.
if (_dyld_get_all_image_infos) {
return _dyld_get_all_image_infos();
}
#elif MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_13
// When building with the 10.13 SDK or later, but able to run on pre-10.13,
// look for _dyld_get_all_image_infos in the same module that provides
// _dyld_image_count. Theres no symbol in the SDK to link against, so this is
// a little more involved than the pre-10.13 SDK case above.
Dl_info dli;
if (!dladdr(reinterpret_cast<void*>(_dyld_image_count), &dli)) {
LOG(WARNING) << "dladdr: failed";
} else {
ScopedModuleHandle module(
dlopen(dli.dli_fname, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD));
if (!module.valid()) {
LOG(WARNING) << "dlopen: " << dlerror();
} else {
using DyldGetAllImageInfosType = const dyld_all_image_infos*(*)();
const auto _dyld_get_all_image_infos =
module.LookUpSymbol<DyldGetAllImageInfosType>(
"_dyld_get_all_image_infos");
if (_dyld_get_all_image_infos) {
return _dyld_get_all_image_infos();
}
}
}
#endif
// On 10.13 and later, do it the hard way.
ProcessReader process_reader;
if (!process_reader.Initialize(mach_task_self())) {
return nullptr;
}
mach_vm_address_t all_image_info_addr_m =
process_reader.DyldAllImageInfo(nullptr);
if (!all_image_info_addr_m) {
return nullptr;
}
uintptr_t all_image_info_addr_u;
if (!AssignIfInRange(&all_image_info_addr_u, all_image_info_addr_m)) {
LOG(ERROR) << "all_image_info_addr_m " << all_image_info_addr_m
<< " out of range";
return nullptr;
}
return reinterpret_cast<const dyld_all_image_infos*>(all_image_info_addr_u);
}
} // namespace test
} // namespace crashpad

View File

@ -17,13 +17,17 @@
#include <mach-o/dyld_images.h>
extern "C" {
namespace crashpad {
namespace test {
// Returns a pointer to this process dyld_all_image_infos structure. This is
// implemented as a non-public dyld API, declared in 10.9.2
// dyld-239.4/include/mach-o/dyld_priv.h.
const struct dyld_all_image_infos* _dyld_get_all_image_infos();
//! \brief Calls or emulates the `_dyld_get_all_image_infos()` private/internal
//! function.
//!
//! \return A pointer to this process dyld_all_image_infos structure, or
//! `nullptr` on failure with a message logged.
const dyld_all_image_infos* DyldGetAllImageInfos();
} // extern "C"
} // namespace test
} // namespace crashpad
#endif // CRASHPAD_TEST_MAC_DYLD_H_

View File

@ -0,0 +1,46 @@
// 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 "test/scoped_module_handle.h"
#include "base/logging.h"
namespace crashpad {
namespace test {
// static
void ScopedModuleHandle::Impl::Close(ModuleHandle handle) {
#if defined(OS_POSIX)
if (dlclose(handle) != 0) {
LOG(ERROR) << "dlclose: " << dlerror();
}
#elif defined(OS_WIN)
if (!FreeLibrary(handle)) {
PLOG(ERROR) << "FreeLibrary";
}
#else
#error Port
#endif
}
ScopedModuleHandle::ScopedModuleHandle(ModuleHandle handle) : handle_(handle) {}
ScopedModuleHandle::~ScopedModuleHandle() {
if (valid()) {
Impl::Close(handle_);
}
}
} // namespace test
} // namespace crashpad

View File

@ -0,0 +1,81 @@
// 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_TEST_SCOPED_MODULE_HANDLE_H_
#define CRASHPAD_TEST_SCOPED_MODULE_HANDLE_H_
#include "base/macros.h"
#include "build/build_config.h"
#if defined(OS_POSIX)
#include <dlfcn.h>
#elif defined(OS_WIN)
#include <windows.h>
#endif
namespace crashpad {
namespace test {
//! \brief Maintains ownership of a loadable module handle, releasing it as
//! appropriate on destruction.
class ScopedModuleHandle {
private:
class Impl {
public:
#if defined(OS_POSIX)
using ModuleHandle = void*;
static void* LookUpSymbol(ModuleHandle handle, const char* symbol_name) {
return dlsym(handle, symbol_name);
}
#elif defined(OS_WIN)
using ModuleHandle = HMODULE;
static void* LookUpSymbol(ModuleHandle handle, const char* symbol_name) {
return GetProcAddress(handle, symbol_name);
}
#endif
static void Close(ModuleHandle handle);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Impl);
};
public:
using ModuleHandle = Impl::ModuleHandle;
explicit ScopedModuleHandle(ModuleHandle handle);
~ScopedModuleHandle();
//! \return `true` if this object manages a valid loadable module handle.
bool valid() const { return handle_ != nullptr; }
//! \return The value of the symbol named by \a symbol_name, or `nullptr` on
//! failure.
template <typename T>
T LookUpSymbol(const char* symbol_name) const {
return reinterpret_cast<T>(Impl::LookUpSymbol(handle_, symbol_name));
}
private:
ModuleHandle handle_;
DISALLOW_COPY_AND_ASSIGN(ScopedModuleHandle);
};
} // namespace test
} // namespace crashpad
#endif // CRASHPAD_TEST_SCOPED_MODULE_HANDLE_H_

View File

@ -22,6 +22,7 @@
'type': 'static_library',
'dependencies': [
'../compat/compat.gyp:crashpad_compat',
'../snapshot/snapshot.gyp:crashpad_snapshot',
'../third_party/gtest/gtest.gyp:gtest',
'../third_party/mini_chromium/mini_chromium.gyp:base',
'../util/util.gyp:crashpad_util',
@ -37,6 +38,7 @@
'gtest_death_check.h',
'hex_string.cc',
'hex_string.h',
'mac/dyld.cc',
'mac/dyld.h',
'mac/mach_errors.cc',
'mac/mach_errors.h',
@ -49,6 +51,8 @@
'multiprocess_exec_posix.cc',
'multiprocess_exec_win.cc',
'multiprocess_posix.cc',
'scoped_module_handle.cc',
'scoped_module_handle.h',
'scoped_temp_dir.cc',
'scoped_temp_dir.h',
'scoped_temp_dir_posix.cc',

View File

@ -156,6 +156,27 @@ void CheckedReadFileAtEOF(FileHandle file) {
}
}
bool LoggingReadEntireFile(const base::FilePath& path, std::string* contents) {
ScopedFileHandle handle(LoggingOpenFileForRead(path));
if (!handle.is_valid()) {
return false;
}
char buffer[4096];
FileOperationResult rv;
std::string local_contents;
while ((rv = ReadFile(handle.get(), buffer, sizeof(buffer))) > 0) {
DCHECK_LE(static_cast<size_t>(rv), sizeof(buffer));
local_contents.append(buffer, rv);
}
if (rv < 0) {
PLOG(ERROR) << internal::kNativeReadFunctionName;
return false;
}
contents->swap(local_contents);
return true;
}
void CheckedCloseFile(FileHandle file) {
CHECK(LoggingCloseFile(file));
}

View File

@ -17,6 +17,8 @@
#include <sys/types.h>
#include <string>
#include "build/build_config.h"
#if defined(OS_POSIX)
@ -306,6 +308,12 @@ void CheckedWriteFile(FileHandle file, const void* buffer, size_t size);
//! \sa ReadFile
void CheckedReadFileAtEOF(FileHandle file);
//! brief Wraps LoggingOpenFileForRead() and ReadFile() reading the entire file
//! into \a contents.
//!
//! \return `true` on success, or `false` with a message logged.
bool LoggingReadEntireFile(const base::FilePath& path, std::string* contents);
//! \brief Wraps `open()` or `CreateFile()`, opening an existing file for
//! reading.
//!

View File

@ -0,0 +1,62 @@
// 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 "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;
}
} // namespace crashpad

View File

@ -0,0 +1,74 @@
// 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"
#include "util/misc/reinterpret_bytes.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 ReinterpretBytes(iter->second, value);
}
protected:
std::map<uint64_t, uint64_t> values_;
private:
template <typename ULong>
bool Read(pid_t pid);
DISALLOW_COPY_AND_ASSIGN(AuxiliaryVector);
};
} // namespace crashpad
#endif // CRASHPAD_UTIL_LINUX_AUXILIARY_VECTOR_H_

View 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

View File

@ -0,0 +1,37 @@
// 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_CHECKED_LINUX_ADDRESS_RANGE_H_
#define CRASHPAD_UTIL_LINUX_CHECKED_LINUX_ADDRESS_RANGE_H_
#include "util/linux/address_types.h"
#include "util/numeric/checked_address_range.h"
namespace crashpad {
//! \brief Ensures that a range, composed of a base and a size, does not
//! overflow the pointer type of the process it describes a range in.
//!
//! This class checks bases of type #LinuxVMAddress and sizes of type
//! #LinuxVMSize against a process whose pointer type is either 32 or 64
//! bits wide.
//!
//! Aside from varying the overall range on the basis of a process pointer type
//! width, this class functions very similarly to CheckedRange.
using CheckedLinuxAddressRange =
internal::CheckedAddressRangeGeneric<LinuxVMAddress, LinuxVMSize>;
} // namespace crashpad
#endif // CRASHPAD_UTIL_LINUX_CHECKED_LINUX_ADDRESS_RANGE_H_

270
util/linux/memory_map.cc Normal file
View File

@ -0,0 +1,270 @@
// 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 <linux/kdev_t.h>
#include <stdio.h>
#include <string.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 {
// This function is used in this file specfically for signed or unsigned longs.
// longs are typically either int or int64 sized, but pointers to longs are not
// automatically coerced to pointers to ints when they are the same size.
// Simply adding a StringToNumber for longs doesn't work since sometimes long
// and int64_t are actually the same type, resulting in a redefinition error.
template <typename Type>
bool LocalStringToNumber(const base::StringPiece& string, Type* number) {
static_assert(sizeof(Type) == sizeof(int) || sizeof(Type) == sizeof(int64_t),
"Unexpected Type size");
if (sizeof(Type) == sizeof(int)) {
return std::numeric_limits<Type>::is_signed
? StringToNumber(string, reinterpret_cast<int*>(number))
: StringToNumber(string,
reinterpret_cast<unsigned int*>(number));
} else {
return std::numeric_limits<Type>::is_signed
? StringToNumber(string, reinterpret_cast<int64_t*>(number))
: StringToNumber(string, reinterpret_cast<uint64_t*>(number));
}
}
template <typename Type>
bool HexStringToNumber(const std::string& string, Type* number) {
return LocalStringToNumber("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;
}
// TODO(jperaza): set bitness properly
#if defined(ARCH_CPU_64_BITS)
const bool is_64_bit = true;
#else
const 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;
}
int major, minor;
if (maps_file_reader->GetDelim(' ', &field) !=
DelimitedFileReader::Result::kSuccess ||
(field.pop_back(), field.size() != 5) ||
!HexStringToNumber(field.substr(0, 2), &major) ||
!HexStringToNumber(field.substr(3, 2), &minor)) {
LOG(ERROR) << "format error";
return ParseResult::kError;
}
mapping.device = MKDEV(major, minor);
if (maps_file_reader->GetDelim(' ', &field) !=
DelimitedFileReader::Result::kSuccess ||
(field.pop_back(), !LocalStringToNumber(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;
}
} // 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::Initialize(pid_t pid) {
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", pid);
if (!LoggingReadEntireFile(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;
}
} // namespace crashpad

84
util/linux/memory_map.h Normal file
View File

@ -0,0 +1,84 @@
// 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_MEMORY_MAP_H_
#define CRASHPAD_UTIL_LINUX_MEMORY_MAP_H_
#include <sys/types.h>
#include <string>
#include <vector>
#include "util/linux/address_types.h"
#include "util/linux/checked_linux_address_range.h"
#include "util/misc/initialization_state_dcheck.h"
namespace crashpad {
//! \brief Accesses information about mapped memory in another process.
//!
//! The target process must be stopped to guarantee correct mappings. If the
//! target process is not stopped, mappings may be invalid after the return from
//! Initialize(), and even mappings existing at the time Initialize() was called
//! may not be found.
class MemoryMap {
public:
//! \brief Information about a mapped region of memory.
struct Mapping {
Mapping();
std::string name;
CheckedLinuxAddressRange range;
off_t offset;
dev_t device;
ino_t inode;
bool readable;
bool writable;
bool executable;
bool shareable;
};
MemoryMap();
~MemoryMap();
//! \brief Initializes this object with information about the mapped memory
//! regions in the process whose ID is \a pid.
//!
//! This method must be called successfully prior to calling any other method
//! in this class. This method may only be called once.
//!
//! \param[in] pid The process ID to obtain information for.
//!
//! \return `true` on success, `false` on failure with a message logged.
bool Initialize(pid_t pid);
//! \return The Mapping containing \a address or `nullptr` if no match is
//! found. The caller does not take ownership of this object. It is scoped
//! to the lifetime of the MemoryMap object that it was obtained from.
const Mapping* FindMapping(LinuxVMAddress address) const;
//! \return The Mapping with the lowest base address whose name is \a name or
//! `nullptr` if no match is found. The caller does not take ownership of
//! this object. It is scoped to the lifetime of the MemoryMap object that
//! it was obtained from.
const Mapping* FindMappingWithName(const std::string& name) const;
private:
std::vector<Mapping> mappings_;
InitializationStateDcheck initialized_;
};
} // namespace crashpad
#endif // CRASHPAD_UTIL_LINUX_MEMORY_MAP_H_

View File

@ -0,0 +1,342 @@
// 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 <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include "base/files/file_path.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "test/errors.h"
#include "test/file.h"
#include "test/multiprocess.h"
#include "test/scoped_temp_dir.h"
#include "util/file/file_io.h"
#include "util/linux/scoped_ptrace_attach.h"
#include "util/misc/clock.h"
#include "util/misc/from_pointer_cast.h"
#include "util/posix/scoped_mmap.h"
namespace crashpad {
namespace test {
namespace {
TEST(MemoryMap, SelfBasic) {
ScopedMmap mmapping;
ASSERT_TRUE(mmapping.ResetMmap(nullptr,
getpagesize(),
PROT_EXEC | PROT_READ,
MAP_SHARED | MAP_ANON,
-1,
0));
MemoryMap map;
ASSERT_TRUE(map.Initialize(getpid()));
auto stack_address = FromPointerCast<LinuxVMAddress>(&map);
const MemoryMap::Mapping* mapping = map.FindMapping(stack_address);
ASSERT_TRUE(mapping);
EXPECT_GE(stack_address, mapping->range.Base());
EXPECT_LT(stack_address, mapping->range.End());
EXPECT_TRUE(mapping->readable);
EXPECT_TRUE(mapping->writable);
auto code_address = FromPointerCast<LinuxVMAddress>(getpid);
mapping = map.FindMapping(code_address);
ASSERT_TRUE(mapping);
EXPECT_GE(code_address, mapping->range.Base());
EXPECT_LT(code_address, mapping->range.End());
EXPECT_TRUE(mapping->readable);
EXPECT_FALSE(mapping->writable);
EXPECT_TRUE(mapping->executable);
auto mapping_address = mmapping.addr_as<LinuxVMAddress>();
mapping = map.FindMapping(mapping_address);
ASSERT_TRUE(mapping);
mapping = map.FindMapping(mapping_address + mmapping.len() - 1);
ASSERT_TRUE(mapping);
EXPECT_EQ(mapping_address, mapping->range.Base());
EXPECT_EQ(mapping_address + mmapping.len(), mapping->range.End());
EXPECT_TRUE(mapping->readable);
EXPECT_FALSE(mapping->writable);
EXPECT_TRUE(mapping->executable);
EXPECT_TRUE(mapping->shareable);
}
class MapChildTest : public Multiprocess {
public:
MapChildTest() : Multiprocess(), page_size_(getpagesize()) {}
~MapChildTest() {}
private:
void MultiprocessParent() override {
LinuxVMAddress code_address;
CheckedReadFileExactly(
ReadPipeHandle(), &code_address, sizeof(code_address));
LinuxVMAddress stack_address;
CheckedReadFileExactly(
ReadPipeHandle(), &stack_address, sizeof(stack_address));
LinuxVMAddress mapped_address;
CheckedReadFileExactly(
ReadPipeHandle(), &mapped_address, sizeof(mapped_address));
LinuxVMAddress mapped_file_address;
CheckedReadFileExactly(
ReadPipeHandle(), &mapped_file_address, sizeof(mapped_file_address));
LinuxVMSize path_length;
CheckedReadFileExactly(ReadPipeHandle(), &path_length, sizeof(path_length));
std::string mapped_file_name(path_length, std::string::value_type());
CheckedReadFileExactly(ReadPipeHandle(), &mapped_file_name[0], path_length);
ScopedPtraceAttach attachment;
attachment.ResetAttach(ChildPID());
MemoryMap map;
ASSERT_TRUE(map.Initialize(ChildPID()));
const MemoryMap::Mapping* mapping = map.FindMapping(code_address);
ASSERT_TRUE(mapping);
EXPECT_GE(code_address, mapping->range.Base());
EXPECT_LT(code_address, mapping->range.End());
EXPECT_TRUE(mapping->readable);
EXPECT_TRUE(mapping->executable);
EXPECT_FALSE(mapping->writable);
mapping = map.FindMapping(stack_address);
ASSERT_TRUE(mapping);
EXPECT_GE(stack_address, mapping->range.Base());
EXPECT_LT(stack_address, mapping->range.End());
EXPECT_TRUE(mapping->readable);
EXPECT_TRUE(mapping->writable);
mapping = map.FindMapping(mapped_address);
ASSERT_TRUE(mapping);
EXPECT_EQ(mapped_address, mapping->range.Base());
EXPECT_EQ(mapped_address + page_size_, mapping->range.End());
EXPECT_FALSE(mapping->readable);
EXPECT_FALSE(mapping->writable);
EXPECT_FALSE(mapping->executable);
EXPECT_TRUE(mapping->shareable);
mapping = map.FindMapping(mapped_file_address);
ASSERT_TRUE(mapping);
EXPECT_EQ(mapped_file_address, mapping->range.Base());
EXPECT_EQ(mapping->offset, static_cast<int64_t>(page_size_));
EXPECT_TRUE(mapping->readable);
EXPECT_TRUE(mapping->writable);
EXPECT_FALSE(mapping->executable);
EXPECT_FALSE(mapping->shareable);
EXPECT_EQ(mapping->name, mapped_file_name);
struct stat file_stat;
ASSERT_EQ(stat(mapped_file_name.c_str(), &file_stat), 0)
<< ErrnoMessage("stat");
EXPECT_EQ(mapping->device, file_stat.st_dev);
EXPECT_EQ(mapping->inode, file_stat.st_ino);
EXPECT_EQ(map.FindMappingWithName(mapping->name), mapping);
}
void MultiprocessChild() override {
auto code_address = FromPointerCast<LinuxVMAddress>(getpid);
CheckedWriteFile(WritePipeHandle(), &code_address, sizeof(code_address));
auto stack_address = FromPointerCast<LinuxVMAddress>(&code_address);
CheckedWriteFile(WritePipeHandle(), &stack_address, sizeof(stack_address));
ScopedMmap mapping;
ASSERT_TRUE(mapping.ResetMmap(
nullptr, page_size_, PROT_NONE, MAP_SHARED | MAP_ANON, -1, 0));
auto mapped_address = mapping.addr_as<LinuxVMAddress>();
CheckedWriteFile(
WritePipeHandle(), &mapped_address, sizeof(mapped_address));
ScopedTempDir temp_dir;
base::FilePath path =
temp_dir.path().Append(FILE_PATH_LITERAL("test_file"));
ASSERT_FALSE(FileExists(path));
std::string path_string = path.value();
ScopedFileHandle handle(LoggingOpenFileForReadAndWrite(
path, FileWriteMode::kReuseOrCreate, FilePermissions::kOwnerOnly));
ASSERT_TRUE(handle.is_valid());
std::string file_contents(page_size_ * 2, std::string::value_type());
CheckedWriteFile(handle.get(), file_contents.c_str(), file_contents.size());
ScopedMmap file_mapping;
ASSERT_TRUE(file_mapping.ResetMmap(nullptr,
page_size_,
PROT_READ | PROT_WRITE,
MAP_PRIVATE,
handle.get(),
page_size_));
auto mapped_file_address = file_mapping.addr_as<LinuxVMAddress>();
CheckedWriteFile(
WritePipeHandle(), &mapped_file_address, sizeof(mapped_file_address));
LinuxVMSize path_length = path_string.size();
CheckedWriteFile(WritePipeHandle(), &path_length, sizeof(path_length));
CheckedWriteFile(WritePipeHandle(), path_string.c_str(), path_length);
CheckedReadFileAtEOF(ReadPipeHandle());
}
const size_t page_size_;
DISALLOW_COPY_AND_ASSIGN(MapChildTest);
};
TEST(MemoryMap, MapChild) {
MapChildTest test;
test.Run();
}
// Some systems optimize mappings by allocating new mappings inside existing
// mappings with matching permissions. Work around this by allocating one large
// mapping and then switching up the permissions of individual pages to force
// populating more entries in the maps file.
void InitializeMappings(ScopedMmap* mappings,
size_t num_mappings,
size_t mapping_size) {
ASSERT_TRUE(mappings->ResetMmap(nullptr,
mapping_size * num_mappings,
PROT_READ,
MAP_PRIVATE | MAP_ANON,
-1,
0));
auto region = mappings->addr_as<LinuxVMAddress>();
for (size_t index = 0; index < num_mappings; index += 2) {
ASSERT_EQ(mprotect(reinterpret_cast<void*>(region + index * mapping_size),
mapping_size,
PROT_READ | PROT_WRITE),
0)
<< ErrnoMessage("mprotect");
}
}
void ExpectMappings(const MemoryMap& map,
LinuxVMAddress region_addr,
size_t num_mappings,
size_t mapping_size) {
for (size_t index = 0; index < num_mappings; ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
auto mapping_address = region_addr + index * mapping_size;
const MemoryMap::Mapping* mapping = map.FindMapping(mapping_address);
ASSERT_TRUE(mapping);
EXPECT_EQ(mapping_address, mapping->range.Base());
EXPECT_EQ(mapping_address + mapping_size, mapping->range.End());
EXPECT_TRUE(mapping->readable);
EXPECT_FALSE(mapping->shareable);
EXPECT_FALSE(mapping->executable);
if (index % 2 == 0) {
EXPECT_TRUE(mapping->writable);
} else {
EXPECT_FALSE(mapping->writable);
}
}
}
TEST(MemoryMap, SelfLargeMapFile) {
constexpr size_t kNumMappings = 1024;
const size_t page_size = getpagesize();
ScopedMmap mappings;
ASSERT_NO_FATAL_FAILURE(
InitializeMappings(&mappings, kNumMappings, page_size));
MemoryMap map;
ASSERT_TRUE(map.Initialize(getpid()));
ExpectMappings(
map, mappings.addr_as<LinuxVMAddress>(), kNumMappings, page_size);
}
class MapRunningChildTest : public Multiprocess {
public:
MapRunningChildTest() : Multiprocess(), page_size_(getpagesize()) {}
~MapRunningChildTest() {}
private:
void MultiprocessParent() override {
// Let the child get started
LinuxVMAddress region_addr;
CheckedReadFileExactly(ReadPipeHandle(), &region_addr, sizeof(region_addr));
for (int iter = 0; iter < 8; ++iter) {
SCOPED_TRACE(base::StringPrintf("iter %d", iter));
// Let the child get back to its work
SleepNanoseconds(1000);
ScopedPtraceAttach attachment;
attachment.ResetAttach(ChildPID());
MemoryMap map;
ASSERT_TRUE(map.Initialize(ChildPID()));
// We should at least find the original mappings. The extra mappings may
// or not be found depending on scheduling.
ExpectMappings(map, region_addr, kNumMappings, page_size_);
}
}
void MultiprocessChild() override {
ASSERT_EQ(fcntl(ReadPipeHandle(), F_SETFL, O_NONBLOCK), 0)
<< ErrnoMessage("fcntl");
ScopedMmap mappings;
ASSERT_NO_FATAL_FAILURE(
InitializeMappings(&mappings, kNumMappings, page_size_));
// Let the parent start mapping us
auto region_addr = mappings.addr_as<LinuxVMAddress>();
CheckedWriteFile(WritePipeHandle(), &region_addr, sizeof(region_addr));
// But don't stop there!
constexpr size_t kNumExtraMappings = 256;
ScopedMmap extra_mappings;
while (true) {
ASSERT_NO_FATAL_FAILURE(
InitializeMappings(&extra_mappings, kNumExtraMappings, page_size_));
// Quit when the parent is done
char c;
FileOperationResult res = ReadFile(ReadPipeHandle(), &c, sizeof(c));
if (res == 0) {
break;
}
ASSERT_EQ(errno, EAGAIN);
}
}
static constexpr size_t kNumMappings = 1024;
const size_t page_size_;
DISALLOW_COPY_AND_ASSIGN(MapRunningChildTest);
};
TEST(MemoryMap, MapRunningChild) {
MapRunningChildTest test;
test.Run();
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -24,6 +24,7 @@
#include "test/errors.h"
#include "test/multiprocess.h"
#include "util/file/file_io.h"
#include "util/misc/from_pointer_cast.h"
#include "util/posix/scoped_mmap.h"
namespace crashpad {
@ -66,7 +67,7 @@ class ReadTest : public TargetProcessTest {
ProcessMemory memory;
ASSERT_TRUE(memory.Initialize(pid));
LinuxVMAddress address = reinterpret_cast<LinuxVMAddress>(region_.get());
LinuxVMAddress address = FromPointerCast<LinuxVMAddress>(region_.get());
std::unique_ptr<char[]> result(new char[region_size_]);
// Ensure that the entire region can be read.
@ -123,7 +124,7 @@ TEST(ProcessMemory, ReadForked) {
bool ReadCString(const ProcessMemory& memory,
const char* pointer,
std::string* result) {
return memory.ReadCString(reinterpret_cast<LinuxVMAddress>(pointer), result);
return memory.ReadCString(FromPointerCast<LinuxVMAddress>(pointer), result);
}
bool ReadCStringSizeLimited(const ProcessMemory& memory,
@ -131,7 +132,7 @@ bool ReadCStringSizeLimited(const ProcessMemory& memory,
size_t size,
std::string* result) {
return memory.ReadCStringSizeLimited(
reinterpret_cast<LinuxVMAddress>(pointer), size, result);
FromPointerCast<LinuxVMAddress>(pointer), size, result);
}
const char kConstCharEmpty[] = "";

338
util/linux/thread_info.cc Normal file
View File

@ -0,0 +1,338 @@
// 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/thread_info.h"
#include <linux/elf.h>
#include <string.h>
#include <sys/ptrace.h>
#include <sys/uio.h>
#include "base/logging.h"
#include "util/misc/from_pointer_cast.h"
#if defined(ARCH_CPU_X86_FAMILY)
#include <asm/ldt.h>
#endif
namespace crashpad {
namespace {
#if defined(ARCH_CPU_X86_FAMILY)
template <typename Destination>
bool GetRegisterSet(pid_t tid, int set, Destination* dest) {
iovec iov;
iov.iov_base = dest;
iov.iov_len = sizeof(*dest);
if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(set), &iov) != 0) {
PLOG(ERROR) << "ptrace";
return false;
}
if (iov.iov_len != sizeof(*dest)) {
LOG(ERROR) << "Unexpected registers size";
return false;
}
return true;
}
bool GetFloatingPointRegisters32(pid_t tid, FloatContext* context) {
return GetRegisterSet(tid, NT_PRXFPREG, &context->f32.fxsave);
}
bool GetFloatingPointRegisters64(pid_t tid, FloatContext* context) {
return GetRegisterSet(tid, NT_PRFPREG, &context->f64.fxsave);
}
#elif defined(ARCH_CPU_ARM_FAMILY)
#if defined(ARCH_CPU_ARMEL)
// PTRACE_GETREGSET, introduced in Linux 2.6.34 (2225a122ae26), requires kernel
// support enabled by HAVE_ARCH_TRACEHOOK. This has been set for x86 (including
// x86_64) since Linux 2.6.28 (99bbc4b1e677a), but for ARM only since
// Linux 3.5.0 (0693bf68148c4). Older Linux kernels support PTRACE_GETREGS,
// PTRACE_GETFPREGS, and PTRACE_GETVFPREGS instead, which don't allow checking
// the size of data copied.
//
// Fortunately, 64-bit ARM support only appeared in Linux 3.7.0, so if
// PTRACE_GETREGSET fails on ARM with EIO, indicating that the request is not
// supported, the kernel must be old enough that 64-bit ARM isnt supported
// either.
//
// TODO(mark): Once helpers to interpret the kernel version are available, add
// a DCHECK to ensure that the kernel is older than 3.5.
bool GetGeneralPurposeRegistersLegacy(pid_t tid, ThreadContext* context) {
if (ptrace(PTRACE_GETREGS, tid, nullptr, &context->t32) != 0) {
PLOG(ERROR) << "ptrace";
return false;
}
return true;
}
bool GetFloatingPointRegistersLegacy(pid_t tid, FloatContext* context) {
if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) {
PLOG(ERROR) << "ptrace";
return false;
}
context->f32.have_fpregs = true;
if (ptrace(PTRACE_GETVFPREGS, tid, nullptr, &context->f32.vfp) != 0) {
switch (errno) {
case EINVAL:
// These registers are optional on 32-bit ARM cpus
break;
default:
PLOG(ERROR) << "ptrace";
return false;
}
} else {
context->f32.have_vfp = true;
}
return true;
}
#endif // ARCH_CPU_ARMEL
// Normally, the Linux kernel will copy out register sets according to the size
// of the struct that contains them. Tracing a 32-bit ARM process running in
// compatibility mode on a 64-bit ARM cpu will only copy data for the number of
// registers times the size of the register, which won't include any possible
// trailing padding in the struct. These are the sizes of the register data, not
// including any possible padding.
constexpr size_t kArmVfpSize = 32 * 8 + 4;
// Target is 32-bit
bool GetFloatingPointRegisters32(pid_t tid, FloatContext* context) {
context->f32.have_fpregs = false;
context->f32.have_vfp = false;
iovec iov;
iov.iov_base = &context->f32.fpregs;
iov.iov_len = sizeof(context->f32.fpregs);
if (ptrace(
PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRFPREG), &iov) !=
0) {
switch (errno) {
#if defined(ARCH_CPU_ARMEL)
case EIO:
return GetFloatingPointRegistersLegacy(tid, context);
#endif // ARCH_CPU_ARMEL
case EINVAL:
// A 32-bit process running on a 64-bit CPU doesn't have this register
// set. It should have a VFP register set instead.
break;
default:
PLOG(ERROR) << "ptrace";
return false;
}
} else {
if (iov.iov_len != sizeof(context->f32.fpregs)) {
LOG(ERROR) << "Unexpected registers size";
return false;
}
context->f32.have_fpregs = true;
}
iov.iov_base = &context->f32.vfp;
iov.iov_len = sizeof(context->f32.vfp);
if (ptrace(
PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_ARM_VFP), &iov) !=
0) {
switch (errno) {
case EINVAL:
// VFP may not be present on 32-bit ARM cpus.
break;
default:
PLOG(ERROR) << "ptrace";
return false;
}
} else {
if (iov.iov_len != kArmVfpSize && iov.iov_len != sizeof(context->f32.vfp)) {
LOG(ERROR) << "Unexpected registers size";
return false;
}
context->f32.have_vfp = true;
}
if (!(context->f32.have_fpregs || context->f32.have_vfp)) {
LOG(ERROR) << "Unable to collect registers";
return false;
}
return true;
}
// Target is 64-bit
bool GetFloatingPointRegisters64(pid_t tid, FloatContext* context) {
iovec iov;
iov.iov_base = context;
iov.iov_len = sizeof(*context);
if (ptrace(
PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRFPREG), &iov) !=
0) {
PLOG(ERROR) << "ptrace";
return false;
}
if (iov.iov_len != sizeof(context->f64)) {
LOG(ERROR) << "Unexpected registers size";
return false;
}
return true;
}
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
} // namespace
ThreadContext::ThreadContext() {
memset(this, 0, sizeof(*this));
}
ThreadContext::~ThreadContext() {}
FloatContext::FloatContext() {
memset(this, 0, sizeof(*this));
}
FloatContext::~FloatContext() {}
ThreadInfo::ThreadInfo()
: context_(), attachment_(), tid_(-1), initialized_(), is_64_bit_(false) {}
ThreadInfo::~ThreadInfo() {}
bool ThreadInfo::Initialize(pid_t tid) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
if (!attachment_.ResetAttach(tid)) {
return false;
}
tid_ = tid;
size_t length = GetGeneralPurposeRegistersAndLength(&context_);
if (length == sizeof(context_.t64)) {
is_64_bit_ = true;
} else if (length == sizeof(context_.t32)) {
is_64_bit_ = false;
} else {
LOG(ERROR) << "Unexpected registers size";
return false;
}
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
bool ThreadInfo::Is64Bit() {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return is_64_bit_;
}
void ThreadInfo::GetGeneralPurposeRegisters(ThreadContext* context) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
*context = context_;
}
size_t ThreadInfo::GetGeneralPurposeRegistersAndLength(ThreadContext* context) {
iovec iov;
iov.iov_base = context;
iov.iov_len = sizeof(*context);
if (ptrace(
PTRACE_GETREGSET, tid_, reinterpret_cast<void*>(NT_PRSTATUS), &iov) !=
0) {
switch (errno) {
#if defined(ARCH_CPU_ARMEL)
case EIO:
if (GetGeneralPurposeRegistersLegacy(tid_, context)) {
return sizeof(context->t32);
}
#endif // ARCH_CPU_ARMEL
default:
PLOG(ERROR) << "ptrace";
return 0;
}
}
return iov.iov_len;
}
bool ThreadInfo::GetFloatingPointRegisters(FloatContext* context) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return is_64_bit_ ? GetFloatingPointRegisters64(tid_, context)
: GetFloatingPointRegisters32(tid_, context);
}
bool ThreadInfo::GetThreadArea(LinuxVMAddress* address) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
if (is_64_bit_) {
*address = context_.t64.fs_base;
return true;
}
user_desc desc;
iovec iov;
iov.iov_base = &desc;
iov.iov_len = sizeof(desc);
*address = 0;
if (ptrace(
PTRACE_GETREGSET, tid_, reinterpret_cast<void*>(NT_386_TLS), &iov) !=
0) {
PLOG(ERROR) << "ptrace";
return false;
}
*address = desc.base_addr;
return true;
#elif defined(ARCH_CPU_ARM_FAMILY)
if (is_64_bit_) {
iovec iov;
iov.iov_base = address;
iov.iov_len = sizeof(*address);
if (ptrace(PTRACE_GETREGSET,
tid_,
reinterpret_cast<void*>(NT_ARM_TLS),
&iov) != 0) {
PLOG(ERROR) << "ptrace";
return false;
}
if (iov.iov_len != 8) {
LOG(ERROR) << "address size mismatch";
return false;
}
return true;
}
#if defined(ARCH_CPU_ARMEL)
void* result;
if (ptrace(PTRACE_GET_THREAD_AREA, tid_, nullptr, &result) != 0) {
PLOG(ERROR) << "ptrace";
return false;
}
*address = FromPointerCast<LinuxVMAddress>(result);
return true;
#else
// TODO(jperaza): it doesn't look like there is a way for a 64-bit ARM process
// to get the thread area for a 32-bit ARM process with ptrace.
LOG(WARNING) << "64-bit ARM cannot trace TLS area for a 32-bit process";
return false;
#endif // ARCH_CPU_ARMEL
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
}
} // namespace crashpad

304
util/linux/thread_info.h Normal file
View File

@ -0,0 +1,304 @@
// 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_THREAD_INFO_H_
#define CRASHPAD_UTIL_LINUX_THREAD_INFO_H_
#include <stdint.h>
#include <sys/types.h>
#include <sys/user.h>
#include <type_traits>
#include "build/build_config.h"
#include "util/linux/address_types.h"
#include "util/linux/scoped_ptrace_attach.h"
#include "util/misc/initialization_state_dcheck.h"
#include "util/numeric/int128.h"
#if defined(OS_ANDROID)
#include <android/api-level.h>
#endif
namespace crashpad {
//! \brief The set of general purpose registers for an architecture family.
union ThreadContext {
ThreadContext();
~ThreadContext();
//! \brief The general purpose registers used by the 32-bit variant of the
//! architecture.
struct t32 {
#if defined(ARCH_CPU_X86_FAMILY)
// Reflects user_regs_struct in sys/user.h.
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
uint32_t esi;
uint32_t edi;
uint32_t ebp;
uint32_t eax;
uint32_t xds;
uint32_t xes;
uint32_t xfs;
uint32_t xgs;
uint32_t orig_eax;
uint32_t eip;
uint32_t xcs;
uint32_t eflags;
uint32_t esp;
uint32_t xss;
#elif defined(ARCH_CPU_ARM_FAMILY)
// Reflects user_regs in sys/user.h.
uint32_t regs[11];
uint32_t fp;
uint32_t ip;
uint32_t sp;
uint32_t lr;
uint32_t pc;
uint32_t cpsr;
uint32_t orig_r0;
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
} t32;
//! \brief The general purpose registers used by the 64-bit variant of the
//! architecture.
struct t64 {
#if defined(ARCH_CPU_X86_FAMILY)
// Reflects user_regs_struct in sys/user.h.
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t rbp;
uint64_t rbx;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rax;
uint64_t rcx;
uint64_t rdx;
uint64_t rsi;
uint64_t rdi;
uint64_t orig_rax;
uint64_t rip;
uint64_t cs;
uint64_t eflags;
uint64_t rsp;
uint64_t ss;
uint64_t fs_base;
uint64_t gs_base;
uint64_t ds;
uint64_t es;
uint64_t fs;
uint64_t gs;
#elif defined(ARCH_CPU_ARM_FAMILY)
// Reflects user_regs_struct in sys/user.h.
uint64_t regs[31];
uint64_t sp;
uint64_t pc;
uint64_t pstate;
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
} t64;
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
using NativeThreadContext = user_regs_struct;
#elif defined(ARCH_CPU_ARMEL)
using NativeThreadContext = user_regs;
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY || ARCH_CPU_ARM64
#if defined(ARCH_CPU_32_BITS)
static_assert(sizeof(t32) == sizeof(NativeThreadContext), "Size mismatch");
#else // ARCH_CPU_64_BITS
static_assert(sizeof(t64) == sizeof(NativeThreadContext), "Size mismatch");
#endif // ARCH_CPU_32_BITS
};
static_assert(std::is_standard_layout<ThreadContext>::value,
"Not standard layout");
//! \brief The floating point registers used for an architecture family.
union FloatContext {
FloatContext();
~FloatContext();
//! \brief The floating point registers used by the 32-bit variant of the
//! architecture.
struct f32 {
#if defined(ARCH_CPU_X86_FAMILY)
// Reflects user_fpxregs_struct in sys/user.h
struct fxsave {
uint16_t cwd;
uint16_t swd;
uint16_t twd;
uint16_t fop;
uint32_t fip;
uint32_t fcs;
uint32_t foo;
uint32_t fos;
uint32_t mxcsr;
uint32_t reserved;
uint32_t st_space[32];
uint32_t xmm_space[32];
uint32_t padding[56];
} fxsave;
#elif defined(ARCH_CPU_ARM_FAMILY)
// Reflects user_fpregs in sys/user.h.
struct fpregs {
struct fp_reg {
uint32_t sign1 : 1;
uint32_t unused : 15;
uint32_t sign2 : 1;
uint32_t exponent : 14;
uint32_t j : 1;
uint32_t mantissa1 : 31;
uint32_t mantisss0 : 32;
} fpregs[8];
uint32_t fpsr : 32;
uint32_t fpcr : 32;
uint8_t type[8];
uint32_t init_flag;
} fpregs;
// Reflects user_vfp in sys/user.h.
struct vfp {
uint64_t fpregs[32];
uint32_t fpscr;
} vfp;
bool have_fpregs;
bool have_vfp;
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
} f32;
//! \brief The floating point registers used by the 64-bit variant of the
//! architecture.
struct f64 {
#if defined(ARCH_CPU_X86_FAMILY)
// Refelects user_fpregs_struct in sys/user.h
struct fxsave {
uint16_t cwd;
uint16_t swd;
uint16_t ftw;
uint16_t fop;
uint64_t rip;
uint64_t rdp;
uint32_t mxcsr;
uint32_t mxcr_mask;
uint32_t st_space[32];
uint32_t xmm_space[64];
uint32_t padding[24];
} fxsave;
#elif defined(ARCH_CPU_ARM_FAMILY)
uint128_struct vregs[32];
uint32_t fpsr;
uint32_t fpcr;
uint8_t padding[8];
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
} f64;
#if defined(ARCH_CPU_X86)
#if defined(OS_ANDROID) && __ANDROID_API__ <= 19
using NativeFpxregs = user_fxsr_struct;
#else
using NativeFpxregs = user_fpxregs_struct;
#endif // OS_ANDROID
static_assert(sizeof(f32::fxsave) == sizeof(NativeFpxregs), "Size mismatch");
#elif defined(ARCH_CPU_X86_64)
static_assert(sizeof(f64::fxsave) == sizeof(user_fpregs_struct),
"Size mismatch");
#elif defined(ARCH_CPU_ARMEL)
static_assert(sizeof(f32::fpregs) == sizeof(user_fpregs), "Size mismatch");
static_assert(sizeof(f32::vfp) == sizeof(user_vfp), "Size mismatch");
#elif defined(ARCH_CPU_ARM64)
static_assert(sizeof(f64) == sizeof(user_fpsimd_struct), "Size mismatch");
#else
#error Port.
#endif // ARCH_CPU_X86
};
static_assert(std::is_standard_layout<FloatContext>::value,
"Not standard layout");
class ThreadInfo {
public:
ThreadInfo();
~ThreadInfo();
//! \brief Initializes this object with information about the thread whose ID
//! is \a tid.
//!
//! This method must be called successfully prior to calling any other method
//! in this class. This method may only be called once.
//!
//! It is unspecified whether the information that an object of this class
//! returns is loaded at the time Initialize() is called or subsequently, and
//! whether this information is cached in the object or not.
//!
//! \param[in] tid The thread ID to obtain information for.
//!
//! \return `true` on success, `false` on failure with a message logged.
bool Initialize(pid_t tid);
//! \brief Determines the target threads bitness.
//!
//! \return `true` if the target is 64-bit.
bool Is64Bit();
//! \brief Uses `ptrace` to collect general purpose registers from the target
//! thread and places the result in \a context.
//!
//! \param[out] context The registers read from the target thread.
void GetGeneralPurposeRegisters(ThreadContext* context);
//! \brief Uses `ptrace` to collect floating point registers from the target
//! thread and places the result in \a context.
//!
//! \param[out] context The registers read from the target thread.
//!
//! \return `true` on success, with \a context set. Otherwise, `false` with a
//! message logged.
bool GetFloatingPointRegisters(FloatContext* context);
//! \brief Uses `ptrace` to determine the thread-local storage address for the
//! target thread and places the result in \a address.
//!
//! \param[out] address The address of the TLS area.
//!
//! \return `true` on success. `false` on failure with a message logged.
bool GetThreadArea(LinuxVMAddress* address);
private:
size_t GetGeneralPurposeRegistersAndLength(ThreadContext* context);
ThreadContext context_;
ScopedPtraceAttach attachment_;
pid_t tid_;
InitializationStateDcheck initialized_;
bool is_64_bit_;
};
} // namespace crashpad
#endif // CRASHPAD_UTIL_LINUX_THREAD_INFO_H_

View File

@ -0,0 +1,100 @@
// 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/thread_info.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "test/multiprocess.h"
#include "util/file/file_io.h"
#include "util/misc/from_pointer_cast.h"
namespace crashpad {
namespace test {
namespace {
class SameBitnessTest : public Multiprocess {
public:
SameBitnessTest() : Multiprocess() {}
~SameBitnessTest() {}
private:
void MultiprocessParent() override {
LinuxVMAddress expected_tls;
CheckedReadFileExactly(
ReadPipeHandle(), &expected_tls, sizeof(expected_tls));
ThreadInfo thread_info;
ASSERT_TRUE(thread_info.Initialize(ChildPID()));
#if defined(ARCH_CPU_64_BITS)
const bool am_64_bit = true;
#else
const bool am_64_bit = false;
#endif // ARCH_CPU_64_BITS
EXPECT_EQ(thread_info.Is64Bit(), am_64_bit);
ThreadContext thread_context;
thread_info.GetGeneralPurposeRegisters(&thread_context);
#if defined(ARCH_CPU_X86_64)
EXPECT_EQ(thread_context.t64.fs_base, expected_tls);
#endif // ARCH_CPU_X86_64
FloatContext float_context;
EXPECT_TRUE(thread_info.GetFloatingPointRegisters(&float_context));
LinuxVMAddress tls_address;
ASSERT_TRUE(thread_info.GetThreadArea(&tls_address));
EXPECT_EQ(tls_address, expected_tls);
}
void MultiprocessChild() override {
LinuxVMAddress expected_tls;
#if defined(ARCH_CPU_ARMEL)
// 0xffff0fe0 is the address of the kernel user helper __kuser_get_tls().
auto kuser_get_tls = reinterpret_cast<void* (*)()>(0xffff0fe0);
expected_tls = FromPointerCast<LinuxVMAddress>(kuser_get_tls());
#elif defined(ARCH_CPU_ARM64)
// Linux/aarch64 places the tls address in system register tpidr_el0.
asm("mrs %0, tpidr_el0" : "=r"(expected_tls));
#elif defined(ARCH_CPU_X86)
uint32_t expected_tls_32;
asm("movl %%gs:0x0, %0" : "=r"(expected_tls_32));
expected_tls = expected_tls_32;
#elif defined(ARCH_CPU_X86_64)
asm("movq %%fs:0x0, %0" : "=r"(expected_tls));
#else
#error Port.
#endif // ARCH_CPU_ARMEL
CheckedWriteFile(WritePipeHandle(), &expected_tls, sizeof(expected_tls));
CheckedReadFileAtEOF(ReadPipeHandle());
}
DISALLOW_COPY_AND_ASSIGN(SameBitnessTest);
};
TEST(ThreadInfo, SameBitness) {
SameBitnessTest test;
test.Run();
}
// TODO(jperaza): Test against a process with different bitness.
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -111,6 +111,10 @@ def main(args):
parser = argparse.ArgumentParser()
parser.add_argument('--developer-dir', help='Path to Xcode')
parser.add_argument('--sdk', help='Path to SDK')
parser.add_argument('--include',
default=[],
action='append',
help='Additional include directory')
parser.add_argument('defs')
parser.add_argument('user_c')
parser.add_argument('server_c')
@ -124,10 +128,12 @@ def main(args):
'-header', parsed.user_h,
'-sheader', parsed.server_h,
]
if parsed.sdk is not None:
command.extend(['-isysroot', parsed.sdk])
if parsed.developer_dir is not None:
os.environ['DEVELOPER_DIR'] = parsed.developer_dir
if parsed.sdk is not None:
command.extend(['-isysroot', parsed.sdk])
for include in parsed.include:
command.extend(['-I' + include])
command.append(parsed.defs)
subprocess.check_call(command)
FixUserImplementation(parsed.user_c)

View File

@ -25,6 +25,7 @@
#include "base/mac/scoped_mach_vm.h"
#include "gtest/gtest.h"
#include "test/mac/mach_errors.h"
#include "util/misc/from_pointer_cast.h"
namespace crashpad {
namespace test {
@ -165,7 +166,7 @@ TEST(TaskMemory, ReadSelfUnmapped) {
bool ReadCStringSelf(TaskMemory* memory,
const char* pointer,
std::string* result) {
return memory->ReadCString(reinterpret_cast<mach_vm_address_t>(pointer),
return memory->ReadCString(FromPointerCast<mach_vm_address_t>(pointer),
result);
}
@ -272,7 +273,7 @@ bool ReadCStringSizeLimitedSelf(TaskMemory* memory,
size_t size,
std::string* result) {
return memory->ReadCStringSizeLimited(
reinterpret_cast<mach_vm_address_t>(pointer), size, result);
FromPointerCast<mach_vm_address_t>(pointer), size, result);
}
TEST(TaskMemory, ReadCStringSizeLimited_ConstCharEmpty) {
@ -462,7 +463,7 @@ TEST(TaskMemory, MappedMemoryDeallocates) {
static const char kTestBuffer[] = "hello!";
mach_vm_address_t test_address =
reinterpret_cast<mach_vm_address_t>(&kTestBuffer);
FromPointerCast<mach_vm_address_t>(&kTestBuffer);
ASSERT_TRUE((mapped = memory.ReadMapped(test_address, sizeof(kTestBuffer))));
EXPECT_EQ(memcmp(kTestBuffer, mapped->data(), sizeof(kTestBuffer)), 0);
@ -477,7 +478,7 @@ TEST(TaskMemory, MappedMemoryDeallocates) {
// deallocated.
const size_t kBigSize = 4 * PAGE_SIZE;
std::unique_ptr<char[]> big_buffer(new char[kBigSize]);
test_address = reinterpret_cast<mach_vm_address_t>(&big_buffer[0]);
test_address = FromPointerCast<mach_vm_address_t>(&big_buffer[0]);
ASSERT_TRUE((mapped = memory.ReadMapped(test_address, kBigSize)));
mapped_address = reinterpret_cast<vm_address_t>(mapped->data());
@ -499,7 +500,7 @@ TEST(TaskMemory, MappedMemoryReadCString) {
static const char kTestBuffer[] = "0\0" "2\0" "45\0" "789";
const mach_vm_address_t kTestAddress =
reinterpret_cast<mach_vm_address_t>(&kTestBuffer);
FromPointerCast<mach_vm_address_t>(&kTestBuffer);
ASSERT_TRUE((mapped = memory.ReadMapped(kTestAddress, 10)));
std::string string;

View File

@ -0,0 +1,95 @@
// 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_MISC_FROM_POINTER_CAST_H_
#define CRASHPAD_UTIL_MISC_FROM_POINTER_CAST_H_
#include <stdint.h>
#include <cstddef>
#include <type_traits>
#include "base/numerics/safe_conversions.h"
namespace crashpad {
#if DOXYGEN
//! \brief Casts from a pointer type to an integer.
//!
//! Compared to `reinterpret_cast<>()`, FromPointerCast<>() defines whether a
//! pointer type is sign-extended or zero-extended. Casts to signed integral
//! types are sign-extended. Casts to unsigned integral types are zero-extended.
//!
//! Use FromPointerCast<>() instead of `reinterpret_cast<>()` when casting a
//! pointer to an integral type that may not be the same width as a pointer.
//! There is no need to prefer FromPointerCast<>() when casting to an integral
//! type thats definitely the same width as a pointer, such as `uintptr_t` and
//! `intptr_t`.
template <typename To, typename From>
FromPointerCast(From from) {
return reinterpret_cast<To>(from);
}
#else // DOXYGEN
// Cast std::nullptr_t to any type.
//
// In C++14, the nullptr_t check could use std::is_null_pointer<From>::value
// instead of the is_same<remove_cv<From>::type, nullptr_t>::type construct.
template <typename To, typename From>
typename std::enable_if<
std::is_same<typename std::remove_cv<From>::type, std::nullptr_t>::value,
To>::type
FromPointerCast(From) {
return To();
}
// Cast a pointer to any other pointer type.
template <typename To, typename From>
typename std::enable_if<std::is_pointer<From>::value &&
std::is_pointer<To>::value,
To>::type
FromPointerCast(From from) {
return reinterpret_cast<To>(from);
}
// Cast a pointer to an integral type. Sign-extend when casting to a signed
// type, zero-extend when casting to an unsigned type.
template <typename To, typename From>
typename std::enable_if<std::is_pointer<From>::value &&
std::is_integral<To>::value,
To>::type
FromPointerCast(From from) {
const auto intermediate =
reinterpret_cast<typename std::conditional<std::is_signed<To>::value,
intptr_t,
uintptr_t>::type>(from);
if (sizeof(To) >= sizeof(From)) {
// If the destination integral type is at least as wide as the source
// pointer type, use static_cast<>() and just return it.
return static_cast<To>(intermediate);
}
// If the destination integral type is narrower than the source pointer type,
// use checked_cast<>().
return base::checked_cast<To>(intermediate);
}
#endif // DOXYGEN
} // namespace crashpad
#endif // CRASHPAD_UTIL_MISC_FROM_POINTER_CAST_H_

View File

@ -0,0 +1,261 @@
// 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/misc/from_pointer_cast.h"
#include <stdlib.h>
#include <sys/types.h>
#include <limits>
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "test/gtest_death_check.h"
namespace crashpad {
namespace test {
namespace {
struct SomeType {};
template <typename T>
class FromPointerCastTest : public testing::Test {};
using FromPointerCastTestTypes = testing::Types<void*,
const void*,
volatile void*,
const volatile void*,
SomeType*,
const SomeType*,
volatile SomeType*,
const volatile SomeType*>;
TYPED_TEST_CASE(FromPointerCastTest, FromPointerCastTestTypes);
TYPED_TEST(FromPointerCastTest, ToSigned) {
EXPECT_EQ(FromPointerCast<int64_t>(nullptr), 0);
EXPECT_EQ(FromPointerCast<int64_t>(reinterpret_cast<TypeParam>(1)), 1);
EXPECT_EQ(FromPointerCast<int64_t>(reinterpret_cast<TypeParam>(-1)), -1);
EXPECT_EQ(FromPointerCast<int64_t>(reinterpret_cast<TypeParam>(
std::numeric_limits<uintptr_t>::max())),
static_cast<intptr_t>(std::numeric_limits<uintptr_t>::max()));
EXPECT_EQ(FromPointerCast<int64_t>(reinterpret_cast<TypeParam>(
std::numeric_limits<intptr_t>::min())),
std::numeric_limits<intptr_t>::min());
EXPECT_EQ(FromPointerCast<int64_t>(reinterpret_cast<TypeParam>(
std::numeric_limits<intptr_t>::max())),
std::numeric_limits<intptr_t>::max());
}
TYPED_TEST(FromPointerCastTest, ToUnsigned) {
EXPECT_EQ(FromPointerCast<uint64_t>(nullptr), 0u);
EXPECT_EQ(FromPointerCast<uint64_t>(reinterpret_cast<TypeParam>(1)), 1u);
EXPECT_EQ(FromPointerCast<uint64_t>(reinterpret_cast<TypeParam>(-1)),
static_cast<uintptr_t>(-1));
EXPECT_EQ(FromPointerCast<uint64_t>(reinterpret_cast<TypeParam>(
std::numeric_limits<uintptr_t>::max())),
std::numeric_limits<uintptr_t>::max());
EXPECT_EQ(FromPointerCast<uint64_t>(reinterpret_cast<TypeParam>(
std::numeric_limits<intptr_t>::min())),
static_cast<uintptr_t>(std::numeric_limits<intptr_t>::min()));
EXPECT_EQ(FromPointerCast<uint64_t>(reinterpret_cast<TypeParam>(
std::numeric_limits<intptr_t>::max())),
static_cast<uintptr_t>(std::numeric_limits<intptr_t>::max()));
}
// MatchCV<YourType, CVQualifiedType>::Type adapts YourType to match the
// const/void qualification of CVQualifiedType.
template <typename Base, typename MatchTo>
struct MatchCV {
private:
using NonCVBase = typename std::remove_cv<Base>::type;
public:
using Type = typename std::conditional<
std::is_const<MatchTo>::value,
typename std::conditional<std::is_volatile<MatchTo>::value,
const volatile NonCVBase,
const NonCVBase>::type,
typename std::conditional<std::is_volatile<MatchTo>::value,
volatile NonCVBase,
NonCVBase>::type>::type;
};
#if defined(COMPILER_MSVC) && _MSC_VER < 1910
// gtest under MSVS 2015 (MSC 19.0) doesnt handle EXPECT_EQ(a, b) when a or b
// is a pointer to a volatile type, because it cant figure out how to print
// them.
template <typename T>
typename std::remove_volatile<typename std::remove_pointer<T>::type>::type*
MaybeRemoveVolatile(const T& value) {
return const_cast<typename std::remove_volatile<
typename std::remove_pointer<T>::type>::type*>(value);
}
#else // COMPILER_MSVC && _MSC_VER < 1910
// This isnt a problem in MSVS 2017 (MSC 19.1) or with other compilers.
template <typename T>
T MaybeRemoveVolatile(const T& value) {
return value;
}
#endif // COMPILER_MSVC && _MSC_VER < 1910
TYPED_TEST(FromPointerCastTest, ToPointer) {
using CVSomeType =
typename MatchCV<SomeType,
typename std::remove_pointer<TypeParam>::type>::Type;
EXPECT_EQ(MaybeRemoveVolatile(FromPointerCast<CVSomeType*>(nullptr)),
MaybeRemoveVolatile(static_cast<CVSomeType*>(nullptr)));
EXPECT_EQ(MaybeRemoveVolatile(
FromPointerCast<CVSomeType*>(reinterpret_cast<TypeParam>(1))),
MaybeRemoveVolatile(reinterpret_cast<CVSomeType*>(1)));
EXPECT_EQ(MaybeRemoveVolatile(
FromPointerCast<CVSomeType*>(reinterpret_cast<TypeParam>(-1))),
MaybeRemoveVolatile(reinterpret_cast<CVSomeType*>(-1)));
EXPECT_EQ(
MaybeRemoveVolatile(FromPointerCast<CVSomeType*>(
reinterpret_cast<TypeParam>(std::numeric_limits<uintptr_t>::max()))),
MaybeRemoveVolatile(reinterpret_cast<CVSomeType*>(
std::numeric_limits<uintptr_t>::max())));
EXPECT_EQ(
MaybeRemoveVolatile(FromPointerCast<CVSomeType*>(
reinterpret_cast<TypeParam>(std::numeric_limits<intptr_t>::min()))),
MaybeRemoveVolatile(
reinterpret_cast<CVSomeType*>(std::numeric_limits<intptr_t>::min())));
EXPECT_EQ(
MaybeRemoveVolatile(FromPointerCast<CVSomeType*>(
reinterpret_cast<TypeParam>(std::numeric_limits<intptr_t>::max()))),
MaybeRemoveVolatile(
reinterpret_cast<CVSomeType*>(std::numeric_limits<intptr_t>::max())));
}
TEST(FromPointerCast, FromFunctionPointer) {
// These casts should work with or without the & in &malloc.
EXPECT_NE(FromPointerCast<int64_t>(malloc), 0);
EXPECT_NE(FromPointerCast<int64_t>(&malloc), 0);
EXPECT_NE(FromPointerCast<uint64_t>(malloc), 0u);
EXPECT_NE(FromPointerCast<uint64_t>(&malloc), 0u);
EXPECT_EQ(FromPointerCast<void*>(malloc), reinterpret_cast<void*>(malloc));
EXPECT_EQ(FromPointerCast<void*>(&malloc), reinterpret_cast<void*>(malloc));
EXPECT_EQ(FromPointerCast<const void*>(malloc),
reinterpret_cast<const void*>(malloc));
EXPECT_EQ(FromPointerCast<const void*>(&malloc),
reinterpret_cast<const void*>(malloc));
EXPECT_EQ(MaybeRemoveVolatile(FromPointerCast<volatile void*>(malloc)),
MaybeRemoveVolatile(reinterpret_cast<volatile void*>(malloc)));
EXPECT_EQ(MaybeRemoveVolatile(FromPointerCast<volatile void*>(&malloc)),
MaybeRemoveVolatile(reinterpret_cast<volatile void*>(malloc)));
EXPECT_EQ(
MaybeRemoveVolatile(FromPointerCast<const volatile void*>(malloc)),
MaybeRemoveVolatile(reinterpret_cast<const volatile void*>(malloc)));
EXPECT_EQ(
MaybeRemoveVolatile(FromPointerCast<const volatile void*>(&malloc)),
MaybeRemoveVolatile(reinterpret_cast<const volatile void*>(malloc)));
EXPECT_EQ(FromPointerCast<SomeType*>(malloc),
reinterpret_cast<SomeType*>(malloc));
EXPECT_EQ(FromPointerCast<SomeType*>(&malloc),
reinterpret_cast<SomeType*>(malloc));
EXPECT_EQ(FromPointerCast<const SomeType*>(malloc),
reinterpret_cast<const SomeType*>(malloc));
EXPECT_EQ(FromPointerCast<const SomeType*>(&malloc),
reinterpret_cast<const SomeType*>(malloc));
EXPECT_EQ(MaybeRemoveVolatile(FromPointerCast<volatile SomeType*>(malloc)),
MaybeRemoveVolatile(reinterpret_cast<volatile SomeType*>(malloc)));
EXPECT_EQ(MaybeRemoveVolatile(FromPointerCast<volatile SomeType*>(&malloc)),
MaybeRemoveVolatile(reinterpret_cast<volatile SomeType*>(malloc)));
EXPECT_EQ(
MaybeRemoveVolatile(FromPointerCast<const volatile SomeType*>(malloc)),
MaybeRemoveVolatile(reinterpret_cast<const volatile SomeType*>(malloc)));
EXPECT_EQ(
MaybeRemoveVolatile(FromPointerCast<const volatile SomeType*>(&malloc)),
MaybeRemoveVolatile(reinterpret_cast<const volatile SomeType*>(malloc)));
}
TEST(FromPointerCast, ToNarrowInteger) {
EXPECT_EQ(FromPointerCast<int>(nullptr), 0);
EXPECT_EQ(FromPointerCast<int>(reinterpret_cast<void*>(1)), 1);
EXPECT_EQ(FromPointerCast<int>(reinterpret_cast<void*>(-1)), -1);
EXPECT_EQ(FromPointerCast<int>(reinterpret_cast<void*>(
static_cast<intptr_t>(std::numeric_limits<int>::max()))),
std::numeric_limits<int>::max());
EXPECT_EQ(FromPointerCast<int>(reinterpret_cast<void*>(
static_cast<intptr_t>(std::numeric_limits<int>::min()))),
std::numeric_limits<int>::min());
EXPECT_EQ(FromPointerCast<unsigned int>(nullptr), 0u);
EXPECT_EQ(FromPointerCast<unsigned int>(reinterpret_cast<void*>(1)), 1u);
EXPECT_EQ(
FromPointerCast<unsigned int>(reinterpret_cast<void*>(
static_cast<uintptr_t>(std::numeric_limits<unsigned int>::max()))),
std::numeric_limits<unsigned int>::max());
EXPECT_EQ(FromPointerCast<unsigned int>(reinterpret_cast<void*>(
static_cast<uintptr_t>(std::numeric_limits<int>::max()))),
static_cast<unsigned int>(std::numeric_limits<int>::max()));
// int and unsigned int may not be narrower than a pointer, so also test short
// and unsigned short.
EXPECT_EQ(FromPointerCast<short>(nullptr), 0);
EXPECT_EQ(FromPointerCast<short>(reinterpret_cast<void*>(1)), 1);
EXPECT_EQ(FromPointerCast<short>(reinterpret_cast<void*>(-1)), -1);
EXPECT_EQ(FromPointerCast<short>(reinterpret_cast<void*>(
static_cast<intptr_t>(std::numeric_limits<short>::max()))),
std::numeric_limits<short>::max());
EXPECT_EQ(FromPointerCast<short>(reinterpret_cast<void*>(
static_cast<intptr_t>(std::numeric_limits<short>::min()))),
std::numeric_limits<short>::min());
EXPECT_EQ(FromPointerCast<unsigned short>(nullptr), 0u);
EXPECT_EQ(FromPointerCast<unsigned short>(reinterpret_cast<void*>(1)), 1u);
EXPECT_EQ(
FromPointerCast<unsigned short>(reinterpret_cast<void*>(
static_cast<uintptr_t>(std::numeric_limits<unsigned short>::max()))),
std::numeric_limits<unsigned short>::max());
EXPECT_EQ(FromPointerCast<unsigned short>(reinterpret_cast<void*>(
static_cast<uintptr_t>(std::numeric_limits<short>::max()))),
static_cast<unsigned short>(std::numeric_limits<short>::max()));
}
TEST(FromPointerCastDeathTest, ToNarrowInteger) {
if (sizeof(int) < sizeof(void*)) {
EXPECT_DEATH(FromPointerCast<int>(
reinterpret_cast<void*>(static_cast<uintptr_t>(
std::numeric_limits<unsigned int>::max() + 1ull))),
"");
EXPECT_DEATH(FromPointerCast<unsigned int>(
reinterpret_cast<void*>(static_cast<uintptr_t>(
std::numeric_limits<unsigned int>::max() + 1ull))),
"");
}
// int and unsigned int may not be narrower than a pointer, so also test short
// and unsigned short.
EXPECT_DEATH(FromPointerCast<short>(
reinterpret_cast<void*>(static_cast<uintptr_t>(
std::numeric_limits<unsigned short>::max() + 1u))),
"");
EXPECT_DEATH(FromPointerCast<unsigned short>(
reinterpret_cast<void*>(static_cast<uintptr_t>(
std::numeric_limits<unsigned short>::max() + 1u))),
"");
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -14,7 +14,12 @@
#include "util/misc/initialization_state_dcheck.h"
#include <stdlib.h>
#include <memory>
#include "base/logging.h"
#include "base/memory/free_deleter.h"
#include "gtest/gtest.h"
#include "test/gtest_death_check.h"
@ -98,33 +103,45 @@ TEST(InitializationStateDcheckDeathTest, Destroyed_NotUninitialized) {
// This tests that an attempt to reinitialize a destroyed object fails. See
// the InitializationState.InitializationState test for an explanation of this
// use-after-free test.
InitializationStateDcheck* initialization_state_dcheck_pointer;
{
InitializationStateDcheck initialization_state_dcheck;
initialization_state_dcheck_pointer = &initialization_state_dcheck;
INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck);
INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck);
INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck);
}
ASSERT_DEATH_CHECK(INITIALIZATION_STATE_SET_INITIALIZING(
*initialization_state_dcheck_pointer),
"kStateUninitialized");
std::unique_ptr<InitializationStateDcheck, base::FreeDeleter>
initialization_state_dcheck_buffer(static_cast<InitializationStateDcheck*>(
malloc(sizeof(InitializationStateDcheck))));
InitializationStateDcheck* initialization_state_dcheck =
new (initialization_state_dcheck_buffer.get())
InitializationStateDcheck();
INITIALIZATION_STATE_SET_INITIALIZING(*initialization_state_dcheck);
INITIALIZATION_STATE_SET_VALID(*initialization_state_dcheck);
INITIALIZATION_STATE_DCHECK_VALID(*initialization_state_dcheck);
initialization_state_dcheck->~InitializationStateDcheck();
ASSERT_DEATH_CHECK(
INITIALIZATION_STATE_SET_INITIALIZING(*initialization_state_dcheck),
"kStateUninitialized");
}
TEST(InitializationStateDcheckDeathTest, Destroyed_NotValid) {
// This tests that an attempt to use a destroyed object fails. See the
// InitializationState.InitializationState test for an explanation of this
// use-after-free test.
InitializationStateDcheck* initialization_state_dcheck_pointer;
{
InitializationStateDcheck initialization_state_dcheck;
initialization_state_dcheck_pointer = &initialization_state_dcheck;
INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck);
INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck);
INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck);
}
std::unique_ptr<InitializationStateDcheck, base::FreeDeleter>
initialization_state_dcheck_buffer(static_cast<InitializationStateDcheck*>(
malloc(sizeof(InitializationStateDcheck))));
InitializationStateDcheck* initialization_state_dcheck =
new (initialization_state_dcheck_buffer.get())
InitializationStateDcheck();
INITIALIZATION_STATE_SET_INITIALIZING(*initialization_state_dcheck);
INITIALIZATION_STATE_SET_VALID(*initialization_state_dcheck);
INITIALIZATION_STATE_DCHECK_VALID(*initialization_state_dcheck);
initialization_state_dcheck->~InitializationStateDcheck();
ASSERT_DEATH_CHECK(
INITIALIZATION_STATE_DCHECK_VALID(*initialization_state_dcheck_pointer),
INITIALIZATION_STATE_DCHECK_VALID(*initialization_state_dcheck),
"kStateValid");
}

View File

@ -14,6 +14,11 @@
#include "util/misc/initialization_state.h"
#include <stdlib.h>
#include <memory>
#include "base/memory/free_deleter.h"
#include "gtest/gtest.h"
namespace crashpad {
@ -21,36 +26,41 @@ namespace test {
namespace {
TEST(InitializationState, InitializationState) {
InitializationState* initialization_state_pointer;
{
InitializationState initialization_state;
initialization_state_pointer = &initialization_state;
// Use placement new so that the buffer used to host the object remains live
// even after the object is destroyed.
std::unique_ptr<InitializationState, base::FreeDeleter>
initialization_state_buffer(
static_cast<InitializationState*>(malloc(sizeof(InitializationState))));
EXPECT_TRUE(initialization_state.is_uninitialized());
EXPECT_FALSE(initialization_state.is_valid());
InitializationState* initialization_state =
new (initialization_state_buffer.get()) InitializationState();
initialization_state.set_invalid();
EXPECT_TRUE(initialization_state->is_uninitialized());
EXPECT_FALSE(initialization_state->is_valid());
EXPECT_FALSE(initialization_state.is_uninitialized());
EXPECT_FALSE(initialization_state.is_valid());
initialization_state->set_invalid();
initialization_state.set_valid();
EXPECT_FALSE(initialization_state->is_uninitialized());
EXPECT_FALSE(initialization_state->is_valid());
EXPECT_FALSE(initialization_state.is_uninitialized());
EXPECT_TRUE(initialization_state.is_valid());
}
initialization_state->set_valid();
// initialization_state_pointer points to something that no longer exists.
// This portion of the test is intended to check that after an
// InitializationState object goes out of scope, it will not be considered
// valid on a use-after-free, assuming that nothing else was written to its
// former home in memory.
EXPECT_FALSE(initialization_state->is_uninitialized());
EXPECT_TRUE(initialization_state->is_valid());
initialization_state->~InitializationState();
// initialization_state points to something that no longer exists. This
// portion of the test is intended to check that after an InitializationState
// object is destroyed, it will not be considered valid on a use-after-free,
// assuming that nothing else was written to its former home in memory.
//
// This portion of the test is technically not valid C++, but it exists to
// test that the behavior is as desired when other code uses the language
// improperly.
EXPECT_FALSE(initialization_state_pointer->is_uninitialized());
EXPECT_FALSE(initialization_state_pointer->is_valid());
// Because initialization_state was constructed via placement new into a
// buffer thats still valid and its destructor was called directly, this
// approximates use-after-free without risking that the memory formerly used
// for the InitializationState object has been repurposed.
EXPECT_FALSE(initialization_state->is_uninitialized());
EXPECT_FALSE(initialization_state->is_valid());
}
} // namespace

View File

@ -0,0 +1,70 @@
// 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/misc/reinterpret_bytes.h"
#include <string.h>
#include <algorithm>
#include "base/logging.h"
namespace crashpad {
namespace internal {
bool ReinterpretBytesImpl(const char* data,
size_t data_size,
char* dest,
size_t dest_size) {
// 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) {
auto extra_bytes = data;
#if defined(ARCH_CPU_LITTLE_ENDIAN)
extra_bytes += dest_size;
#endif // ARCH_CPU_LITTLE_ENDIAN
uint64_t zero = 0;
size_t extra_bytes_size = data_size - dest_size;
while (extra_bytes_size > 0) {
size_t to_check = std::min(extra_bytes_size, sizeof(zero));
if (memcmp(extra_bytes, &zero, to_check) != 0) {
LOG(ERROR) << "information loss";
return false;
}
extra_bytes += to_check;
extra_bytes_size -= to_check;
}
}
// 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, 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 + data_size - dest_size, dest_size);
} else {
memcpy(dest + dest_size - data_size, data, data_size);
}
#endif // ARCH_CPU_LITTLE_ENDIAN
return true;
}
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,48 @@
// 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_MISC_REINTERPRET_BYTES_H_
#define CRASHPAD_UTIL_MISC_REINTERPRET_BYTES_H_
#include <stddef.h>
namespace crashpad {
namespace internal {
bool ReinterpretBytesImpl(const char* from,
size_t from_size,
char* to,
size_t to_size);
} // namespace internal
//! \brief Copies the bytes of \a from to \to.
//!
//! This function is similar to `bit_cast`, except that it can operate on
//! differently sized types.
//!
//! \return `true` if the copy is possible without information loss, otherwise
//! `false` with a message logged.
template <typename From, typename To>
bool ReinterpretBytes(const From& from, To* to) {
return internal::ReinterpretBytesImpl(reinterpret_cast<const char*>(&from),
sizeof(From),
reinterpret_cast<char*>(to),
sizeof(To));
}
} // namespace crashpad
#endif // CRASHPAD_UTIL_MISC_REINTERPRET_BYTES_H_

View 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/misc/reinterpret_bytes.h"
#include <stdint.h>
#include <limits>
#include "base/bit_cast.h"
#include "gtest/gtest.h"
#include "util/numeric/int128.h"
namespace crashpad {
namespace test {
namespace {
template <typename From, typename To>
void ExpectReinterpret(From from, To* to, To expected) {
ASSERT_TRUE(ReinterpretBytes(from, to));
EXPECT_EQ(*to, expected);
}
template <typename From, typename To>
void ExpectUnsignedEqual(From from, To* to) {
To expected = static_cast<To>(from);
ExpectReinterpret(from, to, expected);
}
TEST(ReinterpretBytes, ToUnsigned) {
uint64_t from64, to64;
uint32_t from32, to32;
from32 = 0;
ExpectUnsignedEqual(from32, &to32);
ExpectUnsignedEqual(from32, &to64);
from32 = std::numeric_limits<uint32_t>::max();
ExpectUnsignedEqual(from32, &to32);
ExpectUnsignedEqual(from32, &to64);
from64 = 0;
ExpectUnsignedEqual(from64, &to32);
ExpectUnsignedEqual(from64, &to64);
from64 = std::numeric_limits<uint64_t>::max();
ExpectUnsignedEqual(from64, &to64);
EXPECT_FALSE(ReinterpretBytes(from64, &to32));
uint8_t to8 = std::numeric_limits<uint8_t>::max();
uint128_struct from128;
from128.lo = to8;
from128.hi = 0;
ExpectReinterpret(from128, &to8, to8);
}
TEST(ReinterpretBytes, ToSigned) {
uint64_t from64;
int64_t to64;
int32_t to32;
from64 = 0;
ExpectReinterpret(from64, &to32, static_cast<int32_t>(0));
ExpectReinterpret(from64, &to64, static_cast<int64_t>(0));
to32 = -1;
from64 = bit_cast<uint32_t>(to32);
ExpectReinterpret(from64, &to32, to32);
to32 = std::numeric_limits<int32_t>::max();
from64 = bit_cast<uint32_t>(to32);
ExpectReinterpret(from64, &to32, to32);
to32 = std::numeric_limits<int32_t>::min();
from64 = bit_cast<uint32_t>(to32);
ExpectReinterpret(from64, &to32, to32);
to64 = -1;
from64 = bit_cast<uint64_t>(to64);
ExpectReinterpret(from64, &to64, to64);
to64 = std::numeric_limits<int64_t>::max();
from64 = bit_cast<uint64_t>(to64);
ExpectReinterpret(from64, &to64, to64);
to64 = std::numeric_limits<int64_t>::min();
from64 = bit_cast<uint64_t>(to64);
ExpectReinterpret(from64, &to64, to64);
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -14,12 +14,15 @@
#include "util/numeric/checked_address_range.h"
#include "base/format_macros.h"
#include "base/strings/stringprintf.h"
#if defined(OS_MACOSX)
#include <mach/mach.h>
#elif defined(OS_WIN)
#include "util/win/address_types.h"
#elif defined(OS_LINUX) || defined(OS_ANDROID)
#include "util/linux/address_types.h"
#endif // OS_MACOSX
namespace crashpad {
@ -113,8 +116,10 @@ bool CheckedAddressRangeGeneric<ValueType, SizeType>::ContainsRange(
template <class ValueType, class SizeType>
std::string CheckedAddressRangeGeneric<ValueType, SizeType>::AsString() const {
return base::StringPrintf(
"0x%llx + 0x%llx (%s)", Base(), Size(), Is64Bit() ? "64" : "32");
return base::StringPrintf("0x%" PRIx64 " + 0x%" PRIx64 " (%s)",
uint64_t{Base()},
uint64_t{Size()},
Is64Bit() ? "64" : "32");
}
// Explicit instantiations for the cases we use.
@ -122,6 +127,8 @@ std::string CheckedAddressRangeGeneric<ValueType, SizeType>::AsString() const {
template class CheckedAddressRangeGeneric<mach_vm_address_t, mach_vm_size_t>;
#elif defined(OS_WIN)
template class CheckedAddressRangeGeneric<WinVMAddress, WinVMSize>;
#elif defined(OS_LINUX) || defined(OS_ANDROID)
template class CheckedAddressRangeGeneric<LinuxVMAddress, LinuxVMSize>;
#endif // OS_MACOSX
} // namespace internal

View File

@ -143,8 +143,6 @@ class CheckedAddressRangeGeneric {
// uniformly, but these types are too wide for the underlying pointer and size
// types in 32-bit processes.
bool range_ok_;
DISALLOW_COPY_AND_ASSIGN(CheckedAddressRangeGeneric);
};
} // namespace internal

View File

@ -15,12 +15,8 @@
#include "util/posix/process_info.h"
#include <ctype.h>
#include <elf.h>
#include <errno.h>
#include <string.h>
#include <sys/ptrace.h>
#include <sys/uio.h>
#include <sys/user.h>
#include <time.h>
#include <unistd.h>
@ -31,8 +27,9 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "util/file/delimited_file_reader.h"
#include "util/file/file_io.h"
#include "util/file/file_reader.h"
#include "util/linux/scoped_ptrace_attach.h"
#include "util/linux/thread_info.h"
namespace crashpad {
@ -78,20 +75,6 @@ bool AdvancePastNumber(const char** input, T* value) {
return false;
}
bool ReadEntireFile(const char* path, std::string* contents) {
FileReader file;
if (!file.Open(base::FilePath(path))) {
return false;
}
char buffer[4096];
FileOperationResult length;
while ((length = file.Read(buffer, sizeof(buffer))) > 0) {
contents->append(buffer, length);
}
return length >= 0;
}
void SubtractTimespec(const timespec& t1, const timespec& t2,
timespec* result) {
result->tv_sec = t1.tv_sec - t2.tv_sec;
@ -318,57 +301,11 @@ bool ProcessInfo::Is64Bit(bool* is_64_bit) const {
if (pid_ == getpid()) {
is_64_bit_ = am_64_bit;
} else {
ScopedPtraceAttach ptrace_attach;
if (!ptrace_attach.ResetAttach(pid_)) {
ThreadInfo thread_info;
if (!thread_info.Initialize(pid_)) {
return false;
}
// Allocate more buffer space than is required to hold registers for this
// process. If the kernel fills the extra space, the target process uses
// more/larger registers than this process. If the kernel fills less space
// than sizeof(regs) then the target process uses smaller/fewer registers.
struct {
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
using PrStatusType = user_regs_struct;
#elif defined(ARCH_CPU_ARMEL)
using PrStatusType = user_regs;
#endif
PrStatusType regs;
char extra;
} regbuf;
iovec iov;
iov.iov_base = &regbuf;
iov.iov_len = sizeof(regbuf);
if (ptrace(PTRACE_GETREGSET,
pid_,
reinterpret_cast<void*>(NT_PRSTATUS),
&iov) != 0) {
switch (errno) {
#if defined(ARCH_CPU_ARMEL)
case EIO:
// PTRACE_GETREGSET, introduced in Linux 2.6.34 (2225a122ae26),
// requires kernel support enabled by HAVE_ARCH_TRACEHOOK. This has
// been set for x86 (including x86_64) since Linux 2.6.28
// (99bbc4b1e677a), but for ARM only since Linux 3.5.0
// (0693bf68148c4). Fortunately, 64-bit ARM support only appeared in
// Linux 3.7.0, so if PTRACE_GETREGSET fails on ARM with EIO,
// indicating that the request is not supported, the kernel must be
// old enough that 64-bit ARM isnt supported either.
//
// TODO(mark): Once helpers to interpret the kernel version are
// available, add a DCHECK to ensure that the kernel is older than
// 3.5.
is_64_bit_ = false;
break;
#endif
default:
PLOG(ERROR) << "ptrace";
return false;
}
} else {
is_64_bit_ = am_64_bit == (iov.iov_len == sizeof(regbuf.regs));
}
is_64_bit_ = thread_info.Is64Bit();
}
is_64_bit_initialized_.set_valid();
@ -391,7 +328,7 @@ bool ProcessInfo::StartTime(timeval* start_time) const {
char path[32];
snprintf(path, sizeof(path), "/proc/%d/stat", pid_);
std::string stat_contents;
if (!ReadEntireFile(path, &stat_contents)) {
if (!LoggingReadEntireFile(base::FilePath(path), &stat_contents)) {
return false;
}

View File

@ -20,6 +20,8 @@
#include <sys/mman.h>
#include <sys/types.h>
#include "util/misc/from_pointer_cast.h"
namespace crashpad {
//! \brief Maintains a memory-mapped region created by `mmap()`.
@ -85,7 +87,7 @@ class ScopedMmap {
//! a type of the callers choosing.
template <typename T>
T addr_as() const {
return reinterpret_cast<T>(addr_);
return FromPointerCast<T>(addr_);
}
//! \brief Returns the size of the memory-mapped region.

View File

@ -180,7 +180,7 @@ void CauseSignal(int sig) {
case SIGSYS: {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
int rv = syscall(0);
int rv = syscall(4095);
#pragma clang diagnostic pop
if (rv != 0) {
PLOG(ERROR) << "syscall";

View File

@ -23,7 +23,6 @@
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "build/build_config.h"
#include "util/stdlib/cxx.h"
@ -55,7 +54,7 @@ void AlignedFree(void* pointer);
//! This is similar to `std::allocator<T>`, with the addition of an alignment
//! guarantee. \a Alignment must be a power of 2. If \a Alignment is not
//! specified, the default alignment for type \a T is used.
template <class T, size_t Alignment = ALIGNOF(T)>
template <class T, size_t Alignment = alignof(T)>
struct AlignedAllocator {
public:
using value_type = T;
@ -130,7 +129,7 @@ bool operator!=(const AlignedAllocator<T1, Alignment>& lhs,
//! This is similar to `std::vector<T>`, with the addition of an alignment
//! guarantee. \a Alignment must be a power of 2. If \a Alignment is not
//! specified, the default alignment for type \a T is used.
template <typename T, size_t Alignment = ALIGNOF(T)>
template <typename T, size_t Alignment = alignof(T)>
using AlignedVector = std::vector<T, AlignedAllocator<T, Alignment>>;
} // namespace crashpad

View File

@ -16,6 +16,7 @@
#include <stdint.h>
#include "base/compiler_specific.h"
#include "gtest/gtest.h"
#if defined(OS_WIN)
@ -40,30 +41,30 @@ TEST(AlignedAllocator, AlignedVector) {
AlignedVector<NaturalAlignedStruct> natural_aligned_vector;
natural_aligned_vector.push_back(NaturalAlignedStruct());
EXPECT_TRUE(
IsAligned(&natural_aligned_vector[0], ALIGNOF(NaturalAlignedStruct)));
IsAligned(&natural_aligned_vector[0], alignof(NaturalAlignedStruct)));
natural_aligned_vector.resize(3);
EXPECT_TRUE(
IsAligned(&natural_aligned_vector[0], ALIGNOF(NaturalAlignedStruct)));
IsAligned(&natural_aligned_vector[0], alignof(NaturalAlignedStruct)));
EXPECT_TRUE(
IsAligned(&natural_aligned_vector[1], ALIGNOF(NaturalAlignedStruct)));
IsAligned(&natural_aligned_vector[1], alignof(NaturalAlignedStruct)));
EXPECT_TRUE(
IsAligned(&natural_aligned_vector[2], ALIGNOF(NaturalAlignedStruct)));
IsAligned(&natural_aligned_vector[2], alignof(NaturalAlignedStruct)));
// Test a structure that declares its own alignment.
struct ALIGNAS(32) AlignedStruct {
struct alignas(32) AlignedStruct {
int i;
};
ASSERT_EQ(ALIGNOF(AlignedStruct), 32u);
ASSERT_EQ(alignof(AlignedStruct), 32u);
AlignedVector<AlignedStruct> aligned_vector;
aligned_vector.push_back(AlignedStruct());
EXPECT_TRUE(IsAligned(&aligned_vector[0], ALIGNOF(AlignedStruct)));
EXPECT_TRUE(IsAligned(&aligned_vector[0], alignof(AlignedStruct)));
aligned_vector.resize(3);
EXPECT_TRUE(IsAligned(&aligned_vector[0], ALIGNOF(AlignedStruct)));
EXPECT_TRUE(IsAligned(&aligned_vector[1], ALIGNOF(AlignedStruct)));
EXPECT_TRUE(IsAligned(&aligned_vector[2], ALIGNOF(AlignedStruct)));
EXPECT_TRUE(IsAligned(&aligned_vector[0], alignof(AlignedStruct)));
EXPECT_TRUE(IsAligned(&aligned_vector[1], alignof(AlignedStruct)));
EXPECT_TRUE(IsAligned(&aligned_vector[2], alignof(AlignedStruct)));
// Try a custom alignment. Since the structure itself doesnt specify an
// alignment constraint, only the base address will be aligned to the
@ -73,19 +74,19 @@ TEST(AlignedAllocator, AlignedVector) {
EXPECT_TRUE(IsAligned(&custom_aligned_vector[0], 64));
// Try a structure with a pretty big alignment request.
struct ALIGNAS(1024) BigAlignedStruct {
struct alignas(1024) BigAlignedStruct {
int i;
};
ASSERT_EQ(ALIGNOF(BigAlignedStruct), 1024u);
ASSERT_EQ(alignof(BigAlignedStruct), 1024u);
AlignedVector<BigAlignedStruct> big_aligned_vector;
big_aligned_vector.push_back(BigAlignedStruct());
EXPECT_TRUE(IsAligned(&big_aligned_vector[0], ALIGNOF(BigAlignedStruct)));
EXPECT_TRUE(IsAligned(&big_aligned_vector[0], alignof(BigAlignedStruct)));
big_aligned_vector.resize(3);
EXPECT_TRUE(IsAligned(&big_aligned_vector[0], ALIGNOF(BigAlignedStruct)));
EXPECT_TRUE(IsAligned(&big_aligned_vector[1], ALIGNOF(BigAlignedStruct)));
EXPECT_TRUE(IsAligned(&big_aligned_vector[2], ALIGNOF(BigAlignedStruct)));
EXPECT_TRUE(IsAligned(&big_aligned_vector[0], alignof(BigAlignedStruct)));
EXPECT_TRUE(IsAligned(&big_aligned_vector[1], alignof(BigAlignedStruct)));
EXPECT_TRUE(IsAligned(&big_aligned_vector[2], alignof(BigAlignedStruct)));
}
void BadAlignmentTest() {

View File

@ -45,8 +45,15 @@
'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',
'linux/process_memory.cc',
'linux/process_memory.h',
'linux/thread_info.cc',
'linux/thread_info.h',
'linux/scoped_ptrace_attach.cc',
'linux/scoped_ptrace_attach.h',
'mac/checked_mach_address_range.h',
@ -98,6 +105,7 @@
'misc/clock_mac.cc',
'misc/clock_posix.cc',
'misc/clock_win.cc',
'misc/from_pointer_cast.h',
'misc/implicit_cast.h',
'misc/initialization_state.h',
'misc/initialization_state_dcheck.cc',
@ -112,6 +120,8 @@
'misc/pdb_structures.h',
'misc/random_string.cc',
'misc/random_string.h',
'misc/reinterpret_bytes.cc',
'misc/reinterpret_bytes.h',
'misc/scoped_forbid_return.cc',
'misc/scoped_forbid_return.h',
'misc/symbolic_constants_common.h',
@ -275,7 +285,11 @@
'<(INTERMEDIATE_DIR)/util/mach/<(RULE_INPUT_ROOT)Server.h',
],
'action': [
'python', '<@(_inputs)', '<(RULE_INPUT_PATH)', '<@(_outputs)'
'python',
'<@(_inputs)',
'<(RULE_INPUT_PATH)',
'<@(_outputs)',
'--include=../compat/mac'
],
'process_outputs_as_sources': 1,
},

View File

@ -39,7 +39,10 @@
'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',
'linux/scoped_ptrace_attach_test.cc',
'mac/launchd_test.mm',
'mac/mac_util_test.mm',
@ -62,11 +65,13 @@
'mach/task_memory_test.cc',
'misc/arraysize_unsafe_test.cc',
'misc/clock_test.cc',
'misc/from_pointer_cast_test.cc',
'misc/initialization_state_dcheck_test.cc',
'misc/initialization_state_test.cc',
'misc/paths_test.cc',
'misc/scoped_forbid_return_test.cc',
'misc/random_string_test.cc',
'misc/reinterpret_bytes_test.cc',
'misc/uuid_test.cc',
'net/http_body_gzip_test.cc',
'net/http_body_test.cc',
@ -135,12 +140,6 @@
['exclude', '^net/http_transport_test\\.cc$'],
]
}],
['OS=="android" or OS=="linux"' , {
# Things not yet ported to Android or Linux
'sources/' : [
['exclude', '^numeric/checked_address_range_test\\.cc$'],
]
}],
],
'target_conditions': [
['OS=="android"', {

View File

@ -17,6 +17,7 @@
#include <stdint.h>
#include "base/numerics/safe_conversions.h"
#include "util/misc/from_pointer_cast.h"
namespace crashpad {
@ -26,7 +27,7 @@ namespace crashpad {
// back to the same HANDLE value.
int HandleToInt(HANDLE handle) {
return base::checked_cast<int>(reinterpret_cast<intptr_t>(handle));
return FromPointerCast<int>(handle);
}
HANDLE IntToHandle(int handle_int) {

View File

@ -28,6 +28,7 @@
#include "base/process/memory.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "util/misc/from_pointer_cast.h"
#include "util/numeric/safe_assignment.h"
#include "util/win/get_function.h"
#include "util/win/handle.h"
@ -133,7 +134,7 @@ bool RegionIsAccessible(const MEMORY_BASIC_INFORMATION64& memory_info) {
MEMORY_BASIC_INFORMATION64 MemoryBasicInformationToMemoryBasicInformation64(
const MEMORY_BASIC_INFORMATION& mbi) {
MEMORY_BASIC_INFORMATION64 mbi64 = {0};
mbi64.BaseAddress = reinterpret_cast<ULONGLONG>(mbi.BaseAddress);
mbi64.BaseAddress = FromPointerCast<ULONGLONG>(mbi.BaseAddress);
mbi64.AllocationBase = reinterpret_cast<ULONGLONG>(mbi.AllocationBase);
mbi64.AllocationProtect = mbi.AllocationProtect;
mbi64.RegionSize = mbi.RegionSize;

View File

@ -30,6 +30,7 @@
#include "test/test_paths.h"
#include "test/win/child_launcher.h"
#include "util/file/file_io.h"
#include "util/misc/from_pointer_cast.h"
#include "util/misc/random_string.h"
#include "util/misc/uuid.h"
#include "util/win/command_line.h"
@ -125,7 +126,7 @@ TEST(ProcessInfo, Self) {
// Find something we know is a code address and confirm expected memory
// information settings.
VerifyAddressInInCodePage(process_info,
reinterpret_cast<WinVMAddress>(_ReturnAddress()));
FromPointerCast<WinVMAddress>(_ReturnAddress()));
}
void TestOtherProcess(const base::string16& directory_modification) {
@ -640,7 +641,7 @@ TEST(ProcessInfo, OutOfRangeCheck) {
EXPECT_TRUE(
info.LoggingRangeIsFullyReadable(CheckedRange<WinVMAddress, WinVMSize>(
reinterpret_cast<WinVMAddress>(safe_memory.get()), kAllocationSize)));
FromPointerCast<WinVMAddress>(safe_memory.get()), kAllocationSize)));
EXPECT_FALSE(info.LoggingRangeIsFullyReadable(
CheckedRange<WinVMAddress, WinVMSize>(0, 1024)));
}