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() {
38 HandleCloser::~HandleCloser() {
41 ResultCode
HandleCloser::AddHandle(const base::char16
* handle_type
,
42 const base::char16
* handle_name
) {
44 return SBOX_ERROR_BAD_PARAMS
;
46 base::string16 resolved_name
;
48 resolved_name
= handle_name
;
49 if (handle_type
== base::string16(L
"Key"))
50 if (!ResolveRegistryName(resolved_name
, &resolved_name
))
51 return SBOX_ERROR_BAD_PARAMS
;
54 HandleMap::iterator names
= handles_to_close_
.find(handle_type
);
55 if (names
== handles_to_close_
.end()) { // We have no entries for this type.
56 std::pair
<HandleMap::iterator
, bool> result
= handles_to_close_
.insert(
57 HandleMap::value_type(handle_type
, HandleMap::mapped_type()));
60 names
->second
.insert(resolved_name
);
61 } else if (!handle_name
) { // Now we need to close all handles of this type.
62 names
->second
.clear();
63 } else if (!names
->second
.empty()) { // Add another name for this type.
64 names
->second
.insert(resolved_name
);
65 } // If we're already closing all handles of type then we're done.
70 size_t HandleCloser::GetBufferSize() {
71 size_t bytes_total
= offsetof(HandleCloserInfo
, handle_entries
);
73 for (HandleMap::iterator i
= handles_to_close_
.begin();
74 i
!= handles_to_close_
.end(); ++i
) {
75 size_t bytes_entry
= offsetof(HandleListEntry
, handle_type
) +
76 (i
->first
.size() + 1) * sizeof(base::char16
);
77 for (HandleMap::mapped_type::iterator j
= i
->second
.begin();
78 j
!= i
->second
.end(); ++j
) {
79 bytes_entry
+= ((*j
).size() + 1) * sizeof(base::char16
);
82 // Round up to the nearest multiple of word size.
83 bytes_entry
= RoundUpToWordSize(bytes_entry
);
84 bytes_total
+= bytes_entry
;
90 bool HandleCloser::InitializeTargetHandles(TargetProcess
* target
) {
91 // Do nothing on an empty list (global pointer already initialized to NULL).
92 if (handles_to_close_
.empty())
95 size_t bytes_needed
= GetBufferSize();
96 scoped_ptr
<size_t[]> local_buffer(
97 new size_t[bytes_needed
/ sizeof(size_t)]);
99 if (!SetupHandleList(local_buffer
.get(), bytes_needed
))
102 HANDLE child
= target
->Process();
104 // Allocate memory in the target process without specifying the address
105 void* remote_data
= ::VirtualAllocEx(child
, NULL
, bytes_needed
,
106 MEM_COMMIT
, PAGE_READWRITE
);
107 if (NULL
== remote_data
)
110 // Copy the handle buffer over.
111 SIZE_T bytes_written
;
112 BOOL result
= ::WriteProcessMemory(child
, remote_data
, local_buffer
.get(),
113 bytes_needed
, &bytes_written
);
114 if (!result
|| bytes_written
!= bytes_needed
) {
115 ::VirtualFreeEx(child
, remote_data
, 0, MEM_RELEASE
);
119 g_handles_to_close
= reinterpret_cast<HandleCloserInfo
*>(remote_data
);
121 ResultCode rc
= target
->TransferVariable("g_handles_to_close",
123 sizeof(g_handles_to_close
));
125 return (SBOX_ALL_OK
== rc
);
128 bool HandleCloser::SetupHandleList(void* buffer
, size_t buffer_bytes
) {
129 ::ZeroMemory(buffer
, buffer_bytes
);
130 HandleCloserInfo
* handle_info
= reinterpret_cast<HandleCloserInfo
*>(buffer
);
131 handle_info
->record_bytes
= buffer_bytes
;
132 handle_info
->num_handle_types
= handles_to_close_
.size();
134 base::char16
* output
= reinterpret_cast<base::char16
*>(
135 &handle_info
->handle_entries
[0]);
136 base::char16
* end
= reinterpret_cast<base::char16
*>(
137 reinterpret_cast<char*>(buffer
) + buffer_bytes
);
138 for (HandleMap::iterator i
= handles_to_close_
.begin();
139 i
!= handles_to_close_
.end(); ++i
) {
142 HandleListEntry
* list_entry
= reinterpret_cast<HandleListEntry
*>(output
);
143 output
= &list_entry
->handle_type
[0];
145 // Copy the typename and set the offset and count.
146 i
->first
._Copy_s(output
, i
->first
.size(), i
->first
.size());
147 *(output
+= i
->first
.size()) = L
'\0';
149 list_entry
->offset_to_names
= reinterpret_cast<char*>(output
) -
150 reinterpret_cast<char*>(list_entry
);
151 list_entry
->name_count
= i
->second
.size();
153 // Copy the handle names.
154 for (HandleMap::mapped_type::iterator j
= i
->second
.begin();
155 j
!= i
->second
.end(); ++j
) {
156 output
= std::copy((*j
).begin(), (*j
).end(), output
) + 1;
159 // Round up to the nearest multiple of sizeof(size_t).
160 output
= RoundUpToWordSize(output
);
161 list_entry
->record_bytes
= reinterpret_cast<char*>(output
) -
162 reinterpret_cast<char*>(list_entry
);
165 DCHECK_EQ(reinterpret_cast<size_t>(output
), reinterpret_cast<size_t>(end
));
166 return output
<= end
;
169 bool GetHandleName(HANDLE handle
, base::string16
* handle_name
) {
170 static NtQueryObject QueryObject
= NULL
;
172 ResolveNTFunctionPtr("NtQueryObject", &QueryObject
);
174 ULONG size
= MAX_PATH
;
175 scoped_ptr
<UNICODE_STRING
, base::FreeDeleter
> name
;
179 name
.reset(static_cast<UNICODE_STRING
*>(malloc(size
)));
181 result
= QueryObject(handle
, ObjectNameInformation
, name
.get(),
183 } while (result
== STATUS_INFO_LENGTH_MISMATCH
||
184 result
== STATUS_BUFFER_OVERFLOW
);
186 if (NT_SUCCESS(result
) && name
->Buffer
&& name
->Length
)
187 handle_name
->assign(name
->Buffer
, name
->Length
/ sizeof(wchar_t));
189 handle_name
->clear();
191 return NT_SUCCESS(result
);
194 } // namespace sandbox