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