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)23 void* 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)123 bool 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