mac: Make 64-bit handler able to read 32-bit module lists

The 32-bit process_types definition of dyld_all_image_infos winds up
with four extra bytes of tail padding when built into a 64-bit
crashpad_handler compared to a 32-bit one, and compared to the
structure’s native size. This prevents a 64-bit crashpad_handler from
being able to create a module snapshot of a 32-bit process for
dyld_all_image_infos versions 14 (since 10.9) and 15 (since 10.12).

Work around this by placing a zero-length “end” marker and using
offsetof(dyld_all_image_infos, end) in preference to
sizeof(dyld_all_image_infos).

BUG=crashpad:156,crashpad:148,crashpad:120
TEST=crashpad_snapshot_test ProcessTypes.DyldImagesSelf,
     run_with_crashpad --handler=crashpad_handler{,32} builtin_trap{,32}

Change-Id: I406ad53851b5bd29ec802b7ad3073836ebe8c34c
Reviewed-on: https://chromium-review.googlesource.com/437924
Reviewed-by: Robert Sesek <rsesek@chromium.org>
This commit is contained in:
Mark Mentovai 2017-02-07 13:57:09 -05:00
parent 948fd2d019
commit 594eb43b58
5 changed files with 86 additions and 43 deletions

View File

@ -27,13 +27,21 @@ namespace crashpad {
namespace process_types {
namespace internal {
// Some structure definitions have tail padding when built in a 64-bit
// environment, but will have less (or no) tail padding in a 32-bit environment.
// These structures apparent sizes will differ between these two environments,
// which is incorrect. Use this “Nothing” type to place an “end” marker after
// all of the real members of such structures, and use offsetof(type, end) to
// compute their actual sizes.
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 structures more precisely.
using Reserved32_64Only32 = char[0];
using Reserved32_64Only32 = Nothing;
using Reserved32_64Only64 = uint32_t;
using Reserved64_64Only32 = char[0];
using Reserved64_64Only32 = Nothing;
using Reserved64_64Only64 = uint64_t;
} // namespace internal
@ -65,6 +73,7 @@ DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64)
using UIntPtr = internal::TraitsGeneric::UIntPtr; \
using Reserved32_64Only = internal::TraitsGeneric::Reserved32_64Only; \
using Reserved64_64Only = internal::TraitsGeneric::Reserved64_64Only; \
using Nothing = internal::TraitsGeneric::Nothing; \
\
/* Initializes an object with data read from |process_reader| at \
* |address|, properly genericized. */ \
@ -88,7 +97,7 @@ DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64)
/* Similar to Size(), but computes the expected size of a structure based \
* on the process bitness. This can be used prior to reading any data \
* from a process. */ \
static size_t ExpectedSize(ProcessReader* process_reader); \
static size_t ExpectedSize(ProcessReader* process_reader);
#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) \
member_type member_name __VA_ARGS__;
@ -155,6 +164,7 @@ DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64)
using UIntPtr = typename Traits::UIntPtr; \
using Reserved32_64Only = typename Traits::Reserved32_64Only; \
using Reserved64_64Only = typename Traits::Reserved64_64Only; \
using Nothing = typename Traits::Nothing; \
\
/* Read(), ReadArrayInto(), and Size() are as in the generic user-visible \
* struct above. */ \

View File

