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 base::char16
* handle_type
,
38 const base::char16
* handle_name
) {
40 return SBOX_ERROR_BAD_PARAMS
;
42 base::string16 resolved_name
;
44 resolved_name
= handle_name
;
45 if (handle_type
== base::string16(L
"Key"))
46 if (!ResolveRegistryName(resolved_name
, &resolved_name
))
47 return SBOX_ERROR_BAD_PARAMS
;
50 HandleMap::iterator names
= handles_to_close_
.find(handle_type
);
51 if (names
== handles_to_close_
.end()) { // We have no entries for this type.
52 std::pair
<HandleMap::iterator
, bool> result
= handles_to_close_
.insert(
53 HandleMap::value_type(handle_type
, HandleMap::mapped_type()));
56 names
->second
.insert(resolved_name
);
57 } else if (!handle_name
) { // Now we need to close all handles of this type.
58 names
->second
.clear();
59 } else if (!names
->second
.empty()) { // Add another name for this type.
60 names
->second
.insert(resolved_name
);
61 } // If we're already closing all handles of type then we're done.
66 size_t HandleCloser::GetBufferSize() {
67 size_t bytes_total
= offsetof(HandleCloserInfo
, handle_entries
);
69 for (HandleMap::iterator i
= handles_to_close_
.begin();
70 i
!= handles_to_close_
.end(); ++i
) {
71 size_t bytes_entry
= offsetof(HandleListEntry
, handle_type
) +
72 (i
->first
.size() + 1) * sizeof(base::char16
);
73 for (HandleMap::mapped_type::iterator j
= i
->second
.begin();
74 j
!= i
->second
.end(); ++j
) {
75 bytes_entry
+= ((*j
).size() + 1) * sizeof(base::char16
);
78 // Round up to the nearest multiple of word size.
79 bytes_entry
= RoundUpToWordSize(bytes_entry
);
80 bytes_total
+= bytes_entry
;
86 bool HandleCloser::InitializeTargetHandles(TargetProcess
* target
) {
87 // Do nothing on an empty list (global pointer already initialized to NULL).
88 if (handles_to_close_
.empty())
91 size_t bytes_needed
= GetBufferSize();
92 scoped_ptr
<size_t[]> local_buffer(
93 new size_t[bytes_needed
/ sizeof(size_t)]);
95 if (!SetupHandleList(local_buffer
.get(), bytes_needed
))
98 HANDLE child
= target
->Process();
100 // Allocate memory in the target process without specifying the address
101 void* remote_data
= ::VirtualAllocEx(child
, NULL
, bytes_needed
,
102 MEM_COMMIT
, PAGE_READWRITE
);
103 if (NULL
== remote_data
)
106 // Copy the handle buffer over.
107 SIZE_T bytes_written
;
108 BOOL result
= ::WriteProcessMemory(child
, remote_data
, local_buffer
.get(),
109 bytes_needed
, &bytes_written
);
110 if (!result
|| bytes_written
!= bytes_needed
) {
111 ::VirtualFreeEx(child
, remote_data
, 0, MEM_RELEASE
);
115 g_handles_to_close
= reinterpret_cast<HandleCloserInfo
*>(remote_data
);
117 ResultCode rc
= target
->TransferVariable("g_handles_to_close",
119 sizeof(g_handles_to_close
));
121 return (SBOX_ALL_OK
== rc
);
124 bool HandleCloser::SetupHandleList(void* buffer
, size_t buffer_bytes
) {
125 ::ZeroMemory(buffer
, buffer_bytes
);
126 HandleCloserInfo
* handle_info
= reinterpret_cast<HandleCloserInfo
*>(buffer
);
127 handle_info
->record_bytes
= buffer_bytes
;
128 handle_info
->num_handle_types
= handles_to_close_
.size();
130 base::char16
* output
= reinterpret_cast<base::char16
*>(
131 &handle_info
->handle_entries
[0]);
132 base::char16
* end
= reinterpret_cast<base::char16
*>(
133 reinterpret_cast<char*>(buffer
) + buffer_bytes
);
134 for (HandleMap::iterator i
= handles_to_close_
.begin();
135 i
!= handles_to_close_
.end(); ++i
) {
138 HandleListEntry
* list_entry
= reinterpret_cast<HandleListEntry
*>(output
);
139 output
= &list_entry
->handle_type
[0];
141 // Copy the typename and set the offset and count.
142 i
->first
._Copy_s(output
, i
->first
.size(), i
->first
.size());
143 *(output
+= i
->first
.size()) = L
'\0';
145 list_entry
->offset_to_names
= reinterpret_cast<char*>(output
) -
146 reinterpret_cast<char*>(list_entry
);
147 list_entry
->name_count
= i
->second
.size();
149 // Copy the handle names.
150 for (HandleMap::mapped_type::iterator j
= i
->second
.begin();
151 j
!= i
->second
.end(); ++j
) {
152 output
= std::copy((*j
).begin(), (*j
).end(), output
) + 1;
155 // Round up to the nearest multiple of sizeof(size_t).
156 output
= RoundUpToWordSize(output
);
157 list_entry
->record_bytes
= reinterpret_cast<char*>(output
) -
158 reinterpret_cast<char*>(list_entry
);
161 DCHECK_EQ(reinterpret_cast<size_t>(output
), reinterpret_cast<size_t>(end
));
162 return output
<= end
;
165 bool GetHandleName(HANDLE handle
, base::string16
* handle_name
) {
166 static NtQueryObject QueryObject
= NULL
;
168 ResolveNTFunctionPtr("NtQueryObject", &QueryObject
);
170 ULONG size
= MAX_PATH
;
171 scoped_ptr
<UNICODE_STRING
, base::FreeDeleter
> name
;
175 name
.reset(static_cast<UNICODE_STRING
*>(malloc(size
)));
177 result
= QueryObject(handle
, ObjectNameInformation
, name
.get(),
179 } while (result
== STATUS_INFO_LENGTH_MISMATCH
||
180 result
== STATUS_BUFFER_OVERFLOW
);
182 if (NT_SUCCESS(result
) && name
->Buffer
&& name
->Length
)
183 handle_name
->assign(name
->Buffer
, name
->Length
/ sizeof(wchar_t));
185 handle_name
->clear();
187 return NT_SUCCESS(result
);
190 } // namespace sandbox