1 // Copyright (c) 2011 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 "base/strings/stringprintf.h"
6 #include "base/win/scoped_handle.h"
7 #include "sandbox/win/src/handle_closer_agent.h"
8 #include "sandbox/win/src/sandbox.h"
9 #include "sandbox/win/src/sandbox_factory.h"
10 #include "sandbox/win/src/target_services.h"
11 #include "sandbox/win/tests/common/controller.h"
12 #include "testing/gtest/include/gtest/gtest.h"
16 const wchar_t *kFileExtensions
[] = { L
".1", L
".2", L
".3", L
".4" };
18 // Returns a handle to a unique marker file that can be retrieved between runs.
19 HANDLE
GetMarkerFile(const wchar_t *extension
) {
20 wchar_t path_buffer
[MAX_PATH
+ 1];
21 CHECK(::GetTempPath(MAX_PATH
, path_buffer
));
22 base::string16 marker_path
= path_buffer
;
23 marker_path
+= L
"\\sbox_marker_";
25 // Generate a unique value from the exe's size and timestamp.
26 CHECK(::GetModuleFileName(NULL
, path_buffer
, MAX_PATH
));
27 base::win::ScopedHandle
module(::CreateFile(path_buffer
,
28 FILE_READ_ATTRIBUTES
, FILE_SHARE_READ
, NULL
,
29 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
));
30 CHECK(module
.IsValid());
32 CHECK(::GetFileTime(module
, ×tamp
, NULL
, NULL
));
33 marker_path
+= base::StringPrintf(L
"%08x%08x%08x",
34 ::GetFileSize(module
, NULL
),
35 timestamp
.dwLowDateTime
,
36 timestamp
.dwHighDateTime
);
37 marker_path
+= extension
;
39 // Make the file delete-on-close so cleanup is automatic.
40 return CreateFile(marker_path
.c_str(), FILE_ALL_ACCESS
,
41 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
42 NULL
, OPEN_ALWAYS
, FILE_FLAG_DELETE_ON_CLOSE
, NULL
);
45 // Used by the thread pool tests.
47 const int kWaitCount
= 20;
53 // Checks for the presence of a list of files (in object path form).
54 // Format: CheckForFileHandle (Y|N) \path\to\file1 [\path\to\file2 ...]
55 // - Y or N depending if the file should exist or not.
56 SBOX_TESTS_COMMAND
int CheckForFileHandles(int argc
, wchar_t **argv
) {
58 return SBOX_TEST_FAILED_TO_RUN_TEST
;
59 bool should_find
= argv
[0][0] == L
'Y';
60 if (argv
[0][1] != L
'\0' || !should_find
&& argv
[0][0] != L
'N')
61 return SBOX_TEST_FAILED_TO_RUN_TEST
;
63 static int state
= BEFORE_INIT
;
66 // Create a unique marker file that is open while the test is running.
67 // The handles leak, but it will be closed by the test or on exit.
68 for (int i
= 0; i
< arraysize(kFileExtensions
); ++i
)
69 EXPECT_NE(GetMarkerFile(kFileExtensions
[i
]), INVALID_HANDLE_VALUE
);
70 return SBOX_TEST_SUCCEEDED
;
73 // Brute force the handle table to find what we're looking for.
74 DWORD handle_count
= UINT_MAX
;
75 const int kInvalidHandleThreshold
= 100;
76 const size_t kHandleOffset
= 4; // Handles are always a multiple of 4.
78 int invalid_count
= 0;
79 base::string16 handle_name
;
81 if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count
))
82 return SBOX_TEST_FAILED_TO_RUN_TEST
;
84 while (handle_count
&& invalid_count
< kInvalidHandleThreshold
) {
85 reinterpret_cast<size_t&>(handle
) += kHandleOffset
;
86 if (GetHandleName(handle
, &handle_name
)) {
87 for (int i
= 1; i
< argc
; ++i
) {
88 if (handle_name
== argv
[i
])
89 return should_find
? SBOX_TEST_SUCCEEDED
: SBOX_TEST_FAILED
;
97 return should_find
? SBOX_TEST_FAILED
: SBOX_TEST_SUCCEEDED
;
100 default: // Do nothing.
104 return SBOX_TEST_SUCCEEDED
;
107 TEST(HandleCloserTest
, CheckForMarkerFiles
) {
109 runner
.SetTimeout(2000);
110 runner
.SetTestState(EVERY_STATE
);
111 sandbox::TargetPolicy
* policy
= runner
.GetPolicy();
113 base::string16 command
= base::string16(L
"CheckForFileHandles Y");
114 for (int i
= 0; i
< arraysize(kFileExtensions
); ++i
) {
115 base::string16 handle_name
;
116 base::win::ScopedHandle
marker(GetMarkerFile(kFileExtensions
[i
]));
117 CHECK(marker
.IsValid());
118 CHECK(sandbox::GetHandleName(marker
, &handle_name
));
120 command
+= handle_name
;
123 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(command
.c_str())) <<
124 "Failed: " << command
;
127 TEST(HandleCloserTest
, CloseMarkerFiles
) {
129 runner
.SetTimeout(2000);
130 runner
.SetTestState(EVERY_STATE
);
131 sandbox::TargetPolicy
* policy
= runner
.GetPolicy();
133 base::string16 command
= base::string16(L
"CheckForFileHandles N");
134 for (int i
= 0; i
< arraysize(kFileExtensions
); ++i
) {
135 base::string16 handle_name
;
136 base::win::ScopedHandle
marker(GetMarkerFile(kFileExtensions
[i
]));
137 CHECK(marker
.IsValid());
138 CHECK(sandbox::GetHandleName(marker
, &handle_name
));
139 CHECK_EQ(policy
->AddKernelObjectToClose(L
"File", handle_name
.c_str()),
142 command
+= handle_name
;
145 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(command
.c_str())) <<
146 "Failed: " << command
;
149 void WINAPI
ThreadPoolTask(void* event
, BOOLEAN timeout
) {
150 static volatile LONG waiters_remaining
= kWaitCount
;
152 CHECK(::CloseHandle(event
));
153 if (::InterlockedDecrement(&waiters_remaining
) == 0)
154 CHECK(::SetEvent(finish_event
));
157 // Run a thread pool inside a sandbox without a CSRSS connection.
158 SBOX_TESTS_COMMAND
int RunThreadPool(int argc
, wchar_t **argv
) {
159 HANDLE wait_list
[20];
160 finish_event
= ::CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
163 // Set up a bunch of waiters.
165 for (int i
= 0; i
< kWaitCount
; ++i
) {
166 HANDLE event
= ::CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
168 CHECK(::RegisterWaitForSingleObject(&pool
, event
, ThreadPoolTask
, event
,
169 INFINITE
, WT_EXECUTEONLYONCE
));
170 wait_list
[i
] = event
;
173 // Signal all the waiters.
174 for (int i
= 0; i
< kWaitCount
; ++i
)
175 CHECK(::SetEvent(wait_list
[i
]));
177 CHECK_EQ(::WaitForSingleObject(finish_event
, INFINITE
), WAIT_OBJECT_0
);
178 CHECK(::CloseHandle(finish_event
));
180 return SBOX_TEST_SUCCEEDED
;
183 TEST(HandleCloserTest
, RunThreadPool
) {
185 runner
.SetTimeout(2000);
186 runner
.SetTestState(AFTER_REVERT
);
187 sandbox::TargetPolicy
* policy
= runner
.GetPolicy();
189 // Sever the CSRSS connection by closing ALPC ports inside the sandbox.
190 CHECK_EQ(policy
->AddKernelObjectToClose(L
"ALPC Port", NULL
), SBOX_ALL_OK
);
192 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"RunThreadPool"));
195 } // namespace sandbox