| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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/win/src/handle_closer_agent.h" | |
| 6 | |
| 7 #include <limits.h> | |
| 8 #include <stddef.h> | |
| 9 | |
| 10 #include "base/logging.h" | |
| 11 #include "sandbox/win/src/nt_internals.h" | |
| 12 #include "sandbox/win/src/win_utils.h" | |
| 13 | |
| 14 namespace { | |
| 15 | |
| 16 // Returns type infomation for an NT object. This routine is expected to be | |
| 17 // called for invalid handles so it catches STATUS_INVALID_HANDLE exceptions | |
| 18 // that can be generated when handle tracing is enabled. | |
| 19 NTSTATUS QueryObjectTypeInformation(HANDLE handle, | |
| 20 void* buffer, | |
| 21 ULONG* size) { | |
| 22 static NtQueryObject QueryObject = NULL; | |
| 23 if (!QueryObject) | |
| 24 ResolveNTFunctionPtr("NtQueryObject", &QueryObject); | |
| 25 | |
| 26 NTSTATUS status = STATUS_UNSUCCESSFUL; | |
| 27 __try { | |
| 28 status = QueryObject(handle, ObjectTypeInformation, buffer, *size, size); | |
| 29 } __except(GetExceptionCode() == STATUS_INVALID_HANDLE ? | |
| 30 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { | |
| 31 status = STATUS_INVALID_HANDLE; | |
| 32 } | |
| 33 return status; | |
| 34 } | |
| 35 | |
| 36 } // namespace | |
| 37 | |
| 38 namespace sandbox { | |
| 39 | |
| 40 // Memory buffer mapped from the parent, with the list of handles. | |
| 41 SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close = NULL; | |
| 42 | |
| 43 bool HandleCloserAgent::NeedsHandlesClosed() { | |
| 44 return g_handles_to_close != NULL; | |
| 45 } | |
| 46 | |
| 47 HandleCloserAgent::HandleCloserAgent() | |
| 48 : dummy_handle_(::CreateEvent(NULL, FALSE, FALSE, NULL)) { | |
| 49 } | |
| 50 | |
| 51 HandleCloserAgent::~HandleCloserAgent() { | |
| 52 } | |
| 53 | |
| 54 // Attempts to stuff |closed_handle| with a duplicated handle for a dummy Event | |
| 55 // with no access. This should allow the handle to be closed, to avoid | |
| 56 // generating EXCEPTION_INVALID_HANDLE on shutdown, but nothing else. For now | |
| 57 // the only supported |type| is Event or File. | |
| 58 bool HandleCloserAgent::AttemptToStuffHandleSlot(HANDLE closed_handle, | |
| 59 const base::string16& type) { | |
| 60 // Only attempt to stuff Files and Events at the moment. | |
| 61 if (type != L"Event" && type != L"File") { | |
| 62 return true; | |
| 63 } | |
| 64 | |
| 65 if (!dummy_handle_.IsValid()) | |
| 66 return false; | |
| 67 | |
| 68 // This should never happen, as g_dummy is created before closing to_stuff. | |
| 69 DCHECK(dummy_handle_.Get() != closed_handle); | |
| 70 | |
| 71 std::vector<HANDLE> to_close; | |
| 72 HANDLE dup_dummy = NULL; | |
| 73 size_t count = 16; | |
| 74 | |
| 75 do { | |
| 76 if (!::DuplicateHandle(::GetCurrentProcess(), dummy_handle_.Get(), | |
| 77 ::GetCurrentProcess(), &dup_dummy, 0, FALSE, 0)) | |
| 78 break; | |
| 79 if (dup_dummy != closed_handle) | |
| 80 to_close.push_back(dup_dummy); | |
| 81 } while (count-- && | |
| 82 reinterpret_cast<uintptr_t>(dup_dummy) < | |
| 83 reinterpret_cast<uintptr_t>(closed_handle)); | |
| 84 | |
| 85 for (auto h : to_close) | |
| 86 ::CloseHandle(h); | |
| 87 | |
| 88 // Useful to know when we're not able to stuff handles. | |
| 89 DCHECK(dup_dummy == closed_handle); | |
| 90 | |
| 91 return dup_dummy == closed_handle; | |
| 92 } | |
| 93 | |
| 94 // Reads g_handles_to_close and creates the lookup map. | |
| 95 void HandleCloserAgent::InitializeHandlesToClose(bool* is_csrss_connected) { | |
| 96 CHECK(g_handles_to_close != NULL); | |
| 97 | |
| 98 // Default to connected state | |
| 99 *is_csrss_connected = true; | |
| 100 | |
| 101 // Grab the header. | |
| 102 HandleListEntry* entry = g_handles_to_close->handle_entries; | |
| 103 for (size_t i = 0; i < g_handles_to_close->num_handle_types; ++i) { | |
| 104 // Set the type name. | |
| 105 base::char16* input = entry->handle_type; | |
| 106 if (!wcscmp(input, L"ALPC Port")) { | |
| 107 *is_csrss_connected = false; | |
| 108 } | |
| 109 HandleMap::mapped_type& handle_names = handles_to_close_[input]; | |
| 110 input = reinterpret_cast<base::char16*>(reinterpret_cast<char*>(entry) | |
| 111 + entry->offset_to_names); | |
| 112 // Grab all the handle names. | |
| 113 for (size_t j = 0; j < entry->name_count; ++j) { | |
| 114 std::pair<HandleMap::mapped_type::iterator, bool> name | |
| 115 = handle_names.insert(input); | |
| 116 CHECK(name.second); | |
| 117 input += name.first->size() + 1; | |
| 118 } | |
| 119 | |
| 120 // Move on to the next entry. | |
| 121 entry = reinterpret_cast<HandleListEntry*>(reinterpret_cast<char*>(entry) | |
| 122 + entry->record_bytes); | |
| 123 | |
| 124 DCHECK(reinterpret_cast<base::char16*>(entry) >= input); | |
| 125 DCHECK(reinterpret_cast<base::char16*>(entry) - input < | |
| 126 static_cast<ptrdiff_t>(sizeof(size_t) / sizeof(base::char16))); | |
| 127 } | |
| 128 | |
| 129 // Clean up the memory we copied over. | |
| 130 ::VirtualFree(g_handles_to_close, 0, MEM_RELEASE); | |
| 131 g_handles_to_close = NULL; | |
| 132 } | |
| 133 | |
| 134 bool HandleCloserAgent::CloseHandles() { | |
| 135 DWORD handle_count = UINT_MAX; | |
| 136 const int kInvalidHandleThreshold = 100; | |
| 137 const size_t kHandleOffset = 4; // Handles are always a multiple of 4. | |
| 138 | |
| 139 if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count)) | |
| 140 return false; | |
| 141 | |
| 142 // Set up buffers for the type info and the name. | |
| 143 std::vector<BYTE> type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) + | |
| 144 32 * sizeof(wchar_t)); | |
| 145 OBJECT_TYPE_INFORMATION* type_info = | |
| 146 reinterpret_cast<OBJECT_TYPE_INFORMATION*>(&(type_info_buffer[0])); | |
| 147 base::string16 handle_name; | |
| 148 HANDLE handle = NULL; | |
| 149 int invalid_count = 0; | |
| 150 | |
| 151 // Keep incrementing until we hit the number of handles reported by | |
| 152 // GetProcessHandleCount(). If we hit a very long sequence of invalid | |
| 153 // handles we assume that we've run past the end of the table. | |
| 154 while (handle_count && invalid_count < kInvalidHandleThreshold) { | |
| 155 reinterpret_cast<size_t&>(handle) += kHandleOffset; | |
| 156 NTSTATUS rc; | |
| 157 | |
| 158 // Get the type name, reusing the buffer. | |
| 159 ULONG size = static_cast<ULONG>(type_info_buffer.size()); | |
| 160 rc = QueryObjectTypeInformation(handle, type_info, &size); | |
| 161 while (rc == STATUS_INFO_LENGTH_MISMATCH || | |
| 162 rc == STATUS_BUFFER_OVERFLOW) { | |
| 163 type_info_buffer.resize(size + sizeof(wchar_t)); | |
| 164 type_info = reinterpret_cast<OBJECT_TYPE_INFORMATION*>( | |
| 165 &(type_info_buffer[0])); | |
| 166 rc = QueryObjectTypeInformation(handle, type_info, &size); | |
| 167 // Leave padding for the nul terminator. | |
| 168 if (NT_SUCCESS(rc) && size == type_info_buffer.size()) | |
| 169 rc = STATUS_INFO_LENGTH_MISMATCH; | |
| 170 } | |
| 171 if (!NT_SUCCESS(rc) || !type_info->Name.Buffer) { | |
| 172 ++invalid_count; | |
| 173 continue; | |
| 174 } | |
| 175 | |
| 176 --handle_count; | |
| 177 type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0'; | |
| 178 | |
| 179 // Check if we're looking for this type of handle. | |
| 180 HandleMap::iterator result = | |
| 181 handles_to_close_.find(type_info->Name.Buffer); | |
| 182 if (result != handles_to_close_.end()) { | |
| 183 HandleMap::mapped_type& names = result->second; | |
| 184 // Empty set means close all handles of this type; otherwise check name. | |
| 185 if (!names.empty()) { | |
| 186 // Move on to the next handle if this name doesn't match. | |
| 187 if (!GetHandleName(handle, &handle_name) || !names.count(handle_name)) | |
| 188 continue; | |
| 189 } | |
| 190 | |
| 191 if (!::SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0)) | |
| 192 return false; | |
| 193 if (!::CloseHandle(handle)) | |
| 194 return false; | |
| 195 // Attempt to stuff this handle with a new dummy Event. | |
| 196 AttemptToStuffHandleSlot(handle, result->first); | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 return true; | |
| 201 } | |
| 202 | |
| 203 } // namespace sandbox | |
| OLD | NEW |