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 E : FakeAsanBlock fake_large_block(
41 : 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 E : FakeAsanBlock fake_block(
57 : 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_t 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 E : } while (fake_block.block_info.header->checksum == header_checksum &&
78 : 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 E : ShadowWalker shadow_walker(
89 : runtime_->shadow(), false,
90 : reinterpret_cast<const uint8_t*>(range_info.address),
91 : reinterpret_cast<const uint8_t*>(range_info.address) + range_info.length);
92 E : BlockInfo block_info = {};
93 E : EXPECT_TRUE(shadow_walker.Next(&block_info));
94 E : EXPECT_EQ(fake_block.block_info.header, block_info.header);
95 E : EXPECT_FALSE(shadow_walker.Next(&block_info));
96 :
97 E : fake_block.block_info.header->checksum = header_checksum;
98 E : fake_block.block_info.RawBody(0) = original_value;
99 E : EXPECT_FALSE(heap_checker.IsHeapCorrupt(&corrupt_ranges));
100 E : }
101 :
102 E : TEST_F(HeapCheckerTest, IsHeapCorruptInvalidMagicNumber) {
103 E : const size_t kAllocSize = 100;
104 E : FakeAsanBlock fake_block(
105 : runtime_->shadow(), kShadowRatioLog, runtime_->stack_cache());
106 :
107 E : fake_block.InitializeBlock(kAllocSize);
108 E : base::RandBytes(fake_block.block_info.body, kAllocSize);
109 :
110 E : HeapChecker heap_checker(runtime_->shadow());
111 E : HeapChecker::CorruptRangesVector corrupt_ranges;
112 E : EXPECT_FALSE(heap_checker.IsHeapCorrupt(&corrupt_ranges));
113 :
114 : // Corrupt the header of the block and ensure that the heap corruption gets
115 : // detected.
116 E : fake_block.block_info.header->magic = ~fake_block.block_info.header->magic;
117 E : EXPECT_TRUE(heap_checker.IsHeapCorrupt(&corrupt_ranges));
118 E : ASSERT_EQ(1, corrupt_ranges.size());
119 E : AsanCorruptBlockRange range_info = *corrupt_ranges.begin();
120 :
121 E : EXPECT_EQ(1, range_info.block_count);
122 E : ShadowWalker shadow_walker(
123 : runtime_->shadow(), false,
124 : reinterpret_cast<const uint8_t*>(range_info.address),
125 : reinterpret_cast<const uint8_t*>(range_info.address) + range_info.length);
126 E : BlockInfo block_info = {};
127 E : EXPECT_TRUE(shadow_walker.Next(&block_info));
128 E : EXPECT_EQ(fake_block.block_info.header, block_info.header);
129 E : EXPECT_FALSE(shadow_walker.Next(&block_info));
130 :
131 E : fake_block.block_info.header->magic = ~fake_block.block_info.header->magic;
132 E : EXPECT_FALSE(heap_checker.IsHeapCorrupt(&corrupt_ranges));
133 E : }
134 :
135 E : TEST_F(HeapCheckerTest, IsHeapCorrupt) {
136 E : const size_t kAllocSize = 100;
137 :
138 E : BlockLayout block_layout = {};
139 E : EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, kAllocSize, 0, 0,
140 E : &block_layout));
141 :
142 E : const size_t kNumberOfBlocks = 4;
143 E : size_t total_alloc_size = block_layout.block_size * kNumberOfBlocks;
144 : uint8_t* global_alloc =
145 E : reinterpret_cast<uint8_t*>(::malloc(total_alloc_size));
146 :
147 : uint8_t* blocks[kNumberOfBlocks];
148 : BlockHeader* block_headers[kNumberOfBlocks];
149 :
150 E : for (size_t i = 0; i < kNumberOfBlocks; ++i) {
151 E : blocks[i] = global_alloc + i * block_layout.block_size;
152 E : BlockInfo block_info = {};
153 E : BlockInitialize(block_layout, blocks[i], false, &block_info);
154 E : runtime_->shadow()->PoisonAllocatedBlock(block_info);
155 E : BlockSetChecksum(block_info);
156 E : block_headers[i] = block_info.header;
157 E : EXPECT_EQ(block_headers[i], reinterpret_cast<BlockHeader*>(blocks[i]));
158 E : }
159 :
160 E : HeapChecker heap_checker(runtime_->shadow());
161 E : HeapChecker::CorruptRangesVector corrupt_ranges;
162 E : EXPECT_FALSE(heap_checker.IsHeapCorrupt(&corrupt_ranges));
163 :
164 : // Corrupt the header of the first two blocks and of the last one.
165 E : block_headers[0]->magic++;
166 E : block_headers[1]->magic++;
167 E : block_headers[kNumberOfBlocks - 1]->magic++;
168 :
169 E : EXPECT_TRUE(heap_checker.IsHeapCorrupt(&corrupt_ranges));
170 :
171 : // We expect the heap to contain 2 ranges of corrupt blocks, the first one
172 : // containing the 2 first blocks and the second one containing the last block.
173 :
174 E : EXPECT_EQ(2, corrupt_ranges.size());
175 :
176 E : BlockInfo block_info = {};
177 E : ShadowWalker shadow_walker_1(
178 : runtime_->shadow(), false,
179 : reinterpret_cast<const uint8_t*>(corrupt_ranges[0].address),
180 : reinterpret_cast<const uint8_t*>(corrupt_ranges[0].address) +
181 : corrupt_ranges[0].length);
182 E : EXPECT_TRUE(shadow_walker_1.Next(&block_info));
183 E : EXPECT_EQ(reinterpret_cast<const BlockHeader*>(block_info.header),
184 E : block_headers[0]);
185 E : EXPECT_TRUE(shadow_walker_1.Next(&block_info));
186 E : EXPECT_EQ(reinterpret_cast<const BlockHeader*>(block_info.header),
187 E : block_headers[1]);
188 E : EXPECT_FALSE(shadow_walker_1.Next(&block_info));
189 :
190 E : ShadowWalker shadow_walker_2(
191 : runtime_->shadow(), false,
192 : reinterpret_cast<const uint8_t*>(corrupt_ranges[1].address),
193 : reinterpret_cast<const uint8_t*>(corrupt_ranges[1].address) +
194 : corrupt_ranges[1].length);
195 E : EXPECT_TRUE(shadow_walker_2.Next(&block_info));
196 E : EXPECT_EQ(reinterpret_cast<const BlockHeader*>(block_info.header),
197 E : block_headers[kNumberOfBlocks - 1]);
198 E : EXPECT_FALSE(shadow_walker_2.Next(&block_info));
199 :
200 : // Restore the checksum of the blocks.
201 E : block_headers[0]->magic--;
202 E : block_headers[1]->magic--;
203 E : block_headers[kNumberOfBlocks - 1]->magic--;
204 :
205 E : runtime_->shadow()->Unpoison(global_alloc, total_alloc_size);
206 E : ::free(global_alloc);
207 E : }
208 :
209 : } // namespace asan
210 : } // namespace agent
|