crash_analyzer_unittest.cc 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/gwp_asan/crash_handler/crash_analyzer.h"
  5. #include <memory>
  6. #include <string>
  7. #include <utility>
  8. #include <vector>
  9. #include "base/callback_helpers.h"
  10. #include "base/debug/stack_trace.h"
  11. #include "base/test/gtest_util.h"
  12. #include "base/test/metrics/histogram_tester.h"
  13. #include "build/build_config.h"
  14. #include "components/gwp_asan/client/guarded_page_allocator.h"
  15. #include "components/gwp_asan/common/allocator_state.h"
  16. #include "components/gwp_asan/common/crash_key_name.h"
  17. #include "components/gwp_asan/crash_handler/crash.pb.h"
  18. #include "testing/gmock/include/gmock/gmock.h"
  19. #include "testing/gtest/include/gtest/gtest.h"
  20. #include "third_party/crashpad/crashpad/client/annotation.h"
  21. #include "third_party/crashpad/crashpad/snapshot/annotation_snapshot.h"
  22. #include "third_party/crashpad/crashpad/snapshot/cpu_context.h"
  23. #include "third_party/crashpad/crashpad/snapshot/test/test_exception_snapshot.h"
  24. #include "third_party/crashpad/crashpad/snapshot/test/test_module_snapshot.h"
  25. #include "third_party/crashpad/crashpad/snapshot/test/test_process_snapshot.h"
  26. #include "third_party/crashpad/crashpad/test/process_type.h"
  27. #include "third_party/crashpad/crashpad/util/process/process_memory_native.h"
  28. #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  29. #include "third_party/crashpad/crashpad/test/linux/fake_ptrace_connection.h"
  30. #endif
  31. namespace gwp_asan {
  32. namespace internal {
  33. namespace {
  34. constexpr const char* kMallocHistogramName =
  35. "GwpAsan.CrashAnalysisResult.Malloc";
  36. constexpr const char* kPartitionAllocHistogramName =
  37. "GwpAsan.CrashAnalysisResult.PartitionAlloc";
  38. } // namespace
  39. class CrashAnalyzerTest : public testing::Test {
  40. protected:
  41. void SetUp() final {
  42. gpa_.Init(1, 1, 1, base::DoNothing(), false);
  43. InitializeSnapshot();
  44. }
  45. // Initializes the ProcessSnapshot so that it appears the given allocator was
  46. // used for backing malloc.
  47. void InitializeSnapshot() {
  48. std::string crash_key_value = gpa_.GetCrashKey();
  49. std::vector<uint8_t> crash_key_vector(crash_key_value.begin(),
  50. crash_key_value.end());
  51. std::vector<crashpad::AnnotationSnapshot> annotations;
  52. annotations.emplace_back(
  53. kMallocCrashKey,
  54. static_cast<uint16_t>(crashpad::Annotation::Type::kString),
  55. crash_key_vector);
  56. auto module = std::make_unique<crashpad::test::TestModuleSnapshot>();
  57. module->SetAnnotationObjects(annotations);
  58. auto exception = std::make_unique<crashpad::test::TestExceptionSnapshot>();
  59. // Just match the bitness, the actual architecture doesn't matter.
  60. #if defined(ARCH_CPU_64_BITS)
  61. exception->MutableContext()->architecture =
  62. crashpad::CPUArchitecture::kCPUArchitectureX86_64;
  63. #else
  64. exception->MutableContext()->architecture =
  65. crashpad::CPUArchitecture::kCPUArchitectureX86;
  66. #endif
  67. #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  68. ASSERT_TRUE(connection_.Initialize(getpid()));
  69. auto memory = std::make_unique<crashpad::ProcessMemoryLinux>(&connection_);
  70. #else
  71. auto memory = std::make_unique<crashpad::ProcessMemoryNative>();
  72. ASSERT_TRUE(memory->Initialize(crashpad::test::GetSelfProcess()));
  73. #endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||
  74. // BUILDFLAG(IS_CHROMEOS)
  75. process_snapshot_.AddModule(std::move(module));
  76. process_snapshot_.SetException(std::move(exception));
  77. process_snapshot_.SetProcessMemory(std::move(memory));
  78. }
  79. GuardedPageAllocator gpa_;
  80. crashpad::test::TestProcessSnapshot process_snapshot_;
  81. #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  82. crashpad::test::FakePtraceConnection connection_;
  83. #endif
  84. };
  85. // Stack trace collection on Android builds with frame pointers enabled does
  86. // not use base::debug::StackTrace, so the stack traces may vary slightly and
  87. // break this test.
  88. #if !BUILDFLAG(IS_ANDROID) || !BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
  89. TEST_F(CrashAnalyzerTest, StackTraceCollection) {
  90. void* ptr = gpa_.Allocate(10);
  91. ASSERT_NE(ptr, nullptr);
  92. gpa_.Deallocate(ptr);
  93. // Lets pretend a double free() occurred on the allocation we saw previously.
  94. gpa_.state_.double_free_address = reinterpret_cast<uintptr_t>(ptr);
  95. base::HistogramTester histogram_tester;
  96. gwp_asan::Crash proto;
  97. bool proto_present =
  98. CrashAnalyzer::GetExceptionInfo(process_snapshot_, &proto);
  99. ASSERT_TRUE(proto_present);
  100. int result = static_cast<int>(GwpAsanCrashAnalysisResult::kGwpAsanCrash);
  101. EXPECT_THAT(histogram_tester.GetAllSamples(kMallocHistogramName),
  102. testing::ElementsAre(base::Bucket(result, 1)));
  103. histogram_tester.ExpectTotalCount(kPartitionAllocHistogramName, 0);
  104. ASSERT_TRUE(proto.has_allocation());
  105. ASSERT_TRUE(proto.has_deallocation());
  106. base::debug::StackTrace st;
  107. size_t trace_len;
  108. const void* const* trace = st.Addresses(&trace_len);
  109. ASSERT_NE(trace, nullptr);
  110. ASSERT_GT(trace_len, 0U);
  111. // Adjust the stack trace to point to the entry above the current frame.
  112. while (trace_len > 0) {
  113. if (trace[0] == __builtin_return_address(0))
  114. break;
  115. trace++;
  116. trace_len--;
  117. }
  118. ASSERT_GT(proto.allocation().stack_trace_size(), (int)trace_len);
  119. ASSERT_GT(proto.deallocation().stack_trace_size(), (int)trace_len);
  120. // Ensure that the allocation and deallocation stack traces match the stack
  121. // frames that we collected above the current frame.
  122. for (size_t i = 1; i <= trace_len; i++) {
  123. SCOPED_TRACE(i);
  124. ASSERT_EQ(proto.allocation().stack_trace(
  125. proto.allocation().stack_trace_size() - i),
  126. reinterpret_cast<uintptr_t>(trace[trace_len - i]));
  127. ASSERT_EQ(proto.deallocation().stack_trace(
  128. proto.deallocation().stack_trace_size() - i),
  129. reinterpret_cast<uintptr_t>(trace[trace_len - i]));
  130. }
  131. }
  132. #endif
  133. TEST_F(CrashAnalyzerTest, InternalError) {
  134. // Lets pretend an invalid free() occurred in the allocator region.
  135. gpa_.state_.free_invalid_address =
  136. reinterpret_cast<uintptr_t>(gpa_.state_.first_page_addr);
  137. // Out of bounds slot_to_metadata_idx, allocator was initialized with only a
  138. // single entry slot/metadata entry.
  139. gpa_.slot_to_metadata_idx_[0] = 5;
  140. base::HistogramTester histogram_tester;
  141. gwp_asan::Crash proto;
  142. bool proto_present =
  143. CrashAnalyzer::GetExceptionInfo(process_snapshot_, &proto);
  144. ASSERT_TRUE(proto_present);
  145. int result =
  146. static_cast<int>(GwpAsanCrashAnalysisResult::kErrorBadMetadataIndex);
  147. EXPECT_THAT(histogram_tester.GetAllSamples(kMallocHistogramName),
  148. testing::ElementsAre(base::Bucket(result, 1)));
  149. histogram_tester.ExpectTotalCount(kPartitionAllocHistogramName, 0);
  150. EXPECT_TRUE(proto.has_internal_error());
  151. ASSERT_TRUE(proto.has_missing_metadata());
  152. EXPECT_TRUE(proto.missing_metadata());
  153. }
  154. } // namespace internal
  155. } // namespace gwp_asan