@ -17,6 +17,8 @@
#include <string.h>
#include <sys/types.h>
#include <type_traits>
#include "base/logging.h"
#include "snapshot/mac/process_types/internal.h"
#include "util/mach/task_memory.h"
@ -62,46 +64,31 @@ bool ReadIntoVersioned(ProcessReader* process_reader,
template <typename Traits>
size_t dyld_all_image_infos<Traits>::ExpectedSizeForVersion(
decltype(dyld_all_image_infos<Traits>::version) version) {
if (version >= 14) {
return sizeof(dyld_all_image_infos<Traits>);
const size_t kSizeForVersion[] = {
offsetof(dyld_all_image_infos<Traits>, infoArrayCount), // 0
offsetof(dyld_all_image_infos<Traits>, libSystemInitialized), // 1
offsetof(dyld_all_image_infos<Traits>, jitInfo), // 2
offsetof(dyld_all_image_infos<Traits>, dyldVersion), // 3
offsetof(dyld_all_image_infos<Traits>, dyldVersion), // 4
offsetof(dyld_all_image_infos<Traits>, coreSymbolicationShmPage), // 5
offsetof(dyld_all_image_infos<Traits>, systemOrderFlag), // 6
offsetof(dyld_all_image_infos<Traits>, uuidArrayCount), // 7
offsetof(dyld_all_image_infos<Traits>, dyldAllImageInfosAddress), // 8
offsetof(dyld_all_image_infos<Traits>, initialImageCount), // 9
offsetof(dyld_all_image_infos<Traits>, errorKind), // 10
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
};
if (version >= arraysize(kSizeForVersion)) {
return kSizeForVersion[arraysize(kSizeForVersion) - 1];
}
if (version >= 13) {
return offsetof(dyld_all_image_infos<Traits>, infoArrayChangeTimestamp);
}
if (version >= 12) {
return offsetof(dyld_all_image_infos<Traits>, sharedCacheUUID);
}
if (version >= 11) {
return offsetof(dyld_all_image_infos<Traits>, sharedCacheSlide);
}
if (version >= 10) {
return offsetof(dyld_all_image_infos<Traits>, errorKind);
}
if (version >= 9) {
return offsetof(dyld_all_image_infos<Traits>, initialImageCount);
}
if (version >= 8) {
return offsetof(dyld_all_image_infos<Traits>, dyldAllImageInfosAddress);
}
if (version >= 7) {
return offsetof(dyld_all_image_infos<Traits>, uuidArrayCount);
}
if (version >= 6) {
return offsetof(dyld_all_image_infos<Traits>, systemOrderFlag);
}
if (version >= 5) {
return offsetof(dyld_all_image_infos<Traits>, coreSymbolicationShmPage);
}
if (version >= 3) {
return offsetof(dyld_all_image_infos<Traits>, dyldVersion);
}
if (version >= 2) {
return offsetof(dyld_all_image_infos<Traits>, jitInfo);
}
if (version >= 1) {
return offsetof(dyld_all_image_infos<Traits>, libSystemInitialized);
}
return offsetof(dyld_all_image_infos<Traits>, infoArrayCount);
static_assert(std::is_unsigned<decltype(version)>::value,
"version must be unsigned");
return kSizeForVersion[version];
}
// static

View File

@ -123,6 +123,13 @@ PROCESS_TYPE_STRUCT_BEGIN(dyld_all_image_infos)
PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_6)
PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_7)
PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_8)
// 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)
PROCESS_TYPE_STRUCT_END(dyld_all_image_infos)
#endif // ! PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO &&

View File

@ -22,7 +22,8 @@
// DECLARE_PROCESS_TYPE_TRAITS_CLASS before #including this file again and after
// the last #include of this file.
//
// |Reserved*| are used for padding fields that may be zero-length, and thus
// |Reserved*| is used for padding fields that may be zero-length, and |Nothing|
// is always zero-length and is used solely as an anchor to compute offsets.
// __VA_ARGS__, which is intended to set the alignment of the 64-bit types, is
// not used for those type aliases.
#define DECLARE_PROCESS_TYPE_TRAITS_CLASS(traits_name, lp_bits, ...) \
@ -37,6 +38,7 @@
using UIntPtr = uint##lp_bits##_t __VA_ARGS__; \
using Reserved32_64Only = Reserved32_64Only##lp_bits; \
using Reserved64_64Only = Reserved64_64Only##lp_bits; \
using Nothing = Nothing; \
}; \
} \
} \

View File

@ -24,6 +24,7 @@
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "snapshot/mac/process_types/internal.h"
#include "test/mac/dyld.h"
#include "util/mac/mac_util.h"
#include "util/misc/implicit_cast.h"
@ -116,6 +117,42 @@ TEST(ProcessTypes, DyldImagesSelf) {
process_types::dyld_all_image_infos::ExpectedSizeForVersion(
&process_reader, kDyldAllImageInfosVersionInSDK));
// Make sure that the computed sizes of various versions of this structure are
// correct at different bitnessses.
const struct {
uint32_t version;
size_t size_32;
size_t size_64;
} kVersionsAndSizes[] = {
{1, 17, 25},
{2, 24, 40},
{3, 28, 48},
{5, 40, 72},
{6, 44, 80},
{7, 48, 88},
{8, 56, 104},
{9, 60, 112},
{10, 64, 120},
{11, 80, 152},
{12, 84, 160},
{13, 104, 184},
{14, 164, 304},
{15, 164, 304},
};
for (size_t index = 0; index < arraysize(kVersionsAndSizes); ++index) {
uint32_t version = kVersionsAndSizes[index].version;
SCOPED_TRACE(base::StringPrintf("index %zu, version %u", index, version));
EXPECT_EQ(kVersionsAndSizes[index].size_32,
process_types::internal::dyld_all_image_infos<
process_types::internal::Traits32>::
ExpectedSizeForVersion(version));
EXPECT_EQ(kVersionsAndSizes[index].size_64,
process_types::internal::dyld_all_image_infos<
process_types::internal::Traits64>::
ExpectedSizeForVersion(version));
}
process_types::dyld_all_image_infos proctype_image_infos;
ASSERT_TRUE(proctype_image_infos.Read(&process_reader,
dyld_info.all_image_info_addr));