summaryrefslogtreecommitdiffstats
path: root/chrome_frame/buggy_bho_handling.cc
diff options
context:
space:
mode:
authortommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-21 16:07:24 +0000
committertommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-21 16:07:24 +0000
commit0535809cc310e18eefdf3afbd32ff35b593b182f (patch)
tree269d36f76b6ce9e69ab4d88254871d26366dbfa5 /chrome_frame/buggy_bho_handling.cc
parent2e4c50cd49a81bef973b934c236b110e660f3746 (diff)
downloadchromium_src-0535809cc310e18eefdf3afbd32ff35b593b182f.zip
chromium_src-0535809cc310e18eefdf3afbd32ff35b593b182f.tar.gz
chromium_src-0535809cc310e18eefdf3afbd32ff35b593b182f.tar.bz2
A different approach to avoid crashes in buggy 3rd party BHOs.This time we're more preceise and only target the buggy components.Behaviour for components that handle browser events correctly, is unchanged even in the presence of buggy DLLs.The core class here is the BuggyBhoTls class and here's the comment for that class:
// Construct an instance of this class on the stack when firing web browser // events that can be sent to buggy BHOs. This class will intercept those // BHOs (see list in cc file) and ignore notifications to those components // for as long as the BuggyBhoTls instance on the stack lives. TEST=A better fix for bug 44463 but also fixes bug 49373 and a number of crashes that haven't been associated with bug reports yet. BUG=44463, 49373 Review URL: http://codereview.chromium.org/3031009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@53194 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame/buggy_bho_handling.cc')
-rw-r--r--chrome_frame/buggy_bho_handling.cc199
1 files changed, 199 insertions, 0 deletions
diff --git a/chrome_frame/buggy_bho_handling.cc b/chrome_frame/buggy_bho_handling.cc
new file mode 100644
index 0000000..1c6df94
--- /dev/null
+++ b/chrome_frame/buggy_bho_handling.cc
@@ -0,0 +1,199 @@
+// Copyright (c) 2010 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_frame/buggy_bho_handling.h"
+
+#include "base/logging.h"
+#include "base/scoped_comptr_win.h"
+
+#include "chrome_frame/function_stub.h"
+#include "chrome_frame/utils.h"
+#include "chrome_frame/vtable_patch_manager.h"
+
+namespace buggy_bho {
+
+base::ThreadLocalPointer<BuggyBhoTls> BuggyBhoTls::s_bad_object_tls_;
+
+struct ModuleAndVersion {
+ const char* module_name_;
+ const uint32 major_version_;
+ const uint32 minor_version_;
+};
+
+const ModuleAndVersion kBuggyModules[] = {
+ { "askbar.dll", 4, 1 }, // troublemaker: 4.1.0.5.
+ { "gbieh.dll", 3, 8 }, // troublemaker: 3.8.14.12
+ { "gbiehcef.dll", 3, 8 }, // troublemaker: 3.8.11.23
+ { "alot.dll", 2, 5 }, // troublemaker: 2.5.12000.509
+ { "srchbxex.dll", 1, 2 }, // troublemaker: 1.2.123.0
+ { "tbabso.dll", 4, 5 }, // troublemaker: 4.5.156.0
+ { "tbabs0.dll.dll", 4, 5 }, // troublemaker: 4.5.156.0
+ { "tbbes0.dll", 4, 5 }, // troublemaker: 4.5.153.0
+ { "ctbr.dll", 5, 1 }, // troublemaker: 5.1.0.95
+
+ // Viruses?
+ { "msgsc2.dll", 0xffff, 0xffff }, // troublemaker: 1.2.3000.1
+ { "essclis.dll", 0xffff, 0xffff }, // troublemaker: 4.3.1800.2
+};
+
+bool IsBuggyBho(HMODULE mod) {
+ DCHECK(mod);
+
+ char path[MAX_PATH * 2] = {0};
+ ::GetModuleFileNameA(mod, path, arraysize(path));
+ const char* file_name = ::PathFindFileNameA(path);
+ for (size_t i = 0; i < arraysize(kBuggyModules); ++i) {
+ if (lstrcmpiA(file_name, kBuggyModules[i].module_name_) == 0) {
+ uint32 version = 0;
+ GetModuleVersion(mod, &version, NULL);
+ const ModuleAndVersion& buggy = kBuggyModules[i];
+ if (HIWORD(version) < buggy.major_version_ ||
+ (HIWORD(version) == buggy.major_version_ &&
+ LOWORD(version) <= buggy.minor_version_)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+BuggyBhoTls::BuggyBhoTls() : previous_instance_(s_bad_object_tls_.Get()) {
+ s_bad_object_tls_.Set(this);
+}
+
+BuggyBhoTls::~BuggyBhoTls() {
+ DCHECK(FromCurrentThread() == this);
+ s_bad_object_tls_.Set(previous_instance_);
+}
+
+void BuggyBhoTls::AddBuggyObject(IDispatch* obj) {
+ bad_objects_.push_back(obj);
+}
+
+bool BuggyBhoTls::IsBuggyObject(IDispatch* obj) const {
+ return std::find(bad_objects_.begin(), bad_objects_.end(), obj) !=
+ bad_objects_.end();
+}
+
+// static
+BuggyBhoTls* BuggyBhoTls::FromCurrentThread() {
+ return s_bad_object_tls_.Get();
+}
+
+// static
+STDMETHODIMP BuggyBhoTls::BuggyBhoInvoke(InvokeFunc original, IDispatch* me,
+ DISPID dispid, REFIID riid, LCID lcid,
+ WORD flags, DISPPARAMS* params,
+ VARIANT* result, EXCEPINFO* ei,
+ UINT* err) {
+ DLOG(INFO) << __FUNCTION__;
+
+ const BuggyBhoTls* tls = BuggyBhoTls::FromCurrentThread();
+ if (tls && tls->IsBuggyObject(me)) {
+ // Ignore this call and avoid the bug.
+ // TODO(tommi): Maybe we should check a specific list of DISPIDs too?
+ return S_OK;
+ }
+
+ return original(me, dispid, riid, lcid, flags, params, result, ei, err);
+}
+
+// static
+HRESULT BuggyBhoTls::PatchInvokeMethod(PROC* invoke) {
+ CCritSecLock lock(_pAtlModule->m_csStaticDataInitAndTypeInfo, true);
+
+ FunctionStub* stub = FunctionStub::FromCode(*invoke);
+ if (stub)
+ return S_FALSE;
+
+ DWORD flags = 0;
+ if (!::VirtualProtect(invoke, sizeof(PROC), PAGE_EXECUTE_READWRITE, &flags))
+ return AtlHresultFromLastError();
+
+ HRESULT hr = S_OK;
+
+ stub = FunctionStub::Create(reinterpret_cast<uintptr_t>(*invoke),
+ BuggyBhoInvoke);
+ if (!stub) {
+ hr = E_OUTOFMEMORY;
+ } else {
+ if (!vtable_patch::internal::ReplaceFunctionPointer(
+ reinterpret_cast<void**>(invoke), stub->code(),
+ reinterpret_cast<void*>(stub->argument()))) {
+ hr = E_UNEXPECTED;
+ FunctionStub::Destroy(stub);
+ } else {
+ ::FlushInstructionCache(::GetCurrentProcess(), invoke, sizeof(PROC));
+ }
+ }
+
+ ::VirtualProtect(invoke, sizeof(PROC), flags, &flags);
+
+ return hr;
+}
+
+// static
+bool BuggyBhoTls::PatchIfBuggy(CONNECTDATA* cd, const IID& diid) {
+ DCHECK(cd);
+ PROC* methods = *reinterpret_cast<PROC**>(cd->pUnk);
+ HMODULE mod = GetModuleFromAddress(methods[0]);
+ if (!IsBuggyBho(mod))
+ return false;
+
+ ScopedComPtr<IDispatch> disp;
+ HRESULT hr = cd->pUnk->QueryInterface(diid,
+ reinterpret_cast<void**>(disp.Receive()));
+ if (FAILED(hr)) // Sometimes only IDispatch QI is supported
+ hr = disp.QueryFrom(cd->pUnk);
+ DCHECK(SUCCEEDED(hr));
+
+ if (SUCCEEDED(hr)) {
+ const int kInvokeIndex = 6;
+ DCHECK(static_cast<IUnknown*>(disp) == cd->pUnk);
+ if (SUCCEEDED(PatchInvokeMethod(&methods[kInvokeIndex]))) {
+ BuggyBhoTls* tls = BuggyBhoTls::FromCurrentThread();
+ DCHECK(tls);
+ if (tls) {
+ tls->AddBuggyObject(disp);
+ }
+ }
+ }
+
+ return false;
+}
+
+// static
+HRESULT BuggyBhoTls::PatchBuggyBHOs(IWebBrowser2* browser) {
+ DCHECK(browser);
+ DCHECK(BuggyBhoTls::FromCurrentThread())
+ << "You must first have an instance of BuggyBhoTls on this thread";
+
+ ScopedComPtr<IConnectionPointContainer> cpc;
+ HRESULT hr = cpc.QueryFrom(browser);
+ if (SUCCEEDED(hr)) {
+ const GUID sinks[] = { DIID_DWebBrowserEvents2, DIID_DWebBrowserEvents };
+ for (size_t i = 0; i < arraysize(sinks); ++i) {
+ ScopedComPtr<IConnectionPoint> cp;
+ cpc->FindConnectionPoint(sinks[i], cp.Receive());
+ if (cp) {
+ ScopedComPtr<IEnumConnections> connections;
+ cp->EnumConnections(connections.Receive());
+ if (connections) {
+ CONNECTDATA cd = {0};
+ DWORD fetched = 0;
+ while (connections->Next(1, &cd, &fetched) == S_OK && fetched) {
+ PatchIfBuggy(&cd, sinks[i]);
+ cd.pUnk->Release();
+ fetched = 0;
+ }
+ }
+ }
+ }
+ }
+
+ return hr;
+}
+
+} // end namespace buggy_bho