raw_ptr_asan_service.cc 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // Copyright 2022 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 "base/memory/raw_ptr_asan_service.h"
  5. #if BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)
  6. #include <sanitizer/allocator_interface.h>
  7. #include <sanitizer/asan_interface.h>
  8. #include <stdarg.h>
  9. #include <string.h>
  10. #include "base/check_op.h"
  11. #include "base/logging.h"
  12. #include "base/strings/stringprintf.h"
  13. namespace base {
  14. RawPtrAsanService RawPtrAsanService::instance_;
  15. namespace {
  16. // https://github.com/llvm/llvm-project/blob/b84673b3f424882c4c1961fb2c49b6302b68f344/compiler-rt/lib/asan/asan_mapping.h#L154
  17. constexpr size_t kShadowScale = 3;
  18. // https://github.com/llvm/llvm-project/blob/b84673b3f424882c4c1961fb2c49b6302b68f344/compiler-rt/lib/asan/asan_allocator.cpp#L143
  19. constexpr size_t kChunkHeaderSize = 16;
  20. // https://github.com/llvm/llvm-project/blob/b84673b3f424882c4c1961fb2c49b6302b68f344/compiler-rt/lib/asan/asan_internal.h#L138
  21. constexpr uint8_t kAsanHeapLeftRedzoneMagic = 0xfa;
  22. // https://github.com/llvm/llvm-project/blob/b84673b3f424882c4c1961fb2c49b6302b68f344/compiler-rt/lib/asan/asan_internal.h#L145
  23. constexpr uint8_t kAsanUserPoisonedMemoryMagic = 0xf7;
  24. } // namespace
  25. #if defined(COMPONENT_BUILD) && defined(_WIN32)
  26. // In component builds on Windows, weak function exported by ASan have the
  27. // `__dll` suffix. ASan itself uses the `alternatename` directive to account for
  28. // that.
  29. __pragma(comment(linker,
  30. "/alternatename:__sanitizer_report_error_summary=__sanitizer_"
  31. "report_error_summary__dll"));
  32. #endif // defined(COMPONENT_BUILD) && defined(_WIN32)
  33. // static
  34. void RawPtrAsanService::Log(const char* format, ...) {
  35. va_list ap;
  36. va_start(ap, format);
  37. auto formatted_message = StringPrintV(format, ap);
  38. va_end(ap);
  39. // Despite its name, the function just prints the input to the destination
  40. // configured by ASan.
  41. __sanitizer_report_error_summary(formatted_message.c_str());
  42. }
  43. // Mark the first eight bytes of every allocation's header as "user poisoned".
  44. // This allows us to filter out allocations made before BRP-ASan is activated.
  45. // The change shouldn't reduce the regular ASan coverage.
  46. // static
  47. NO_SANITIZE("address")
  48. void RawPtrAsanService::MallocHook(const volatile void* ptr, size_t size) {
  49. uint8_t* header =
  50. static_cast<uint8_t*>(const_cast<void*>(ptr)) - kChunkHeaderSize;
  51. *RawPtrAsanService::GetInstance().GetShadow(header) =
  52. kAsanUserPoisonedMemoryMagic;
  53. }
  54. NO_SANITIZE("address")
  55. bool RawPtrAsanService::IsSupportedAllocation(void* allocation_start) const {
  56. uint8_t* header = static_cast<uint8_t*>(allocation_start) - kChunkHeaderSize;
  57. return *GetShadow(header) == kAsanUserPoisonedMemoryMagic;
  58. }
  59. NO_SANITIZE("address")
  60. void RawPtrAsanService::Configure(
  61. EnableDereferenceCheck enable_dereference_check,
  62. EnableExtractionCheck enable_extraction_check,
  63. EnableInstantiationCheck enable_instantiation_check) {
  64. CHECK_EQ(mode_, Mode::kUninitialized);
  65. Mode new_mode = enable_dereference_check || enable_extraction_check ||
  66. enable_instantiation_check
  67. ? Mode::kEnabled
  68. : Mode::kDisabled;
  69. if (new_mode == Mode::kEnabled) {
  70. // The constants we use aren't directly exposed by the API, so
  71. // validate them at runtime as carefully as possible.
  72. size_t shadow_scale;
  73. __asan_get_shadow_mapping(&shadow_scale, &shadow_offset_);
  74. CHECK_EQ(shadow_scale, kShadowScale);
  75. uint8_t* dummy_alloc = new uint8_t;
  76. CHECK_EQ(*GetShadow(dummy_alloc - kChunkHeaderSize),
  77. kAsanHeapLeftRedzoneMagic);
  78. __asan_poison_memory_region(dummy_alloc, 1);
  79. CHECK_EQ(*GetShadow(dummy_alloc), kAsanUserPoisonedMemoryMagic);
  80. delete dummy_alloc;
  81. __sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook);
  82. __asan_set_error_report_callback(ErrorReportCallback);
  83. is_dereference_check_enabled_ = !!enable_dereference_check;
  84. is_extraction_check_enabled_ = !!enable_extraction_check;
  85. is_instantiation_check_enabled_ = !!enable_instantiation_check;
  86. }
  87. mode_ = new_mode;
  88. }
  89. uint8_t* RawPtrAsanService::GetShadow(void* ptr) const {
  90. return reinterpret_cast<uint8_t*>(
  91. (reinterpret_cast<uintptr_t>(ptr) >> kShadowScale) + shadow_offset_);
  92. }
  93. // static
  94. void RawPtrAsanService::SetPendingReport(ReportType type,
  95. const volatile void* ptr) {
  96. // The actual ASan crash may occur at an offset from the pointer passed
  97. // here, so track the whole region.
  98. void* region_base;
  99. size_t region_size;
  100. __asan_locate_address(const_cast<void*>(ptr), nullptr, 0, &region_base,
  101. &region_size);
  102. GetPendingReport() = {type, reinterpret_cast<uintptr_t>(region_base),
  103. region_size};
  104. }
  105. // static
  106. void RawPtrAsanService::ErrorReportCallback(const char* report) {
  107. if (strcmp(__asan_get_report_description(), "heap-use-after-free") != 0)
  108. return;
  109. const char* status_body;
  110. auto& pending_report = GetPendingReport();
  111. uintptr_t ptr = reinterpret_cast<uintptr_t>(__asan_get_report_address());
  112. if (pending_report.allocation_base <= ptr &&
  113. ptr < pending_report.allocation_base + pending_report.allocation_size) {
  114. bool is_supported_allocation =
  115. RawPtrAsanService::GetInstance().IsSupportedAllocation(
  116. reinterpret_cast<void*>(pending_report.allocation_base));
  117. switch (pending_report.type) {
  118. case ReportType::kDereference: {
  119. if (is_supported_allocation) {
  120. status_body =
  121. "PROTECTED\n"
  122. "The crash occurred while a raw_ptr<T> object containing a "
  123. "dangling pointer was being dereferenced.\n"
  124. "MiraclePtr should make this crash non-exploitable in regular "
  125. "builds.";
  126. } else {
  127. status_body =
  128. "NOT PROTECTED\n"
  129. "The region was allocated before MiraclePtr was activated.";
  130. }
  131. break;
  132. }
  133. case ReportType::kExtraction: {
  134. if (is_supported_allocation) {
  135. status_body =
  136. "MANUAL ANALYSIS REQUIRED\n"
  137. "A pointer to the same region was extracted from a raw_ptr<T> "
  138. "object prior to the crash.\n"
  139. "To determine the protection status, enable extraction warnings "
  140. "and check whether the raw_ptr<T>\n"
  141. "object can be destroyed or overwritten between the extraction "
  142. "and "
  143. "use.";
  144. } else {
  145. status_body =
  146. "NOT PROTECTED\n"
  147. "The region was allocated before MiraclePtr was activated.";
  148. }
  149. break;
  150. }
  151. case ReportType::kInstantiation: {
  152. status_body =
  153. "NOT PROTECTED\n"
  154. "A pointer to an already freed region was assigned to a raw_ptr<T> "
  155. "object, which may lead to memory\n"
  156. "corruption.";
  157. }
  158. }
  159. } else {
  160. status_body =
  161. "NOT PROTECTED\n"
  162. "No raw_ptr<T> access to this region was detected prior to the crash.";
  163. }
  164. Log("\nMiraclePtr Status: %s\n"
  165. "Refer to "
  166. "https://chromium.googlesource.com/chromium/src/+/main/base/memory/"
  167. "raw_ptr.md for details.",
  168. status_body);
  169. }
  170. // static
  171. RawPtrAsanService::PendingReport& RawPtrAsanService::GetPendingReport() {
  172. static thread_local PendingReport report;
  173. return report;
  174. }
  175. } // namespace base
  176. #endif // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)