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 "sandbox/win/src/handle_closer.h"
7 #include "base/logging.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/win/windows_version.h"
10 #include "sandbox/win/src/interceptors.h"
11 #include "sandbox/win/src/internal_types.h"
12 #include "sandbox/win/src/nt_internals.h"
13 #include "sandbox/win/src/process_thread_interception.h"
14 #include "sandbox/win/src/win_utils.h"
18 template<typename T
> T
RoundUpToWordSize(T v
) {
19 if (size_t mod
= v
% sizeof(size_t))
20 v
+= sizeof(size_t) - mod
;
24 template<typename T
> T
* RoundUpToWordSize(T
* v
) {
25 return reinterpret_cast<T
*>(RoundUpToWordSize(reinterpret_cast<size_t>(v
)));
32 // Memory buffer mapped from the parent, with the list of handles.
33 SANDBOX_INTERCEPT HandleCloserInfo
* g_handles_to_close
;
35 HandleCloser::HandleCloser() {}
37 ResultCode
HandleCloser::AddHandle(const char16
* handle_type
,
38 const char16
* handle_name
) {
40 return SBOX_ERROR_BAD_PARAMS
;
42 HandleMap::iterator names
= handles_to_close_
.find(handle_type
);
43 if (names
== handles_to_close_
.end()) { // We have no entries for this type.
44 std::pair
<HandleMap::iterator
, bool> result
= handles_to_close_
.insert(
45 HandleMap::value_type(handle_type
, HandleMap::mapped_type()));
48 names
->second
.insert(handle_name
);
49 } else if (!handle_name
) { // Now we need to close all handles of this type.
50 names
->second
.clear();
51 } else if (!names
->second
.empty()) { // Add another name for this type.
52 names
->second
.insert(handle_name
);
53 } // If we're already closing all handles of type then we're done.
58 size_t HandleCloser::GetBufferSize() {
59 size_t bytes_total
= offsetof(HandleCloserInfo
, handle_entries
);
61 for (HandleMap::iterator i
= handles_to_close_
.begin();
62 i
!= handles_to_close_
.end(); ++i
) {
63 size_t bytes_entry
= offsetof(HandleListEntry
, handle_type
) +
64 (i
->first
.size() + 1) * sizeof(char16
);
65 for (HandleMap::mapped_type::iterator j
= i
->second
.begin();
66 j
!= i
->second
.end(); ++j
) {
67 bytes_entry
+= ((*j
).size() + 1) * sizeof(char16
);
70 // Round up to the nearest multiple of word size.
71 bytes_entry
= RoundUpToWordSize(bytes_entry
);
72 bytes_total
+= bytes_entry
;
78 bool HandleCloser::InitializeTargetHandles(TargetProcess
* target
) {
79 // Do nothing on an empty list (global pointer already initialized to NULL).
80 if (handles_to_close_
.empty())
83 size_t bytes_needed
= GetBufferSize();
84 scoped_ptr
<size_t[]> local_buffer(
85 new size_t[bytes_needed
/ sizeof(size_t)]);
87 if (!SetupHandleList(local_buffer
.get(), bytes_needed
))
90 HANDLE child
= target
->Process();
92 // Allocate memory in the target process without specifying the address
93 void* remote_data
= ::VirtualAllocEx(child
, NULL
, bytes_needed
,
94 MEM_COMMIT
, PAGE_READWRITE
);
95 if (NULL
== remote_data
)
98 // Copy the handle buffer over.
100 BOOL result
= ::WriteProcessMemory(child
, remote_data
, local_buffer
.get(),
101 bytes_needed
, &bytes_written
);
102 if (!result
|| bytes_written
!= bytes_needed
) {
103 ::VirtualFreeEx(child
, remote_data
, 0, MEM_RELEASE
);
107 g_handles_to_close
= reinterpret_cast<HandleCloserInfo
*>(remote_data
);
109 ResultCode rc
= target
->TransferVariable("g_handles_to_close",
111 sizeof(g_handles_to_close
));
113 return (SBOX_ALL_OK
== rc
);
116 bool HandleCloser::SetupHandleList(void* buffer
, size_t buffer_bytes
) {
117 ::ZeroMemory(buffer
, buffer_bytes
);
118 HandleCloserInfo
* handle_info
= reinterpret_cast<HandleCloserInfo
*>(buffer
);
119 handle_info
->record_bytes
= buffer_bytes
;
120 handle_info
->num_handle_types
= handles_to_close_
.size();
122 char16
* output
= reinterpret_cast<char16
*>(&handle_info
->handle_entries
[0]);
123 char16
* end
= reinterpret_cast<char16
*>(
124 reinterpret_cast<char*>(buffer
) + buffer_bytes
);
125 for (HandleMap::iterator i
= handles_to_close_
.begin();
126 i
!= handles_to_close_
.end(); ++i
) {
129 HandleListEntry
* list_entry
= reinterpret_cast<HandleListEntry
*>(output
);
130 output
= &list_entry
->handle_type
[0];
132 // Copy the typename and set the offset and count.
133 i
->first
._Copy_s(output
, i
->first
.size(), i
->first
.size());
134 *(output
+= i
->first
.size()) = L
'\0';
136 list_entry
->offset_to_names
= reinterpret_cast<char*>(output
) -
137 reinterpret_cast<char*>(list_entry
);
138 list_entry
->name_count
= i
->second
.size();
140 // Copy the handle names.
141 for (HandleMap::mapped_type::iterator j
= i
->second
.begin();
142 j
!= i
->second
.end(); ++j
) {
143 output
= std::copy((*j
).begin(), (*j
).end(), output
) + 1;
146 // Round up to the nearest multiple of sizeof(size_t).
147 output
= RoundUpToWordSize(output
);
148 list_entry
->record_bytes
= reinterpret_cast<char*>(output
) -
149 reinterpret_cast<char*>(list_entry
);
152 DCHECK_EQ(reinterpret_cast<size_t>(output
), reinterpret_cast<size_t>(end
));
153 return output
<= end
;
156 bool HandleCloser::SetupHandleInterceptions(InterceptionManager
* manager
) {
157 // We need to intercept CreateThread if we're closing ALPC port clients.
158 HandleMap::iterator names
= handles_to_close_
.find(L
"ALPC Port");
159 if (base::win::GetVersion() >= base::win::VERSION_VISTA
&&
160 names
!= handles_to_close_
.end() &&
161 (names
->second
.empty() || names
->second
.size() == 0)) {
162 if (!INTERCEPT_EAT(manager
, kKerneldllName
, CreateThread
,
163 CREATE_THREAD_ID
, 28)) {
166 if (!INTERCEPT_EAT(manager
, kKerneldllName
, GetUserDefaultLCID
,
167 GET_USER_DEFAULT_LCID_ID
, 4)) {
177 bool GetHandleName(HANDLE handle
, string16
* handle_name
) {
178 static NtQueryObject QueryObject
= NULL
;
180 ResolveNTFunctionPtr("NtQueryObject", &QueryObject
);
182 ULONG size
= MAX_PATH
;
183 scoped_ptr
<UNICODE_STRING
, base::FreeDeleter
> name
;
187 name
.reset(static_cast<UNICODE_STRING
*>(malloc(size
)));
189 result
= QueryObject(handle
, ObjectNameInformation
, name
.get(),
191 } while (result
== STATUS_INFO_LENGTH_MISMATCH
||
192 result
== STATUS_BUFFER_OVERFLOW
);
194 if (NT_SUCCESS(result
) && name
->Buffer
&& name
->Length
)
195 handle_name
->assign(name
->Buffer
, name
->Length
/ sizeof(wchar_t));
197 handle_name
->clear();
199 return NT_SUCCESS(result
);
202 } // namespace sandbox