1 // Copyright 2022 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "partition_alloc/gwp_asan_support.h" 6 7 #if BUILDFLAG(ENABLE_GWP_ASAN_SUPPORT) 8 9 #include "build/build_config.h" 10 #include "partition_alloc/freeslot_bitmap_constants.h" 11 #include "partition_alloc/in_slot_metadata.h" 12 #include "partition_alloc/page_allocator_constants.h" 13 #include "partition_alloc/partition_alloc_base/no_destructor.h" 14 #include "partition_alloc/partition_alloc_check.h" 15 #include "partition_alloc/partition_bucket.h" 16 #include "partition_alloc/partition_lock.h" 17 #include "partition_alloc/partition_page.h" 18 #include "partition_alloc/partition_root.h" 19 20 namespace partition_alloc { 21 22 // static MapRegion(size_t slot_count,std::vector<uint16_t> & free_list)23void* GwpAsanSupport::MapRegion(size_t slot_count, 24 std::vector<uint16_t>& free_list) { 25 PA_CHECK(slot_count > 0); 26 27 constexpr PartitionOptions kConfig = []() { 28 PartitionOptions opts; 29 opts.backup_ref_ptr = PartitionOptions::kEnabled; 30 return opts; 31 }(); 32 static internal::base::NoDestructor<PartitionRoot> root(kConfig); 33 34 const size_t kSlotSize = 2 * internal::SystemPageSize(); 35 uint16_t bucket_index = PartitionRoot::SizeToBucketIndex( 36 kSlotSize, root->GetBucketDistribution()); 37 auto* bucket = root->buckets + bucket_index; 38 39 const size_t kSuperPagePayloadStartOffset = 40 internal::SuperPagePayloadStartOffset( 41 /* is_managed_by_normal_buckets = */ true, 42 /* with_quarantine = */ false); 43 PA_CHECK(kSuperPagePayloadStartOffset % kSlotSize == 0); 44 const size_t kSuperPageGwpAsanSlotAreaBeginOffset = 45 kSuperPagePayloadStartOffset; 46 const size_t kSuperPageGwpAsanSlotAreaEndOffset = 47 internal::SuperPagePayloadEndOffset(); 48 const size_t kSuperPageGwpAsanSlotAreaSize = 49 kSuperPageGwpAsanSlotAreaEndOffset - kSuperPageGwpAsanSlotAreaBeginOffset; 50 const size_t kSlotsPerSlotSpan = bucket->get_bytes_per_span() / kSlotSize; 51 const size_t kSlotsPerSuperPage = 52 kSuperPageGwpAsanSlotAreaSize / (kSlotsPerSlotSpan * kSlotSize); 53 54 size_t super_page_count = 1 + ((slot_count - 1) / kSlotsPerSuperPage); 55 PA_CHECK(super_page_count <= 56 std::numeric_limits<size_t>::max() / kSuperPageSize); 57 uintptr_t super_page_span_start; 58 { 59 internal::ScopedGuard locker{internal::PartitionRootLock(root.get())}; 60 super_page_span_start = bucket->AllocNewSuperPageSpanForGwpAsan( 61 root.get(), super_page_count, AllocFlags::kNone); 62 63 if (!super_page_span_start) { 64 return nullptr; 65 } 66 67 #if defined(ARCH_CPU_64_BITS) 68 // Mapping the GWP-ASan region in to the lower 32-bits of address space 69 // makes it much more likely that a bad pointer dereference points into 70 // our region and triggers a false positive report. We rely on the fact 71 // that PA address pools are never allocated in the first 4GB due to 72 // their alignment requirements. 73 PA_CHECK(super_page_span_start >= (1ULL << 32)); 74 #endif // defined(ARCH_CPU_64_BITS) 75 76 uintptr_t super_page_span_end = 77 super_page_span_start + super_page_count * kSuperPageSize; 78 PA_CHECK(super_page_span_start < super_page_span_end); 79 80 for (uintptr_t super_page = super_page_span_start; 81 super_page < super_page_span_end; super_page += kSuperPageSize) { 82 auto* page_metadata = 83 internal::PartitionSuperPageToMetadataArea(super_page); 84 85 // Index 0 is invalid because it is the super page extent metadata. 86 for (size_t partition_page_idx = 87 1 + internal::NumPartitionPagesPerFreeSlotBitmap(); 88 partition_page_idx + bucket->get_pages_per_slot_span() < 89 internal::NumPartitionPagesPerSuperPage(); 90 partition_page_idx += bucket->get_pages_per_slot_span()) { 91 auto* slot_span_metadata = 92 &page_metadata[partition_page_idx].slot_span_metadata; 93 bucket->InitializeSlotSpanForGwpAsan(slot_span_metadata); 94 auto slot_span_start = 95 internal::SlotSpanMetadata::ToSlotSpanStart(slot_span_metadata); 96 97 for (uintptr_t slot_idx = 0; slot_idx < kSlotsPerSlotSpan; ++slot_idx) { 98 auto slot_start = slot_span_start + slot_idx * kSlotSize; 99 PartitionRoot::InSlotMetadataPointerFromSlotStartAndSize(slot_start, 100 kSlotSize) 101 ->InitalizeForGwpAsan(); 102 size_t global_slot_idx = (slot_start - super_page_span_start - 103 kSuperPageGwpAsanSlotAreaBeginOffset) / 104 kSlotSize; 105 PA_DCHECK(global_slot_idx < std::numeric_limits<uint16_t>::max()); 106 free_list.push_back(global_slot_idx); 107 if (free_list.size() == slot_count) { 108 return reinterpret_cast<void*>( 109 super_page_span_start + kSuperPageGwpAsanSlotAreaBeginOffset - 110 internal::SystemPageSize()); // Depends on the PA guard region 111 // in front of the super page 112 // payload area. 113 } 114 } 115 } 116 } 117 } 118 119 PA_NOTREACHED(); 120 } 121 122 // static CanReuse(uintptr_t slot_start)123bool GwpAsanSupport::CanReuse(uintptr_t slot_start) { 124 const size_t kSlotSize = 2 * internal::SystemPageSize(); 125 return PartitionRoot::InSlotMetadataPointerFromSlotStartAndSize(slot_start, 126 kSlotSize) 127 ->CanBeReusedByGwpAsan(); 128 } 129 130 } // namespace partition_alloc 131 132 #endif // BUILDFLAG(ENABLE_GWP_ASAN_SUPPORT) 133