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/nt_internals.h"
9 #include "sandbox/win/src/sandbox.h"
10 #include "sandbox/win/src/sandbox_factory.h"
11 #include "sandbox/win/src/target_services.h"
12 #include "sandbox/win/tests/common/controller.h"
13 #include "testing/gtest/include/gtest/gtest.h"
17 const wchar_t *kFileExtensions
[] = { L
".1", L
".2", L
".3", L
".4" };
19 // Returns a handle to a unique marker file that can be retrieved between runs.
20 HANDLE
GetMarkerFile(const wchar_t *extension
) {
21 wchar_t path_buffer
[MAX_PATH
+ 1];
22 CHECK(::GetTempPath(MAX_PATH
, path_buffer
));
23 base::string16 marker_path
= path_buffer
;
24 marker_path
+= L
"\\sbox_marker_";
26 // Generate a unique value from the exe's size and timestamp.
27 CHECK(::GetModuleFileName(NULL
, path_buffer
, MAX_PATH
));
28 base::win::ScopedHandle
module(::CreateFile(path_buffer
,
29 FILE_READ_ATTRIBUTES
, FILE_SHARE_READ
, NULL
,
30 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
));
31 CHECK(module
.IsValid());
33 CHECK(::GetFileTime(module
.Get(), ×tamp
, NULL
, NULL
));
34 marker_path
+= base::StringPrintf(L
"%08x%08x%08x",
35 ::GetFileSize(module
.Get(), NULL
),
36 timestamp
.dwLowDateTime
,
37 timestamp
.dwHighDateTime
);
38 marker_path
+= extension
;
40 // Make the file delete-on-close so cleanup is automatic.
41 return CreateFile(marker_path
.c_str(), FILE_ALL_ACCESS
,
42 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
43 NULL
, OPEN_ALWAYS
, FILE_FLAG_DELETE_ON_CLOSE
, NULL
);
46 // Returns type infomation for an NT object. This routine is expected to be
47 // called for invalid handles so it catches STATUS_INVALID_HANDLE exceptions
48 // that can be generated when handle tracing is enabled.
49 NTSTATUS
QueryObjectTypeInformation(HANDLE handle
, void* buffer
, ULONG
* size
) {
50 static NtQueryObject QueryObject
= NULL
;
52 ResolveNTFunctionPtr("NtQueryObject", &QueryObject
);
54 NTSTATUS status
= STATUS_UNSUCCESSFUL
;
56 status
= QueryObject(handle
, ObjectTypeInformation
, buffer
, *size
, size
);
58 __except(GetExceptionCode() == STATUS_INVALID_HANDLE
59 ? EXCEPTION_EXECUTE_HANDLER
60 : EXCEPTION_CONTINUE_SEARCH
) {
61 status
= STATUS_INVALID_HANDLE
;
66 // Used by the thread pool tests.
68 const int kWaitCount
= 20;
74 // Checks for the presence of a list of files (in object path form).
75 // Format: CheckForFileHandle (Y|N) \path\to\file1 [\path\to\file2 ...]
76 // - Y or N depending if the file should exist or not.
77 SBOX_TESTS_COMMAND
int CheckForFileHandles(int argc
, wchar_t **argv
) {
79 return SBOX_TEST_FAILED_TO_RUN_TEST
;
80 bool should_find
= argv
[0][0] == L
'Y';
81 if (argv
[0][1] != L
'\0' || !should_find
&& argv
[0][0] != L
'N')
82 return SBOX_TEST_FAILED_TO_RUN_TEST
;
84 static int state
= BEFORE_INIT
;
87 // Create a unique marker file that is open while the test is running.
88 // The handles leak, but it will be closed by the test or on exit.
89 for (int i
= 0; i
< arraysize(kFileExtensions
); ++i
)
90 CHECK_NE(GetMarkerFile(kFileExtensions
[i
]), INVALID_HANDLE_VALUE
);
91 return SBOX_TEST_SUCCEEDED
;
94 // Brute force the handle table to find what we're looking for.
95 DWORD handle_count
= UINT_MAX
;
96 const int kInvalidHandleThreshold
= 100;
97 const size_t kHandleOffset
= 4; // Handles are always a multiple of 4.
99 int invalid_count
= 0;
100 base::string16 handle_name
;
102 if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count
))
103 return SBOX_TEST_FAILED_TO_RUN_TEST
;
105 while (handle_count
&& invalid_count
< kInvalidHandleThreshold
) {
106 reinterpret_cast<size_t&>(handle
) += kHandleOffset
;
107 if (GetHandleName(handle
, &handle_name
)) {
108 for (int i
= 1; i
< argc
; ++i
) {
109 if (handle_name
== argv
[i
])
110 return should_find
? SBOX_TEST_SUCCEEDED
: SBOX_TEST_FAILED
;
118 return should_find
? SBOX_TEST_FAILED
: SBOX_TEST_SUCCEEDED
;
121 default: // Do nothing.
125 return SBOX_TEST_SUCCEEDED
;
128 // Checks that supplied handle is an Event and it's not waitable.
129 // Format: CheckForEventHandles
130 SBOX_TESTS_COMMAND
int CheckForEventHandles(int argc
, wchar_t** argv
) {
131 static int state
= BEFORE_INIT
;
132 static std::vector
<HANDLE
> to_check
;
136 // Create a unique marker file that is open while the test is running.
137 for (int i
= 0; i
< arraysize(kFileExtensions
); ++i
) {
138 HANDLE handle
= GetMarkerFile(kFileExtensions
[i
]);
139 CHECK_NE(handle
, INVALID_HANDLE_VALUE
);
140 to_check
.push_back(handle
);
142 return SBOX_TEST_SUCCEEDED
;
145 for (auto handle
: to_check
) {
146 // Set up buffers for the type info and the name.
147 std::vector
<BYTE
> type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION
) +
148 32 * sizeof(wchar_t));
149 OBJECT_TYPE_INFORMATION
* type_info
=
150 reinterpret_cast<OBJECT_TYPE_INFORMATION
*>(&(type_info_buffer
[0]));
153 // Get the type name, reusing the buffer.
154 ULONG size
= static_cast<ULONG
>(type_info_buffer
.size());
155 rc
= QueryObjectTypeInformation(handle
, type_info
, &size
);
156 while (rc
== STATUS_INFO_LENGTH_MISMATCH
||
157 rc
== STATUS_BUFFER_OVERFLOW
) {
158 type_info_buffer
.resize(size
+ sizeof(wchar_t));
159 type_info
= reinterpret_cast<OBJECT_TYPE_INFORMATION
*>(
160 &(type_info_buffer
[0]));
161 rc
= QueryObjectTypeInformation(handle
, type_info
, &size
);
162 // Leave padding for the nul terminator.
163 if (NT_SUCCESS(rc
) && size
== type_info_buffer
.size())
164 rc
= STATUS_INFO_LENGTH_MISMATCH
;
167 CHECK(NT_SUCCESS(rc
));
168 CHECK(type_info
->Name
.Buffer
);
170 type_info
->Name
.Buffer
[type_info
->Name
.Length
/ sizeof(wchar_t)] =
173 // Should be an Event now.
174 CHECK_EQ(wcslen(type_info
->Name
.Buffer
), 5U);
175 CHECK_EQ(wcscmp(L
"Event", type_info
->Name
.Buffer
), 0);
177 // Should not be able to wait.
178 CHECK_EQ(WaitForSingleObject(handle
, INFINITE
), WAIT_FAILED
);
180 // Should be able to close.
181 CHECK_EQ(TRUE
, CloseHandle(handle
));
183 return SBOX_TEST_SUCCEEDED
;
185 default: // Do nothing.
189 return SBOX_TEST_SUCCEEDED
;
192 TEST(HandleCloserTest
, CheckForMarkerFiles
) {
194 runner
.SetTimeout(2000);
195 runner
.SetTestState(EVERY_STATE
);
197 base::string16 command
= base::string16(L
"CheckForFileHandles Y");
198 for (int i
= 0; i
< arraysize(kFileExtensions
); ++i
) {
199 base::string16 handle_name
;
200 base::win::ScopedHandle
marker(GetMarkerFile(kFileExtensions
[i
]));
201 CHECK(marker
.IsValid());
202 CHECK(sandbox::GetHandleName(marker
.Get(), &handle_name
));
204 command
+= handle_name
;
207 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(command
.c_str())) <<
208 "Failed: " << command
;
211 TEST(HandleCloserTest
, CloseMarkerFiles
) {
213 runner
.SetTimeout(2000);
214 runner
.SetTestState(EVERY_STATE
);
215 sandbox::TargetPolicy
* policy
= runner
.GetPolicy();
217 base::string16 command
= base::string16(L
"CheckForFileHandles N");
218 for (int i
= 0; i
< arraysize(kFileExtensions
); ++i
) {
219 base::string16 handle_name
;
220 base::win::ScopedHandle
marker(GetMarkerFile(kFileExtensions
[i
]));
221 CHECK(marker
.IsValid());
222 CHECK(sandbox::GetHandleName(marker
.Get(), &handle_name
));
223 CHECK_EQ(policy
->AddKernelObjectToClose(L
"File", handle_name
.c_str()),
226 command
+= handle_name
;
229 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(command
.c_str())) <<
230 "Failed: " << command
;
233 TEST(HandleCloserTest
, CheckStuffedHandle
) {
235 runner
.SetTimeout(2000);
236 runner
.SetTestState(EVERY_STATE
);
237 sandbox::TargetPolicy
* policy
= runner
.GetPolicy();
239 for (int i
= 0; i
< arraysize(kFileExtensions
); ++i
) {
240 base::string16 handle_name
;
241 base::win::ScopedHandle
marker(GetMarkerFile(kFileExtensions
[i
]));
242 CHECK(marker
.IsValid());
243 CHECK(sandbox::GetHandleName(marker
.Get(), &handle_name
));
244 CHECK_EQ(policy
->AddKernelObjectToClose(L
"File", handle_name
.c_str()),
248 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"CheckForEventHandles"));
251 void WINAPI
ThreadPoolTask(void* event
, BOOLEAN timeout
) {
252 static volatile LONG waiters_remaining
= kWaitCount
;
254 CHECK(::CloseHandle(event
));
255 if (::InterlockedDecrement(&waiters_remaining
) == 0)
256 CHECK(::SetEvent(finish_event
));
259 // Run a thread pool inside a sandbox without a CSRSS connection.
260 SBOX_TESTS_COMMAND
int RunThreadPool(int argc
, wchar_t **argv
) {
261 HANDLE wait_list
[20];
262 finish_event
= ::CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
265 // Set up a bunch of waiters.
267 for (int i
= 0; i
< kWaitCount
; ++i
) {
268 HANDLE event
= ::CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
270 CHECK(::RegisterWaitForSingleObject(&pool
, event
, ThreadPoolTask
, event
,
271 INFINITE
, WT_EXECUTEONLYONCE
));
272 wait_list
[i
] = event
;
275 // Signal all the waiters.
276 for (int i
= 0; i
< kWaitCount
; ++i
)
277 CHECK(::SetEvent(wait_list
[i
]));
279 CHECK_EQ(::WaitForSingleObject(finish_event
, INFINITE
), WAIT_OBJECT_0
);
280 CHECK(::CloseHandle(finish_event
));
282 return SBOX_TEST_SUCCEEDED
;
285 TEST(HandleCloserTest
, RunThreadPool
) {
287 runner
.SetTimeout(2000);
288 runner
.SetTestState(AFTER_REVERT
);
289 sandbox::TargetPolicy
* policy
= runner
.GetPolicy();
291 // Sever the CSRSS connection by closing ALPC ports inside the sandbox.
292 CHECK_EQ(policy
->AddKernelObjectToClose(L
"ALPC Port", NULL
), SBOX_ALL_OK
);
294 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"RunThreadPool"));
297 } // namespace sandbox