diff options
author | jschuh@chromium.org <jschuh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-24 22:17:06 +0000 |
---|---|---|
committer | jschuh@chromium.org <jschuh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-24 22:17:06 +0000 |
commit | 284dadf39378ae2ef6b18edce0a7b4f54364f9a0 (patch) | |
tree | 538aaaa212067f195cb138d0d5a8322aea7d0d50 /sandbox/src | |
parent | 1f751e27b7e0153f718564989bd9064b461e8ed2 (diff) | |
download | chromium_src-284dadf39378ae2ef6b18edce0a7b4f54364f9a0.zip chromium_src-284dadf39378ae2ef6b18edce0a7b4f54364f9a0.tar.gz chromium_src-284dadf39378ae2ef6b18edce0a7b4f54364f9a0.tar.bz2 |
Factor Windows handle enumeration code into its own classesI did some general cleanup and isolated out the handle enumeration.
TEST=sbox_unittests.exe --gtest_filter=HandleTable.*
BUG=86521
Review URL: http://codereview.chromium.org/7206007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@90449 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox/src')
-rw-r--r-- | sandbox/src/handle_table.cc | 195 | ||||
-rw-r--r-- | sandbox/src/handle_table.h | 161 | ||||
-rw-r--r-- | sandbox/src/handle_table_unittest.cc | 66 | ||||
-rw-r--r-- | sandbox/src/nt_internals.h | 87 |
4 files changed, 509 insertions, 0 deletions
diff --git a/sandbox/src/handle_table.cc b/sandbox/src/handle_table.cc new file mode 100644 index 0000000..be84fa3 --- /dev/null +++ b/sandbox/src/handle_table.cc @@ -0,0 +1,195 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/handle_table.h" + +#include <algorithm> +#include <cstdlib> + +#include "base/memory/scoped_ptr.h" + +namespace { + +const wchar_t kNtdllDllName[] = L"ntdll.dll"; + +bool CompareHandleEntries(const SYSTEM_HANDLE_INFORMATION& a, + const SYSTEM_HANDLE_INFORMATION& b) { + return a.ProcessId < b.ProcessId; +} + +} // namespace + +namespace sandbox { + +HMODULE HandleTable::ntdll_ = 0; +const char16* HandleTable::kTypeProcess = L"Process"; +const char16* HandleTable::kTypeThread = L"Thread"; +const char16* HandleTable::kTypeFile = L"File"; +const char16* HandleTable::kTypeDirectory = L"Directory"; +const char16* HandleTable::kTypeKey = L"Key"; +const char16* HandleTable::kTypeWindowStation = L"WindowStation"; +const char16* HandleTable::kTypeDesktop = L"Desktop"; +const char16* HandleTable::kTypeService = L"Service"; +const char16* HandleTable::kTypeMutex = L"Mutex"; +const char16* HandleTable::kTypeSemaphore = L"Semaphore"; +const char16* HandleTable::kTypeEvent = L"Event"; +const char16* HandleTable::kTypeTimer = L"Timer"; +const char16* HandleTable::kTypeNamedPipe = L"NamedPipe"; +const char16* HandleTable::kTypeJobObject = L"JobObject"; +const char16* HandleTable::kTypeFileMap = L"FileMap"; +const char16* HandleTable::kTypeAlpcPort = L"ALPC Port"; + +HandleTable::HandleTable() { + static NtQuerySystemInformation QuerySystemInformation; + if (!QuerySystemInformation) { + if (!ntdll_ && !(ntdll_ = ::GetModuleHandle(kNtdllDllName))) + return; + QuerySystemInformation = reinterpret_cast<NtQuerySystemInformation>( + ::GetProcAddress(ntdll_, "NtQuerySystemInformation")); + if (!QuerySystemInformation) + return; + } + + ULONG size = 0x15000; + NTSTATUS result; + do { + handle_info_buffer_.resize(size); + result = QuerySystemInformation(SystemHandleInformation, + handle_info_internal(), size, &size); + } while (result == STATUS_INFO_LENGTH_MISMATCH); + + // We failed, so make an empty table. + if (!NT_SUCCESS(result)) { + handle_info_buffer_.resize(0); + return; + } + + // Sort it to make process lookups faster. + std::sort(handle_info_internal()->Information, + handle_info_internal()->Information + + handle_info_internal()->NumberOfHandles, CompareHandleEntries); +} + +HandleTable::Iterator HandleTable::HandlesForProcess(ULONG process_id) const { + SYSTEM_HANDLE_INFORMATION key; + key.ProcessId = process_id; + + const SYSTEM_HANDLE_INFORMATION* start = handle_info()->Information; + const SYSTEM_HANDLE_INFORMATION* finish = + &handle_info()->Information[handle_info()->NumberOfHandles]; + + start = std::lower_bound(start, finish, key, CompareHandleEntries); + if (start->ProcessId != process_id) + return Iterator(*this, finish, finish); + finish = std::upper_bound(start, finish, key, CompareHandleEntries); + return Iterator(*this, start, finish); +} + +HandleTable::HandleEntry::HandleEntry( + const SYSTEM_HANDLE_INFORMATION* handle_info_entry) + : handle_entry_(handle_info_entry), last_entry_(0) { +} + +void HandleTable::HandleEntry::UpdateInfo(UpdateType flag) { + static NtQueryObject QueryObject; + if (!QueryObject) { + if (!ntdll_ && !(ntdll_ = ::GetModuleHandle(kNtdllDllName))) + return; + QueryObject = reinterpret_cast<NtQueryObject>(::GetProcAddress(ntdll_, + "NtQueryObject")); + if (!QueryObject) + return; + } + + NTSTATUS result; + + // Always update the basic type info, but grab the names as needed. + if (needs_info_update()) { + handle_name_.clear(); + type_name_.clear(); + last_entry_ = handle_entry_; + + // Most handle names are very short, so start small and reuse this buffer. + if (type_info_buffer_.empty()) + type_info_buffer_.resize(sizeof(OBJECT_TYPE_INFORMATION) + + (32 * sizeof(wchar_t))); + ULONG size = static_cast<ULONG>(type_info_buffer_.size()); + result = QueryObject(reinterpret_cast<HANDLE>(handle_entry_->Handle), + ObjectTypeInformation, type_info_internal(), size, &size); + while (result == STATUS_INFO_LENGTH_MISMATCH) { + type_info_buffer_.resize(size); + result = QueryObject(reinterpret_cast<HANDLE>(handle_entry_->Handle), + ObjectTypeInformation, type_info_internal(), size, &size); + } + + if (!NT_SUCCESS(result)) { + type_info_buffer_.clear(); + return; + } + } + + // Don't bother copying out names until we ask for them, and then cache them. + switch (flag) { + case UPDATE_INFO_AND_NAME: + if (type_info_buffer_.size() && handle_name_.empty()) { + ULONG size = MAX_PATH; + scoped_ptr<UNICODE_STRING> name; + do { + name.reset(reinterpret_cast<UNICODE_STRING*>(new BYTE[size])); + result = QueryObject(reinterpret_cast<HANDLE>( + handle_entry_->Handle), ObjectNameInformation, name.get(), + size, &size); + } while (result == STATUS_INFO_LENGTH_MISMATCH); + + if (NT_SUCCESS(result)) { + handle_name_.assign(name->Buffer, name->Length / sizeof(wchar_t)); + } + } + break; + + case UPDATE_INFO_AND_TYPE_NAME: + if (!type_info_buffer_.empty() && type_info_internal()->Name.Buffer && + type_name_.empty()) { + type_name_.assign(type_info_internal()->Name.Buffer, + type_info_internal()->Name.Length / sizeof(wchar_t)); + } + break; + } +} + +const OBJECT_TYPE_INFORMATION* HandleTable::HandleEntry::TypeInfo() { + UpdateInfo(UPDATE_INFO_ONLY); + return type_info_buffer_.empty() ? NULL : type_info_internal(); +} + +const string16& HandleTable::HandleEntry::Name() { + UpdateInfo(UPDATE_INFO_AND_NAME); + return handle_name_; +} + +const string16& HandleTable::HandleEntry::Type() { + UpdateInfo(UPDATE_INFO_AND_TYPE_NAME); + return type_name_; +} + +bool HandleTable::HandleEntry::IsType(const string16& type_string) { + UpdateInfo(UPDATE_INFO_ONLY); + if (type_info_buffer_.empty()) + return false; + return type_string.compare(0, + type_info_internal()->Name.Length / sizeof(wchar_t), + type_info_internal()->Name.Buffer) == 0; +} + +HandleTable::Iterator::Iterator(const HandleTable& table, + const SYSTEM_HANDLE_INFORMATION* start, + const SYSTEM_HANDLE_INFORMATION* end) + : table_(table), current_(start), end_(end) { +} + +HandleTable::Iterator::Iterator(const Iterator& it) + : table_(it.table_), current_(it.current_.handle_entry_), end_(it.end_) { +} + +} // namespace sandbox diff --git a/sandbox/src/handle_table.h b/sandbox/src/handle_table.h new file mode 100644 index 0000000..8d24524 --- /dev/null +++ b/sandbox/src/handle_table.h @@ -0,0 +1,161 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_HANDLE_TABLE_H_ +#define SANDBOX_SRC_HANDLE_TABLE_H_ +#pragma once + +#include <windows.h> +#include <vector> + +#include "base/basictypes.h" +#include "base/string16.h" +#include "sandbox/src/nt_internals.h" + +namespace sandbox { + +// HandleTable retrieves the global handle table and provides helper classes +// for iterating through the table and retrieving handle info. +class HandleTable { + public: + static const char16* HandleTable::kTypeProcess; + static const char16* HandleTable::kTypeThread; + static const char16* HandleTable::kTypeFile; + static const char16* HandleTable::kTypeDirectory; + static const char16* HandleTable::kTypeKey; + static const char16* HandleTable::kTypeWindowStation; + static const char16* HandleTable::kTypeDesktop; + static const char16* HandleTable::kTypeService; + static const char16* HandleTable::kTypeMutex; + static const char16* HandleTable::kTypeSemaphore; + static const char16* HandleTable::kTypeEvent; + static const char16* HandleTable::kTypeTimer; + static const char16* HandleTable::kTypeNamedPipe; + static const char16* HandleTable::kTypeJobObject; + static const char16* HandleTable::kTypeFileMap; + static const char16* HandleTable::kTypeAlpcPort; + + class Iterator; + + // Used by the iterator to provide simple caching accessors to handle data. + class HandleEntry { + public: + bool operator==(const HandleEntry& rhs) const { + return handle_entry_ == rhs.handle_entry_; + } + + bool operator!=(const HandleEntry& rhs) const { + return handle_entry_ != rhs.handle_entry_; + } + + const SYSTEM_HANDLE_INFORMATION* handle_entry() const { + return handle_entry_; + } + + const OBJECT_TYPE_INFORMATION* TypeInfo(); + + const string16& Name(); + + const string16& Type(); + + bool IsType(const string16& type_string); + + private: + friend class Iterator; + friend class HandleTable; + + enum UpdateType { + UPDATE_INFO_ONLY, + UPDATE_INFO_AND_NAME, + UPDATE_INFO_AND_TYPE_NAME, + }; + + explicit HandleEntry(const SYSTEM_HANDLE_INFORMATION* handle_info_entry); + + bool needs_info_update() { return handle_entry_ != last_entry_; } + + void UpdateInfo(UpdateType flag); + + OBJECT_TYPE_INFORMATION* type_info_internal() { + return reinterpret_cast<OBJECT_TYPE_INFORMATION*>( + &(type_info_buffer_[0])); + } + + const SYSTEM_HANDLE_INFORMATION* handle_entry_; + const SYSTEM_HANDLE_INFORMATION* last_entry_; + std::vector<BYTE> type_info_buffer_; + string16 handle_name_; + string16 type_name_; + + DISALLOW_COPY_AND_ASSIGN(HandleEntry); + }; + + class Iterator { + public: + Iterator(const HandleTable& table, const SYSTEM_HANDLE_INFORMATION* start, + const SYSTEM_HANDLE_INFORMATION* stop); + + Iterator(const Iterator& it); + + Iterator& operator++() { + if (++(current_.handle_entry_) == end_) + current_.handle_entry_ = table_.end(); + return *this; + } + + bool operator==(const Iterator& rhs) const { + return current_ == rhs.current_; + } + + bool operator!=(const Iterator& rhs) const { + return current_ != rhs.current_; + } + + HandleEntry& operator*() { return current_; } + + operator const SYSTEM_HANDLE_INFORMATION*() { + return current_.handle_entry_; + } + + HandleEntry* operator->() { return ¤t_; } + + private: + const HandleTable& table_; + HandleEntry current_; + const SYSTEM_HANDLE_INFORMATION* end_; + }; + + HandleTable(); + + Iterator begin() const { + return Iterator(*this, handle_info()->Information, + &handle_info()->Information[handle_info()->NumberOfHandles]); + } + + const SYSTEM_HANDLE_INFORMATION_EX* handle_info() const { + return reinterpret_cast<const SYSTEM_HANDLE_INFORMATION_EX*>( + &(handle_info_buffer_[0])); + } + + // Returns an iterator to the handles for only the supplied process ID. + Iterator HandlesForProcess(ULONG process_id) const; + const SYSTEM_HANDLE_INFORMATION* end() const { + return &handle_info()->Information[handle_info()->NumberOfHandles]; + } + + private: + SYSTEM_HANDLE_INFORMATION_EX* handle_info_internal() { + return reinterpret_cast<SYSTEM_HANDLE_INFORMATION_EX*>( + &(handle_info_buffer_[0])); + } + + std::vector<BYTE> handle_info_buffer_; + static HMODULE ntdll_; + + DISALLOW_COPY_AND_ASSIGN(HandleTable); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_HANDLE_TABLE_H_ diff --git a/sandbox/src/handle_table_unittest.cc b/sandbox/src/handle_table_unittest.cc new file mode 100644 index 0000000..696037f --- /dev/null +++ b/sandbox/src/handle_table_unittest.cc @@ -0,0 +1,66 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <windows.h> + +#include "sandbox/src/handle_table.h" +#include "sandbox/tests/common/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(HandleTable, IsTableValid) { + using sandbox::HandleTable; + const ULONG my_process_id = ::GetCurrentProcessId(); + ULONG last_process_id = 0; + ULONG total_handles = 0; + bool found_my_process = false; + bool found_other_process = false; + + HandleTable handles; + EXPECT_NE(0u, handles.handle_info()->NumberOfHandles); + + for (HandleTable::Iterator it = handles.begin(); it != handles.end(); ++it) { + ULONG process_id = it->handle_entry()->ProcessId; + bool is_current_process = process_id == my_process_id; + found_my_process |= is_current_process; + found_other_process |= !is_current_process; + + EXPECT_LE(last_process_id, process_id); + last_process_id = process_id; + total_handles++; + } + + EXPECT_EQ(handles.handle_info()->NumberOfHandles, total_handles); + EXPECT_TRUE(found_my_process); + EXPECT_TRUE(found_other_process); +} + +TEST(HandleTable, FindHandle) { + using sandbox::HandleTable; + + // Create a temp file so we have a handle to find. + wchar_t temp_directory[MAX_PATH]; + wchar_t my_file[MAX_PATH]; + ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, my_file), 0u); + HANDLE file = ::CreateFile(my_file, FILE_ALL_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL); + EXPECT_NE(INVALID_HANDLE_VALUE, file); + + // Look for the handle in our process + bool handle_found = false; + HandleTable handles; + for (HandleTable::Iterator it = + handles.HandlesForProcess(::GetCurrentProcessId()); + it != handles.end(); ++it) { + if (it->IsType(HandleTable::kTypeFile) && it->Name().compare(my_file)) { + handle_found = true; + break; + } + } + EXPECT_TRUE(handle_found); + + // Clean up the file we made. + EXPECT_TRUE(::CloseHandle(file)); +} diff --git a/sandbox/src/nt_internals.h b/sandbox/src/nt_internals.h index fa6b59e..08bf1a1 100644 --- a/sandbox/src/nt_internals.h +++ b/sandbox/src/nt_internals.h @@ -16,6 +16,7 @@ typedef LONG NTSTATUS; #define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) #define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) #define STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002L) +#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) #ifndef STATUS_INVALID_PARAMETER // It is now defined in Windows 2008 SDK. #define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL) @@ -441,6 +442,35 @@ typedef enum _OBJECT_INFORMATION_CLASS { ObjectDataInformation } OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS; +typedef struct _OBJDIR_INFORMATION { + UNICODE_STRING ObjectName; + UNICODE_STRING ObjectTypeName; + BYTE Data[1]; +} OBJDIR_INFORMATION; + +typedef struct _PUBLIC_OBJECT_BASIC_INFORMATION { + ULONG Attributes; + ACCESS_MASK GrantedAccess; + ULONG HandleCount; + ULONG PointerCount; + ULONG Reserved[10]; // reserved for internal use + } PUBLIC_OBJECT_BASIC_INFORMATION, *PPUBLIC_OBJECT_BASIC_INFORMATION; + +typedef struct __PUBLIC_OBJECT_TYPE_INFORMATION { + UNICODE_STRING TypeName; + ULONG Reserved[22]; // reserved for internal use +} PUBLIC_OBJECT_TYPE_INFORMATION, *PPUBLIC_OBJECT_TYPE_INFORMATION; + +typedef enum _POOL_TYPE { + NonPagedPool, + PagedPool, + NonPagedPoolMustSucceed, + ReservedType, + NonPagedPoolCacheAligned, + PagedPoolCacheAligned, + NonPagedPoolCacheAlignedMustS +} POOL_TYPE; + typedef struct _OBJECT_BASIC_INFORMATION { ULONG Attributes; ACCESS_MASK GrantedAccess; @@ -455,6 +485,50 @@ typedef struct _OBJECT_BASIC_INFORMATION { LARGE_INTEGER CreateTime; } OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION; +typedef struct _OBJECT_TYPE_INFORMATION { + UNICODE_STRING Name; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG TotalPagedPoolUsage; + ULONG TotalNonPagedPoolUsage; + ULONG TotalNamePoolUsage; + ULONG TotalHandleTableUsage; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + ULONG HighWaterPagedPoolUsage; + ULONG HighWaterNonPagedPoolUsage; + ULONG HighWaterNamePoolUsage; + ULONG HighWaterHandleTableUsage; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ULONG ValidAccess; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + USHORT MaintainTypeList; + POOL_TYPE PoolType; + ULONG PagedPoolUsage; + ULONG NonPagedPoolUsage; +} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; + +typedef enum _SYSTEM_INFORMATION_CLASS { + SystemHandleInformation = 16 +} SYSTEM_INFORMATION_CLASS; + +typedef struct _SYSTEM_HANDLE_INFORMATION { + USHORT ProcessId; + USHORT CreatorBackTraceIndex; + UCHAR ObjectTypeNumber; + UCHAR Flags; + USHORT Handle; + PVOID Object; + ACCESS_MASK GrantedAccess; +} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; + +typedef struct _SYSTEM_HANDLE_INFORMATION_EX { + ULONG NumberOfHandles; + SYSTEM_HANDLE_INFORMATION Information[1]; +} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; + typedef struct _OBJECT_NAME_INFORMATION { UNICODE_STRING ObjectName; } OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; @@ -481,6 +555,19 @@ typedef NTSTATUS (WINAPI *NtSignalAndWaitForSingleObjectFunction)( IN BOOLEAN Alertable, IN PLARGE_INTEGER Timeout OPTIONAL); +typedef NTSTATUS (WINAPI *NtQuerySystemInformation)( + IN SYSTEM_INFORMATION_CLASS SystemInformationClass, + OUT PVOID SystemInformation, + IN ULONG SystemInformationLength, + OUT PULONG ReturnLength); + +typedef NTSTATUS (WINAPI *NtQueryObject)( + IN HANDLE Handle, + IN OBJECT_INFORMATION_CLASS ObjectInformationClass, + OUT PVOID ObjectInformation, + IN ULONG ObjectInformationLength, + OUT PULONG ReturnLength); + // ----------------------------------------------------------------------- // Strings |