1 : // Copyright 2014 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/agent/asan/heaps/large_block_heap.h"
16 :
17 : #include <windows.h>
18 :
19 : #include <algorithm>
20 : #include <iterator>
21 :
22 : #include "base/logging.h"
23 : #include "syzygy/agent/asan/page_protection_helpers.h"
24 : #include "syzygy/common/align.h"
25 :
26 : namespace agent {
27 : namespace asan {
28 : namespace heaps {
29 :
30 : LargeBlockHeap::LargeBlockHeap(MemoryNotifierInterface* memory_notifier,
31 : HeapInterface* internal_heap)
32 E : : allocs_(HeapAllocator<void*>(internal_heap)),
33 E : memory_notifier_(memory_notifier) {
34 E : DCHECK_NE(static_cast<MemoryNotifierInterface*>(nullptr), memory_notifier);
35 E : }
36 :
37 E : LargeBlockHeap::~LargeBlockHeap() {
38 : // No need to lock here, as concurrent access to an object under destruction
39 : // is a programming error.
40 :
41 : // Ideally there shouldn't be any allocations left in the heap (otherwise
42 : // it means that there's a memory leak), but it's not always the case in
43 : // Chrome so we need to release all the resources that we've acquired.
44 E : FreeAllAllocations();
45 :
46 E : CHECK(allocs_.empty());
47 E : }
48 :
49 E : HeapType LargeBlockHeap::GetHeapType() const {
50 E : return kLargeBlockHeap;
51 E : }
52 :
53 E : uint32_t LargeBlockHeap::GetHeapFeatures() const {
54 E : return kHeapSupportsIsAllocated | kHeapSupportsGetAllocationSize |
55 : kHeapReportsReservations;
56 E : }
57 :
58 E : void* LargeBlockHeap::Allocate(uint32_t bytes) {
59 : // Always allocate some memory so as to guarantee that zero-sized
60 : // allocations get an actual distinct address each time.
61 E : size_t size = std::max<size_t>(bytes, 1u);
62 :
63 : // TODO(chrisha): Make this allocate with the OS allocation granularity.
64 E : size = ::common::AlignUp(size, GetPageSize());
65 E : void* alloc = ::VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
66 E : Allocation allocation = { alloc, bytes };
67 :
68 E : if (alloc != NULL) {
69 E : ::common::AutoRecursiveLock lock(lock_);
70 :
71 E : bool inserted = allocs_.insert(allocation).second;
72 E : DCHECK(inserted);
73 E : }
74 :
75 E : memory_notifier_->NotifyFutureHeapUse(alloc, size);
76 E : return alloc;
77 E : }
78 :
79 E : bool LargeBlockHeap::Free(void* alloc) {
80 E : Allocation allocation = { alloc, 0 };
81 :
82 E : size_t size = 0;
83 : {
84 : // First lookup the allocation to ensure it was made by us.
85 E : ::common::AutoRecursiveLock lock(lock_);
86 E : AllocationSet::iterator it = allocs_.find(allocation);
87 E : if (it == allocs_.end())
88 i : return false;
89 E : size = it->size;
90 E : allocs_.erase(it);
91 E : }
92 :
93 : // Notify the OS that this memory has been returned.
94 E : memory_notifier_->NotifyReturnedToOS(alloc, size);
95 E : ::VirtualFree(alloc, 0, MEM_RELEASE);
96 E : return true;
97 E : }
98 :
99 E : bool LargeBlockHeap::IsAllocated(const void* alloc) {
100 E : Allocation allocation = { alloc, 0 };
101 :
102 : {
103 E : ::common::AutoRecursiveLock lock(lock_);
104 E : AllocationSet::iterator it = allocs_.find(allocation);
105 E : if (it == allocs_.end())
106 E : return false;
107 E : }
108 :
109 E : return true;
110 E : }
111 :
112 E : uint32_t LargeBlockHeap::GetAllocationSize(const void* alloc) {
113 E : Allocation allocation = { alloc, 0 };
114 :
115 : {
116 E : ::common::AutoRecursiveLock lock(lock_);
117 E : AllocationSet::iterator it = allocs_.find(allocation);
118 E : if (it == allocs_.end())
119 i : return kUnknownSize;
120 E : return it->size;
121 : }
122 E : }
123 :
124 E : void LargeBlockHeap::Lock() {
125 E : lock_.Acquire();
126 E : }
127 :
128 E : void LargeBlockHeap::Unlock() {
129 E : lock_.Release();
130 E : }
131 :
132 E : bool LargeBlockHeap::TryLock() {
133 E : return lock_.Try();
134 E : }
135 :
136 : void* LargeBlockHeap::AllocateBlock(uint32_t size,
137 : uint32_t min_left_redzone_size,
138 : uint32_t min_right_redzone_size,
139 E : BlockLayout* layout) {
140 E : DCHECK_NE(static_cast<BlockLayout*>(nullptr), layout);
141 :
142 : // Plan the layout with full guard pages.
143 E : const uint32_t kPageSize = static_cast<uint32_t>(GetPageSize());
144 E : if (!BlockPlanLayout(kPageSize, kPageSize, size, kPageSize, kPageSize,
145 : layout)) {
146 E : return nullptr;
147 : }
148 E : DCHECK_EQ(0u, layout->block_size % kPageSize);
149 :
150 E : return Allocate(layout->block_size);
151 E : }
152 :
153 E : bool LargeBlockHeap::FreeBlock(const BlockInfo& block_info) {
154 E : DCHECK_NE(static_cast<BlockHeader*>(nullptr), block_info.header);
155 E : return Free(block_info.header);
156 E : }
157 :
158 E : void LargeBlockHeap::FreeAllAllocations() {
159 : // Start by copying the blocks into a temporary vector as the call to |Free|
160 : // will remove them from |allocs_|.
161 E : std::vector<Allocation> allocs_to_free;
162 E : std::copy(allocs_.begin(), allocs_.end(), std::back_inserter(allocs_to_free));
163 E : for (const auto& alloc : allocs_to_free)
164 E : CHECK(Free(const_cast<void*>(alloc.address)));
165 E : }
166 :
167 : } // namespace heaps
168 : } // namespace asan
169 : } // namespace agent
|