crashpad/minidump/minidump_context_writer_test.cc

334 lines
11 KiB
C++
Raw Normal View History

// Copyright 2014 The Crashpad Authors
//
// 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 "minidump/minidump_context_writer.h"
#include <stdint.h>
#include <string>
#include <type_traits>
#include "base/notreached.h"
#include "gtest/gtest.h"
#include "minidump/minidump_context.h"
#include "minidump/test/minidump_context_test_util.h"
#include "minidump/test/minidump_writable_test_util.h"
#include "snapshot/cpu_context.h"
#include "snapshot/test/test_cpu_context.h"
#include "util/file/string_file.h"
namespace crashpad {
namespace test {
namespace {
template <typename Writer, typename Context, typename RVAType>
void EmptyContextTest(void (*expect_context)(uint32_t, const Context*, bool)) {
Writer context_writer;
StringFile string_file;
EXPECT_TRUE(context_writer.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), sizeof(Context));
const Context* observed =
MinidumpWritableAtRVA<Context>(string_file.string(), RVAType(0));
ASSERT_TRUE(observed);
expect_context(0, observed, false);
}
class TestTypeNames {
public:
template <typename T>
static std::string GetName(int) {
if (std::is_same<T, RVA>()) {
return "RVA";
}
if (std::is_same<T, RVA64>()) {
return "RVA64";
}
NOTREACHED_IN_MIGRATION();
return "";
}
};
template <typename RVAType>
class MinidumpContextWriter : public ::testing::Test {};
using RVATypes = ::testing::Types<RVA, RVA64>;
TYPED_TEST_SUITE(MinidumpContextWriter, RVATypes, TestTypeNames);
TYPED_TEST(MinidumpContextWriter, MinidumpContextX86Writer) {
StringFile string_file;
{
// Make sure that a context writer thats untouched writes a zeroed-out
// context.
SCOPED_TRACE("zero");
EmptyContextTest<MinidumpContextX86Writer, MinidumpContextX86, TypeParam>(
ExpectMinidumpContextX86);
}
{
SCOPED_TRACE("nonzero");
string_file.Reset();
constexpr uint32_t kSeed = 0x8086;
MinidumpContextX86Writer context_writer;
InitializeMinidumpContextX86(context_writer.context(), kSeed);
EXPECT_TRUE(context_writer.WriteEverything(&string_file));
test: Use (actual, [un]expected) in gtest {ASSERT,EXPECT}_{EQ,NE} gtest used to require (expected, actual) ordering for arguments to EXPECT_EQ and ASSERT_EQ, and in failed test assertions would identify each side as “expected” or “actual.” Tests in Crashpad adhered to this traditional ordering. After a gtest change in February 2016, it is now agnostic with respect to the order of these arguments. This change mechanically updates all uses of these macros to (actual, expected) by reversing them. This provides consistency with our use of the logging CHECK_EQ and DCHECK_EQ macros, and makes for better readability by ordinary native speakers. The rough (but working!) conversion tool is https://chromium-review.googlesource.com/c/466727/1/rewrite_expectassert_eq.py, and “git cl format” cleaned up its output. EXPECT_NE and ASSERT_NE never had a preferred ordering. gtest never made a judgment that one side or the other needed to provide an “unexpected” value. Consequently, some code used (unexpected, actual) while other code used (actual, unexpected). For consistency with the new EXPECT_EQ and ASSERT_EQ usage, as well as consistency with CHECK_NE and DCHECK_NE, this change also updates these use sites to (actual, unexpected) where one side can be called “unexpected” as, for example, std::string::npos can be. Unfortunately, this portion was a manual conversion. References: https://github.com/google/googletest/blob/master/googletest/docs/Primer.md#binary-comparison https://github.com/google/googletest/commit/77d6b173380332b1c1bc540532641f410ec82d65 https://github.com/google/googletest/pull/713 Change-Id: I978fef7c94183b8b1ef63f12f5ab4d6693626be3 Reviewed-on: https://chromium-review.googlesource.com/466727 Reviewed-by: Scott Graham <scottmg@chromium.org>
2017-04-04 00:35:21 -04:00
ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextX86));
const MinidumpContextX86* observed =
MinidumpWritableAtRVA<MinidumpContextX86>(string_file.string(),
TypeParam(0));
ASSERT_TRUE(observed);
ExpectMinidumpContextX86(kSeed, observed, false);
}
}
TYPED_TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) {
Heap-allocate MinidumpContextAMD64Writer objects with proper alignment While making crashpad_minidump_test run in Chromium’s try- and buildbots (https://crbug.com/779790), crashes in the MinidumpThreadWriter.OneThread_AMD64_Stack test were observed in 32-bit x86 Windows builds produced by Clang in the release configuration. These crashes occurred in crashpad::test::InitializeMinidumpContextAMD64, which heap-allocates a MinidumpContextAMD64Writer object. These objects have an alignment requirement of 16, based on the alignment requirement of their MinidumpContextAMD64 member. Although this problem was never observed with MSVC, Clang was making use of the known strict alignment and producing code that depended on it. This code crashed if the requirement was not met. MSVC had raised a warning about this usage (C4316), but the warning was disabled as it did not appear to have any ill effect on code produced by that compiler. The problem surfaced in test code, but heap-allocated MinidumpContextAMD64Writer objects are created in non-test code as well. The impact is limited, because a 32-bit Windows Crashpad handler would not have a need to allocate one of these objects. As a fix, MinidumpContextAMD64Writer is given a custom allocation function (a static “operator new()” member and matching “operator delete()”) that returns properly aligned memory. Change-Id: I0cb924da91716eb01b88ec2ae952a69262cc2de6 Reviewed-on: https://chromium-review.googlesource.com/746539 Reviewed-by: Leonard Mosescu <mosescu@chromium.org>
2017-10-31 22:25:34 -04:00
{
// Make sure that a heap-allocated context writer has the proper alignment,
// because it may be nonstandard.
auto context_writer = std::make_unique<MinidumpContextAMD64Writer>();
EXPECT_EQ(reinterpret_cast<uintptr_t>(context_writer.get()) &
(alignof(MinidumpContextAMD64Writer) - 1),
0u);
}
StringFile string_file;
{
// Make sure that a context writer thats untouched writes a zeroed-out
// context.
SCOPED_TRACE("zero");
EmptyContextTest<MinidumpContextAMD64Writer,
MinidumpContextAMD64,
TypeParam>(ExpectMinidumpContextAMD64);
}
{
SCOPED_TRACE("nonzero");
string_file.Reset();
constexpr uint32_t kSeed = 0x808664;
MinidumpContextAMD64Writer context_writer;
InitializeMinidumpContextAMD64(context_writer.context(), kSeed);
EXPECT_TRUE(context_writer.WriteEverything(&string_file));
test: Use (actual, [un]expected) in gtest {ASSERT,EXPECT}_{EQ,NE} gtest used to require (expected, actual) ordering for arguments to EXPECT_EQ and ASSERT_EQ, and in failed test assertions would identify each side as “expected” or “actual.” Tests in Crashpad adhered to this traditional ordering. After a gtest change in February 2016, it is now agnostic with respect to the order of these arguments. This change mechanically updates all uses of these macros to (actual, expected) by reversing them. This provides consistency with our use of the logging CHECK_EQ and DCHECK_EQ macros, and makes for better readability by ordinary native speakers. The rough (but working!) conversion tool is https://chromium-review.googlesource.com/c/466727/1/rewrite_expectassert_eq.py, and “git cl format” cleaned up its output. EXPECT_NE and ASSERT_NE never had a preferred ordering. gtest never made a judgment that one side or the other needed to provide an “unexpected” value. Consequently, some code used (unexpected, actual) while other code used (actual, unexpected). For consistency with the new EXPECT_EQ and ASSERT_EQ usage, as well as consistency with CHECK_NE and DCHECK_NE, this change also updates these use sites to (actual, unexpected) where one side can be called “unexpected” as, for example, std::string::npos can be. Unfortunately, this portion was a manual conversion. References: https://github.com/google/googletest/blob/master/googletest/docs/Primer.md#binary-comparison https://github.com/google/googletest/commit/77d6b173380332b1c1bc540532641f410ec82d65 https://github.com/google/googletest/pull/713 Change-Id: I978fef7c94183b8b1ef63f12f5ab4d6693626be3 Reviewed-on: https://chromium-review.googlesource.com/466727 Reviewed-by: Scott Graham <scottmg@chromium.org>
2017-04-04 00:35:21 -04:00
ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextAMD64));
const MinidumpContextAMD64* observed =
MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(),
TypeParam(0));
ASSERT_TRUE(observed);
ExpectMinidumpContextAMD64(kSeed, observed, false);
}
}
TYPED_TEST(MinidumpContextWriter, MinidumpContextRISCV64Writer) {
{
// Make sure that a heap-allocated context writer has the proper alignment,
// because it may be nonstandard.
auto context_writer = std::make_unique<MinidumpContextRISCV64Writer>();
EXPECT_EQ(reinterpret_cast<uintptr_t>(context_writer.get()) &
(alignof(MinidumpContextRISCV64Writer) - 1),
0u);
}
StringFile string_file;
{
// Make sure that a context writer thats untouched writes a zeroed-out
// context.
SCOPED_TRACE("zero");
EmptyContextTest<MinidumpContextRISCV64Writer,
MinidumpContextRISCV64,
TypeParam>(ExpectMinidumpContextRISCV64);
}
{
SCOPED_TRACE("nonzero");
string_file.Reset();
constexpr uint32_t kSeed = 0x808664;
MinidumpContextRISCV64Writer context_writer;
InitializeMinidumpContextRISCV64(context_writer.context(), kSeed);
EXPECT_TRUE(context_writer.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextRISCV64));
const MinidumpContextRISCV64* observed =
MinidumpWritableAtRVA<MinidumpContextRISCV64>(string_file.string(),
TypeParam(0));
ASSERT_TRUE(observed);
ExpectMinidumpContextRISCV64(kSeed, observed, false);
}
}
template <typename Writer, typename Context, typename RVAType>
void FromSnapshotTest(const CPUContext& snapshot_context,
void (*expect_context)(uint32_t, const Context*, bool),
uint32_t seed) {
std::unique_ptr<::crashpad::MinidumpContextWriter> context_writer =
::crashpad::MinidumpContextWriter::CreateFromSnapshot(&snapshot_context);
ASSERT_TRUE(context_writer);
StringFile string_file;
ASSERT_TRUE(context_writer->WriteEverything(&string_file));
const Context* observed =
MinidumpWritableAtRVA<Context>(string_file.string(), RVAType(0));
ASSERT_TRUE(observed);
expect_context(seed, observed, true);
}
TYPED_TEST(MinidumpContextWriter, X86_FromSnapshot) {
constexpr uint32_t kSeed = 32;
CPUContextX86 context_x86;
CPUContext context;
context.x86 = &context_x86;
InitializeCPUContextX86(&context, kSeed);
FromSnapshotTest<MinidumpContextX86Writer, MinidumpContextX86, TypeParam>(
context, ExpectMinidumpContextX86, kSeed);
}
TYPED_TEST(MinidumpContextWriter, AMD64_FromSnapshot) {
constexpr uint32_t kSeed = 64;
CPUContextX86_64 context_x86_64;
CPUContext context;
context.x86_64 = &context_x86_64;
InitializeCPUContextX86_64(&context, kSeed);
FromSnapshotTest<MinidumpContextAMD64Writer, MinidumpContextAMD64, TypeParam>(
context, ExpectMinidumpContextAMD64, kSeed);
}
TYPED_TEST(MinidumpContextWriter, AMD64_CetFromSnapshot) {
Write compacted xsave contexts in minidumps Adds new structures and offsets for minidump extended contexts. This information will be captured from threads in a later CL so this CL does not yet write different dumps, except in testing. Minidump format for extended compacted contexts has been determined by experiment. Offsets for where to write various parts of the context are hardcoded to 0x550 as this matches values seen in Windows. Offsets for misc_info_5 match those seen in working minidumps that can be opened in windbg. Our hope is that while these could change in future, CPU and OS vendors are unlikely to change them. See doc[0] for a discussion of these fields and offsets in the minidump. See "MANAGING STATE USING THE XSAVE FEATURE SET" Chapter 13 in the Intel SDM[1]. Many of the offsets and sizes of the extended features are provided by cpu specific values. We can access these in Windows using the SDK, and transfer these to the saved extended context which in turn is understandable by windbg. Further information is available from AMD Ch. 18 "Shadow Stacks"[2]. [0] https://docs.google.com/document/d/1Dn8n97r5B7kxYouvujNnPIYd_7QeVHpahSRmB92Qn6g/edit#heading=h.hivqj2jg39y [1] https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html. [2] https://www.amd.com/system/files/TechDocs/24593.pdf Bug: 1250098 Change-Id: Ia9041acc379c6d38329ee99737a2a0a77f7a1ee0 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3536964 Reviewed-by: Joshua Peraza <jperaza@chromium.org> Commit-Queue: Alex Gough <ajgo@chromium.org>
2022-05-14 22:40:24 -07:00
constexpr uint32_t kSeed = 77;
CPUContextX86_64 context_x86_64;
CPUContext context;
context.x86_64 = &context_x86_64;
InitializeCPUContextX86_64(&context, kSeed);
context_x86_64.xstate.enabled_features |= XSTATE_MASK_CET_U;
context_x86_64.xstate.cet_u.cetmsr = 1;
context_x86_64.xstate.cet_u.ssp = kSeed * kSeed;
// We cannot use FromSnapshotTest as we write more than the fixed context.
std::unique_ptr<::crashpad::MinidumpContextWriter> context_writer =
::crashpad::MinidumpContextWriter::CreateFromSnapshot(&context);
Write compacted xsave contexts in minidumps Adds new structures and offsets for minidump extended contexts. This information will be captured from threads in a later CL so this CL does not yet write different dumps, except in testing. Minidump format for extended compacted contexts has been determined by experiment. Offsets for where to write various parts of the context are hardcoded to 0x550 as this matches values seen in Windows. Offsets for misc_info_5 match those seen in working minidumps that can be opened in windbg. Our hope is that while these could change in future, CPU and OS vendors are unlikely to change them. See doc[0] for a discussion of these fields and offsets in the minidump. See "MANAGING STATE USING THE XSAVE FEATURE SET" Chapter 13 in the Intel SDM[1]. Many of the offsets and sizes of the extended features are provided by cpu specific values. We can access these in Windows using the SDK, and transfer these to the saved extended context which in turn is understandable by windbg. Further information is available from AMD Ch. 18 "Shadow Stacks"[2]. [0] https://docs.google.com/document/d/1Dn8n97r5B7kxYouvujNnPIYd_7QeVHpahSRmB92Qn6g/edit#heading=h.hivqj2jg39y [1] https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html. [2] https://www.amd.com/system/files/TechDocs/24593.pdf Bug: 1250098 Change-Id: Ia9041acc379c6d38329ee99737a2a0a77f7a1ee0 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3536964 Reviewed-by: Joshua Peraza <jperaza@chromium.org> Commit-Queue: Alex Gough <ajgo@chromium.org>
2022-05-14 22:40:24 -07:00
ASSERT_TRUE(context_writer);
StringFile string_file;
ASSERT_TRUE(context_writer->WriteEverything(&string_file));
const MinidumpContextAMD64* observed =
MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(),
TypeParam(0));
Write compacted xsave contexts in minidumps Adds new structures and offsets for minidump extended contexts. This information will be captured from threads in a later CL so this CL does not yet write different dumps, except in testing. Minidump format for extended compacted contexts has been determined by experiment. Offsets for where to write various parts of the context are hardcoded to 0x550 as this matches values seen in Windows. Offsets for misc_info_5 match those seen in working minidumps that can be opened in windbg. Our hope is that while these could change in future, CPU and OS vendors are unlikely to change them. See doc[0] for a discussion of these fields and offsets in the minidump. See "MANAGING STATE USING THE XSAVE FEATURE SET" Chapter 13 in the Intel SDM[1]. Many of the offsets and sizes of the extended features are provided by cpu specific values. We can access these in Windows using the SDK, and transfer these to the saved extended context which in turn is understandable by windbg. Further information is available from AMD Ch. 18 "Shadow Stacks"[2]. [0] https://docs.google.com/document/d/1Dn8n97r5B7kxYouvujNnPIYd_7QeVHpahSRmB92Qn6g/edit#heading=h.hivqj2jg39y [1] https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html. [2] https://www.amd.com/system/files/TechDocs/24593.pdf Bug: 1250098 Change-Id: Ia9041acc379c6d38329ee99737a2a0a77f7a1ee0 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3536964 Reviewed-by: Joshua Peraza <jperaza@chromium.org> Commit-Queue: Alex Gough <ajgo@chromium.org>
2022-05-14 22:40:24 -07:00
ASSERT_TRUE(observed);
ExpectMinidumpContextAMD64(kSeed, observed, true);
}
TYPED_TEST(MinidumpContextWriter, ARM_Zeros) {
EmptyContextTest<MinidumpContextARMWriter, MinidumpContextARM, TypeParam>(
ExpectMinidumpContextARM);
}
TYPED_TEST(MinidumpContextWriter, ARM64_Zeros) {
EmptyContextTest<MinidumpContextARM64Writer, MinidumpContextARM64, TypeParam>(
ExpectMinidumpContextARM64);
}
TYPED_TEST(MinidumpContextWriter, ARM_FromSnapshot) {
constexpr uint32_t kSeed = 32;
CPUContextARM context_arm;
CPUContext context;
context.arm = &context_arm;
InitializeCPUContextARM(&context, kSeed);
FromSnapshotTest<MinidumpContextARMWriter, MinidumpContextARM, TypeParam>(
context, ExpectMinidumpContextARM, kSeed);
}
TYPED_TEST(MinidumpContextWriter, ARM64_FromSnapshot) {
constexpr uint32_t kSeed = 64;
CPUContextARM64 context_arm64;
CPUContext context;
context.arm64 = &context_arm64;
InitializeCPUContextARM64(&context, kSeed);
FromSnapshotTest<MinidumpContextARM64Writer, MinidumpContextARM64, TypeParam>(
context, ExpectMinidumpContextARM64, kSeed);
}
TYPED_TEST(MinidumpContextWriter, MIPS_Zeros) {
EmptyContextTest<MinidumpContextMIPSWriter, MinidumpContextMIPS, TypeParam>(
ExpectMinidumpContextMIPS);
}
TYPED_TEST(MinidumpContextWriter, MIPS64_Zeros) {
EmptyContextTest<MinidumpContextMIPS64Writer,
MinidumpContextMIPS64,
TypeParam>(ExpectMinidumpContextMIPS64);
}
TYPED_TEST(MinidumpContextWriter, MIPS_FromSnapshot) {
constexpr uint32_t kSeed = 32;
CPUContextMIPS context_mips;
CPUContext context;
context.mipsel = &context_mips;
InitializeCPUContextMIPS(&context, kSeed);
FromSnapshotTest<MinidumpContextMIPSWriter, MinidumpContextMIPS, TypeParam>(
context, ExpectMinidumpContextMIPS, kSeed);
}
TYPED_TEST(MinidumpContextWriter, MIPS64_FromSnapshot) {
constexpr uint32_t kSeed = 64;
CPUContextMIPS64 context_mips;
CPUContext context;
context.mips64 = &context_mips;
InitializeCPUContextMIPS64(&context, kSeed);
FromSnapshotTest<MinidumpContextMIPS64Writer,
MinidumpContextMIPS64,
TypeParam>(context, ExpectMinidumpContextMIPS64, kSeed);
}
TYPED_TEST(MinidumpContextWriter, RISCV64_Zeros) {
EmptyContextTest<MinidumpContextRISCV64Writer,
MinidumpContextRISCV64,
TypeParam>(ExpectMinidumpContextRISCV64);
}
TYPED_TEST(MinidumpContextWriter, RISCV64_FromSnapshot) {
constexpr uint32_t kSeed = 64;
CPUContextRISCV64 context_riscv64;
CPUContext context;
context.riscv64 = &context_riscv64;
InitializeCPUContextRISCV64(&context, kSeed);
FromSnapshotTest<MinidumpContextRISCV64Writer,
MinidumpContextRISCV64,
TypeParam>(context, ExpectMinidumpContextRISCV64, kSeed);
}
} // namespace
} // namespace test
} // namespace crashpad