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