123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- // Copyright 2020 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/memory/raw_ptr.h"
- #include <cstdint>
- #include "base/allocator/buildflags.h"
- #include "base/process/process.h"
- // USE_BACKUP_REF_PTR implies USE_PARTITION_ALLOC, needed for code under
- // allocator/partition_allocator/ to be built.
- #if BUILDFLAG(USE_BACKUP_REF_PTR)
- #include "base/allocator/partition_allocator/partition_alloc.h"
- #include "base/allocator/partition_allocator/partition_ref_count.h"
- #include "base/allocator/partition_allocator/partition_root.h"
- #include "base/allocator/partition_allocator/reservation_offset_table.h"
- #include "base/check.h"
- #include "base/dcheck_is_on.h"
- namespace base::internal {
- template <bool AllowDangling>
- void BackupRefPtrImpl<AllowDangling>::AcquireInternal(uintptr_t address) {
- #if DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)
- CHECK(partition_alloc::IsManagedByPartitionAllocBRPPool(address));
- #endif
- uintptr_t slot_start =
- partition_alloc::PartitionAllocGetSlotStartInBRPPool(address);
- if constexpr (AllowDangling)
- partition_alloc::internal::PartitionRefCountPointer(slot_start)
- ->AcquireFromUnprotectedPtr();
- else
- partition_alloc::internal::PartitionRefCountPointer(slot_start)->Acquire();
- }
- template <bool AllowDangling>
- void BackupRefPtrImpl<AllowDangling>::ReleaseInternal(uintptr_t address) {
- #if DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)
- CHECK(partition_alloc::IsManagedByPartitionAllocBRPPool(address));
- #endif
- uintptr_t slot_start =
- partition_alloc::PartitionAllocGetSlotStartInBRPPool(address);
- if constexpr (AllowDangling) {
- if (partition_alloc::internal::PartitionRefCountPointer(slot_start)
- ->ReleaseFromUnprotectedPtr())
- partition_alloc::internal::PartitionAllocFreeForRefCounting(slot_start);
- } else {
- if (partition_alloc::internal::PartitionRefCountPointer(slot_start)
- ->Release())
- partition_alloc::internal::PartitionAllocFreeForRefCounting(slot_start);
- }
- }
- template <bool AllowDangling>
- bool BackupRefPtrImpl<AllowDangling>::IsPointeeAlive(uintptr_t address) {
- #if DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)
- CHECK(partition_alloc::IsManagedByPartitionAllocBRPPool(address));
- #endif
- uintptr_t slot_start =
- partition_alloc::PartitionAllocGetSlotStartInBRPPool(address);
- return partition_alloc::internal::PartitionRefCountPointer(slot_start)
- ->IsAlive();
- }
- template <bool AllowDangling>
- bool BackupRefPtrImpl<AllowDangling>::IsValidSignedDelta(
- uintptr_t address,
- ptrdiff_t delta_in_bytes) {
- return partition_alloc::internal::PartitionAllocIsValidPtrDelta(
- address, delta_in_bytes);
- }
- template <bool AllowDangling>
- bool BackupRefPtrImpl<AllowDangling>::IsValidUnsignedDelta(
- uintptr_t address,
- size_t delta_in_bytes) {
- return partition_alloc::internal::PartitionAllocIsValidPtrDelta(
- address, delta_in_bytes);
- }
- // Explicitly instantiates the two BackupRefPtr variants in the .cc. This
- // ensures the definitions not visible from the .h are available in the binary.
- template struct BackupRefPtrImpl</*AllowDangling=*/false>;
- template struct BackupRefPtrImpl</*AllowDangling=*/true>;
- #if DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)
- void CheckThatAddressIsntWithinFirstPartitionPage(uintptr_t address) {
- if (partition_alloc::internal::IsManagedByDirectMap(address)) {
- uintptr_t reservation_start =
- partition_alloc::internal::GetDirectMapReservationStart(address);
- CHECK(address - reservation_start >= partition_alloc::PartitionPageSize());
- } else {
- CHECK(partition_alloc::internal::IsManagedByNormalBuckets(address));
- CHECK(address % partition_alloc::kSuperPageSize >=
- partition_alloc::PartitionPageSize());
- }
- }
- #endif // DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)
- } // namespace base::internal
- #elif BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)
- #include <sanitizer/asan_interface.h>
- #include "base/logging.h"
- #include "base/memory/raw_ptr_asan_service.h"
- namespace base::internal {
- namespace {
- bool IsFreedHeapPointer(void const volatile* ptr) {
- // Use `__asan_region_is_poisoned` instead of `__asan_address_is_poisoned`
- // because the latter may crash on an invalid pointer.
- if (!__asan_region_is_poisoned(const_cast<void*>(ptr), 1))
- return false;
- // Make sure the address is on the heap and is not in a redzone.
- void* region_ptr;
- size_t region_size;
- const char* allocation_type = __asan_locate_address(
- const_cast<void*>(ptr), nullptr, 0, ®ion_ptr, ®ion_size);
- auto address = reinterpret_cast<uintptr_t>(ptr);
- auto region_base = reinterpret_cast<uintptr_t>(region_ptr);
- if (strcmp(allocation_type, "heap") != 0 || address < region_base ||
- address >=
- region_base + region_size) { // We exclude pointers one past the end
- // of an allocations from the analysis
- // for now because they're to fragile.
- return false;
- }
- // Make sure the allocation has been actually freed rather than
- // user-poisoned.
- int free_thread_id = -1;
- __asan_get_free_stack(region_ptr, nullptr, 0, &free_thread_id);
- return free_thread_id != -1;
- }
- // Force a non-optimizable memory load operation to trigger an ASan crash.
- void ForceRead(void const volatile* ptr) {
- auto unused = *reinterpret_cast<char const volatile*>(ptr);
- asm volatile("" : "+r"(unused));
- }
- } // namespace
- NO_SANITIZE("address")
- void AsanBackupRefPtrImpl::AsanCheckIfValidDereference(
- void const volatile* ptr) {
- if (RawPtrAsanService::GetInstance().is_dereference_check_enabled() &&
- IsFreedHeapPointer(ptr)) {
- RawPtrAsanService::SetPendingReport(
- RawPtrAsanService::ReportType::kDereference, ptr);
- ForceRead(ptr);
- }
- }
- NO_SANITIZE("address")
- void AsanBackupRefPtrImpl::AsanCheckIfValidExtraction(
- void const volatile* ptr) {
- auto& service = RawPtrAsanService::GetInstance();
- if ((service.is_extraction_check_enabled() ||
- service.is_dereference_check_enabled()) &&
- IsFreedHeapPointer(ptr)) {
- RawPtrAsanService::SetPendingReport(
- RawPtrAsanService::ReportType::kExtraction, ptr);
- // If the dereference check is enabled, we still record the extraction event
- // to catch the potential subsequent dangling dereference, but don't report
- // the extraction itself.
- if (service.is_extraction_check_enabled()) {
- RawPtrAsanService::Log(
- "=================================================================\n"
- "==%d==WARNING: MiraclePtr: dangling-pointer-extraction on address "
- "%p\n"
- "extracted here:",
- Process::Current().Pid(), ptr);
- __sanitizer_print_stack_trace();
- __asan_describe_address(const_cast<void*>(ptr));
- RawPtrAsanService::Log(
- "A regular ASan report will follow if the extracted pointer is "
- "dereferenced later.\n"
- "Otherwise, it is still likely a bug to rely on the address of an "
- "already freed allocation.\n"
- "Refer to "
- "https://chromium.googlesource.com/chromium/src/+/main/base/memory/"
- "raw_ptr.md for details.\n"
- "=================================================================");
- }
- }
- }
- NO_SANITIZE("address")
- void AsanBackupRefPtrImpl::AsanCheckIfValidInstantiation(
- void const volatile* ptr) {
- if (RawPtrAsanService::GetInstance().is_instantiation_check_enabled() &&
- IsFreedHeapPointer(ptr)) {
- RawPtrAsanService::SetPendingReport(
- RawPtrAsanService::ReportType::kInstantiation, ptr);
- ForceRead(ptr);
- }
- }
- } // namespace base::internal
- #endif // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)
|