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/heap_checker.h"
16 :
17 : #include "base/rand_util.h"
18 : #include "gtest/gtest.h"
19 : #include "syzygy/agent/asan/logger.h"
20 : #include "syzygy/agent/asan/page_protection_helpers.h"
21 : #include "syzygy/agent/asan/runtime.h"
22 : #include "syzygy/agent/asan/unittest_util.h"
23 :
24 : namespace agent {
25 : namespace asan {
26 :
27 : namespace {
28 :
29 : typedef public testing::TestWithAsanRuntime HeapCheckerTest;
30 :
31 : using testing::FakeAsanBlock;
32 :
33 : } // namespace
34 :
35 E : TEST_F(HeapCheckerTest, HeapCheckerHandlesPageProtections) {
36 : // Make a large allocation bigger than a couple pages. This will ensure
37 : // that its big enough to have page protections. The HeapChecker will have
38 : // to unset these in order to do its work successfully. Otherwise it will
39 : // cause an access violation.
40 : FakeAsanBlock fake_large_block(
41 E : runtime_->shadow(), kShadowRatioLog, runtime_->stack_cache());
42 E : fake_large_block.InitializeBlock(2 * GetPageSize());
43 E : base::RandBytes(fake_large_block.block_info.body, 2 * GetPageSize());
44 E : fake_large_block.MarkBlockAsQuarantined();
45 E : BlockProtectAll(fake_large_block.block_info, runtime_->shadow());
46 :
47 E : HeapChecker heap_checker(runtime_->shadow());
48 E : HeapChecker::CorruptRangesVector corrupt_ranges;
49 E : EXPECT_FALSE(heap_checker.IsHeapCorrupt(&corrupt_ranges));
50 :
51 E : BlockProtectNone(fake_large_block.block_info, runtime_->shadow());
52 E : }
53 :
54 E : TEST_F(HeapCheckerTest, IsHeapCorruptInvalidChecksum) {
55 E : const size_t kAllocSize = 100;
56 : FakeAsanBlock fake_block(
57 E : runtime_->shadow(), kShadowRatioLog, runtime_->stack_cache());
58 :
59 E : fake_block.InitializeBlock(kAllocSize);
60 E : base::RandBytes(fake_block.block_info.body, kAllocSize);
61 :
62 E : HeapChecker heap_checker(runtime_->shadow());
63 E : HeapChecker::CorruptRangesVector corrupt_ranges;
64 E : EXPECT_FALSE(heap_checker.IsHeapCorrupt(&corrupt_ranges));
65 :
66 : // Free the block and corrupt its data.
67 E : ASSERT_TRUE(fake_block.MarkBlockAsQuarantined());
68 E : size_t header_checksum = fake_block.block_info.header->checksum;
69 :
70 : // Corrupt the data in such a way that we can guarantee no hash collision.
71 E : const size_t kMaxIterations = 10;
72 E : size_t iteration = 0;
73 E : uint8 original_value = fake_block.block_info.RawBody(0);
74 : do {
75 E : fake_block.block_info.RawBody(0)++;
76 E : BlockSetChecksum(fake_block.block_info);
77 : } while (fake_block.block_info.header->checksum == header_checksum &&
78 E : iteration++ < kMaxIterations);
79 :
80 : // Restore the checksum to make sure that the corruption gets detected.
81 E : fake_block.block_info.header->checksum = header_checksum;
82 :
83 E : EXPECT_TRUE(heap_checker.IsHeapCorrupt(&corrupt_ranges));
84 E : ASSERT_EQ(1, corrupt_ranges.size());
85 E : AsanCorruptBlockRange range_info = *corrupt_ranges.begin();
86 :
87 E : EXPECT_EQ(1, range_info.block_count);
88 : ShadowWalker shadow_walker(
89 : runtime_->shadow(),
90 : false,
91 : reinterpret_cast<const uint8*>(range_info.address),
92 E : reinterpret_cast<const uint8*>(range_info.address) + range_info.length);
93 E : BlockInfo block_info = {};
94 E : EXPECT_TRUE(shadow_walker.Next(&block_info));
95 E : EXPECT_EQ(fake_block.block_info.header, block_info.header);
96 E : EXPECT_FALSE(shadow_walker.Next(&block_info));
97 :
98 E : fake_block.block_info.header->checksum = header_checksum;
99 E : fake_block.block_info.RawBody(0) = original_value;
100 E : EXPECT_FALSE(heap_checker.IsHeapCorrupt(&corrupt_ranges));
101 E : }
102 :
103 E : TEST_F(HeapCheckerTest, IsHeapCorruptInvalidMagicNumber) {
104 E : const size_t kAllocSize = 100;
105 : FakeAsanBlock fake_block(
106 E : runtime_->shadow(), kShadowRatioLog, runtime_->stack_cache());
107 :
108 E : fake_block.InitializeBlock(kAllocSize);
109 E : base::RandBytes(fake_block.block_info.body, kAllocSize);
110 :
111 E : HeapChecker heap_checker(runtime_->shadow());
112 E : HeapChecker::CorruptRangesVector corrupt_ranges;
113 E : EXPECT_FALSE(heap_checker.IsHeapCorrupt(&corrupt_ranges));
114 :
115 : // Corrupt the header of the block and ensure that the heap corruption gets
116 : // detected.
117 E : fake_block.block_info.header->magic = ~fake_block.block_info.header->magic;
118 E : EXPECT_TRUE(heap_checker.IsHeapCorrupt(&corrupt_ranges));
119 E : ASSERT_EQ(1, corrupt_ranges.size());
120 E : AsanCorruptBlockRange range_info = *corrupt_ranges.begin();
121 :
122 E : EXPECT_EQ(1, range_info.block_count);
123 : ShadowWalker shadow_walker(
124 : runtime_->shadow(),
125 : false,
126 : reinterpret_cast<const uint8*>(range_info.address),
127 E : reinterpret_cast<const uint8*>(range_info.address) + range_info.length);
128 E : BlockInfo block_info = {};
129 E : EXPECT_TRUE(shadow_walker.Next(&block_info));
130 E : EXPECT_EQ(fake_block.block_info.header, block_info.header);
131 E : EXPECT_FALSE(shadow_walker.Next(&block_info));
132 :
133 E : fake_block.block_info.header->magic = ~fake_block.block_info.header->magic;
134 E : EXPECT_FALSE(heap_checker.IsHeapCorrupt(&corrupt_ranges));
135 E : }
136 :
137 E : TEST_F(HeapCheckerTest, IsHeapCorrupt) {
138 E : const size_t kAllocSize = 100;
139 :
140 E : BlockLayout block_layout = {};
141 : EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, kAllocSize, 0, 0,
142 E : &block_layout));
143 :
144 E : const size_t kNumberOfBlocks = 4;
145 E : size_t total_alloc_size = block_layout.block_size * kNumberOfBlocks;
146 E : uint8* global_alloc = reinterpret_cast<uint8*>(::malloc(total_alloc_size));
147 :
148 : uint8* blocks[kNumberOfBlocks];
149 : BlockHeader* block_headers[kNumberOfBlocks];
150 :
151 E : for (size_t i = 0; i < kNumberOfBlocks; ++i) {
152 E : blocks[i] = global_alloc + i * block_layout.block_size;
153 E : BlockInfo block_info = {};
154 E : BlockInitialize(block_layout, blocks[i], false, &block_info);
155 E : runtime_->shadow()->PoisonAllocatedBlock(block_info);
156 E : BlockSetChecksum(block_info);
157 E : block_headers[i] = block_info.header;
158 E : EXPECT_EQ(block_headers[i], reinterpret_cast<BlockHeader*>(blocks[i]));
159 E : }
160 :
161 E : HeapChecker heap_checker(runtime_->shadow());
162 E : HeapChecker::CorruptRangesVector corrupt_ranges;
163 E : EXPECT_FALSE(heap_checker.IsHeapCorrupt(&corrupt_ranges));
164 :
165 : // Corrupt the header of the first two blocks and of the last one.
166 E : block_headers[0]->magic++;
167 E : block_headers[1]->magic++;
168 E : block_headers[kNumberOfBlocks - 1]->magic++;
169 :
170 E : EXPECT_TRUE(heap_checker.IsHeapCorrupt(&corrupt_ranges));
171 :
172 : // We expect the heap to contain 2 ranges of corrupt blocks, the first one
173 : // containing the 2 first blocks and the second one containing the last block.
174 :
175 E : EXPECT_EQ(2, corrupt_ranges.size());
176 :
177 E : BlockInfo block_info = {};
178 : ShadowWalker shadow_walker_1(
179 : runtime_->shadow(),
180 : false,
181 : reinterpret_cast<const uint8*>(corrupt_ranges[0].address),
182 : reinterpret_cast<const uint8*>(corrupt_ranges[0].address) +
183 E : corrupt_ranges[0].length);
184 E : EXPECT_TRUE(shadow_walker_1.Next(&block_info));
185 : EXPECT_EQ(reinterpret_cast<const BlockHeader*>(block_info.header),
186 E : block_headers[0]);
187 E : EXPECT_TRUE(shadow_walker_1.Next(&block_info));
188 : EXPECT_EQ(reinterpret_cast<const BlockHeader*>(block_info.header),
189 E : block_headers[1]);
190 E : EXPECT_FALSE(shadow_walker_1.Next(&block_info));
191 :
192 : ShadowWalker shadow_walker_2(
193 : runtime_->shadow(),
194 : false,
195 : reinterpret_cast<const uint8*>(corrupt_ranges[1].address),
196 : reinterpret_cast<const uint8*>(corrupt_ranges[1].address) +
197 E : corrupt_ranges[1].length);
198 E : EXPECT_TRUE(shadow_walker_2.Next(&block_info));
199 : EXPECT_EQ(reinterpret_cast<const BlockHeader*>(block_info.header),
200 E : block_headers[kNumberOfBlocks - 1]);
201 E : EXPECT_FALSE(shadow_walker_2.Next(&block_info));
202 :
203 : // Restore the checksum of the blocks.
204 E : block_headers[0]->magic--;
205 E : block_headers[1]->magic--;
206 E : block_headers[kNumberOfBlocks - 1]->magic--;
207 :
208 E : runtime_->shadow()->Unpoison(global_alloc, total_alloc_size);
209 E : ::free(global_alloc);
210 E : }
211 :
212 : } // namespace asan
213 : } // namespace agent
|