Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / chrome_elf / create_file / chrome_create_file_unittest.cc
blob5b776073681070545ec019a76580546a7b394122
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/file_util.h"
14 #include "base/files/file_path.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 virtual 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_FALSE(file_handle == INVALID_HANDLE_VALUE);
246 ::WriteFile(file_handle, kTestData, buffer_size, &bytes_written, NULL);
247 EXPECT_EQ(buffer_size, bytes_written);
250 void DoReadCheck(const base::FilePath& path, DWORD flag, bool is_system) {
251 base::win::ScopedHandle file_handle;
252 const char kTestData[] = "0123456789";
253 int buffer_size = sizeof(kTestData) - 1;
254 DWORD bytes_read;
255 char read_buffer[10];
257 if (is_system) {
258 file_handle.Set(::CreateFileW(path.value().c_str(),
259 GENERIC_READ,
261 NULL,
262 OPEN_ALWAYS,
263 FILE_ATTRIBUTE_NORMAL | flag,
264 NULL));
265 } else {
266 file_handle.Set(CreateFileNTDLL(path.value().c_str(),
267 GENERIC_READ,
269 NULL,
270 OPEN_ALWAYS,
271 FILE_ATTRIBUTE_NORMAL | flag,
272 NULL));
275 EXPECT_FALSE(file_handle == INVALID_HANDLE_VALUE);
276 ::ReadFile(file_handle, read_buffer, buffer_size, &bytes_read, NULL);
277 EXPECT_EQ(buffer_size, bytes_read);
278 EXPECT_EQ(0, memcmp(kTestData, read_buffer, bytes_read));
281 void RunChecks(DWORD flag, bool check_reads) {
282 // Make sure we can write to this file handle when called via the system.
283 base::FilePath junk_path_1 = temp_dir_.path().Append(L"junk_1.txt");
284 base::FilePath junk_path_2 = temp_dir_.path().Append(L"junk_2.txt");
285 DoWriteCheck(junk_path_1, flag, true);
286 DoWriteCheck(junk_path_2, flag, false);
287 CheckParams();
289 if (check_reads) {
290 // Make sure we can read from this file handle when called via the system.
291 DoReadCheck(junk_path_1, flag, true);
292 DoReadCheck(junk_path_2, flag, false);
293 CheckParams();
295 base::DeleteFile(junk_path_1, false);
296 base::DeleteFile(junk_path_2, false);
300 static ChromeCreateFileTest* self_;
302 NtCreateFileFunction old_func_ptr_;
303 base::ScopedTempDir temp_dir_;
304 base::ScopedTempDir temp_dir2_;
305 base::win::IATPatchFunction patcher_;
306 NtCreateFileParams kernel_params_;
307 NtCreateFileParams elf_params_;
308 base::PlatformThreadId original_thread_;
311 ChromeCreateFileTest* ChromeCreateFileTest::self_ = NULL;
313 // Tests ---------------------------------------------------------------------
314 TEST_F(ChromeCreateFileTest, CheckParams_FILE_ATTRIBUTE_NORMAL) {
315 RedirectNtCreateFileCalls();
316 RunChecks(FILE_ATTRIBUTE_NORMAL, true);
317 ResetNtCreateFileCalls();
320 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_WRITE_THROUGH) {
321 RedirectNtCreateFileCalls();
322 RunChecks(FILE_FLAG_WRITE_THROUGH, true);
323 ResetNtCreateFileCalls();
326 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_RANDOM_ACCESS) {
327 RedirectNtCreateFileCalls();
328 RunChecks(FILE_FLAG_RANDOM_ACCESS, true);
329 ResetNtCreateFileCalls();
332 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_SEQUENTIAL_SCAN) {
333 RedirectNtCreateFileCalls();
334 RunChecks(FILE_FLAG_SEQUENTIAL_SCAN, true);
335 ResetNtCreateFileCalls();
338 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_DELETE_ON_CLOSE) {
339 RedirectNtCreateFileCalls();
340 RunChecks(FILE_FLAG_DELETE_ON_CLOSE, false);
341 ResetNtCreateFileCalls();
344 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_BACKUP_SEMANTICS) {
345 RedirectNtCreateFileCalls();
346 RunChecks(FILE_FLAG_BACKUP_SEMANTICS, true);
347 ResetNtCreateFileCalls();
350 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_OPEN_REPARSE_POINT) {
351 RedirectNtCreateFileCalls();
352 RunChecks(FILE_FLAG_OPEN_REPARSE_POINT, true);
353 ResetNtCreateFileCalls();
356 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_OPEN_NO_RECALL) {
357 RedirectNtCreateFileCalls();
358 RunChecks(FILE_FLAG_OPEN_NO_RECALL, true);
359 ResetNtCreateFileCalls();
362 TEST_F(ChromeCreateFileTest, BypassTest) {
363 std::wstring UNC_filepath_file(L"\\\\.\\some_file.txt");
365 base::FilePath local_path;
366 PathService::Get(base::DIR_LOCAL_APP_DATA, &local_path);
368 base::FilePath local_prefs_path = local_path.Append(kAppDataDirName).Append(
369 kUserDataDirName).Append(L"default\\Preferences");
370 base::FilePath local_state_path = local_path.Append(kAppDataDirName).Append(
371 kUserDataDirName).Append(L"ninja\\Local State");
372 base::FilePath local_junk_path = local_path.Append(kAppDataDirName).Append(
373 kUserDataDirName).Append(L"default\\Junk");
375 base::FilePath desktop_path;
376 PathService::Get(base::DIR_USER_DESKTOP, &desktop_path);
377 base::FilePath desktop_junk_path =
378 desktop_path.Append(L"Downloads\\junk.txt");
379 base::FilePath desktop_prefs_path =
380 desktop_path.Append(L"Downloads\\Preferences");
382 // Don't redirect UNC files.
383 EXPECT_FALSE(ShouldBypass(UNC_filepath_file.c_str()));
385 // Don't redirect if file is not in UserData directory.
386 EXPECT_FALSE(ShouldBypass(desktop_junk_path.value().c_str()));
387 EXPECT_FALSE(ShouldBypass(desktop_prefs_path.value().c_str()));
389 // Only redirect "Preferences" and "Local State" files.
390 EXPECT_TRUE(ShouldBypass(local_prefs_path.value().c_str()));
391 EXPECT_TRUE(ShouldBypass(local_state_path.value().c_str()));
392 EXPECT_FALSE(ShouldBypass(local_junk_path.value().c_str()));
395 TEST_F(ChromeCreateFileTest, ReadWriteFromNtDll) {
396 UnsetThunkStorage();
397 base::FilePath file_name = temp_dir_.path().Append(L"some_file.txt");
398 DoWriteCheck(file_name, FILE_ATTRIBUTE_NORMAL, false);
399 DoReadCheck(file_name, FILE_ATTRIBUTE_NORMAL, false);
402 TEST_F(ChromeCreateFileTest, ReadWriteFromThunk) {
403 base::FilePath file_name = temp_dir_.path().Append(L"some_file.txt");
404 DoWriteCheck(file_name, FILE_ATTRIBUTE_NORMAL, false);
405 DoReadCheck(file_name, FILE_ATTRIBUTE_NORMAL, false);
408 } // namespace