Roll WebRTC 9745:9761, Libjingle 9742:9761
[chromium-blink-merge.git] / chrome_elf / create_file / chrome_create_file_unittest.cc
blob8b0331fe88d7095e52c90e0348275336800bb2d9
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome_elf/create_file/chrome_create_file.h"
7 #include <windows.h>
9 #include <bitset>
10 #include <string>
12 #include "base/base_paths_win.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/files/scoped_temp_dir.h"
16 #include "base/path_service.h"
17 #include "base/threading/platform_thread.h"
18 #include "base/win/iat_patch_function.h"
19 #include "base/win/scoped_handle.h"
20 #include "base/win/windows_version.h"
21 #include "chrome_elf/chrome_elf_constants.h"
22 #include "chrome_elf/ntdll_cache.h"
23 #include "sandbox/win/src/interception_internal.h"
24 #include "sandbox/win/src/nt_internals.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "testing/platform_test.h"
29 namespace {
31 // Test fixtures -------------------------------------------------------------
33 class ChromeCreateFileTest : public PlatformTest {
34 protected:
35 struct NtCreateFileParams {
36 ACCESS_MASK desired_access;
37 OBJECT_ATTRIBUTES object_attributes;
38 PLARGE_INTEGER allocation_size;
39 ULONG file_attributes;
40 ULONG share_access;
41 ULONG create_disposition;
42 ULONG create_options;
43 PVOID ea_buffer;
44 ULONG ea_length;
47 enum CallPath {
48 ELF,
49 KERNEL
52 template<CallPath path>
53 static NTSTATUS WINAPI FakeNtCreateFile(
54 PHANDLE file_handle,
55 ACCESS_MASK desired_access,
56 POBJECT_ATTRIBUTES object_attributes,
57 PIO_STATUS_BLOCK io_status_block,
58 PLARGE_INTEGER allocation_size,
59 ULONG file_attributes,
60 ULONG share_access,
61 ULONG create_disposition,
62 ULONG create_options,
63 PVOID ea_buffer,
64 ULONG ea_length) {
65 return self_->HandleCreateFileCall(file_handle,
66 desired_access,
67 object_attributes,
68 io_status_block,
69 allocation_size,
70 file_attributes,
71 share_access,
72 create_disposition,
73 create_options,
74 ea_buffer,
75 ea_length,
76 path);
79 void SetUp() override {
80 original_thread_ = base::PlatformThread::CurrentId();
81 InitCache();
82 PlatformTest::SetUp();
84 base::FilePath user_data_dir;
85 PathService::Get(base::DIR_LOCAL_APP_DATA, &user_data_dir);
86 ASSERT_TRUE(temp_dir_.CreateUniqueTempDirUnderPath(user_data_dir));
87 ASSERT_TRUE(temp_dir2_.CreateUniqueTempDir());
88 self_ = this;
91 void UnsetThunkStorage() {
92 DWORD old_protect = 0;
93 EXPECT_TRUE(::VirtualProtect(&g_nt_thunk_storage,
94 sizeof(g_nt_thunk_storage),
95 PAGE_EXECUTE_READWRITE,
96 &old_protect));
97 memset(&g_nt_thunk_storage, 0, sizeof(g_nt_thunk_storage));
99 EXPECT_TRUE(::VirtualProtect(&g_nt_thunk_storage,
100 sizeof(g_nt_thunk_storage),
101 PAGE_EXECUTE_READ,
102 &old_protect));
105 void RedirectNtCreateFileCalls() {
106 UnsetThunkStorage();
107 old_func_ptr_ =
108 reinterpret_cast<NtCreateFileFunction>(g_ntdll_lookup["NtCreateFile"]);
110 // KernelBase.dll only exists for Win7 and later, prior to that, kernel32
111 // imports from ntdll directly.
112 if (base::win::GetVersion() < base::win::VERSION_WIN7) {
113 patcher_.Patch(L"kernel32.dll", "ntdll.dll", "NtCreateFile",
114 reinterpret_cast<void(*)()>(&FakeNtCreateFile<KERNEL>));
115 } else {
116 patcher_.Patch(L"kernelbase.dll", "ntdll.dll", "NtCreateFile",
117 reinterpret_cast<void(*)()>(&FakeNtCreateFile<KERNEL>));
120 g_ntdll_lookup["NtCreateFile"] = reinterpret_cast<void(*)()>(
121 &ChromeCreateFileTest::FakeNtCreateFile<ELF>);
124 void ResetNtCreateFileCalls() {
125 g_ntdll_lookup["NtCreateFile"] = reinterpret_cast<void*>(old_func_ptr_);
126 patcher_.Unpatch();
129 NTSTATUS HandleCreateFileCall(PHANDLE file_handle,
130 ACCESS_MASK desired_access,
131 POBJECT_ATTRIBUTES object_attributes,
132 PIO_STATUS_BLOCK io_status_block,
133 PLARGE_INTEGER allocation_size,
134 ULONG file_attributes,
135 ULONG share_access,
136 ULONG create_disposition,
137 ULONG create_options,
138 PVOID ea_buffer,
139 ULONG ea_length,
140 CallPath call_path) {
141 if (original_thread_ == base::PlatformThread::CurrentId()) {
142 SetParams(desired_access,
143 object_attributes,
144 allocation_size,
145 file_attributes,
146 share_access,
147 create_disposition,
148 create_options,
149 ea_buffer,
150 ea_length,
151 call_path == ELF ? &elf_params_ : &kernel_params_);
154 // Forward the call to the real NTCreateFile.
155 return old_func_ptr_(file_handle,
156 desired_access,
157 object_attributes,
158 io_status_block,
159 allocation_size,
160 file_attributes,
161 share_access,
162 create_disposition,
163 create_options,
164 ea_buffer,
165 ea_length);
168 void SetParams(ACCESS_MASK desired_access,
169 POBJECT_ATTRIBUTES object_attributes,
170 PLARGE_INTEGER allocation_size,
171 ULONG file_attributes,
172 ULONG share_access,
173 ULONG create_disposition,
174 ULONG create_options,
175 PVOID ea_buffer,
176 ULONG ea_length,
177 NtCreateFileParams* params) {
178 params->desired_access = desired_access;
179 params->object_attributes.Length = object_attributes->Length;
180 params->object_attributes.ObjectName = object_attributes->ObjectName;
181 params->object_attributes.RootDirectory = object_attributes->RootDirectory;
182 params->object_attributes.Attributes = object_attributes->Attributes;
183 params->object_attributes.SecurityDescriptor =
184 object_attributes->SecurityDescriptor;
185 params->object_attributes.SecurityQualityOfService =
186 object_attributes->SecurityQualityOfService;
187 params->allocation_size = allocation_size;
188 params->file_attributes = file_attributes;
189 params->share_access = share_access;
190 params->create_disposition = create_disposition;
191 params->create_options = create_options;
192 params->ea_buffer = ea_buffer;
193 params->ea_length = ea_length;
196 void CheckParams() {
197 std::bitset<32> elf((int) elf_params_.desired_access);
198 std::bitset<32> ker((int) kernel_params_.desired_access);
200 EXPECT_EQ(kernel_params_.desired_access, elf_params_.desired_access)
201 << elf << "\n" << ker;
202 EXPECT_EQ(kernel_params_.object_attributes.Length,
203 elf_params_.object_attributes.Length);
204 EXPECT_EQ(kernel_params_.object_attributes.RootDirectory,
205 elf_params_.object_attributes.RootDirectory);
206 EXPECT_EQ(kernel_params_.object_attributes.Attributes,
207 elf_params_.object_attributes.Attributes);
208 EXPECT_EQ(kernel_params_.object_attributes.SecurityDescriptor,
209 elf_params_.object_attributes.SecurityDescriptor);
210 EXPECT_EQ(kernel_params_.allocation_size, elf_params_.allocation_size);
211 EXPECT_EQ(kernel_params_.file_attributes, elf_params_.file_attributes);
212 EXPECT_EQ(kernel_params_.share_access, elf_params_.share_access);
213 EXPECT_EQ(kernel_params_.create_disposition,
214 elf_params_.create_disposition);
215 EXPECT_EQ(kernel_params_.create_options, elf_params_.create_options);
216 EXPECT_EQ(kernel_params_.ea_buffer, elf_params_.ea_buffer);
217 EXPECT_EQ(kernel_params_.ea_length, elf_params_.ea_length);
220 void DoWriteCheck(const base::FilePath& path, DWORD flag, bool is_system) {
221 base::win::ScopedHandle file_handle;
222 const char kTestData[] = "0123456789";
223 int buffer_size = sizeof(kTestData) - 1;
224 DWORD bytes_written;
226 if (is_system) {
227 file_handle.Set(::CreateFileW(path.value().c_str(),
228 GENERIC_WRITE,
229 FILE_SHARE_READ,
230 NULL,
231 CREATE_ALWAYS,
232 FILE_ATTRIBUTE_NORMAL | flag,
233 NULL));
234 } else {
235 file_handle.Set(CreateFileNTDLL(path.value().c_str(),
236 GENERIC_WRITE,
237 FILE_SHARE_READ,
238 NULL,
239 CREATE_ALWAYS,
240 FILE_ATTRIBUTE_NORMAL | flag,
241 NULL));
245 EXPECT_TRUE(file_handle.IsValid());
246 ::WriteFile(file_handle.Get(), kTestData, buffer_size, &bytes_written,
247 NULL);
248 EXPECT_EQ(buffer_size, bytes_written);
251 void DoReadCheck(const base::FilePath& path, DWORD flag, bool is_system) {
252 base::win::ScopedHandle file_handle;
253 const char kTestData[] = "0123456789";
254 int buffer_size = sizeof(kTestData) - 1;
255 DWORD bytes_read;
256 char read_buffer[10];
258 if (is_system) {
259 file_handle.Set(::CreateFileW(path.value().c_str(),
260 GENERIC_READ,
262 NULL,
263 OPEN_ALWAYS,
264 FILE_ATTRIBUTE_NORMAL | flag,
265 NULL));
266 } else {
267 file_handle.Set(CreateFileNTDLL(path.value().c_str(),
268 GENERIC_READ,
270 NULL,
271 OPEN_ALWAYS,
272 FILE_ATTRIBUTE_NORMAL | flag,
273 NULL));
276 EXPECT_TRUE(file_handle.IsValid());
277 ::ReadFile(file_handle.Get(), read_buffer, buffer_size, &bytes_read, NULL);
278 EXPECT_EQ(buffer_size, bytes_read);
279 EXPECT_EQ(0, memcmp(kTestData, read_buffer, bytes_read));
282 void RunChecks(DWORD flag, bool check_reads) {
283 // Make sure we can write to this file handle when called via the system.
284 base::FilePath junk_path_1 = temp_dir_.path().Append(L"junk_1.txt");
285 base::FilePath junk_path_2 = temp_dir_.path().Append(L"junk_2.txt");
286 DoWriteCheck(junk_path_1, flag, true);
287 DoWriteCheck(junk_path_2, flag, false);
288 CheckParams();
290 if (check_reads) {
291 // Make sure we can read from this file handle when called via the system.
292 DoReadCheck(junk_path_1, flag, true);
293 DoReadCheck(junk_path_2, flag, false);
294 CheckParams();
296 base::DeleteFile(junk_path_1, false);
297 base::DeleteFile(junk_path_2, false);
301 static ChromeCreateFileTest* self_;
303 NtCreateFileFunction old_func_ptr_;
304 base::ScopedTempDir temp_dir_;
305 base::ScopedTempDir temp_dir2_;
306 base::win::IATPatchFunction patcher_;
307 NtCreateFileParams kernel_params_;
308 NtCreateFileParams elf_params_;
309 base::PlatformThreadId original_thread_;
312 ChromeCreateFileTest* ChromeCreateFileTest::self_ = NULL;
314 // Tests ---------------------------------------------------------------------
315 TEST_F(ChromeCreateFileTest, CheckParams_FILE_ATTRIBUTE_NORMAL) {
316 RedirectNtCreateFileCalls();
317 RunChecks(FILE_ATTRIBUTE_NORMAL, true);
318 ResetNtCreateFileCalls();
321 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_WRITE_THROUGH) {
322 RedirectNtCreateFileCalls();
323 RunChecks(FILE_FLAG_WRITE_THROUGH, true);
324 ResetNtCreateFileCalls();
327 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_RANDOM_ACCESS) {
328 RedirectNtCreateFileCalls();
329 RunChecks(FILE_FLAG_RANDOM_ACCESS, true);
330 ResetNtCreateFileCalls();
333 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_SEQUENTIAL_SCAN) {
334 RedirectNtCreateFileCalls();
335 RunChecks(FILE_FLAG_SEQUENTIAL_SCAN, true);
336 ResetNtCreateFileCalls();
339 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_DELETE_ON_CLOSE) {
340 RedirectNtCreateFileCalls();
341 RunChecks(FILE_FLAG_DELETE_ON_CLOSE, false);
342 ResetNtCreateFileCalls();
345 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_BACKUP_SEMANTICS) {
346 RedirectNtCreateFileCalls();
347 RunChecks(FILE_FLAG_BACKUP_SEMANTICS, true);
348 ResetNtCreateFileCalls();
351 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_OPEN_REPARSE_POINT) {
352 RedirectNtCreateFileCalls();
353 RunChecks(FILE_FLAG_OPEN_REPARSE_POINT, true);
354 ResetNtCreateFileCalls();
357 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_OPEN_NO_RECALL) {
358 RedirectNtCreateFileCalls();
359 RunChecks(FILE_FLAG_OPEN_NO_RECALL, true);
360 ResetNtCreateFileCalls();
363 TEST_F(ChromeCreateFileTest, BypassTest) {
364 std::wstring UNC_filepath_file(L"\\\\.\\some_file.txt");
366 base::FilePath local_path;
367 PathService::Get(base::DIR_LOCAL_APP_DATA, &local_path);
369 base::FilePath local_prefs_path = local_path.Append(kAppDataDirName).Append(
370 kUserDataDirName).Append(L"default\\Preferences");
371 base::FilePath local_state_path = local_path.Append(kAppDataDirName).Append(
372 kUserDataDirName).Append(L"ninja\\Local State");
373 base::FilePath local_junk_path = local_path.Append(kAppDataDirName).Append(
374 kUserDataDirName).Append(L"default\\Junk");
376 base::FilePath desktop_path;
377 PathService::Get(base::DIR_USER_DESKTOP, &desktop_path);
378 base::FilePath desktop_junk_path =
379 desktop_path.Append(L"Downloads\\junk.txt");
380 base::FilePath desktop_prefs_path =
381 desktop_path.Append(L"Downloads\\Preferences");
383 // Don't redirect UNC files.
384 EXPECT_FALSE(ShouldBypass(UNC_filepath_file.c_str()));
386 // Don't redirect if file is not in UserData directory.
387 EXPECT_FALSE(ShouldBypass(desktop_junk_path.value().c_str()));
388 EXPECT_FALSE(ShouldBypass(desktop_prefs_path.value().c_str()));
390 // Only redirect "Preferences" and "Local State" files.
391 EXPECT_TRUE(ShouldBypass(local_prefs_path.value().c_str()));
392 EXPECT_TRUE(ShouldBypass(local_state_path.value().c_str()));
393 EXPECT_FALSE(ShouldBypass(local_junk_path.value().c_str()));
396 TEST_F(ChromeCreateFileTest, ReadWriteFromNtDll) {
397 UnsetThunkStorage();
398 base::FilePath file_name = temp_dir_.path().Append(L"some_file.txt");
399 DoWriteCheck(file_name, FILE_ATTRIBUTE_NORMAL, false);
400 DoReadCheck(file_name, FILE_ATTRIBUTE_NORMAL, false);
403 TEST_F(ChromeCreateFileTest, ReadWriteFromThunk) {
404 base::FilePath file_name = temp_dir_.path().Append(L"some_file.txt");
405 DoWriteCheck(file_name, FILE_ATTRIBUTE_NORMAL, false);
406 DoReadCheck(file_name, FILE_ATTRIBUTE_NORMAL, false);
409 } // namespace