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 : #include <windows.h>
15 :
16 : #include "gtest/gtest.h"
17 : #include "syzygy/agent/asan/asan_shadow.h"
18 : #include "syzygy/agent/asan/unittest_util.h"
19 : #include "syzygy/core/unittest_util.h"
20 :
21 : namespace agent {
22 : namespace asan {
23 :
24 : namespace {
25 :
26 : // The access check function invoked by the below.
27 : FARPROC check_access_fn = NULL;
28 : // A flag used in asan callback to ensure that a memory error has been detected.
29 : bool memory_error_detected = false;
30 : // A pointer to a context to ensure that we're able to restore the context when
31 : // an asan error is found.
32 : CONTEXT* context_before_hook = NULL;
33 :
34 : // Shorthand for discussing all the asan runtime functions.
35 : #define ASAN_RTL_FUNCTIONS(F) \
36 : F(HANDLE, HeapCreate, \
37 : (DWORD options, SIZE_T initial_size, SIZE_T maximum_size)) \
38 : F(BOOL, HeapDestroy, \
39 : (HANDLE heap)) \
40 : F(LPVOID, HeapAlloc, \
41 : (HANDLE heap, DWORD flags, SIZE_T bytes)) \
42 : F(LPVOID, HeapReAlloc, \
43 : (HANDLE heap, DWORD flags, LPVOID mem, SIZE_T bytes)) \
44 : F(BOOL, HeapFree, \
45 : (HANDLE heap, DWORD flags, LPVOID mem)) \
46 : F(SIZE_T, HeapSize, \
47 : (HANDLE heap, DWORD flags, LPCVOID mem)) \
48 : F(BOOL, HeapValidate, \
49 : (HANDLE heap, DWORD flags, LPCVOID mem)) \
50 : F(SIZE_T, HeapCompact, \
51 : (HANDLE heap, DWORD flags)) \
52 : F(BOOL, HeapLock, (HANDLE heap)) \
53 : F(BOOL, HeapUnlock, (HANDLE heap)) \
54 : F(BOOL, HeapWalk, \
55 : (HANDLE heap, LPPROCESS_HEAP_ENTRY entry)) \
56 : F(BOOL, HeapSetInformation, \
57 : (HANDLE heap, HEAP_INFORMATION_CLASS info_class, \
58 : PVOID info, SIZE_T info_length)) \
59 : F(BOOL, HeapQueryInformation, \
60 : (HANDLE heap, HEAP_INFORMATION_CLASS info_class, \
61 : PVOID info, SIZE_T info_length, PSIZE_T return_length)) \
62 : F(void, SetCallBack, \
63 : (void (*callback)(CONTEXT* context))) \
64 :
65 : #define DECLARE_ASAN_FUNCTION_PTR(ret, name, args) \
66 : typedef ret (WINAPI* name##FunctionPtr)args;
67 :
68 : ASAN_RTL_FUNCTIONS(DECLARE_ASAN_FUNCTION_PTR)
69 :
70 : #undef DECLARE_ASAN_FUNCTION_PTR
71 :
72 : class AsanRtlTest : public testing::TestWithAsanLogger {
73 : public:
74 E : AsanRtlTest() : asan_rtl_(NULL), heap_(NULL) {
75 E : }
76 :
77 E : void SetUp() OVERRIDE {
78 E : testing::TestWithAsanLogger::SetUp();
79 :
80 : // Load the ASAN runtime library.
81 E : FilePath asan_rtl_path = testing::GetExeRelativePath(L"asan_rtl.dll");
82 E : asan_rtl_ = ::LoadLibrary(asan_rtl_path.value().c_str());
83 E : ASSERT_TRUE(asan_rtl_ != NULL);
84 :
85 : // Load all the functions and assert that we find them.
86 : #define LOAD_ASAN_FUNCTION(ret, name, args) \
87 : name##Function = reinterpret_cast<name##FunctionPtr>( \
88 : ::GetProcAddress(asan_rtl_, "asan_" #name)); \
89 : ASSERT_TRUE(name##Function != NULL);
90 :
91 E : ASAN_RTL_FUNCTIONS(LOAD_ASAN_FUNCTION)
92 :
93 : #undef LOAD_ASAN_FUNCTION
94 :
95 E : heap_ = HeapCreateFunction(0, 0, 0);
96 E : ASSERT_TRUE(heap_ != NULL);
97 E : }
98 :
99 E : void TearDown() OVERRIDE {
100 E : if (heap_ != NULL) {
101 E : HeapDestroyFunction(heap_);
102 E : heap_ = NULL;
103 : }
104 :
105 E : if (asan_rtl_ != NULL) {
106 E : ::FreeLibrary(asan_rtl_);
107 E : asan_rtl_ = NULL;
108 : }
109 :
110 E : testing::TestWithAsanLogger::TearDown();
111 E : }
112 :
113 : protected:
114 : // The ASAN runtime module to test.
115 : HMODULE asan_rtl_;
116 :
117 : // Scratch heap handle valid from SetUp to TearDown.
118 : HANDLE heap_;
119 :
120 : // Declare the function pointers.
121 : #define DECLARE_FUNCTION_PTR_VARIABLE(ret, name, args) \
122 : static name##FunctionPtr AsanRtlTest::name##Function;
123 :
124 : ASAN_RTL_FUNCTIONS(DECLARE_FUNCTION_PTR_VARIABLE)
125 :
126 : #undef DECLARE_FUNCTION_PTR_VARIABLE
127 : };
128 :
129 : // Define the function pointers.
130 : #define DEFINE_FUNCTION_PTR_VARIABLE(ret, name, args) \
131 : name##FunctionPtr AsanRtlTest::name##Function;
132 :
133 : ASAN_RTL_FUNCTIONS(DEFINE_FUNCTION_PTR_VARIABLE)
134 :
135 : #undef DEFINE_FUNCTION_PTR_VARIABLE
136 :
137 : // Check if the sections of 2 context are equals.
138 : // @param c1 The first context to check.
139 : // @param c2 The second context to check.
140 : // @param flags The sections to compare.
141 E : void ExpectEqualContexts(const CONTEXT& c1, const CONTEXT& c2, DWORD flags) {
142 E : if ((flags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS) {
143 E : EXPECT_EQ(c1.SegGs, c2.SegGs);
144 E : EXPECT_EQ(c1.SegFs, c2.SegFs);
145 E : EXPECT_EQ(c1.SegEs, c2.SegEs);
146 E : EXPECT_EQ(c1.SegDs, c2.SegDs);
147 : }
148 :
149 E : if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) {
150 E : EXPECT_EQ(c1.Edi, c2.Edi);
151 E : EXPECT_EQ(c1.Esi, c2.Esi);
152 E : EXPECT_EQ(c1.Ebx, c2.Ebx);
153 E : EXPECT_EQ(c1.Edx, c2.Edx);
154 E : EXPECT_EQ(c1.Ecx, c2.Ecx);
155 E : EXPECT_EQ(c1.Eax, c2.Eax);
156 : }
157 :
158 E : if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) {
159 E : EXPECT_EQ(c1.Ebp, c2.Ebp);
160 E : EXPECT_EQ(c1.Eip, c2.Eip);
161 E : EXPECT_EQ(c1.SegCs, c2.SegCs);
162 E : EXPECT_EQ(c1.EFlags, c2.EFlags);
163 E : EXPECT_EQ(c1.Esp, c2.Esp);
164 E : EXPECT_EQ(c1.SegSs, c2.SegSs);
165 : }
166 E : }
167 :
168 : void __declspec(naked) CheckAccessAndCaptureContexts(CONTEXT* before,
169 : CONTEXT* after,
170 i : void* ptr) {
171 : __asm {
172 : // Capture the CPU context before calling the access check function.
173 i : push dword ptr[esp + 0x4]
174 i : call dword ptr[RtlCaptureContext]
175 :
176 : // Restore eax, which is stomped by RtlCaptureContext.
177 i : mov eax, dword ptr[esp + 0x4]
178 i : mov eax, dword ptr[eax + CONTEXT.Eax]
179 :
180 : // Push edx as we're required to do by the custom calling convention.
181 i : push edx
182 : // Ptr is the pointer to check.
183 i : mov edx, dword ptr[esp + 0x10]
184 : // Call through.
185 i : call dword ptr[check_access_fn + 0]
186 :
187 : // Capture the CPU context after calling the access check function.
188 i : push dword ptr[esp + 0x8]
189 i : call dword ptr[RtlCaptureContext]
190 :
191 i : ret
192 : }
193 : }
194 :
195 : // We need to disable this warning due to the label in the inline assembly who
196 : // is not compatible with the global optimization.
197 : #pragma warning(push)
198 : #pragma warning(disable: 4740)
199 i : void __declspec(naked) CheckAccess(void* ptr) {
200 i : DCHECK(context_before_hook != NULL);
201 : __asm {
202 i : push dword ptr[context_before_hook]
203 i : call dword ptr[RtlCaptureContext]
204 :
205 : // Fix the values of ebp, esp and eip in the context to make sure they are
206 : // the same as what they'll be after the call to the hook.
207 i : mov eax, dword ptr[context_before_hook]
208 i : mov dword ptr[eax + CONTEXT.Ebp], ebp
209 i : mov dword ptr[eax + CONTEXT.Esp], esp
210 i : mov dword ptr[eax + CONTEXT.Eip], offset expected_eip
211 :
212 : // Restore eax, which is stomped by RtlCaptureContext.
213 i : mov eax, dword ptr[eax + CONTEXT.Eax]
214 :
215 : // Push edx as we're required to do by the custom calling convention.
216 i : push edx
217 : // Ptr is the pointer to check.
218 i : mov edx, dword ptr[esp + 0x8]
219 : // Call through.
220 i : call dword ptr[check_access_fn + 0]
221 : expected_eip:
222 i : ret
223 : }
224 : }
225 : #pragma warning(pop)
226 :
227 E : void CheckAccessAndCompareContexts(void* ptr) {
228 E : CONTEXT before = {};
229 E : CONTEXT after = {};
230 :
231 E : CheckAccessAndCaptureContexts(&before, &after, ptr);
232 :
233 E : ExpectEqualContexts(before, after, CONTEXT_FULL);
234 E : }
235 :
236 E : void AsanErrorCallback(CONTEXT* context) {
237 E : EXPECT_TRUE(context != NULL);
238 E : EXPECT_TRUE(context_before_hook != NULL);
239 :
240 E : EXPECT_FALSE(memory_error_detected);
241 E : memory_error_detected = true;
242 : ExpectEqualContexts(*context_before_hook,
243 : *context,
244 E : CONTEXT_INTEGER | CONTEXT_CONTROL);
245 E : }
246 :
247 E : void AssertMemoryErrorIsDetected(void* ptr) {
248 E : memory_error_detected = false;
249 E : CheckAccess(ptr);
250 E : ASSERT_TRUE(memory_error_detected);
251 E : }
252 :
253 : } // namespace
254 :
255 E : TEST_F(AsanRtlTest, AsanCheckGoodAccess) {
256 : check_access_fn =
257 E : ::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
258 E : ASSERT_TRUE(check_access_fn != NULL);
259 :
260 : // Run through access checking an allocation that's larger than our
261 : // block size (8), but not a multiple thereof to exercise all paths
262 : // in the access check function (save for the failure path).
263 E : const size_t kAllocSize = 13;
264 : uint8* mem = reinterpret_cast<uint8*>(
265 E : HeapAllocFunction(heap_, 0, kAllocSize));
266 E : ASSERT_TRUE(mem != NULL);
267 :
268 E : for (size_t i = 0; i < kAllocSize; ++i) {
269 E : ASSERT_NO_FATAL_FAILURE(CheckAccessAndCompareContexts(mem + i));
270 E : }
271 :
272 E : ASSERT_TRUE(HeapFreeFunction(heap_, 0, mem));
273 E : }
274 :
275 E : TEST_F(AsanRtlTest, AsanCheckBadAccess) {
276 : check_access_fn =
277 E : ::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
278 E : ASSERT_TRUE(check_access_fn != NULL);
279 :
280 E : const size_t kAllocSize = 13;
281 : uint8* mem = reinterpret_cast<uint8*>(
282 E : HeapAllocFunction(heap_, 0, kAllocSize));
283 E : ASSERT_TRUE(mem != NULL);
284 :
285 E : CONTEXT context_before_error = {};
286 E : context_before_hook = &context_before_error;
287 E : SetCallBackFunction(&AsanErrorCallback);
288 E : AssertMemoryErrorIsDetected(mem - 1);
289 E : AssertMemoryErrorIsDetected(mem + kAllocSize);
290 E : ASSERT_TRUE(HeapFreeFunction(heap_, 0, mem));
291 E : AssertMemoryErrorIsDetected(mem);
292 E : ASSERT_TRUE(LogContains("heap-buffer-underflow"));
293 E : ASSERT_TRUE(LogContains("heap-buffer-overflow"));
294 E : ASSERT_TRUE(LogContains("heap-use-after-free"));
295 E : }
296 :
297 : } // namespace asan
298 : } // namespace agent
|