Commit 5418f47e authored by deanm@chromium.org's avatar deanm@chromium.org

Try a new approach to fixing IAT unpatch crashes when the DLL is gone.

Have the IAT patcher take some "ownership" of the DLL, by taking a library name and then calling LoadLibrary() / FreeLibrary() to manage the reference count.  This means as long is there isn't some other reference count balancing bug happening in the process, the DLL will never be unloaded while we are patched.

This effectively reverts r9929, the VirtualQuery additional checks are removed.

BUG=7701

Review URL: http://codereview.chromium.org/21453

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@10467 0039d316-1c4b-4281-b951-d872f2087c98
parent 33b6322f
...@@ -181,7 +181,7 @@ IATPatchFunction::~IATPatchFunction() { ...@@ -181,7 +181,7 @@ IATPatchFunction::~IATPatchFunction() {
} }
} }
DWORD IATPatchFunction::Patch(HMODULE module_handle, DWORD IATPatchFunction::Patch(const wchar_t* module,
const char* imported_from_module, const char* imported_from_module,
const char* function_name, const char* function_name,
void* new_function) { void* new_function) {
...@@ -189,6 +189,13 @@ DWORD IATPatchFunction::Patch(HMODULE module_handle, ...@@ -189,6 +189,13 @@ DWORD IATPatchFunction::Patch(HMODULE module_handle,
DCHECK_EQ(static_cast<IMAGE_THUNK_DATA*>(NULL), iat_thunk_); DCHECK_EQ(static_cast<IMAGE_THUNK_DATA*>(NULL), iat_thunk_);
DCHECK_EQ(static_cast<void*>(NULL), intercept_function_); DCHECK_EQ(static_cast<void*>(NULL), intercept_function_);
HMODULE module_handle = LoadLibraryW(module);
if (module_handle == NULL) {
NOTREACHED();
return GetLastError();
}
DWORD error = InterceptImportedFunction(module_handle, DWORD error = InterceptImportedFunction(module_handle,
imported_from_module, imported_from_module,
function_name, function_name,
...@@ -198,32 +205,19 @@ DWORD IATPatchFunction::Patch(HMODULE module_handle, ...@@ -198,32 +205,19 @@ DWORD IATPatchFunction::Patch(HMODULE module_handle,
if (NO_ERROR == error) { if (NO_ERROR == error) {
DCHECK_NE(original_function_, intercept_function_); DCHECK_NE(original_function_, intercept_function_);
module_handle_ = module_handle;
intercept_function_ = new_function; intercept_function_ = new_function;
} else {
FreeLibrary(module_handle);
} }
return error; return error;
} }
DWORD IATPatchFunction::Unpatch() { DWORD IATPatchFunction::Unpatch() {
DWORD error = 0; DWORD error = RestoreImportedFunction(intercept_function_,
MEMORY_BASIC_INFORMATION memory_info = {0}; original_function_,
iat_thunk_);
// If the module has already unloaded, no point trying to unpatch.
if (!VirtualQuery(original_function_, &memory_info,
sizeof(memory_info))) {
error = GetLastError();
NOTREACHED();
return error;
}
if ((memory_info.State & MEM_COMMIT) != MEM_COMMIT) {
NOTREACHED();
return ERROR_ACCESS_DENIED;
}
error = RestoreImportedFunction(intercept_function_,
original_function_,
iat_thunk_);
DCHECK(NO_ERROR == error); DCHECK(NO_ERROR == error);
// Hands off the intercept if we fail to unpatch. // Hands off the intercept if we fail to unpatch.
...@@ -232,6 +226,9 @@ DWORD IATPatchFunction::Unpatch() { ...@@ -232,6 +226,9 @@ DWORD IATPatchFunction::Unpatch() {
// patch. In this case its better to be hands off the intercept as // patch. In this case its better to be hands off the intercept as
// trying to unpatch again in the destructor of IATPatchFunction is // trying to unpatch again in the destructor of IATPatchFunction is
// not going to be any safer // not going to be any safer
if (module_handle_)
FreeLibrary(module_handle_);
module_handle_ = NULL;
intercept_function_ = NULL; intercept_function_ = NULL;
original_function_ = NULL; original_function_ = NULL;
iat_thunk_ = NULL; iat_thunk_ = NULL;
...@@ -240,4 +237,3 @@ DWORD IATPatchFunction::Unpatch() { ...@@ -240,4 +237,3 @@ DWORD IATPatchFunction::Unpatch() {
} }
} // namespace iat_patch } // namespace iat_patch
...@@ -80,13 +80,19 @@ class IATPatchFunction { ...@@ -80,13 +80,19 @@ class IATPatchFunction {
// during Unpatch // during Unpatch
// //
// Arguments: // Arguments:
// module_handle Module to be intercepted // module Module to be intercepted
// imported_from_module Module that exports the 'function_name' // imported_from_module Module that exports the 'function_name'
// function_name Name of the API to be intercepted // function_name Name of the API to be intercepted
// //
// Returns: Windows error code (winerror.h). NO_ERROR if successful // Returns: Windows error code (winerror.h). NO_ERROR if successful
// //
DWORD Patch(HMODULE module_handle, // Note: Patching a function will make the IAT patch take some "ownership" on
// |module|. It will LoadLibrary(module) to keep the DLL alive until a call
// to Unpatch(), which will call FreeLibrary() and allow the module to be
// unloaded. The idea is to help prevent the DLL from going away while a
// patch is still active.
//
DWORD Patch(const wchar_t* module,
const char* imported_from_module, const char* imported_from_module,
const char* function_name, const char* function_name,
void* new_function); void* new_function);
...@@ -103,6 +109,7 @@ class IATPatchFunction { ...@@ -103,6 +109,7 @@ class IATPatchFunction {
} }
private: private:
HMODULE module_handle_;
void* intercept_function_; void* intercept_function_;
void* original_function_; void* original_function_;
IMAGE_THUNK_DATA* iat_thunk_; IMAGE_THUNK_DATA* iat_thunk_;
......
...@@ -7,8 +7,10 @@ ...@@ -7,8 +7,10 @@
#include <locale> #include <locale>
#include "base/base_drag_source.h" #include "base/base_drag_source.h"
#include "base/basictypes.h"
#include "base/clipboard.h" #include "base/clipboard.h"
#include "base/iat_patch.h" #include "base/iat_patch.h"
#include "base/lazy_instance.h"
#include "base/ref_counted.h" #include "base/ref_counted.h"
#include "base/scoped_clipboard_writer.h" #include "base/scoped_clipboard_writer.h"
#include "base/string_util.h" #include "base/string_util.h"
...@@ -633,26 +635,81 @@ AutocompleteEditView::ScopedSuspendUndo::~ScopedSuspendUndo() { ...@@ -633,26 +635,81 @@ AutocompleteEditView::ScopedSuspendUndo::~ScopedSuspendUndo() {
// TODO (jcampan): these colors should be derived from the system colors to // TODO (jcampan): these colors should be derived from the system colors to
// ensure they show properly. Bug #948807. // ensure they show properly. Bug #948807.
// Colors used to emphasize the scheme in the URL. // Colors used to emphasize the scheme in the URL.
static const COLORREF kSecureSchemeColor = RGB(0, 150, 20);
static const COLORREF kInsecureSchemeColor = RGB(200, 0, 0); namespace {
const COLORREF kSecureSchemeColor = RGB(0, 150, 20);
const COLORREF kInsecureSchemeColor = RGB(200, 0, 0);
// Colors used to strike-out the scheme when it is insecure. // Colors used to strike-out the scheme when it is insecure.
static const SkColor kSchemeStrikeoutColor = SkColorSetRGB(210, 0, 0); const SkColor kSchemeStrikeoutColor = SkColorSetRGB(210, 0, 0);
static const SkColor kSchemeSelectedStrikeoutColor = const SkColor kSchemeSelectedStrikeoutColor =
SkColorSetRGB(255, 255, 255); SkColorSetRGB(255, 255, 255);
// These are used to hook the CRichEditCtrl's calls to BeginPaint() and // These are used to hook the CRichEditCtrl's calls to BeginPaint() and
// EndPaint() and provide a memory DC instead. See OnPaint(). // EndPaint() and provide a memory DC instead. See OnPaint().
static HWND edit_hwnd = NULL; HWND edit_hwnd = NULL;
static PAINTSTRUCT paint_struct; PAINTSTRUCT paint_struct;
HDC BeginPaintIntercept(HWND hWnd, LPPAINTSTRUCT lpPaint) {
if (!edit_hwnd || (hWnd != edit_hwnd))
return ::BeginPaint(hWnd, lpPaint);
*lpPaint = paint_struct;
return paint_struct.hdc;
}
BOOL EndPaintIntercept(HWND hWnd, const PAINTSTRUCT* lpPaint) {
return (edit_hwnd && (hWnd == edit_hwnd)) ?
true : ::EndPaint(hWnd, lpPaint);
}
// Returns a lazily initialized property bag accessor for saving our state in a // Returns a lazily initialized property bag accessor for saving our state in a
// TabContents. // TabContents.
static PropertyAccessor<AutocompleteEditState>* GetStateAccessor() { PropertyAccessor<AutocompleteEditState>* GetStateAccessor() {
static PropertyAccessor<AutocompleteEditState> state; static PropertyAccessor<AutocompleteEditState> state;
return &state; return &state;
} }
class PaintPatcher {
public:
PaintPatcher() : refcount_(0) { }
~PaintPatcher() { DCHECK(refcount_ == 0); }
void RefPatch() {
if (refcount_ == 0) {
DCHECK(!begin_paint_.is_patched());
DCHECK(!end_paint_.is_patched());
begin_paint_.Patch(L"riched20.dll", "user32.dll", "BeginPaint",
&BeginPaintIntercept);
end_paint_.Patch(L"riched20.dll", "user32.dll", "EndPaint",
&EndPaintIntercept);
}
++refcount_;
}
void DerefPatch() {
DCHECK(begin_paint_.is_patched());
DCHECK(end_paint_.is_patched());
--refcount_;
if (refcount_ == 0) {
begin_paint_.Unpatch();
end_paint_.Unpatch();
}
}
private:
size_t refcount_;
iat_patch::IATPatchFunction begin_paint_;
iat_patch::IATPatchFunction end_paint_;
DISALLOW_COPY_AND_ASSIGN(PaintPatcher);
};
base::LazyInstance<PaintPatcher> g_paint_patcher(base::LINKER_INITIALIZED);
} // namespace
AutocompleteEditView::AutocompleteEditView( AutocompleteEditView::AutocompleteEditView(
const ChromeFont& font, const ChromeFont& font,
AutocompleteEditController* controller, AutocompleteEditController* controller,
...@@ -685,22 +742,7 @@ AutocompleteEditView::AutocompleteEditView( ...@@ -685,22 +742,7 @@ AutocompleteEditView::AutocompleteEditView(
saved_selection_for_focus_change_.cpMin = -1; saved_selection_for_focus_change_.cpMin = -1;
// Statics used for global patching of riched20.dll. g_paint_patcher.Pointer()->RefPatch();
static HMODULE richedit_module = NULL;
static iat_patch::IATPatchFunction patch_begin_paint;
static iat_patch::IATPatchFunction patch_end_paint;
if (!richedit_module) {
richedit_module = LoadLibrary(L"riched20.dll");
if (richedit_module) {
DCHECK(!patch_begin_paint.is_patched());
patch_begin_paint.Patch(richedit_module, "user32.dll", "BeginPaint",
&BeginPaintIntercept);
DCHECK(!patch_end_paint.is_patched());
patch_end_paint.Patch(richedit_module, "user32.dll", "EndPaint",
&EndPaintIntercept);
}
}
Create(hwnd, 0, 0, 0, l10n_util::GetExtendedStyles()); Create(hwnd, 0, 0, 0, l10n_util::GetExtendedStyles());
SetReadOnly(popup_window_mode_); SetReadOnly(popup_window_mode_);
...@@ -787,6 +829,11 @@ AutocompleteEditView::~AutocompleteEditView() { ...@@ -787,6 +829,11 @@ AutocompleteEditView::~AutocompleteEditView() {
NotificationType::AUTOCOMPLETE_EDIT_DESTROYED, NotificationType::AUTOCOMPLETE_EDIT_DESTROYED,
Source<AutocompleteEditView>(this), Source<AutocompleteEditView>(this),
NotificationService::NoDetails()); NotificationService::NoDetails());
// We balance our reference count and unpatch when the last instance has
// been destroyed. This prevents us from relying on the AtExit or static
// destructor sequence to do our unpatching, which is generally fragile.
g_paint_patcher.Pointer()->DerefPatch();
} }
void AutocompleteEditView::SaveStateToTab(TabContents* tab) { void AutocompleteEditView::SaveStateToTab(TabContents* tab) {
...@@ -1374,23 +1421,6 @@ bool AutocompleteEditView::SchemeEnd(LPTSTR edit_text, ...@@ -1374,23 +1421,6 @@ bool AutocompleteEditView::SchemeEnd(LPTSTR edit_text,
(edit_text[current_pos + 2] == '/'); (edit_text[current_pos + 2] == '/');
} }
// static
HDC AutocompleteEditView::BeginPaintIntercept(HWND hWnd,
LPPAINTSTRUCT lpPaint) {
if (!edit_hwnd || (hWnd != edit_hwnd))
return ::BeginPaint(hWnd, lpPaint);
*lpPaint = paint_struct;
return paint_struct.hdc;
}
// static
BOOL AutocompleteEditView::EndPaintIntercept(HWND hWnd,
const PAINTSTRUCT* lpPaint) {
return (edit_hwnd && (hWnd == edit_hwnd)) ?
true : ::EndPaint(hWnd, lpPaint);
}
void AutocompleteEditView::OnChar(TCHAR ch, UINT repeat_count, UINT flags) { void AutocompleteEditView::OnChar(TCHAR ch, UINT repeat_count, UINT flags) {
// Don't let alt-enter beep. Not sure this is necessary, as the standard // Don't let alt-enter beep. Not sure this is necessary, as the standard
// alt-enter will hit DiscardWMSysChar() and get thrown away, and // alt-enter will hit DiscardWMSysChar() and get thrown away, and
......
...@@ -669,10 +669,6 @@ class AutocompleteEditView ...@@ -669,10 +669,6 @@ class AutocompleteEditView
// Returns true if |edit_text| starting at |current_pos| is "://". // Returns true if |edit_text| starting at |current_pos| is "://".
static bool SchemeEnd(LPTSTR edit_text, int current_pos, int length); static bool SchemeEnd(LPTSTR edit_text, int current_pos, int length);
// Intercepts. See OnPaint().
static HDC WINAPI BeginPaintIntercept(HWND hWnd, LPPAINTSTRUCT lpPaint);
static BOOL WINAPI EndPaintIntercept(HWND hWnd, CONST PAINTSTRUCT* lpPaint);
// Message handlers // Message handlers
void OnChar(TCHAR ch, UINT repeat_count, UINT flags); void OnChar(TCHAR ch, UINT repeat_count, UINT flags);
void OnContextMenu(HWND window, const CPoint& point); void OnContextMenu(HWND window, const CPoint& point);
......
...@@ -155,8 +155,7 @@ WebPluginDelegateImpl::WebPluginDelegateImpl( ...@@ -155,8 +155,7 @@ WebPluginDelegateImpl::WebPluginDelegateImpl(
handle_event_depth_(0), handle_event_depth_(0),
user_gesture_message_posted_(false), user_gesture_message_posted_(false),
#pragma warning(suppress: 4355) // can use this #pragma warning(suppress: 4355) // can use this
user_gesture_msg_factory_(this), user_gesture_msg_factory_(this) {
plugin_module_handle_(NULL) {
memset(&window_, 0, sizeof(window_)); memset(&window_, 0, sizeof(window_));
const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info(); const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info();
...@@ -201,8 +200,6 @@ WebPluginDelegateImpl::WebPluginDelegateImpl( ...@@ -201,8 +200,6 @@ WebPluginDelegateImpl::WebPluginDelegateImpl(
quirks_ |= PLUGIN_QUIRK_PATCH_TRACKPOPUP_MENU; quirks_ |= PLUGIN_QUIRK_PATCH_TRACKPOPUP_MENU;
quirks_ |= PLUGIN_QUIRK_PATCH_SETCURSOR; quirks_ |= PLUGIN_QUIRK_PATCH_SETCURSOR;
} }
plugin_module_handle_ = ::GetModuleHandle(plugin_info.path.value().c_str());
} }
WebPluginDelegateImpl::~WebPluginDelegateImpl() { WebPluginDelegateImpl::~WebPluginDelegateImpl() {
...@@ -283,7 +280,7 @@ bool WebPluginDelegateImpl::Initialize(const GURL& url, ...@@ -283,7 +280,7 @@ bool WebPluginDelegateImpl::Initialize(const GURL& url,
if (windowless_ && !g_iat_patch_track_popup_menu.Pointer()->is_patched() && if (windowless_ && !g_iat_patch_track_popup_menu.Pointer()->is_patched() &&
(quirks_ & PLUGIN_QUIRK_PATCH_TRACKPOPUP_MENU)) { (quirks_ & PLUGIN_QUIRK_PATCH_TRACKPOPUP_MENU)) {
g_iat_patch_track_popup_menu.Pointer()->Patch( g_iat_patch_track_popup_menu.Pointer()->Patch(
plugin_module_handle_, "user32.dll", "TrackPopupMenu", GetPluginPath().value().c_str(), "user32.dll", "TrackPopupMenu",
WebPluginDelegateImpl::TrackPopupMenuPatch); WebPluginDelegateImpl::TrackPopupMenuPatch);
} }
...@@ -296,7 +293,7 @@ bool WebPluginDelegateImpl::Initialize(const GURL& url, ...@@ -296,7 +293,7 @@ bool WebPluginDelegateImpl::Initialize(const GURL& url,
if (windowless_ && !g_iat_patch_set_cursor.Pointer()->is_patched() && if (windowless_ && !g_iat_patch_set_cursor.Pointer()->is_patched() &&
(quirks_ & PLUGIN_QUIRK_PATCH_SETCURSOR)) { (quirks_ & PLUGIN_QUIRK_PATCH_SETCURSOR)) {
g_iat_patch_set_cursor.Pointer()->Patch( g_iat_patch_set_cursor.Pointer()->Patch(
plugin_module_handle_, "user32.dll", "SetCursor", GetPluginPath().value().c_str(), "user32.dll", "SetCursor",
WebPluginDelegateImpl::SetCursorPatch); WebPluginDelegateImpl::SetCursorPatch);
} }
return true; return true;
......
...@@ -247,9 +247,6 @@ class WebPluginDelegateImpl : public WebPluginDelegate { ...@@ -247,9 +247,6 @@ class WebPluginDelegateImpl : public WebPluginDelegate {
#endif #endif
#if defined(OS_WIN) #if defined(OS_WIN)
// The plugin module handle.
HMODULE plugin_module_handle_;
// TrackPopupMenu interceptor. Parameters are the same as the Win32 function // TrackPopupMenu interceptor. Parameters are the same as the Win32 function
// TrackPopupMenu. // TrackPopupMenu.
static BOOL WINAPI TrackPopupMenuPatch(HMENU menu, unsigned int flags, int x, static BOOL WINAPI TrackPopupMenuPatch(HMENU menu, unsigned int flags, int x,
......
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