1 : // Copyright 2012 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/asan_heap.h"
16 :
17 : #include "base/rand_util.h"
18 : #include "base/sha1.h"
19 : #include "gtest/gtest.h"
20 : #include "syzygy/agent/asan/asan_logger.h"
21 : #include "syzygy/agent/asan/asan_shadow.h"
22 : #include "syzygy/agent/asan/unittest_util.h"
23 :
24 : namespace agent {
25 : namespace asan {
26 :
27 : namespace {
28 :
29 : // A derived class to expose protected members for unit-testing.
30 : class TestHeapProxy : public HeapProxy {
31 : public:
32 : using HeapProxy::BlockHeader;
33 : using HeapProxy::FindAddressBlock;
34 : using HeapProxy::FreeBlockHeader;
35 : using HeapProxy::GetAllocSize;
36 : using HeapProxy::GetBadAccessKind;
37 : using HeapProxy::ToBlock;
38 :
39 E : explicit TestHeapProxy(StackCaptureCache* stack_cache, AsanLogger* logger)
40 : : HeapProxy(stack_cache, logger) {
41 E : }
42 :
43 : // Verify that the access to @p addr contained in @p header is an underflow.
44 E : bool IsUnderflowAccess(uint8* addr, BlockHeader* header) {
45 E : return GetBadAccessKind(addr, header) == HEAP_BUFFER_UNDERFLOW;
46 E : }
47 :
48 : // Verify that the access to @p addr contained in @p header is an overflow.
49 E : bool IsOverflowAccess(uint8* addr, BlockHeader* header) {
50 E : return GetBadAccessKind(addr, header) == HEAP_BUFFER_OVERFLOW;
51 E : }
52 :
53 : // Verify that the access to @p addr contained in @p header is an use after
54 : // free.
55 E : bool IsUseAfterAccess(uint8* addr, BlockHeader* header) {
56 E : return GetBadAccessKind(addr, header) == USE_AFTER_FREE;
57 E : }
58 :
59 : // Determines if the address @p mem corresponds to a block in quarantine.
60 E : bool InQuarantine(const void* mem) {
61 E : base::AutoLock lock(lock_);
62 E : FreeBlockHeader* current_block = head_;
63 E : while (current_block != NULL) {
64 E : void* block_alloc = static_cast<void*>(ToAlloc(current_block));
65 E : EXPECT_TRUE(block_alloc != NULL);
66 E : if (block_alloc == mem)
67 E : return true;
68 E : current_block = current_block->next;
69 E : }
70 E : return false;
71 E : }
72 : };
73 :
74 : class HeapTest : public testing::TestWithAsanLogger {
75 : public:
76 E : HeapTest() : stack_cache_(&logger_), proxy_(&stack_cache_, &logger_) {
77 E : }
78 :
79 E : virtual void SetUp() OVERRIDE {
80 E : testing::TestWithAsanLogger::SetUp();
81 :
82 E : logger_.set_instance_id(instance_id());
83 E : logger_.Init();
84 E : ASSERT_TRUE(proxy_.Create(0, 0, 0));
85 E : }
86 :
87 E : virtual void TearDown() OVERRIDE {
88 E : ASSERT_TRUE(proxy_.Destroy());
89 E : testing::TestWithAsanLogger::TearDown();
90 E : }
91 :
92 : // Verifies that [alloc, alloc + size) is accessible, and that
93 : // [alloc - 1] and [alloc+size] are poisoned.
94 E : void VerifyAllocAccess(void* alloc, size_t size) {
95 E : uint8* mem = reinterpret_cast<uint8*>(alloc);
96 E : ASSERT_FALSE(Shadow::IsAccessible(mem - 1));
97 E : for (size_t i = 0; i < size; ++i)
98 E : ASSERT_TRUE(Shadow::IsAccessible(mem + i));
99 E : ASSERT_FALSE(Shadow::IsAccessible(mem + size));
100 E : }
101 :
102 : // Verifies that [alloc-1, alloc+size] is poisoned.
103 E : void VerifyFreedAccess(void* alloc, size_t size) {
104 E : uint8* mem = reinterpret_cast<uint8*>(alloc);
105 E : ASSERT_FALSE(Shadow::IsAccessible(mem - 1));
106 E : for (size_t i = 0; i < size; ++i)
107 E : ASSERT_FALSE(Shadow::IsAccessible(mem + i));
108 E : ASSERT_FALSE(Shadow::IsAccessible(mem + size));
109 E : }
110 :
111 E : void RandomSetMemory(void* alloc, size_t size) {
112 E : base::RandBytes(alloc, size);
113 E : }
114 :
115 : protected:
116 : // Arbitrary constant for all size limit.
117 : static const size_t kMaxAllocSize = 134584;
118 :
119 : AsanLogger logger_;
120 : StackCaptureCache stack_cache_;
121 : TestHeapProxy proxy_;
122 : };
123 :
124 : } // namespace
125 :
126 E : TEST_F(HeapTest, ToFromHandle) {
127 E : HANDLE handle = HeapProxy::ToHandle(&proxy_);
128 E : ASSERT_TRUE(handle != NULL);
129 E : ASSERT_EQ(&proxy_, HeapProxy::FromHandle(handle));
130 E : }
131 :
132 E : TEST_F(HeapTest, SetQuarantineMaxSize) {
133 E : size_t quarantine_size = proxy_.quarantine_max_size() * 2;
134 : // Increments the quarantine max size if it was set to 0.
135 E : if (quarantine_size == 0)
136 i : quarantine_size++;
137 E : DCHECK_GT(quarantine_size, 0U);
138 E : proxy_.set_quarantine_max_size(quarantine_size);
139 E : ASSERT_EQ(quarantine_size, proxy_.quarantine_max_size());
140 E : }
141 :
142 E : TEST_F(HeapTest, PopOnSetQuarantineMaxSize) {
143 E : const size_t kAllocSize = 100;
144 E : const size_t real_alloc_size = TestHeapProxy::GetAllocSize(kAllocSize);
145 E : LPVOID mem = proxy_.Alloc(0, kAllocSize);
146 E : ASSERT_FALSE(proxy_.InQuarantine(mem));
147 E : proxy_.set_quarantine_max_size(real_alloc_size);
148 E : ASSERT_TRUE(proxy_.Free(0, mem));
149 : // The quarantine is just large enough to keep this block.
150 E : ASSERT_TRUE(proxy_.InQuarantine(mem));
151 : // We resize the quarantine to a smaller size, the block should pop out.
152 E : proxy_.set_quarantine_max_size(real_alloc_size - 1);
153 E : ASSERT_FALSE(proxy_.InQuarantine(mem));
154 E : }
155 :
156 E : TEST_F(HeapTest, Quarantine) {
157 E : const size_t kAllocSize = 100;
158 E : const size_t real_alloc_size = TestHeapProxy::GetAllocSize(kAllocSize);
159 E : const size_t number_of_allocs = 16;
160 E : proxy_.set_quarantine_max_size(real_alloc_size * number_of_allocs);
161 :
162 E : LPVOID mem = proxy_.Alloc(0, kAllocSize);
163 E : ASSERT_TRUE(mem != NULL);
164 E : ASSERT_TRUE(proxy_.Free(0, mem));
165 : // Allocate a bunch of blocks until the first one is pushed out of the
166 : // quarantine.
167 E : for (size_t i = 0;i < number_of_allocs; ++i) {
168 E : ASSERT_TRUE(proxy_.InQuarantine(mem));
169 E : LPVOID mem2 = proxy_.Alloc(0, kAllocSize);
170 E : ASSERT_TRUE(mem2 != NULL);
171 E : ASSERT_TRUE(proxy_.Free(0, mem2));
172 E : ASSERT_TRUE(proxy_.InQuarantine(mem2));
173 E : }
174 :
175 E : ASSERT_FALSE(proxy_.InQuarantine(mem));
176 E : }
177 :
178 E : TEST_F(HeapTest, AllocFree) {
179 E : const size_t kAllocSize = 100;
180 E : LPVOID mem = proxy_.Alloc(0, kAllocSize);
181 E : ASSERT_TRUE(mem != NULL);
182 E : ASSERT_EQ(kAllocSize, proxy_.Size(0, mem));
183 E : const size_t kReAllocSize = 2 * kAllocSize;
184 E : mem = proxy_.ReAlloc(0, mem, kReAllocSize);
185 E : ASSERT_EQ(kReAllocSize, proxy_.Size(0, mem));
186 E : ASSERT_TRUE(proxy_.Free(0, mem));
187 E : }
188 :
189 E : TEST_F(HeapTest, DoubleFree) {
190 E : const size_t kAllocSize = 100;
191 : // Ensure that the quarantine is large enough to keep this block, this is
192 : // needed for the use-after-free check.
193 E : proxy_.set_quarantine_max_size(TestHeapProxy::GetAllocSize(kAllocSize));
194 E : LPVOID mem = proxy_.Alloc(0, kAllocSize);
195 E : ASSERT_TRUE(mem != NULL);
196 E : ASSERT_TRUE(proxy_.Free(0, mem));
197 E : ASSERT_FALSE(proxy_.Free(0, mem));
198 E : ASSERT_TRUE(LogContains("double-free"));
199 E : }
200 :
201 E : TEST_F(HeapTest, AllocsAccessibility) {
202 : // Ensure that the quarantine is large enough to keep the allocated blocks in
203 : // this test.
204 E : proxy_.set_quarantine_max_size(kMaxAllocSize * 2);
205 E : for (size_t size = 10; size < kMaxAllocSize; size = size * 5 + 123) {
206 : // Do an alloc/realloc/free and test that access is correctly managed.
207 E : void* mem = proxy_.Alloc(0, size);
208 E : ASSERT_TRUE(mem != NULL);
209 E : ASSERT_NO_FATAL_FAILURE(VerifyAllocAccess(mem, size));
210 E : RandomSetMemory(mem, size);
211 :
212 E : size_t new_size = size;
213 E : while (new_size == size)
214 E : new_size = base::RandInt(size / 2, size * 2);
215 :
216 E : unsigned char sha1_before[base::kSHA1Length] = {};
217 : base::SHA1HashBytes(reinterpret_cast<unsigned char*>(mem),
218 : std::min(size, new_size),
219 E : sha1_before);
220 :
221 E : void* new_mem = proxy_.ReAlloc(0, mem, size * 2);
222 E : ASSERT_TRUE(new_mem != NULL);
223 E : ASSERT_NE(mem, new_mem);
224 :
225 E : unsigned char sha1_after[base::kSHA1Length] = {};
226 : base::SHA1HashBytes(reinterpret_cast<unsigned char*>(new_mem),
227 : std::min(size, new_size),
228 E : sha1_after);
229 E : ASSERT_EQ(0, memcmp(sha1_before, sha1_after, base::kSHA1Length));
230 :
231 E : ASSERT_NO_FATAL_FAILURE(VerifyFreedAccess(mem, size));
232 E : ASSERT_NO_FATAL_FAILURE(VerifyAllocAccess(new_mem, size * 2));
233 :
234 E : ASSERT_TRUE(proxy_.Free(0, new_mem));
235 E : ASSERT_NO_FATAL_FAILURE(VerifyFreedAccess(new_mem, size * 2));
236 E : }
237 E : }
238 :
239 E : TEST_F(HeapTest, Size) {
240 E : for (size_t size = 10; size < kMaxAllocSize; size = size * 5 + 123) {
241 E : void* mem = proxy_.Alloc(0, size);
242 E : ASSERT_FALSE(mem == NULL);
243 E : ASSERT_EQ(size, proxy_.Size(0, mem));
244 E : ASSERT_TRUE(proxy_.Free(0, mem));
245 E : }
246 E : }
247 :
248 E : TEST_F(HeapTest, Validate) {
249 E : for (size_t size = 10; size < kMaxAllocSize; size = size * 5 + 123) {
250 E : void* mem = proxy_.Alloc(0, size);
251 E : ASSERT_FALSE(mem == NULL);
252 E : ASSERT_TRUE(proxy_.Validate(0, mem));
253 E : ASSERT_TRUE(proxy_.Free(0, mem));
254 E : }
255 E : }
256 :
257 E : TEST_F(HeapTest, Compact) {
258 : // Compact should return a non-zero size.
259 E : ASSERT_LT(0U, proxy_.Compact(0));
260 :
261 : // TODO(siggi): It may not be possible to allocate the size returned due
262 : // to padding - fix and test.
263 E : }
264 :
265 E : TEST_F(HeapTest, LockUnlock) {
266 : // We can't really test these, aside from not crashing.
267 E : ASSERT_TRUE(proxy_.Lock());
268 E : ASSERT_TRUE(proxy_.Unlock());
269 E : }
270 :
271 E : TEST_F(HeapTest, Walk) {
272 : // We assume at least two entries to walk through.
273 E : PROCESS_HEAP_ENTRY entry = {};
274 E : ASSERT_TRUE(proxy_.Walk(&entry));
275 E : ASSERT_TRUE(proxy_.Walk(&entry));
276 E : }
277 :
278 E : TEST_F(HeapTest, SetQueryInformation) {
279 E : ULONG compat_flag = -1;
280 E : unsigned long ret = 0;
281 : // Get the current value of the compat flag.
282 : ASSERT_TRUE(
283 : proxy_.QueryInformation(HeapCompatibilityInformation,
284 E : &compat_flag, sizeof(compat_flag), &ret));
285 E : ASSERT_EQ(sizeof(compat_flag), ret);
286 E : ASSERT_TRUE(compat_flag != -1);
287 :
288 : // Put the heap in LFH, which should always succeed, except when a debugger
289 : // is attached. When a debugger is attached, the heap is wedged in certain
290 : // debug settings.
291 E : if (base::debug::BeingDebugged()) {
292 i : LOG(WARNING) << "Can't test HeapProxy::SetInformation under debugger.";
293 i : return;
294 : }
295 :
296 E : compat_flag = 2;
297 : ASSERT_TRUE(
298 : proxy_.SetInformation(HeapCompatibilityInformation,
299 E : &compat_flag, sizeof(compat_flag)));
300 E : }
301 :
302 E : TEST_F(HeapTest, FindAddressBlock) {
303 E : const size_t kAllocSize = 100;
304 E : void* mem = proxy_.Alloc(0, kAllocSize);
305 E : ASSERT_FALSE(mem == NULL);
306 E : ASSERT_FALSE(proxy_.FindAddressBlock(static_cast<const uint8*>(mem)) == NULL);
307 : uint8* out_of_bounds_address =
308 E : static_cast<uint8*>(mem) + kAllocSize * 2;
309 E : ASSERT_TRUE(proxy_.FindAddressBlock(out_of_bounds_address) == NULL);
310 E : ASSERT_TRUE(proxy_.Free(0, mem));
311 E : }
312 :
313 E : TEST_F(HeapTest, GetBadAccessKind) {
314 E : const size_t kAllocSize = 100;
315 : // Ensure that the quarantine is large enough to keep this block, this is
316 : // needed for the use-after-free check.
317 E : proxy_.set_quarantine_max_size(TestHeapProxy::GetAllocSize(kAllocSize));
318 E : uint8* mem = static_cast<uint8*>(proxy_.Alloc(0, kAllocSize));
319 E : ASSERT_FALSE(mem == NULL);
320 : TestHeapProxy::BlockHeader* header =
321 E : const_cast<TestHeapProxy::BlockHeader*>(proxy_.ToBlock(mem));
322 E : uint8* heap_underflow_address = mem - 1;
323 E : uint8* heap_overflow_address = mem + kAllocSize * sizeof(uint8);
324 E : ASSERT_TRUE(proxy_.IsUnderflowAccess(heap_underflow_address, header));
325 E : ASSERT_TRUE(proxy_.IsOverflowAccess(heap_overflow_address, header));
326 E : ASSERT_TRUE(proxy_.Free(0, mem));
327 E : ASSERT_TRUE(proxy_.IsUseAfterAccess(mem, header));
328 E : }
329 :
330 : } // namespace asan
331 : } // namespace agent
|