raw_ptr.cc 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. // Copyright 2020 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.h"
  5. #include <cstdint>
  6. #include "base/allocator/buildflags.h"
  7. #include "base/process/process.h"
  8. // USE_BACKUP_REF_PTR implies USE_PARTITION_ALLOC, needed for code under
  9. // allocator/partition_allocator/ to be built.
  10. #if BUILDFLAG(USE_BACKUP_REF_PTR)
  11. #include "base/allocator/partition_allocator/partition_alloc.h"
  12. #include "base/allocator/partition_allocator/partition_ref_count.h"
  13. #include "base/allocator/partition_allocator/partition_root.h"
  14. #include "base/allocator/partition_allocator/reservation_offset_table.h"
  15. #include "base/check.h"
  16. #include "base/dcheck_is_on.h"
  17. namespace base::internal {
  18. template <bool AllowDangling>
  19. void BackupRefPtrImpl<AllowDangling>::AcquireInternal(uintptr_t address) {
  20. #if DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)
  21. CHECK(partition_alloc::IsManagedByPartitionAllocBRPPool(address));
  22. #endif
  23. uintptr_t slot_start =
  24. partition_alloc::PartitionAllocGetSlotStartInBRPPool(address);
  25. if constexpr (AllowDangling)
  26. partition_alloc::internal::PartitionRefCountPointer(slot_start)
  27. ->AcquireFromUnprotectedPtr();
  28. else
  29. partition_alloc::internal::PartitionRefCountPointer(slot_start)->Acquire();
  30. }
  31. template <bool AllowDangling>
  32. void BackupRefPtrImpl<AllowDangling>::ReleaseInternal(uintptr_t address) {
  33. #if DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)
  34. CHECK(partition_alloc::IsManagedByPartitionAllocBRPPool(address));
  35. #endif
  36. uintptr_t slot_start =
  37. partition_alloc::PartitionAllocGetSlotStartInBRPPool(address);
  38. if constexpr (AllowDangling) {
  39. if (partition_alloc::internal::PartitionRefCountPointer(slot_start)
  40. ->ReleaseFromUnprotectedPtr())
  41. partition_alloc::internal::PartitionAllocFreeForRefCounting(slot_start);
  42. } else {
  43. if (partition_alloc::internal::PartitionRefCountPointer(slot_start)
  44. ->Release())
  45. partition_alloc::internal::PartitionAllocFreeForRefCounting(slot_start);
  46. }
  47. }
  48. template <bool AllowDangling>
  49. bool BackupRefPtrImpl<AllowDangling>::IsPointeeAlive(uintptr_t address) {
  50. #if DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)
  51. CHECK(partition_alloc::IsManagedByPartitionAllocBRPPool(address));
  52. #endif
  53. uintptr_t slot_start =
  54. partition_alloc::PartitionAllocGetSlotStartInBRPPool(address);
  55. return partition_alloc::internal::PartitionRefCountPointer(slot_start)
  56. ->IsAlive();
  57. }
  58. template <bool AllowDangling>
  59. bool BackupRefPtrImpl<AllowDangling>::IsValidSignedDelta(
  60. uintptr_t address,
  61. ptrdiff_t delta_in_bytes) {
  62. return partition_alloc::internal::PartitionAllocIsValidPtrDelta(
  63. address, delta_in_bytes);
  64. }
  65. template <bool AllowDangling>
  66. bool BackupRefPtrImpl<AllowDangling>::IsValidUnsignedDelta(
  67. uintptr_t address,
  68. size_t delta_in_bytes) {
  69. return partition_alloc::internal::PartitionAllocIsValidPtrDelta(
  70. address, delta_in_bytes);
  71. }
  72. // Explicitly instantiates the two BackupRefPtr variants in the .cc. This
  73. // ensures the definitions not visible from the .h are available in the binary.
  74. template struct BackupRefPtrImpl</*AllowDangling=*/false>;
  75. template struct BackupRefPtrImpl</*AllowDangling=*/true>;
  76. #if DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)
  77. void CheckThatAddressIsntWithinFirstPartitionPage(uintptr_t address) {
  78. if (partition_alloc::internal::IsManagedByDirectMap(address)) {
  79. uintptr_t reservation_start =
  80. partition_alloc::internal::GetDirectMapReservationStart(address);
  81. CHECK(address - reservation_start >= partition_alloc::PartitionPageSize());
  82. } else {
  83. CHECK(partition_alloc::internal::IsManagedByNormalBuckets(address));
  84. CHECK(address % partition_alloc::kSuperPageSize >=
  85. partition_alloc::PartitionPageSize());
  86. }
  87. }
  88. #endif // DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)
  89. } // namespace base::internal
  90. #elif BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)
  91. #include <sanitizer/asan_interface.h>
  92. #include "base/logging.h"
  93. #include "base/memory/raw_ptr_asan_service.h"
  94. namespace base::internal {
  95. namespace {
  96. bool IsFreedHeapPointer(void const volatile* ptr) {
  97. // Use `__asan_region_is_poisoned` instead of `__asan_address_is_poisoned`
  98. // because the latter may crash on an invalid pointer.
  99. if (!__asan_region_is_poisoned(const_cast<void*>(ptr), 1))
  100. return false;
  101. // Make sure the address is on the heap and is not in a redzone.
  102. void* region_ptr;
  103. size_t region_size;
  104. const char* allocation_type = __asan_locate_address(
  105. const_cast<void*>(ptr), nullptr, 0, &region_ptr, &region_size);
  106. auto address = reinterpret_cast<uintptr_t>(ptr);
  107. auto region_base = reinterpret_cast<uintptr_t>(region_ptr);
  108. if (strcmp(allocation_type, "heap") != 0 || address < region_base ||
  109. address >=
  110. region_base + region_size) { // We exclude pointers one past the end
  111. // of an allocations from the analysis
  112. // for now because they're to fragile.
  113. return false;
  114. }
  115. // Make sure the allocation has been actually freed rather than
  116. // user-poisoned.
  117. int free_thread_id = -1;
  118. __asan_get_free_stack(region_ptr, nullptr, 0, &free_thread_id);
  119. return free_thread_id != -1;
  120. }
  121. // Force a non-optimizable memory load operation to trigger an ASan crash.
  122. void ForceRead(void const volatile* ptr) {
  123. auto unused = *reinterpret_cast<char const volatile*>(ptr);
  124. asm volatile("" : "+r"(unused));
  125. }
  126. } // namespace
  127. NO_SANITIZE("address")
  128. void AsanBackupRefPtrImpl::AsanCheckIfValidDereference(
  129. void const volatile* ptr) {
  130. if (RawPtrAsanService::GetInstance().is_dereference_check_enabled() &&
  131. IsFreedHeapPointer(ptr)) {
  132. RawPtrAsanService::SetPendingReport(
  133. RawPtrAsanService::ReportType::kDereference, ptr);
  134. ForceRead(ptr);
  135. }
  136. }
  137. NO_SANITIZE("address")
  138. void AsanBackupRefPtrImpl::AsanCheckIfValidExtraction(
  139. void const volatile* ptr) {
  140. auto& service = RawPtrAsanService::GetInstance();
  141. if ((service.is_extraction_check_enabled() ||
  142. service.is_dereference_check_enabled()) &&
  143. IsFreedHeapPointer(ptr)) {
  144. RawPtrAsanService::SetPendingReport(
  145. RawPtrAsanService::ReportType::kExtraction, ptr);
  146. // If the dereference check is enabled, we still record the extraction event
  147. // to catch the potential subsequent dangling dereference, but don't report
  148. // the extraction itself.
  149. if (service.is_extraction_check_enabled()) {
  150. RawPtrAsanService::Log(
  151. "=================================================================\n"
  152. "==%d==WARNING: MiraclePtr: dangling-pointer-extraction on address "
  153. "%p\n"
  154. "extracted here:",
  155. Process::Current().Pid(), ptr);
  156. __sanitizer_print_stack_trace();
  157. __asan_describe_address(const_cast<void*>(ptr));
  158. RawPtrAsanService::Log(
  159. "A regular ASan report will follow if the extracted pointer is "
  160. "dereferenced later.\n"
  161. "Otherwise, it is still likely a bug to rely on the address of an "
  162. "already freed allocation.\n"
  163. "Refer to "
  164. "https://chromium.googlesource.com/chromium/src/+/main/base/memory/"
  165. "raw_ptr.md for details.\n"
  166. "=================================================================");
  167. }
  168. }
  169. }
  170. NO_SANITIZE("address")
  171. void AsanBackupRefPtrImpl::AsanCheckIfValidInstantiation(
  172. void const volatile* ptr) {
  173. if (RawPtrAsanService::GetInstance().is_instantiation_check_enabled() &&
  174. IsFreedHeapPointer(ptr)) {
  175. RawPtrAsanService::SetPendingReport(
  176. RawPtrAsanService::ReportType::kInstantiation, ptr);
  177. ForceRead(ptr);
  178. }
  179. }
  180. } // namespace base::internal
  181. #endif // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)