michael@0: // Copyright (c) 2012 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_agent.h" michael@0: michael@0: #include "base/logging.h" michael@0: #include "sandbox/win/src/nt_internals.h" michael@0: #include "sandbox/win/src/win_utils.h" michael@0: michael@0: namespace { michael@0: michael@0: // Returns type infomation for an NT object. This routine is expected to be michael@0: // called for invalid handles so it catches STATUS_INVALID_HANDLE exceptions michael@0: // that can be generated when handle tracing is enabled. michael@0: NTSTATUS QueryObjectTypeInformation(HANDLE handle, michael@0: void* buffer, michael@0: ULONG* size) { michael@0: static NtQueryObject QueryObject = NULL; michael@0: if (!QueryObject) michael@0: ResolveNTFunctionPtr("NtQueryObject", &QueryObject); michael@0: michael@0: NTSTATUS status = STATUS_UNSUCCESSFUL; michael@0: __try { michael@0: status = QueryObject(handle, ObjectTypeInformation, buffer, *size, size); michael@0: } __except(GetExceptionCode() == STATUS_INVALID_HANDLE ? michael@0: EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { michael@0: status = STATUS_INVALID_HANDLE; michael@0: } michael@0: return status; 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 = NULL; michael@0: michael@0: bool HandleCloserAgent::NeedsHandlesClosed() { michael@0: return g_handles_to_close != NULL; michael@0: } michael@0: michael@0: // Reads g_handles_to_close and creates the lookup map. michael@0: void HandleCloserAgent::InitializeHandlesToClose() { michael@0: CHECK(g_handles_to_close != NULL); michael@0: michael@0: // Grab the header. michael@0: HandleListEntry* entry = g_handles_to_close->handle_entries; michael@0: for (size_t i = 0; i < g_handles_to_close->num_handle_types; ++i) { michael@0: // Set the type name. michael@0: char16* input = entry->handle_type; michael@0: HandleMap::mapped_type& handle_names = handles_to_close_[input]; michael@0: input = reinterpret_cast(reinterpret_cast(entry) michael@0: + entry->offset_to_names); michael@0: // Grab all the handle names. michael@0: for (size_t j = 0; j < entry->name_count; ++j) { michael@0: std::pair name michael@0: = handle_names.insert(input); michael@0: CHECK(name.second); michael@0: input += name.first->size() + 1; michael@0: } michael@0: michael@0: // Move on to the next entry. michael@0: entry = reinterpret_cast(reinterpret_cast(entry) michael@0: + entry->record_bytes); michael@0: michael@0: DCHECK(reinterpret_cast(entry) >= input); michael@0: DCHECK(reinterpret_cast(entry) - input < michael@0: sizeof(size_t) / sizeof(char16)); michael@0: } michael@0: michael@0: // Clean up the memory we copied over. michael@0: ::VirtualFree(g_handles_to_close, 0, MEM_RELEASE); michael@0: g_handles_to_close = NULL; michael@0: } michael@0: michael@0: bool HandleCloserAgent::CloseHandles() { michael@0: DWORD handle_count = UINT_MAX; michael@0: const int kInvalidHandleThreshold = 100; michael@0: const size_t kHandleOffset = sizeof(HANDLE); michael@0: michael@0: if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count)) michael@0: return false; michael@0: michael@0: // Set up buffers for the type info and the name. michael@0: std::vector type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) + michael@0: 32 * sizeof(wchar_t)); michael@0: OBJECT_TYPE_INFORMATION* type_info = michael@0: reinterpret_cast(&(type_info_buffer[0])); michael@0: string16 handle_name; michael@0: HANDLE handle = NULL; michael@0: int invalid_count = 0; michael@0: michael@0: // Keep incrementing until we hit the number of handles reported by michael@0: // GetProcessHandleCount(). If we hit a very long sequence of invalid michael@0: // handles we assume that we've run past the end of the table. michael@0: while (handle_count && invalid_count < kInvalidHandleThreshold) { michael@0: reinterpret_cast(handle) += kHandleOffset; michael@0: NTSTATUS rc; michael@0: michael@0: // Get the type name, reusing the buffer. michael@0: ULONG size = static_cast(type_info_buffer.size()); michael@0: rc = QueryObjectTypeInformation(handle, type_info, &size); michael@0: while (rc == STATUS_INFO_LENGTH_MISMATCH || michael@0: rc == STATUS_BUFFER_OVERFLOW) { michael@0: type_info_buffer.resize(size + sizeof(wchar_t)); michael@0: type_info = reinterpret_cast( michael@0: &(type_info_buffer[0])); michael@0: rc = QueryObjectTypeInformation(handle, type_info, &size); michael@0: // Leave padding for the nul terminator. michael@0: if (NT_SUCCESS(rc) && size == type_info_buffer.size()) michael@0: rc = STATUS_INFO_LENGTH_MISMATCH; michael@0: } michael@0: if (!NT_SUCCESS(rc) || !type_info->Name.Buffer) { michael@0: ++invalid_count; michael@0: continue; michael@0: } michael@0: michael@0: --handle_count; michael@0: type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0'; michael@0: michael@0: // Check if we're looking for this type of handle. michael@0: HandleMap::iterator result = michael@0: handles_to_close_.find(type_info->Name.Buffer); michael@0: if (result != handles_to_close_.end()) { michael@0: HandleMap::mapped_type& names = result->second; michael@0: // Empty set means close all handles of this type; otherwise check name. michael@0: if (!names.empty()) { michael@0: // Move on to the next handle if this name doesn't match. michael@0: if (!GetHandleName(handle, &handle_name) || !names.count(handle_name)) michael@0: continue; michael@0: } michael@0: michael@0: if (!::SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0)) michael@0: return false; michael@0: if (!::CloseHandle(handle)) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: } // namespace sandbox