| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "sandbox/src/handle_closer.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "base/memory/scoped_ptr.h" | |
| 9 #include "base/win/windows_version.h" | |
| 10 #include "sandbox/src/interceptors.h" | |
| 11 #include "sandbox/src/internal_types.h" | |
| 12 #include "sandbox/src/nt_internals.h" | |
| 13 #include "sandbox/src/process_thread_interception.h" | |
| 14 #include "sandbox/src/win_utils.h" | |
| 15 | |
| 16 namespace { | |
| 17 | |
| 18 template<typename T> T RoundUpToWordSize(T v) { | |
| 19 if (size_t mod = v % sizeof(size_t)) | |
| 20 v += sizeof(size_t) - mod; | |
| 21 return v; | |
| 22 } | |
| 23 | |
| 24 template<typename T> T* RoundUpToWordSize(T* v) { | |
| 25 return reinterpret_cast<T*>(RoundUpToWordSize(reinterpret_cast<size_t>(v))); | |
| 26 } | |
| 27 | |
| 28 } // namespace | |
| 29 | |
| 30 namespace sandbox { | |
| 31 | |
| 32 // Memory buffer mapped from the parent, with the list of handles. | |
| 33 SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close; | |
| 34 | |
| 35 HandleCloser::HandleCloser() {} | |
| 36 | |
| 37 ResultCode HandleCloser::AddHandle(const char16* handle_type, | |
| 38 const char16* handle_name) { | |
| 39 if (!handle_type) | |
| 40 return SBOX_ERROR_BAD_PARAMS; | |
| 41 | |
| 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())); | |
| 46 names = result.first; | |
| 47 if (handle_name) | |
| 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. | |
| 54 | |
| 55 return SBOX_ALL_OK; | |
| 56 } | |
| 57 | |
| 58 size_t HandleCloser::GetBufferSize() { | |
| 59 size_t bytes_total = offsetof(HandleCloserInfo, handle_entries); | |
| 60 | |
| 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); | |
| 68 } | |
| 69 | |
| 70 // Round up to the nearest multiple of word size. | |
| 71 bytes_entry = RoundUpToWordSize(bytes_entry); | |
| 72 bytes_total += bytes_entry; | |
| 73 } | |
| 74 | |
| 75 return bytes_total; | |
| 76 } | |
| 77 | |
| 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()) | |
| 81 return true; | |
| 82 | |
| 83 size_t bytes_needed = GetBufferSize(); | |
| 84 scoped_array<size_t> local_buffer( | |
| 85 new size_t[bytes_needed / sizeof(size_t)]); | |
| 86 | |
| 87 if (!SetupHandleList(local_buffer.get(), bytes_needed)) | |
| 88 return false; | |
| 89 | |
| 90 HANDLE child = target->Process(); | |
| 91 | |
| 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) | |
| 96 return false; | |
| 97 | |
| 98 // Copy the handle buffer over. | |
| 99 SIZE_T bytes_written; | |
| 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); | |
| 104 return false; | |
| 105 } | |
| 106 | |
| 107 g_handles_to_close = reinterpret_cast<HandleCloserInfo*>(remote_data); | |
| 108 | |
| 109 ResultCode rc = target->TransferVariable("g_handles_to_close", | |
| 110 &g_handles_to_close, | |
| 111 sizeof(g_handles_to_close)); | |
| 112 | |
| 113 return (SBOX_ALL_OK == rc); | |
| 114 } | |
| 115 | |
| 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(); | |
| 121 | |
| 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) { | |
| 127 if (output >= end) | |
| 128 return false; | |
| 129 HandleListEntry* list_entry = reinterpret_cast<HandleListEntry*>(output); | |
| 130 output = &list_entry->handle_type[0]; | |
| 131 | |
| 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'; | |
| 135 output++; | |
| 136 list_entry->offset_to_names = reinterpret_cast<char*>(output) - | |
| 137 reinterpret_cast<char*>(list_entry); | |
| 138 list_entry->name_count = i->second.size(); | |
| 139 | |
| 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; | |
| 144 } | |
| 145 | |
| 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); | |
| 150 } | |
| 151 | |
| 152 DCHECK_EQ(reinterpret_cast<size_t>(output), reinterpret_cast<size_t>(end)); | |
| 153 return output <= end; | |
| 154 } | |
| 155 | |
| 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)) { | |
| 164 return false; | |
| 165 } | |
| 166 if (!INTERCEPT_EAT(manager, kKerneldllName, GetUserDefaultLCID, | |
| 167 GET_USER_DEFAULT_LCID_ID, 4)) { | |
| 168 return false; | |
| 169 } | |
| 170 | |
| 171 return true; | |
| 172 } | |
| 173 | |
| 174 return true; | |
| 175 } | |
| 176 | |
| 177 bool GetHandleName(HANDLE handle, string16* handle_name) { | |
| 178 static NtQueryObject QueryObject = NULL; | |
| 179 if (!QueryObject) | |
| 180 ResolveNTFunctionPtr("NtQueryObject", &QueryObject); | |
| 181 | |
| 182 ULONG size = MAX_PATH; | |
| 183 scoped_ptr<UNICODE_STRING> name; | |
| 184 NTSTATUS result; | |
| 185 | |
| 186 do { | |
| 187 name.reset(reinterpret_cast<UNICODE_STRING*>(new BYTE[size])); | |
| 188 result = QueryObject(handle, ObjectNameInformation, name.get(), | |
| 189 size, &size); | |
| 190 } while (result == STATUS_INFO_LENGTH_MISMATCH || | |
| 191 result == STATUS_BUFFER_OVERFLOW); | |
| 192 | |
| 193 if (NT_SUCCESS(result) && name->Buffer && name->Length) | |
| 194 handle_name->assign(name->Buffer, name->Length / sizeof(wchar_t)); | |
| 195 else | |
| 196 handle_name->clear(); | |
| 197 | |
| 198 return NT_SUCCESS(result); | |
| 199 } | |
| 200 | |
| 201 } // namespace sandbox | |
| OLD | NEW |