diff options
author | rvargas <rvargas@chromium.org> | 2014-08-26 12:45:42 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-08-26 19:48:38 +0000 |
commit | 1a4d07b227c51e2010867b3af70b2a58e7f01ad5 (patch) | |
tree | b1710b6032f5567d1ac6392705676c6356c906a9 | |
parent | 67769df5422bdf01a8d4a21f18fed694bec03647 (diff) | |
download | chromium_src-1a4d07b227c51e2010867b3af70b2a58e7f01ad5.zip chromium_src-1a4d07b227c51e2010867b3af70b2a58e7f01ad5.tar.gz chromium_src-1a4d07b227c51e2010867b3af70b2a58e7f01ad5.tar.bz2 |
Improve the ScopedHandle verifier.
1. Automate the selection of the proper channel to enable the verifier.
Now the code is enabled at runtime.
2. Switch to a hash_map to track handles.
3. Intercept CloseHandle to detect the code that is closing handles owned
by ScopedHandles. The initial implementation only covers chrome.exe/dll,
but the plan is to extend that in the future to all modules loaded in the
process.
BUG=362176
R=cpu@chromium.org
R=sky@chromium.org
See https://codereview.chromium.org/490043002/ for the actual review.
TBR=cpu@chromium.org
TBR=sky@chromium.org
Review URL: https://codereview.chromium.org/506013004
Cr-Commit-Position: refs/heads/master@{#291969}
-rw-r--r-- | base/win/scoped_handle.cc | 99 | ||||
-rw-r--r-- | base/win/scoped_handle.h | 19 | ||||
-rw-r--r-- | chrome/BUILD.gn | 2 | ||||
-rw-r--r-- | chrome/app/chrome_main.cc | 4 | ||||
-rw-r--r-- | chrome/app/close_handle_hook_win.cc | 118 | ||||
-rw-r--r-- | chrome/app/close_handle_hook_win.h | 14 | ||||
-rw-r--r-- | chrome/chrome_dll.gypi | 35 |
7 files changed, 260 insertions, 31 deletions
diff --git a/base/win/scoped_handle.cc b/base/win/scoped_handle.cc index 7b38369..fbc7d40 100644 --- a/base/win/scoped_handle.cc +++ b/base/win/scoped_handle.cc @@ -4,11 +4,12 @@ #include "base/win/scoped_handle.h" -#include <map> - +#include "base/containers/hash_tables.h" #include "base/debug/alias.h" +#include "base/hash.h" #include "base/lazy_instance.h" -#include "base/synchronization/lock.h" +#include "base/logging.h" +#include "base/synchronization/lock_impl.h" #include "base/win/windows_version.h" namespace { @@ -19,23 +20,86 @@ struct Info { const void* pc2; DWORD thread_id; }; -typedef std::map<HANDLE, Info> HandleMap; +typedef base::hash_map<HANDLE, Info> HandleMap; +typedef base::internal::LockImpl NativeLock; base::LazyInstance<HandleMap>::Leaky g_handle_map = LAZY_INSTANCE_INITIALIZER; -base::LazyInstance<base::Lock>::Leaky g_lock = LAZY_INSTANCE_INITIALIZER; +base::LazyInstance<NativeLock>::Leaky g_lock = LAZY_INSTANCE_INITIALIZER; +bool g_closing = false; +bool g_verifier_enabled = false; + +bool CloseHandleWrapper(HANDLE handle) { + if (!::CloseHandle(handle)) + CHECK(false); + return true; +} + +// Simple automatic locking using a native critical section so it supports +// recursive locking. +class AutoNativeLock { + public: + explicit AutoNativeLock(NativeLock& lock) : lock_(lock) { + lock_.Lock(); + } + + ~AutoNativeLock() { + lock_.Unlock(); + } + + private: + NativeLock& lock_; + DISALLOW_COPY_AND_ASSIGN(AutoNativeLock); +}; + +inline size_t HashHandle(const HANDLE& handle) { + char buffer[sizeof(handle)]; + memcpy(buffer, &handle, sizeof(handle)); + return base::Hash(buffer, sizeof(buffer)); +} } // namespace +namespace BASE_HASH_NAMESPACE { +#if defined(COMPILER_MSVC) +inline size_t hash_value(const HANDLE& handle) { + return HashHandle(handle); +} +#elif defined (COMPILER_GCC) +template <> +struct hash<HANDLE> { + size_t operator()(const HANDLE& handle) const { + return HashHandle(handle); + } +}; +#endif +} // BASE_HASH_NAMESPACE + namespace base { namespace win { // Static. +bool HandleTraits::CloseHandle(HANDLE handle) { + if (!g_verifier_enabled) + return CloseHandleWrapper(handle); + + AutoNativeLock lock(g_lock.Get()); + g_closing = true; + CloseHandleWrapper(handle); + g_closing = false; + + return true; +} + +// Static. void VerifierTraits::StartTracking(HANDLE handle, const void* owner, const void* pc1, const void* pc2) { + if (!g_verifier_enabled) + return; + // Grab the thread id before the lock. DWORD thread_id = GetCurrentThreadId(); - AutoLock lock(g_lock.Get()); + AutoNativeLock lock(g_lock.Get()); Info handle_info = { owner, pc1, pc2, thread_id }; std::pair<HANDLE, Info> item(handle, handle_info); @@ -50,7 +114,10 @@ void VerifierTraits::StartTracking(HANDLE handle, const void* owner, // Static. void VerifierTraits::StopTracking(HANDLE handle, const void* owner, const void* pc1, const void* pc2) { - AutoLock lock(g_lock.Get()); + if (!g_verifier_enabled) + return; + + AutoNativeLock lock(g_lock.Get()); HandleMap::iterator i = g_handle_map.Get().find(handle); if (i == g_handle_map.Get().end()) CHECK(false); @@ -64,5 +131,23 @@ void VerifierTraits::StopTracking(HANDLE handle, const void* owner, g_handle_map.Get().erase(i); } +void EnableHandleVerifier() { + g_verifier_enabled = true; +} + +void OnHandleBeingClosed(HANDLE handle) { + AutoNativeLock lock(g_lock.Get()); + if (g_closing) + return; + + HandleMap::iterator i = g_handle_map.Get().find(handle); + if (i == g_handle_map.Get().end()) + return; + + Info other = i->second; + debug::Alias(&other); + CHECK(false); +} + } // namespace win } // namespace base diff --git a/base/win/scoped_handle.h b/base/win/scoped_handle.h index 00063af..e1e0f5d 100644 --- a/base/win/scoped_handle.h +++ b/base/win/scoped_handle.h @@ -101,9 +101,7 @@ class GenericScopedHandle { Verifier::StopTracking(handle_, this, BASE_WIN_GET_CALLER, tracked_objects::GetProgramCounter()); - if (!Traits::CloseHandle(handle_)) - CHECK(false); - + Traits::CloseHandle(handle_); handle_ = Traits::NullHandle(); } } @@ -120,9 +118,7 @@ class HandleTraits { typedef HANDLE Handle; // Closes the handle. - static bool CloseHandle(HANDLE handle) { - return ::CloseHandle(handle) != FALSE; - } + static bool BASE_EXPORT CloseHandle(HANDLE handle); // Returns true if the handle value is valid. static bool IsHandleValid(HANDLE handle) { @@ -168,6 +164,17 @@ class BASE_EXPORT VerifierTraits { typedef GenericScopedHandle<HandleTraits, VerifierTraits> ScopedHandle; +// This function should be called by the embedder to enable the use of +// VerifierTraits at runtime. It has no effect if DummyVerifierTraits is used +// for ScopedHandle. +void BASE_EXPORT EnableHandleVerifier(); + +// This should be called whenever the OS is closing a handle, if extended +// verification of improper handle closing is desired. If |handle| is being +// tracked by the handle verifier and ScopedHandle is not the one closing it, +// a CHECK is generated. +void BASE_EXPORT OnHandleBeingClosed(HANDLE handle); + } // namespace win } // namespace base diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn index dc1444e..0b8c79c 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn @@ -102,6 +102,8 @@ shared_library("main_dll") { "app/chrome_main.cc", "app/chrome_main_delegate.cc", "app/chrome_main_delegate.h", + "app/close_handle_hook_win.cc", + "app/close_handle_hook_win.h", "app/delay_load_hook_win.cc", "app/delay_load_hook_win.h", "//base/win/dllmain.cc", diff --git a/chrome/app/chrome_main.cc b/chrome/app/chrome_main.cc index 80cd0fb..ba2fce7 100644 --- a/chrome/app/chrome_main.cc +++ b/chrome/app/chrome_main.cc @@ -9,6 +9,7 @@ #if defined(OS_WIN) #include "base/debug/dump_without_crashing.h" #include "base/win/win_util.h" +#include "chrome/app/close_handle_hook_win.h" #include "chrome/common/chrome_constants.h" #define DLLEXPORT __declspec(dllexport) @@ -41,6 +42,8 @@ int ChromeMain(int argc, const char** argv) { params.instance = instance; params.sandbox_info = sandbox_info; + InstallCloseHandleHooks(); + // SetDumpWithoutCrashingFunction must be passed the DumpProcess function // from the EXE and not from the DLL in order for DumpWithoutCrashing to // function correctly. @@ -57,6 +60,7 @@ int ChromeMain(int argc, const char** argv) { int rv = content::ContentMain(params); #if defined(OS_WIN) + RemoveCloseHandleHooks(); base::win::SetShouldCrashOnProcessDetach(false); #endif diff --git a/chrome/app/close_handle_hook_win.cc b/chrome/app/close_handle_hook_win.cc new file mode 100644 index 0000000..ca0b01e --- /dev/null +++ b/chrome/app/close_handle_hook_win.cc @@ -0,0 +1,118 @@ +// Copyright 2014 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 "chrome/app/close_handle_hook_win.h" + +#include <Windows.h> + +#include <vector> + +#include "base/files/file_path.h" +#include "base/lazy_instance.h" +#include "base/strings/string16.h" +#include "base/win/iat_patch_function.h" +#include "base/win/scoped_handle.h" +#include "chrome/common/chrome_version_info.h" + +namespace { + +typedef BOOL (WINAPI* CloseHandleType) (HANDLE handle); +CloseHandleType g_close_function = NULL; + +// The entry point for CloseHandle interception. This function notifies the +// verifier about the handle that is being closed, and calls the original +// function. +BOOL WINAPI CloseHandleHook(HANDLE handle) { + base::win::OnHandleBeingClosed(handle); + return g_close_function(handle); +} + +// Keeps track of all the hooks needed to intercept CloseHandle. +class CloseHandleHooks { + public: + CloseHandleHooks() {} + ~CloseHandleHooks() {} + + void AddIATPatch(const base::string16& module); + void Unpatch(); + + private: + std::vector<base::win::IATPatchFunction*> hooks_; + DISALLOW_COPY_AND_ASSIGN(CloseHandleHooks); +}; +base::LazyInstance<CloseHandleHooks> g_hooks = LAZY_INSTANCE_INITIALIZER; + +void CloseHandleHooks::AddIATPatch(const base::string16& module) { + if (module.empty()) + return; + + base::win::IATPatchFunction* patch = new base::win::IATPatchFunction; + patch->Patch(module.c_str(), "kernel32.dll", "CloseHandle", CloseHandleHook); + hooks_.push_back(patch); + if (!g_close_function) { + // Things are probably messed up if each intercepted function points to + // a different place, but we need only one function to call. + g_close_function = + reinterpret_cast<CloseHandleType>(patch->original_function()); + } +} + +void CloseHandleHooks::Unpatch() { + for (std::vector<base::win::IATPatchFunction*>::iterator it = hooks_.begin(); + it != hooks_.end(); ++it) { + (*it)->Unpatch(); + } +} + +bool UseHooks() { + chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); + if (channel == chrome::VersionInfo::CHANNEL_CANARY || + channel == chrome::VersionInfo::CHANNEL_DEV) { + return true; + } + + return false; +} + +base::string16 GetModuleName(HMODULE module) { + base::string16 name; + if (!module) + return name; + wchar_t buffer[MAX_PATH]; + int rv = GetModuleFileName(module, buffer, MAX_PATH); + if (rv == MAX_PATH) + return name; + + buffer[MAX_PATH - 1] = L'\0'; + name.assign(buffer); + base::FilePath path(name); + return path.BaseName().AsUTF16Unsafe(); +} + +HMODULE GetChromeDLLModule() { + HMODULE module; + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast<wchar_t*>(&GetChromeDLLModule), + &module)) { + return NULL; + } + return module; +} + +} // namespace + +void InstallCloseHandleHooks() { + if (!UseHooks()) + return; + + base::win::EnableHandleVerifier(); + CloseHandleHooks* hooks = g_hooks.Pointer(); + hooks->AddIATPatch(L"chrome.exe"); + hooks->AddIATPatch(GetModuleName(GetChromeDLLModule())); +} + +void RemoveCloseHandleHooks() { + g_hooks.Get().Unpatch(); +} diff --git a/chrome/app/close_handle_hook_win.h b/chrome/app/close_handle_hook_win.h new file mode 100644 index 0000000..1362ec0 --- /dev/null +++ b/chrome/app/close_handle_hook_win.h @@ -0,0 +1,14 @@ +// Copyright 2014 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 CHROME_APP_CLOSE_HANDLE_HOOK_WIN_H_ +#define CHROME_APP_CLOSE_HANDLE_HOOK_WIN_H_ + +// Installs the hooks required to debug use of improper handles. +void InstallCloseHandleHooks(); + +// Removes the hooks installed by InstallCloseHandleHooks(). +void RemoveCloseHandleHooks(); + +#endif // CHROME_APP_CLOSE_HANDLE_HOOK_WIN_H_ diff --git a/chrome/chrome_dll.gypi b/chrome/chrome_dll.gypi index bc3baf5..72f5024 100644 --- a/chrome/chrome_dll.gypi +++ b/chrome/chrome_dll.gypi @@ -77,6 +77,20 @@ 'variables': { 'enable_wexit_time_destructors': 1, }, + 'sources': [ + 'app/chrome_command_ids.h', + 'app/chrome_dll_resource.h', + 'app/chrome_main.cc', + 'app/chrome_main_delegate.cc', + 'app/chrome_main_delegate.h', + 'app/chrome_main_mac.mm', + 'app/chrome_main_mac.h', + 'app/close_handle_hook_win.cc', + 'app/close_handle_hook_win.h', + 'app/delay_load_hook_win.cc', + 'app/delay_load_hook_win.h', + '../base/win/dllmain.cc', + ], 'dependencies': [ '<@(chromium_browser_dependencies)', '../content/content.gyp:content_app_browser', @@ -117,17 +131,9 @@ '../ui/views/views.gyp:views', ], 'sources': [ - 'app/chrome_command_ids.h', 'app/chrome_dll.rc', - 'app/chrome_dll_resource.h', - 'app/chrome_main.cc', - 'app/chrome_main_delegate.cc', - 'app/chrome_main_delegate.h', - 'app/delay_load_hook_win.cc', - 'app/delay_load_hook_win.h', - + '<(SHARED_INTERMEDIATE_DIR)/chrome_version/chrome_dll_version.rc', - '../base/win/dllmain.cc', # Cursors. '<(SHARED_INTERMEDIATE_DIR)/ui/resources/ui_unscaled_resources.rc', @@ -272,15 +278,6 @@ # sets -order_file. 'ORDER_FILE': 'app/framework.order', }, - 'sources': [ - 'app/chrome_command_ids.h', - 'app/chrome_dll_resource.h', - 'app/chrome_main.cc', - 'app/chrome_main_delegate.cc', - 'app/chrome_main_delegate.h', - 'app/chrome_main_mac.mm', - 'app/chrome_main_mac.h', - ], 'dependencies': [ '../pdf/pdf.gyp:pdf', ], @@ -350,6 +347,8 @@ 'app/chrome_main.cc', 'app/chrome_main_delegate.cc', 'app/chrome_main_delegate.h', + 'app/close_handle_hook_win.cc', + 'app/close_handle_hook_win.h', ], 'conditions': [ ['OS=="win"', { |