Commit 1a4d07b2 authored by rvargas's avatar rvargas Committed by Commit bot

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}
parent 67769df5
...@@ -4,11 +4,12 @@ ...@@ -4,11 +4,12 @@
#include "base/win/scoped_handle.h" #include "base/win/scoped_handle.h"
#include <map> #include "base/containers/hash_tables.h"
#include "base/debug/alias.h" #include "base/debug/alias.h"
#include "base/hash.h"
#include "base/lazy_instance.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" #include "base/win/windows_version.h"
namespace { namespace {
...@@ -19,23 +20,86 @@ struct Info { ...@@ -19,23 +20,86 @@ struct Info {
const void* pc2; const void* pc2;
DWORD thread_id; 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<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
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 base {
namespace win { 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. // Static.
void VerifierTraits::StartTracking(HANDLE handle, const void* owner, void VerifierTraits::StartTracking(HANDLE handle, const void* owner,
const void* pc1, const void* pc2) { const void* pc1, const void* pc2) {
if (!g_verifier_enabled)
return;
// Grab the thread id before the lock. // Grab the thread id before the lock.
DWORD thread_id = GetCurrentThreadId(); DWORD thread_id = GetCurrentThreadId();
AutoLock lock(g_lock.Get()); AutoNativeLock lock(g_lock.Get());
Info handle_info = { owner, pc1, pc2, thread_id }; Info handle_info = { owner, pc1, pc2, thread_id };
std::pair<HANDLE, Info> item(handle, handle_info); std::pair<HANDLE, Info> item(handle, handle_info);
...@@ -50,7 +114,10 @@ void VerifierTraits::StartTracking(HANDLE handle, const void* owner, ...@@ -50,7 +114,10 @@ void VerifierTraits::StartTracking(HANDLE handle, const void* owner,
// Static. // Static.
void VerifierTraits::StopTracking(HANDLE handle, const void* owner, void VerifierTraits::StopTracking(HANDLE handle, const void* owner,
const void* pc1, const void* pc2) { 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); HandleMap::iterator i = g_handle_map.Get().find(handle);
if (i == g_handle_map.Get().end()) if (i == g_handle_map.Get().end())
CHECK(false); CHECK(false);
...@@ -64,5 +131,23 @@ void VerifierTraits::StopTracking(HANDLE handle, const void* owner, ...@@ -64,5 +131,23 @@ void VerifierTraits::StopTracking(HANDLE handle, const void* owner,
g_handle_map.Get().erase(i); 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 win
} // namespace base } // namespace base
...@@ -101,9 +101,7 @@ class GenericScopedHandle { ...@@ -101,9 +101,7 @@ class GenericScopedHandle {
Verifier::StopTracking(handle_, this, BASE_WIN_GET_CALLER, Verifier::StopTracking(handle_, this, BASE_WIN_GET_CALLER,
tracked_objects::GetProgramCounter()); tracked_objects::GetProgramCounter());
if (!Traits::CloseHandle(handle_)) Traits::CloseHandle(handle_);
CHECK(false);
handle_ = Traits::NullHandle(); handle_ = Traits::NullHandle();
} }
} }
...@@ -120,9 +118,7 @@ class HandleTraits { ...@@ -120,9 +118,7 @@ class HandleTraits {
typedef HANDLE Handle; typedef HANDLE Handle;
// Closes the handle. // Closes the handle.
static bool CloseHandle(HANDLE handle) { static bool BASE_EXPORT CloseHandle(HANDLE handle);
return ::CloseHandle(handle) != FALSE;
}
// Returns true if the handle value is valid. // Returns true if the handle value is valid.
static bool IsHandleValid(HANDLE handle) { static bool IsHandleValid(HANDLE handle) {
...@@ -168,6 +164,17 @@ class BASE_EXPORT VerifierTraits { ...@@ -168,6 +164,17 @@ class BASE_EXPORT VerifierTraits {
typedef GenericScopedHandle<HandleTraits, VerifierTraits> ScopedHandle; 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 win
} // namespace base } // namespace base
......
...@@ -102,6 +102,8 @@ shared_library("main_dll") { ...@@ -102,6 +102,8 @@ shared_library("main_dll") {
"app/chrome_main.cc", "app/chrome_main.cc",
"app/chrome_main_delegate.cc", "app/chrome_main_delegate.cc",
"app/chrome_main_delegate.h", "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.cc",
"app/delay_load_hook_win.h", "app/delay_load_hook_win.h",
"//base/win/dllmain.cc", "//base/win/dllmain.cc",
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#if defined(OS_WIN) #if defined(OS_WIN)
#include "base/debug/dump_without_crashing.h" #include "base/debug/dump_without_crashing.h"
#include "base/win/win_util.h" #include "base/win/win_util.h"
#include "chrome/app/close_handle_hook_win.h"
#include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_constants.h"
#define DLLEXPORT __declspec(dllexport) #define DLLEXPORT __declspec(dllexport)
...@@ -41,6 +42,8 @@ int ChromeMain(int argc, const char** argv) { ...@@ -41,6 +42,8 @@ int ChromeMain(int argc, const char** argv) {
params.instance = instance; params.instance = instance;
params.sandbox_info = sandbox_info; params.sandbox_info = sandbox_info;
InstallCloseHandleHooks();
// SetDumpWithoutCrashingFunction must be passed the DumpProcess function // SetDumpWithoutCrashingFunction must be passed the DumpProcess function
// from the EXE and not from the DLL in order for DumpWithoutCrashing to // from the EXE and not from the DLL in order for DumpWithoutCrashing to
// function correctly. // function correctly.
...@@ -57,6 +60,7 @@ int ChromeMain(int argc, const char** argv) { ...@@ -57,6 +60,7 @@ int ChromeMain(int argc, const char** argv) {
int rv = content::ContentMain(params); int rv = content::ContentMain(params);
#if defined(OS_WIN) #if defined(OS_WIN)
RemoveCloseHandleHooks();
base::win::SetShouldCrashOnProcessDetach(false); base::win::SetShouldCrashOnProcessDetach(false);
#endif #endif
......
// 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();
}
// 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_
...@@ -77,6 +77,20 @@ ...@@ -77,6 +77,20 @@
'variables': { 'variables': {
'enable_wexit_time_destructors': 1, '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': [ 'dependencies': [
'<@(chromium_browser_dependencies)', '<@(chromium_browser_dependencies)',
'../content/content.gyp:content_app_browser', '../content/content.gyp:content_app_browser',
...@@ -117,17 +131,9 @@ ...@@ -117,17 +131,9 @@
'../ui/views/views.gyp:views', '../ui/views/views.gyp:views',
], ],
'sources': [ 'sources': [
'app/chrome_command_ids.h',
'app/chrome_dll.rc', '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', '<(SHARED_INTERMEDIATE_DIR)/chrome_version/chrome_dll_version.rc',
'../base/win/dllmain.cc',
# Cursors. # Cursors.
'<(SHARED_INTERMEDIATE_DIR)/ui/resources/ui_unscaled_resources.rc', '<(SHARED_INTERMEDIATE_DIR)/ui/resources/ui_unscaled_resources.rc',
...@@ -272,15 +278,6 @@ ...@@ -272,15 +278,6 @@
# sets -order_file. # sets -order_file.
'ORDER_FILE': 'app/framework.order', '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': [ 'dependencies': [
'../pdf/pdf.gyp:pdf', '../pdf/pdf.gyp:pdf',
], ],
...@@ -350,6 +347,8 @@ ...@@ -350,6 +347,8 @@
'app/chrome_main.cc', 'app/chrome_main.cc',
'app/chrome_main_delegate.cc', 'app/chrome_main_delegate.cc',
'app/chrome_main_delegate.h', 'app/chrome_main_delegate.h',
'app/close_handle_hook_win.cc',
'app/close_handle_hook_win.h',
], ],
'conditions': [ 'conditions': [
['OS=="win"', { ['OS=="win"', {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment