1 : // Copyright 2012 Google Inc.
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 : #include <windows.h>
15 :
16 : #include "base/compiler_specific.h"
17 : #include "base/logging.h"
18 : #include "base/rand_util.h"
19 : #include "base/debug/debugger.h"
20 : #include "gtest/gtest.h"
21 : #include "syzygy/agent/asan/asan_shadow.h"
22 : #include "syzygy/core/unittest_util.h"
23 :
24 : namespace agent {
25 : namespace asan {
26 :
27 : namespace {
28 :
29 : // Shorthand for discussing all the heap functions.
30 : #define HEAP_FUNCTIONS(F) \
31 : F(HANDLE, HeapCreate, \
32 : (DWORD options, SIZE_T initial_size, SIZE_T maximum_size)) \
33 : F(BOOL, HeapDestroy, \
34 : (HANDLE heap)) \
35 : F(LPVOID, HeapAlloc, \
36 : (HANDLE heap, DWORD flags, SIZE_T bytes)) \
37 : F(LPVOID, HeapReAlloc, \
38 : (HANDLE heap, DWORD flags, LPVOID mem, SIZE_T bytes)) \
39 : F(BOOL, HeapFree, \
40 : (HANDLE heap, DWORD flags, LPVOID mem)) \
41 : F(SIZE_T, HeapSize, \
42 : (HANDLE heap, DWORD flags, LPCVOID mem)) \
43 : F(BOOL, HeapValidate, \
44 : (HANDLE heap, DWORD flags, LPCVOID mem)) \
45 : F(SIZE_T, HeapCompact, \
46 : (HANDLE heap, DWORD flags)) \
47 : F(BOOL, HeapLock, (HANDLE heap)) \
48 : F(BOOL, HeapUnlock, (HANDLE heap)) \
49 : F(BOOL, HeapWalk, \
50 : (HANDLE heap, LPPROCESS_HEAP_ENTRY entry)) \
51 : F(BOOL, HeapSetInformation, \
52 : (HANDLE heap, HEAP_INFORMATION_CLASS info_class, \
53 : PVOID info, SIZE_T info_length)) \
54 : F(BOOL, HeapQueryInformation, \
55 : (HANDLE heap, HEAP_INFORMATION_CLASS info_class, \
56 : PVOID info, SIZE_T info_length, PSIZE_T return_length)) \
57 :
58 : #define DECLARE_HEAP_FUNCTION_PTR(ret, name, args) \
59 : typedef ret (WINAPI* name##FunctionPtr)args;
60 :
61 : HEAP_FUNCTIONS(DECLARE_HEAP_FUNCTION_PTR)
62 :
63 : #undef DECLARE_HEAP_FUNCTION_PTR
64 :
65 : class AsanRtlTest : public testing::Test {
66 : public:
67 E : AsanRtlTest() : asan_rtl_(NULL), heap_(NULL) {
68 E : }
69 :
70 E : void SetUp() OVERRIDE {
71 E : FilePath asan_rtl_path = testing::GetExeRelativePath(L"asan_rtl.dll");
72 E : asan_rtl_ = ::LoadLibrary(asan_rtl_path.value().c_str());
73 E : ASSERT_TRUE(asan_rtl_ != NULL);
74 :
75 : // Load all the functions and assert that we find them.
76 : #define LOAD_ASAN_FUNCTION(ret, name, args) \
77 : name##Function = reinterpret_cast<name##FunctionPtr>( \
78 : ::GetProcAddress(asan_rtl_, "asan_" #name)); \
79 : ASSERT_TRUE(name##Function != NULL);
80 :
81 E : HEAP_FUNCTIONS(LOAD_ASAN_FUNCTION)
82 :
83 : #undef LOAD_ASAN_FUNCTION
84 :
85 E : heap_ = HeapCreateFunction(0, 0, 0);
86 E : ASSERT_TRUE(heap_ != NULL);
87 E : }
88 :
89 E : void TearDown() OVERRIDE {
90 E : if (heap_ != NULL) {
91 E : HeapDestroyFunction(heap_);
92 E : heap_ = NULL;
93 : }
94 :
95 E : if (asan_rtl_ != NULL) {
96 E : ::FreeLibrary(asan_rtl_);
97 E : asan_rtl_ = NULL;
98 : }
99 E : }
100 :
101 : protected:
102 : // Arbitrary constant for all size limit.
103 : static const size_t kMaxAllocSize = 134584;
104 :
105 : // The ASAN runtime module to test.
106 : HMODULE asan_rtl_;
107 :
108 : // Scratch heap handle valid from SetUp to TearDown.
109 : HANDLE heap_;
110 :
111 : // Declare the function pointers.
112 : #define DECLARE_FUNCTION_PTR_VARIABLE(ret, name, args) \
113 : static name##FunctionPtr AsanRtlTest::name##Function;
114 :
115 : HEAP_FUNCTIONS(DECLARE_FUNCTION_PTR_VARIABLE)
116 :
117 : #undef DECLARE_FUNCTION_PTR_VARIABLE
118 : };
119 :
120 : // Define the function pointers.
121 : #define DEFINE_FUNCTION_PTR_VARIABLE(ret, name, args) \
122 : name##FunctionPtr AsanRtlTest::name##Function;
123 :
124 : HEAP_FUNCTIONS(DEFINE_FUNCTION_PTR_VARIABLE)
125 :
126 : #undef DEFINE_FUNCTION_PTR_VARIABLE
127 :
128 : } // namespace
129 :
130 E : TEST_F(AsanRtlTest, CreateDestroy) {
131 E : HANDLE heap = HeapCreateFunction(0, 0, 0);
132 E : ASSERT_TRUE(heap != NULL);
133 E : ASSERT_TRUE(HeapDestroyFunction(heap));
134 E : }
135 :
136 E : TEST_F(AsanRtlTest, Alloc) {
137 E : for (size_t size = 10; size < kMaxAllocSize; size = size * 5 + 123) {
138 E : void* mem = HeapAllocFunction(heap_, 0, size);
139 E : ASSERT_TRUE(mem != NULL);
140 E : memset(mem, '\0', size);
141 :
142 E : size_t new_size = size;
143 E : while (new_size == size)
144 E : new_size = base::RandInt(size / 2, size * 2);
145 :
146 E : void* new_mem = HeapReAllocFunction(heap_, 0, mem, new_size);
147 E : ASSERT_TRUE(new_mem != NULL);
148 E : ASSERT_NE(mem, new_mem);
149 :
150 E : ASSERT_TRUE(HeapFreeFunction(heap_, 0, new_mem));
151 E : }
152 E : }
153 :
154 E : TEST_F(AsanRtlTest, Size) {
155 E : for (size_t size = 10; size < kMaxAllocSize; size = size * 5 + 123) {
156 E : void* mem = HeapAllocFunction(heap_, 0, size);
157 E : ASSERT_TRUE(mem != NULL);
158 E : ASSERT_EQ(size, HeapSizeFunction(heap_, 0, mem));
159 E : ASSERT_TRUE(HeapFreeFunction(heap_, 0, mem));
160 E : }
161 E : }
162 :
163 E : TEST_F(AsanRtlTest, Validate) {
164 E : for (size_t size = 10; size < kMaxAllocSize; size = size * 5 + 123) {
165 E : void* mem = HeapAllocFunction(heap_, 0, size);
166 E : ASSERT_TRUE(mem != NULL);
167 E : ASSERT_TRUE(HeapValidateFunction(heap_, 0, mem));
168 E : ASSERT_TRUE(HeapFreeFunction(heap_, 0, mem));
169 E : }
170 E : }
171 :
172 E : TEST_F(AsanRtlTest, Compact) {
173 : // Compact should return a non-zero size.
174 E : ASSERT_LT(0U, HeapCompactFunction(heap_, 0));
175 :
176 : // TODO(siggi): It may not be possible to allocate the size returned due
177 : // to padding - fix and test.
178 E : }
179 :
180 E : TEST_F(AsanRtlTest, LockUnlock) {
181 : // We can't really test these, aside from not crashing.
182 E : ASSERT_TRUE(HeapLockFunction(heap_));
183 E : ASSERT_TRUE(HeapUnlockFunction(heap_));
184 E : }
185 :
186 E : TEST_F(AsanRtlTest, Walk) {
187 : // We assume at least two entries to walk through.
188 E : PROCESS_HEAP_ENTRY entry = {};
189 E : ASSERT_TRUE(HeapWalkFunction(heap_, &entry));
190 E : ASSERT_TRUE(HeapWalkFunction(heap_, &entry));
191 E : }
192 :
193 E : TEST_F(AsanRtlTest, SetQueryInformation) {
194 E : ULONG compat_flag = -1;
195 E : unsigned long ret = 0;
196 : // Get the current value of the compat flag.
197 : ASSERT_TRUE(
198 : HeapQueryInformationFunction(heap_, HeapCompatibilityInformation,
199 E : &compat_flag, sizeof(compat_flag), &ret));
200 E : ASSERT_EQ(sizeof(compat_flag), ret);
201 E : ASSERT_TRUE(compat_flag != -1);
202 :
203 : // Put the heap in LFH, which should always succeed, except when a debugger
204 : // is attached. When a debugger is attached, the heap is wedged in certain
205 : // debug settings.
206 E : if (base::debug::BeingDebugged()) {
207 i : LOG(WARNING) << "Can't test HeapProxy::SetInformation under debugger.";
208 i : return;
209 : }
210 :
211 E : compat_flag = 2;
212 : ASSERT_TRUE(
213 : HeapSetInformationFunction(heap_, HeapCompatibilityInformation,
214 E : &compat_flag, sizeof(compat_flag)));
215 E : }
216 :
217 : namespace {
218 :
219 : // The access check function invoked by the below.
220 : FARPROC check_access_fn = NULL;
221 :
222 : void __declspec(naked) CheckAccessAndCaptureContexts(CONTEXT* before,
223 : CONTEXT* after,
224 E : void* ptr) {
225 : __asm {
226 : // Capture the CPU context before calling the access check function.
227 E : push dword ptr[esp + 0x4]
228 E : call dword ptr[RtlCaptureContext]
229 :
230 : // Restore EAX, which is stomped by RtlCaptureContext.
231 E : mov eax, dword ptr[esp + 0x4]
232 E : mov eax, dword ptr[eax + CONTEXT.Eax]
233 :
234 : // Push eax as we're required to do by the custom calling convention.
235 E : push eax
236 : // Ptr is the pointer to check.
237 E : mov eax, dword ptr[esp + 0x10]
238 : // Call through.
239 E : call dword ptr[check_access_fn + 0]
240 :
241 : // Capture the CPU context after calling the access check function.
242 E : push dword ptr[esp + 0x8]
243 E : call dword ptr[RtlCaptureContext]
244 :
245 E : ret
246 : }
247 : }
248 :
249 E : void CheckAccessAndCompareContexts(void* ptr) {
250 E : CONTEXT before = {};
251 E : CONTEXT after = {};
252 :
253 E : CheckAccessAndCaptureContexts(&before, &after, ptr);
254 :
255 E : EXPECT_EQ(before.SegGs, after.SegGs);
256 E : EXPECT_EQ(before.SegFs, after.SegFs);
257 E : EXPECT_EQ(before.SegEs, after.SegEs);
258 E : EXPECT_EQ(before.SegDs, after.SegDs);
259 :
260 E : EXPECT_EQ(before.Edi, after.Edi);
261 E : EXPECT_EQ(before.Esi, after.Esi);
262 E : EXPECT_EQ(before.Ebx, after.Ebx);
263 E : EXPECT_EQ(before.Edx, after.Edx);
264 E : EXPECT_EQ(before.Ecx, after.Ecx);
265 E : EXPECT_EQ(before.Eax, after.Eax);
266 :
267 E : EXPECT_EQ(before.Ebp, after.Ebp);
268 E : EXPECT_EQ(before.Eip, after.Eip);
269 E : EXPECT_EQ(before.SegCs, after.SegCs);
270 E : EXPECT_EQ(before.EFlags, after.EFlags);
271 E : EXPECT_EQ(before.Esp, after.Esp);
272 E : EXPECT_EQ(before.SegSs, after.SegSs);
273 E : }
274 :
275 : } // namespace
276 :
277 E : TEST_F(AsanRtlTest, asan_check_access) {
278 E : check_access_fn = ::GetProcAddress(asan_rtl_, "asan_check_access");
279 E : ASSERT_TRUE(check_access_fn != NULL);
280 :
281 : // Run through access checking an allocation that's larger than our
282 : // block size (8), but not a multiple thereof to exercise all paths
283 : // in the access check function (save for the failure path).
284 E : const size_t kAllocSize = 13;
285 : uint8* mem = reinterpret_cast<uint8*>(
286 E : HeapAllocFunction(heap_, 0, kAllocSize));
287 E : ASSERT_TRUE(mem != NULL);
288 :
289 E : for (size_t i = 0; i < kAllocSize; ++i) {
290 E : ASSERT_NO_FATAL_FAILURE(CheckAccessAndCompareContexts(mem + i));
291 E : }
292 :
293 E : ASSERT_TRUE(HeapFreeFunction(heap_, 0, mem));
294 E : }
295 :
296 : } // namespace asan
297 : } // namespace agent
|