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 <windows.h>
16 :
17 : #include "base/bind.h"
18 : #include "gtest/gtest.h"
19 : #include "syzygy/agent/asan/error_info.h"
20 : #include "syzygy/agent/asan/unittest_util.h"
21 :
22 : namespace agent {
23 : namespace asan {
24 :
25 : namespace {
26 :
27 : using testing::ScopedAsanAlloc;
28 :
29 E : void AsanErrorCallbackWithoutComparingContext(AsanErrorInfo* error_info) {
30 : // All of our unittests should be cleaning up after themselves and not causing
31 : // any heap corruption.
32 E : ASSERT_NE(CORRUPT_BLOCK, error_info->error_type);
33 :
34 : // Raise an exception indicating that an error was encountered. If this isn't
35 : // caught and handled by the test then the unittest will fail.
36 E : ::RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, 0);
37 i : }
38 :
39 : // Helps to test the asan_ReadFile function.
40 : class AsanRtlReadFileTest : public testing::TestAsanRtl {
41 : public:
42 : typedef testing::TestAsanRtl Super;
43 :
44 E : AsanRtlReadFileTest() : temp_file_handle_(INVALID_HANDLE_VALUE) {
45 E : }
46 :
47 E : void SetUp() OVERRIDE {
48 E : Super::SetUp();
49 E : SetCallBackFunction(&AsanErrorCallbackWithoutComparingContext);
50 E : ASSERT_NO_FATAL_FAILURE(CreateTempFile());
51 E : }
52 :
53 E : void CreateTempFile() {
54 E : ASSERT_EQ(kTestStringLength,
55 : base::WriteFile(temp_file_.path(),
56 : kTestString,
57 : kTestStringLength));
58 :
59 : temp_file_handle_.Set(::CreateFile(temp_file_.path().value().c_str(),
60 E : GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
61 :
62 E : ASSERT_NE(INVALID_HANDLE_VALUE, temp_file_handle_.Get());
63 E : }
64 :
65 : static const char kTestString[];
66 : static const size_t kTestStringLength;
67 :
68 : protected:
69 : testing::ScopedTempFile temp_file_;
70 :
71 : base::win::ScopedHandle temp_file_handle_;
72 : };
73 :
74 : const char AsanRtlReadFileTest::kTestString[] = "Test of asan_ReadFile";
75 : const size_t AsanRtlReadFileTest::kTestStringLength =
76 : sizeof(AsanRtlReadFileTest::kTestString);
77 :
78 : } // namespace
79 :
80 E : TEST_F(AsanRtlReadFileTest, AsanReadFile) {
81 : // Test that the function works correctly with valid parameters. In this case
82 : // we don't pass an OVERLAPPED structure to the function.
83 E : DWORD bytes_read = 0;
84 E : ScopedAsanAlloc<char> alloc(this, kTestStringLength);
85 : EXPECT_TRUE(ReadFileFunction(temp_file_handle_.Get(),
86 : alloc.get(),
87 : kTestStringLength,
88 : &bytes_read,
89 E : NULL));
90 E : EXPECT_EQ(kTestStringLength, bytes_read);
91 E : EXPECT_STREQ(kTestString, alloc.get());
92 E : }
93 :
94 E : TEST_F(AsanRtlReadFileTest, AsanReadFileWithOverlapped) {
95 E : ScopedAsanAlloc<char> alloc(this, kTestStringLength);
96 : // Test that the function works correctly with valid parameters. Here we pass
97 : // an OVERLAPPED structure to the function, which indicates that we want to do
98 : // the read from a given offset.
99 E : OVERLAPPED overlapped = {};
100 : // Start the read from the middle of the test string.
101 E : const size_t kOffset = kTestStringLength / 2;
102 E : overlapped.Offset = kOffset;
103 E : DWORD bytes_read = 0;
104 : EXPECT_TRUE(ReadFileFunction(temp_file_handle_.Get(),
105 : alloc.get(),
106 : kTestStringLength,
107 : &bytes_read,
108 E : &overlapped));
109 E : EXPECT_EQ(kTestStringLength - kOffset, bytes_read);
110 E : EXPECT_STREQ(kTestString + kOffset, alloc.get());
111 E : }
112 :
113 E : TEST_F(AsanRtlReadFileTest, AsanReadFileOverflow) {
114 : // Test that the function works correctly with valid parameters. In this case
115 : // we don't pass an OVERLAPPED structure to the function.
116 E : DWORD bytes_read = 0;
117 E : ScopedAsanAlloc<char> alloc(this, kTestStringLength);
118 : ReadFileFunctionFailing(temp_file_handle_.Get(),
119 : alloc.get(),
120 : kTestStringLength + 1,
121 : &bytes_read,
122 E : NULL);
123 E : EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
124 E : }
125 :
126 E : TEST_F(AsanRtlReadFileTest, AsanReadFileUAFOnOverlapped) {
127 E : ScopedAsanAlloc<char> alloc(this, kTestStringLength);
128 : // Test a use-after-free on the overlapped structure.
129 E : ScopedAsanAlloc<OVERLAPPED> overlapped(this, sizeof(OVERLAPPED));
130 :
131 : // Start the read from the middle of the test string.
132 E : const size_t kOffset = kTestStringLength / 2;
133 E : overlapped->Offset = kOffset;
134 E : DWORD bytes_read = 0;
135 E : OVERLAPPED* overlapped_ptr = overlapped.get();
136 E : overlapped.reset(NULL);
137 : ReadFileFunctionFailing(temp_file_handle_.Get(),
138 : alloc.get(),
139 : kTestStringLength,
140 : &bytes_read,
141 E : overlapped_ptr);
142 E : EXPECT_TRUE(LogContains(kHeapUseAfterFree));
143 E : }
144 :
145 E : TEST_F(AsanRtlReadFileTest, AsanReadFileUseAfterFree) {
146 : // Test if an use-after-free on the destination buffer is correctly detected.
147 E : DWORD bytes_read = 0;
148 E : ScopedAsanAlloc<char> alloc(this, kTestStringLength);
149 E : char* alloc_ptr = alloc.get();
150 E : alloc.reset(NULL);
151 : ReadFileFunctionFailing(temp_file_handle_.Get(),
152 : alloc_ptr,
153 : kTestStringLength + 1,
154 : &bytes_read,
155 E : NULL);
156 E : EXPECT_TRUE(LogContains(kHeapUseAfterFree));
157 E : }
158 :
159 : namespace {
160 :
161 : typedef ScopedAsanAlloc<char>* AsanReadFileCallbackData;
162 : AsanReadFileCallbackData readfile_callback_data = NULL;
163 :
164 E : void AsanReadFileCallback() {
165 E : ASSERT_TRUE(readfile_callback_data != NULL);
166 E : readfile_callback_data->reset(NULL);
167 E : }
168 :
169 : } // namespace
170 :
171 E : TEST_F(AsanRtlReadFileTest, AsanReadFileUAFAfterInternalCall) {
172 : // This test makes sure that use-after-free errors on the input buffer given
173 : // to the ReadFile function are correctly detected.
174 E : ScopedAsanAlloc<char> alloc(this, kTestStringLength);
175 E : ::memset(alloc.get(), 0, kTestStringLength);
176 E : char* alloc_ptr = alloc.get();
177 E : readfile_callback_data = &alloc;
178 :
179 : // Set the callback that we want to use once the internal call to ReadFile
180 : // returns.
181 E : SetInterceptorCallbackFunction(&AsanReadFileCallback);
182 :
183 : // Read from the file using the interceptor, this will call the ReadFile
184 : // callback once the internal call to ReadFile returns, and result in freeing
185 : // the buffer.
186 E : DWORD bytes_read = 0;
187 : ReadFileFunctionFailing(temp_file_handle_.Get(),
188 : alloc.get(),
189 : kTestStringLength,
190 : &bytes_read,
191 E : NULL);
192 E : EXPECT_EQ(kTestStringLength, bytes_read);
193 E : EXPECT_STREQ(kTestString, alloc_ptr);
194 E : EXPECT_TRUE(LogContains(kHeapUseAfterFree));
195 :
196 E : SetInterceptorCallbackFunction(NULL);
197 E : }
198 :
199 : namespace {
200 :
201 : // Helps to test the asan_WriteFile function.
202 : class AsanRtlWriteFileTest : public testing::TestAsanRtl {
203 : public:
204 : typedef testing::TestAsanRtl Super;
205 :
206 E : AsanRtlWriteFileTest()
207 : : temp_file_handle_(INVALID_HANDLE_VALUE) {
208 E : }
209 :
210 E : void SetUp() OVERRIDE {
211 E : Super::SetUp();
212 :
213 : temp_file_handle_.Set(::CreateFile(temp_file_.path().value().c_str(),
214 : GENERIC_READ | GENERIC_WRITE,
215 : 0,
216 : NULL,
217 : OPEN_EXISTING,
218 : FILE_ATTRIBUTE_NORMAL,
219 E : NULL));
220 E : ASSERT_NE(INVALID_HANDLE_VALUE, temp_file_handle_.Get());
221 E : SetCallBackFunction(&AsanErrorCallbackWithoutComparingContext);
222 E : }
223 :
224 E : bool ReadFileContent(std::string* pipe_content, size_t kOffset) {
225 E : EXPECT_TRUE(pipe_content != NULL);
226 E : const size_t kMaxContentLength = 64;
227 E : pipe_content->clear();
228 E : pipe_content->resize(kMaxContentLength);
229 E : DWORD bytes_read = 0;
230 E : ::SetFilePointer(temp_file_handle_.Get(), kOffset, 0, FILE_BEGIN);
231 : if (::ReadFile(temp_file_handle_.Get(),
232 : &(*pipe_content)[0],
233 : kMaxContentLength,
234 : &bytes_read,
235 E : NULL) == FALSE) {
236 i : return false;
237 : }
238 : // Ensures that the buffer is big enough to store the pipe content.
239 E : EXPECT_TRUE(bytes_read < kMaxContentLength);
240 :
241 E : return true;
242 E : }
243 :
244 : static const char kTestString[];
245 : static const size_t kTestStringLength;
246 :
247 : protected:
248 : testing::ScopedTempFile temp_file_;
249 :
250 : base::win::ScopedHandle temp_file_handle_;
251 : };
252 :
253 : const char AsanRtlWriteFileTest::kTestString[] = "Test of asan_WriteFile";
254 : const size_t AsanRtlWriteFileTest::kTestStringLength =
255 : sizeof(AsanRtlWriteFileTest::kTestString);
256 :
257 : } // namespace
258 :
259 E : TEST_F(AsanRtlWriteFileTest, AsanWriteFile) {
260 : // Test that the function works correctly with valid parameters. In this case
261 : // we don't pass an OVERLAPPED structure to the function.
262 E : DWORD bytes_written = 0;
263 E : ScopedAsanAlloc<char> alloc(this, kTestStringLength);
264 : EXPECT_TRUE(WriteFileFunction(temp_file_handle_.Get(),
265 : kTestString,
266 : kTestStringLength,
267 : &bytes_written,
268 E : NULL));
269 E : EXPECT_EQ(kTestStringLength, bytes_written);
270 E : std::string file_content;
271 E : EXPECT_TRUE(ReadFileContent(&file_content, 0));
272 E : EXPECT_STREQ(kTestString, file_content.c_str());
273 E : }
274 :
275 E : TEST_F(AsanRtlWriteFileTest, AsanWriteFileWithOverlapped) {
276 E : ScopedAsanAlloc<char> alloc(this, kTestStringLength);
277 : // Test that the function works correctly with valid parameters. Here we pass
278 : // an OVERLAPPED structure to the function, which indicates that we want to do
279 : // the write after a given offset.
280 E : OVERLAPPED overlapped = {};
281 : // Start the write from the middle of the test string.
282 E : const size_t kOffset = kTestStringLength / 2;
283 E : overlapped.Offset = kOffset;
284 E : DWORD bytes_written = 0;
285 : EXPECT_TRUE(WriteFileFunction(temp_file_handle_.Get(),
286 : kTestString + kOffset,
287 : kTestStringLength - kOffset,
288 : &bytes_written,
289 E : &overlapped));
290 E : EXPECT_EQ(kTestStringLength - kOffset, bytes_written);
291 E : std::string file_content;
292 E : EXPECT_TRUE(ReadFileContent(&file_content, kOffset));
293 E : EXPECT_STREQ(kTestString + kOffset, file_content.c_str());
294 E : }
295 :
296 E : TEST_F(AsanRtlWriteFileTest, AsanWriteFileOverflow) {
297 : // Test that the function works correctly with valid parameters. In this case
298 : // we don't pass an OVERLAPPED structure to the function.
299 E : DWORD bytes_written = 0;
300 E : ScopedAsanAlloc<char> alloc(this, kTestStringLength);
301 E : strcpy(alloc.get(), kTestString);
302 : WriteFileFunctionFailing(temp_file_handle_.Get(),
303 : alloc.get(),
304 : kTestStringLength + 1,
305 : &bytes_written,
306 E : NULL);
307 E : EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
308 E : std::string file_content;
309 E : }
310 :
311 E : TEST_F(AsanRtlWriteFileTest, AsanWriteFileUAFOnOverlapped) {
312 : // Test an use-after-free on the overlapped structure.
313 E : ScopedAsanAlloc<OVERLAPPED> overlapped(this, sizeof(OVERLAPPED));
314 : // Start the write from the middle of the test string.
315 E : const size_t kOffset = kTestStringLength / 2;
316 E : overlapped->Offset = kOffset;
317 E : DWORD bytes_written = 0;
318 E : OVERLAPPED* overlapped_ptr = overlapped.get();
319 E : overlapped.reset(NULL);
320 : WriteFileFunctionFailing(temp_file_handle_.Get(),
321 : kTestString + kOffset,
322 : kTestStringLength - kOffset,
323 : &bytes_written,
324 E : overlapped_ptr);
325 E : EXPECT_TRUE(LogContains(kHeapUseAfterFree));
326 E : }
327 :
328 E : TEST_F(AsanRtlWriteFileTest, AsanWriteFileUseAfterFree) {
329 : // Test if an use-after-free on the destination buffer is correctly detected.
330 E : DWORD bytes_written = 0;
331 E : ScopedAsanAlloc<char> alloc(this, kTestStringLength);
332 E : strcpy(alloc.get(), kTestString);
333 E : char* alloc_ptr = alloc.get();
334 E : alloc.reset(NULL);
335 : WriteFileFunctionFailing(temp_file_handle_.Get(),
336 : alloc_ptr,
337 : kTestStringLength,
338 : &bytes_written,
339 E : NULL);
340 E : EXPECT_TRUE(LogContains(kHeapUseAfterFree));
341 E : }
342 :
343 : namespace {
344 :
345 : typedef ScopedAsanAlloc<char>* AsanWriteFileCallbackData;
346 : AsanWriteFileCallbackData writefile_callback_data = NULL;
347 :
348 E : void AsanWriteFileCallback() {
349 E : ASSERT_TRUE(writefile_callback_data != NULL);
350 E : writefile_callback_data->reset(NULL);
351 E : }
352 :
353 : } // namespace
354 :
355 E : TEST_F(AsanRtlWriteFileTest, AsanWriteFileUAFAfterInternalCall) {
356 : // This test makes sure that use-after-free errors on the input buffer given
357 : // to the WriteFile function are correctly detected.
358 E : ScopedAsanAlloc<char> alloc(this, kTestStringLength);
359 E : strcpy(alloc.get(), kTestString);
360 :
361 E : writefile_callback_data = &alloc;
362 :
363 : // Set the callback that we want to use once the internal call to WriteFile
364 : // returns.
365 E : SetInterceptorCallbackFunction(&AsanWriteFileCallback);
366 :
367 : // Write to the file using the interceptor, this will call the WriteFile
368 : // callback once the internal call to WriteFile returns, and result in freeing
369 : // the buffer.
370 E : DWORD bytes_written = 0;
371 : WriteFileFunctionFailing(temp_file_handle_.Get(),
372 : alloc.get(),
373 : kTestStringLength,
374 : &bytes_written,
375 E : NULL);
376 E : EXPECT_EQ(kTestStringLength, bytes_written);
377 E : EXPECT_TRUE(LogContains(kHeapUseAfterFree));
378 :
379 E : std::string file_content;
380 E : EXPECT_TRUE(ReadFileContent(&file_content, 0));
381 E : EXPECT_STREQ(kTestString, file_content.c_str());
382 :
383 E : SetInterceptorCallbackFunction(NULL);
384 E : }
385 :
386 : } // namespace asan
387 : } // namespace agent
|