michael@0: // Copyright (c) 2011 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "sandbox/win/src/handle_closer.h" michael@0: michael@0: #include "base/logging.h" michael@0: #include "base/memory/scoped_ptr.h" michael@0: #include "base/win/windows_version.h" michael@0: #include "sandbox/win/src/interceptors.h" michael@0: #include "sandbox/win/src/internal_types.h" michael@0: #include "sandbox/win/src/nt_internals.h" michael@0: #include "sandbox/win/src/process_thread_interception.h" michael@0: #include "sandbox/win/src/win_utils.h" michael@0: michael@0: namespace { michael@0: michael@0: template T RoundUpToWordSize(T v) { michael@0: if (size_t mod = v % sizeof(size_t)) michael@0: v += sizeof(size_t) - mod; michael@0: return v; michael@0: } michael@0: michael@0: template T* RoundUpToWordSize(T* v) { michael@0: return reinterpret_cast(RoundUpToWordSize(reinterpret_cast(v))); michael@0: } michael@0: michael@0: } // namespace michael@0: michael@0: namespace sandbox { michael@0: michael@0: // Memory buffer mapped from the parent, with the list of handles. michael@0: SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close; michael@0: michael@0: HandleCloser::HandleCloser() {} michael@0: michael@0: ResultCode HandleCloser::AddHandle(const char16* handle_type, michael@0: const char16* handle_name) { michael@0: if (!handle_type) michael@0: return SBOX_ERROR_BAD_PARAMS; michael@0: michael@0: HandleMap::iterator names = handles_to_close_.find(handle_type); michael@0: if (names == handles_to_close_.end()) { // We have no entries for this type. michael@0: std::pair result = handles_to_close_.insert( michael@0: HandleMap::value_type(handle_type, HandleMap::mapped_type())); michael@0: names = result.first; michael@0: if (handle_name) michael@0: names->second.insert(handle_name); michael@0: } else if (!handle_name) { // Now we need to close all handles of this type. michael@0: names->second.clear(); michael@0: } else if (!names->second.empty()) { // Add another name for this type. michael@0: names->second.insert(handle_name); michael@0: } // If we're already closing all handles of type then we're done. michael@0: michael@0: return SBOX_ALL_OK; michael@0: } michael@0: michael@0: size_t HandleCloser::GetBufferSize() { michael@0: size_t bytes_total = offsetof(HandleCloserInfo, handle_entries); michael@0: michael@0: for (HandleMap::iterator i = handles_to_close_.begin(); michael@0: i != handles_to_close_.end(); ++i) { michael@0: size_t bytes_entry = offsetof(HandleListEntry, handle_type) + michael@0: (i->first.size() + 1) * sizeof(char16); michael@0: for (HandleMap::mapped_type::iterator j = i->second.begin(); michael@0: j != i->second.end(); ++j) { michael@0: bytes_entry += ((*j).size() + 1) * sizeof(char16); michael@0: } michael@0: michael@0: // Round up to the nearest multiple of word size. michael@0: bytes_entry = RoundUpToWordSize(bytes_entry); michael@0: bytes_total += bytes_entry; michael@0: } michael@0: michael@0: return bytes_total; michael@0: } michael@0: michael@0: bool HandleCloser::InitializeTargetHandles(TargetProcess* target) { michael@0: // Do nothing on an empty list (global pointer already initialized to NULL). michael@0: if (handles_to_close_.empty()) michael@0: return true; michael@0: michael@0: size_t bytes_needed = GetBufferSize(); michael@0: scoped_ptr local_buffer( michael@0: new size_t[bytes_needed / sizeof(size_t)]); michael@0: michael@0: if (!SetupHandleList(local_buffer.get(), bytes_needed)) michael@0: return false; michael@0: michael@0: HANDLE child = target->Process(); michael@0: michael@0: // Allocate memory in the target process without specifying the address michael@0: void* remote_data = ::VirtualAllocEx(child, NULL, bytes_needed, michael@0: MEM_COMMIT, PAGE_READWRITE); michael@0: if (NULL == remote_data) michael@0: return false; michael@0: michael@0: // Copy the handle buffer over. michael@0: SIZE_T bytes_written; michael@0: BOOL result = ::WriteProcessMemory(child, remote_data, local_buffer.get(), michael@0: bytes_needed, &bytes_written); michael@0: if (!result || bytes_written != bytes_needed) { michael@0: ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE); michael@0: return false; michael@0: } michael@0: michael@0: g_handles_to_close = reinterpret_cast(remote_data); michael@0: michael@0: ResultCode rc = target->TransferVariable("g_handles_to_close", michael@0: &g_handles_to_close, michael@0: sizeof(g_handles_to_close)); michael@0: michael@0: return (SBOX_ALL_OK == rc); michael@0: } michael@0: michael@0: bool HandleCloser::SetupHandleList(void* buffer, size_t buffer_bytes) { michael@0: ::ZeroMemory(buffer, buffer_bytes); michael@0: HandleCloserInfo* handle_info = reinterpret_cast(buffer); michael@0: handle_info->record_bytes = buffer_bytes; michael@0: handle_info->num_handle_types = handles_to_close_.size(); michael@0: michael@0: char16* output = reinterpret_cast(&handle_info->handle_entries[0]); michael@0: char16* end = reinterpret_cast( michael@0: reinterpret_cast(buffer) + buffer_bytes); michael@0: for (HandleMap::iterator i = handles_to_close_.begin(); michael@0: i != handles_to_close_.end(); ++i) { michael@0: if (output >= end) michael@0: return false; michael@0: HandleListEntry* list_entry = reinterpret_cast(output); michael@0: output = &list_entry->handle_type[0]; michael@0: michael@0: // Copy the typename and set the offset and count. michael@0: i->first._Copy_s(output, i->first.size(), i->first.size()); michael@0: *(output += i->first.size()) = L'\0'; michael@0: output++; michael@0: list_entry->offset_to_names = reinterpret_cast(output) - michael@0: reinterpret_cast(list_entry); michael@0: list_entry->name_count = i->second.size(); michael@0: michael@0: // Copy the handle names. michael@0: for (HandleMap::mapped_type::iterator j = i->second.begin(); michael@0: j != i->second.end(); ++j) { michael@0: output = std::copy((*j).begin(), (*j).end(), output) + 1; michael@0: } michael@0: michael@0: // Round up to the nearest multiple of sizeof(size_t). michael@0: output = RoundUpToWordSize(output); michael@0: list_entry->record_bytes = reinterpret_cast(output) - michael@0: reinterpret_cast(list_entry); michael@0: } michael@0: michael@0: DCHECK_EQ(reinterpret_cast(output), reinterpret_cast(end)); michael@0: return output <= end; michael@0: } michael@0: michael@0: bool HandleCloser::SetupHandleInterceptions(InterceptionManager* manager) { michael@0: // We need to intercept CreateThread if we're closing ALPC port clients. michael@0: HandleMap::iterator names = handles_to_close_.find(L"ALPC Port"); michael@0: if (base::win::GetVersion() >= base::win::VERSION_VISTA && michael@0: names != handles_to_close_.end() && michael@0: (names->second.empty() || names->second.size() == 0)) { michael@0: if (!INTERCEPT_EAT(manager, kKerneldllName, CreateThread, michael@0: CREATE_THREAD_ID, 28)) { michael@0: return false; michael@0: } michael@0: if (!INTERCEPT_EAT(manager, kKerneldllName, GetUserDefaultLCID, michael@0: GET_USER_DEFAULT_LCID_ID, 4)) { michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool GetHandleName(HANDLE handle, string16* handle_name) { michael@0: static NtQueryObject QueryObject = NULL; michael@0: if (!QueryObject) michael@0: ResolveNTFunctionPtr("NtQueryObject", &QueryObject); michael@0: michael@0: ULONG size = MAX_PATH; michael@0: scoped_ptr name; michael@0: NTSTATUS result; michael@0: michael@0: do { michael@0: name.reset(static_cast(malloc(size))); michael@0: DCHECK(name.get()); michael@0: result = QueryObject(handle, ObjectNameInformation, name.get(), michael@0: size, &size); michael@0: } while (result == STATUS_INFO_LENGTH_MISMATCH || michael@0: result == STATUS_BUFFER_OVERFLOW); michael@0: michael@0: if (NT_SUCCESS(result) && name->Buffer && name->Length) michael@0: handle_name->assign(name->Buffer, name->Length / sizeof(wchar_t)); michael@0: else michael@0: handle_name->clear(); michael@0: michael@0: return NT_SUCCESS(result); michael@0: } michael@0: michael@0: } // namespace sandbox