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