WebKit Roll 70305:70328.
[chromium-blink-merge.git] / chrome_frame / buggy_bho_handling.cc
blobc32a5c6a61cecf1fe97307fbe039819620a33047
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome_frame/buggy_bho_handling.h"
7 #include "base/logging.h"
8 #include "base/scoped_comptr_win.h"
10 #include "chrome_frame/exception_barrier.h"
11 #include "chrome_frame/function_stub.h"
12 #include "chrome_frame/utils.h"
13 #include "chrome_frame/vtable_patch_manager.h"
15 namespace buggy_bho {
17 base::ThreadLocalPointer<BuggyBhoTls> BuggyBhoTls::s_bad_object_tls_;
19 struct ModuleAndVersion {
20 const char* module_name_;
21 const uint32 major_version_;
22 const uint32 minor_version_;
25 const ModuleAndVersion kBuggyModules[] = {
26 { "askbar.dll", 4, 1 }, // troublemaker: 4.1.0.5.
27 { "gbieh.dll", 3, 8 }, // troublemaker: 3.8.14.12
28 { "gbiehcef.dll", 3, 8 }, // troublemaker: 3.8.11.23
29 { "alot.dll", 2, 5 }, // troublemaker: 2.5.12000.509
30 { "ctbr.dll", 5, 1 }, // troublemaker: 5.1.0.95
31 { "srchbxex.dll", 1, 2 }, // troublemaker: 1.2.123.0
32 { "iedvtool32.dll", 8, 0 }, // troublemaker: 8.0.0.4
33 { "mst164.dll", 9, 1 }, // troublemaker: 9.1.3700.1
34 { "deposit_ie_com.dll", 0, 1 }, // troublemaker: 0.1.0.72
35 { "rpshell32.dll", 6, 0 }, // troublemaker: 6.0.6000.1389
36 { "msgsres.dll", 6, 0 }, // troublemaker: 6.0.6000.1389
37 { "limewireinttb.dll", 4, 1 }, // troublemaker: 4.1.1.1000
39 // These BHOs seem to be out of the same buggy BHO factory
40 { "tbabso.dll", 4, 5 }, // troublemaker: 4.5.156.0
41 { "tbabs0.dll.dll", 4, 5 }, // troublemaker: 4.5.156.0
42 { "tbbes0.dll", 4, 5 }, // troublemaker: 4.5.153.0
43 { "tbfre0.dll", 4, 5 }, // troublemaker: 4.5.181.1
44 { "tbmypl.dll", 4, 5 }, // troublemaker: 4.5.181.3
45 { "tbmul1.dll", 4, 5 }, // troublemaker: 4.5.181.1
46 { "tbdow1.dll", 4, 5 }, // troublemaker: 4.5.167.0
47 { "tbfree.dll", 4, 5 }, // troublemaker: 4.5.178.0
49 // Viruses?
50 { "msgsc2.dll", 0xffff, 0xffff }, // troublemaker: 1.2.3000.1
51 { "essclis.dll", 0xffff, 0xffff }, // troublemaker: 4.3.1800.2
52 { "snagwb.dll", 0xffff, 0xffff }, // troublemaker: 2.6.0.28
55 bool IsBuggyBho(HMODULE mod) {
56 DCHECK(mod);
58 char path[MAX_PATH * 2] = {0};
59 ::GetModuleFileNameA(mod, path, arraysize(path));
60 const char* file_name = ::PathFindFileNameA(path);
61 for (size_t i = 0; i < arraysize(kBuggyModules); ++i) {
62 if (lstrcmpiA(file_name, kBuggyModules[i].module_name_) == 0) {
63 uint32 version = 0;
64 GetModuleVersion(mod, &version, NULL);
65 const ModuleAndVersion& buggy = kBuggyModules[i];
66 if (HIWORD(version) < buggy.major_version_ ||
67 (HIWORD(version) == buggy.major_version_ &&
68 LOWORD(version) <= buggy.minor_version_)) {
69 return true;
74 return false;
77 BuggyBhoTls::BuggyBhoTls() : previous_instance_(s_bad_object_tls_.Get()) {
78 s_bad_object_tls_.Set(this);
81 BuggyBhoTls::~BuggyBhoTls() {
82 DCHECK(FromCurrentThread() == this);
83 s_bad_object_tls_.Set(previous_instance_);
86 void BuggyBhoTls::AddBuggyObject(IDispatch* obj) {
87 bad_objects_.push_back(obj);
90 bool BuggyBhoTls::IsBuggyObject(IDispatch* obj) const {
91 return std::find(bad_objects_.begin(), bad_objects_.end(), obj) !=
92 bad_objects_.end();
95 // static
96 BuggyBhoTls* BuggyBhoTls::FromCurrentThread() {
97 return s_bad_object_tls_.Get();
100 // static
101 STDMETHODIMP BuggyBhoTls::BuggyBhoInvoke(InvokeFunc original, IDispatch* me,
102 DISPID dispid, REFIID riid, LCID lcid,
103 WORD flags, DISPPARAMS* params,
104 VARIANT* result, EXCEPINFO* ei,
105 UINT* err) {
106 DVLOG(1) << __FUNCTION__;
108 const BuggyBhoTls* tls = BuggyBhoTls::FromCurrentThread();
109 if (tls && tls->IsBuggyObject(me)) {
110 // Ignore this call and avoid the bug.
111 // TODO(tommi): Maybe we should check a specific list of DISPIDs too?
112 return S_OK;
115 // No need to report crashes in those known-to-be-buggy DLLs.
116 ExceptionBarrierReportOnlyModule barrier;
117 return original(me, dispid, riid, lcid, flags, params, result, ei, err);
120 // static
121 HRESULT BuggyBhoTls::PatchInvokeMethod(PROC* invoke) {
122 CCritSecLock lock(_pAtlModule->m_csStaticDataInitAndTypeInfo.m_sec, true);
124 FunctionStub* stub = FunctionStub::FromCode(*invoke);
125 if (stub)
126 return S_FALSE;
128 DWORD flags = 0;
129 if (!::VirtualProtect(invoke, sizeof(PROC), PAGE_EXECUTE_READWRITE, &flags))
130 return AtlHresultFromLastError();
132 HRESULT hr = S_OK;
134 stub = FunctionStub::Create(reinterpret_cast<uintptr_t>(*invoke),
135 BuggyBhoInvoke);
136 if (!stub) {
137 hr = E_OUTOFMEMORY;
138 } else {
139 if (!vtable_patch::internal::ReplaceFunctionPointer(
140 reinterpret_cast<void**>(invoke), stub->code(),
141 reinterpret_cast<void*>(stub->argument()))) {
142 hr = E_UNEXPECTED;
143 FunctionStub::Destroy(stub);
144 } else {
145 PinModule(); // No backing out now.
146 ::FlushInstructionCache(::GetCurrentProcess(), invoke, sizeof(PROC));
150 ::VirtualProtect(invoke, sizeof(PROC), flags, &flags);
152 return hr;
155 // static
156 bool BuggyBhoTls::PatchIfBuggy(CONNECTDATA* cd, const IID& diid) {
157 DCHECK(cd);
158 PROC* methods = *reinterpret_cast<PROC**>(cd->pUnk);
159 HMODULE mod = GetModuleFromAddress(methods[0]);
160 if (!IsBuggyBho(mod))
161 return false;
163 ScopedComPtr<IDispatch> disp;
164 HRESULT hr = cd->pUnk->QueryInterface(diid,
165 reinterpret_cast<void**>(disp.Receive()));
166 if (FAILED(hr)) // Sometimes only IDispatch QI is supported
167 hr = disp.QueryFrom(cd->pUnk);
168 DCHECK(SUCCEEDED(hr));
170 if (SUCCEEDED(hr)) {
171 const int kInvokeIndex = 6;
172 DCHECK(static_cast<IUnknown*>(disp) == cd->pUnk);
173 if (SUCCEEDED(PatchInvokeMethod(&methods[kInvokeIndex]))) {
174 BuggyBhoTls* tls = BuggyBhoTls::FromCurrentThread();
175 DCHECK(tls);
176 if (tls) {
177 tls->AddBuggyObject(disp);
182 return false;
185 // static
186 HRESULT BuggyBhoTls::PatchBuggyBHOs(IWebBrowser2* browser) {
187 DCHECK(browser);
188 DCHECK(BuggyBhoTls::FromCurrentThread())
189 << "You must first have an instance of BuggyBhoTls on this thread";
191 ScopedComPtr<IConnectionPointContainer> cpc;
192 HRESULT hr = cpc.QueryFrom(browser);
193 if (SUCCEEDED(hr)) {
194 const GUID sinks[] = { DIID_DWebBrowserEvents2, DIID_DWebBrowserEvents };
195 for (size_t i = 0; i < arraysize(sinks); ++i) {
196 ScopedComPtr<IConnectionPoint> cp;
197 cpc->FindConnectionPoint(sinks[i], cp.Receive());
198 if (cp) {
199 ScopedComPtr<IEnumConnections> connections;
200 cp->EnumConnections(connections.Receive());
201 if (connections) {
202 CONNECTDATA cd = {0};
203 DWORD fetched = 0;
204 while (connections->Next(1, &cd, &fetched) == S_OK && fetched) {
205 PatchIfBuggy(&cd, sinks[i]);
206 cd.pUnk->Release();
207 fetched = 0;
214 return hr;
217 } // end namespace buggy_bho