[Profiles, Fixit] Making GAIAInfoUpdateService into a ProfileKeyedService.
[chromium-blink-merge.git] / chrome_frame / buggy_bho_handling.cc
blob9d00528cf29dc26d750baa9737d77a4bb73ade03
1 // Copyright (c) 2012 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 <algorithm>
9 #include "base/logging.h"
10 #include "base/process_util.h"
11 #include "base/win/scoped_comptr.h"
12 #include "chrome_frame/exception_barrier.h"
13 #include "chrome_frame/function_stub.h"
14 #include "chrome_frame/utils.h"
15 #include "chrome_frame/vtable_patch_manager.h"
17 namespace buggy_bho {
19 base::ThreadLocalPointer<BuggyBhoTls> BuggyBhoTls::s_bad_object_tls_;
21 struct ModuleAndVersion {
22 const char* module_name_;
23 const uint32 major_version_;
24 const uint32 minor_version_;
27 const ModuleAndVersion kBuggyModules[] = {
28 { "askbar.dll", 4, 1 }, // troublemaker: 4.1.0.5.
29 { "gbieh.dll", 3, 8 }, // troublemaker: 3.8.14.12
30 { "gbiehcef.dll", 3, 8 }, // troublemaker: 3.8.11.23
31 { "alot.dll", 2, 5 }, // troublemaker: 2.5.12000.509
32 { "ctbr.dll", 5, 1 }, // troublemaker: 5.1.0.95
33 { "srchbxex.dll", 1, 2 }, // troublemaker: 1.2.123.0
34 { "iedvtool32.dll", 8, 0 }, // troublemaker: 8.0.0.4
35 { "mst164.dll", 9, 1 }, // troublemaker: 9.1.3700.1
36 { "deposit_ie_com.dll", 0, 1 }, // troublemaker: 0.1.0.72
37 { "rpshell32.dll", 6, 0 }, // troublemaker: 6.0.6000.1389
38 { "msgsres.dll", 6, 0 }, // troublemaker: 6.0.6000.1389
39 { "limewireinttb.dll", 4, 1 }, // troublemaker: 4.1.1.1000
40 { "pxsecure.dll", 3, 0 }, // troublemaker: 3.0.5.220
42 // These BHOs seem to be out of the same buggy BHO factory
43 { "tbabso.dll", 4, 5 }, // troublemaker: 4.5.156.0
44 { "tbabs0.dll.dll", 4, 5 }, // troublemaker: 4.5.156.0
45 { "tbbes0.dll", 4, 5 }, // troublemaker: 4.5.153.0
46 { "tbfre0.dll", 4, 5 }, // troublemaker: 4.5.181.1
47 { "tbmypl.dll", 4, 5 }, // troublemaker: 4.5.181.3
48 { "tbmul1.dll", 4, 5 }, // troublemaker: 4.5.181.1
49 { "tbdow1.dll", 4, 5 }, // troublemaker: 4.5.167.0
50 { "tbfree.dll", 4, 5 }, // troublemaker: 4.5.178.0
52 // Viruses?
53 { "msgsc2.dll", 0xffff, 0xffff }, // troublemaker: 1.2.3000.1
54 { "essclis.dll", 0xffff, 0xffff }, // troublemaker: 4.3.1800.2
55 { "snagwb.dll", 0xffff, 0xffff }, // troublemaker: 2.6.0.28
58 bool IsBuggyBho(HMODULE mod) {
59 DCHECK(mod);
61 char path[MAX_PATH * 2] = {0};
62 ::GetModuleFileNameA(mod, path, arraysize(path));
63 const char* file_name = ::PathFindFileNameA(path);
64 for (size_t i = 0; i < arraysize(kBuggyModules); ++i) {
65 if (lstrcmpiA(file_name, kBuggyModules[i].module_name_) == 0) {
66 uint32 version = 0;
67 GetModuleVersion(mod, &version, NULL);
68 const ModuleAndVersion& buggy = kBuggyModules[i];
69 if (HIWORD(version) < buggy.major_version_ ||
70 (HIWORD(version) == buggy.major_version_ &&
71 LOWORD(version) <= buggy.minor_version_)) {
72 return true;
77 return false;
80 BuggyBhoTls::BuggyBhoTls()
81 : patched_(false) {
82 DCHECK(s_bad_object_tls_.Get() == NULL);
83 s_bad_object_tls_.Set(this);
86 BuggyBhoTls::~BuggyBhoTls() {
87 DCHECK(BuggyBhoTls::GetInstance() == this);
88 s_bad_object_tls_.Set(NULL);
91 void BuggyBhoTls::AddBuggyObject(IDispatch* obj) {
92 bad_objects_.push_back(obj);
95 bool BuggyBhoTls::ShouldSkipInvoke(IDispatch* obj) const {
96 DCHECK(web_browser2_ != NULL);
97 if (IsChromeFrameDocument(web_browser2_)) {
98 return std::find(bad_objects_.begin(), bad_objects_.end(), obj) !=
99 bad_objects_.end();
101 return false;
104 // static
105 BuggyBhoTls* BuggyBhoTls::GetInstance() {
106 BuggyBhoTls* tls_instance = s_bad_object_tls_.Get();
107 if (!tls_instance) {
108 tls_instance = new BuggyBhoTls();
109 DCHECK(s_bad_object_tls_.Get() != NULL);
111 return tls_instance;
114 // static
115 void BuggyBhoTls::DestroyInstance() {
116 BuggyBhoTls* tls_instance = s_bad_object_tls_.Get();
117 if (tls_instance) {
118 delete tls_instance;
119 DCHECK(s_bad_object_tls_.Get() == NULL);
123 HRESULT BuggyBhoTls::PatchBuggyBHOs(IWebBrowser2* browser) {
124 if (patched_)
125 return S_FALSE;
127 DCHECK(browser);
128 DCHECK(web_browser2_ == NULL);
130 base::win::ScopedComPtr<IConnectionPointContainer> cpc;
131 HRESULT hr = cpc.QueryFrom(browser);
132 if (SUCCEEDED(hr)) {
133 const GUID sinks[] = { DIID_DWebBrowserEvents2, DIID_DWebBrowserEvents };
134 for (size_t i = 0; i < arraysize(sinks); ++i) {
135 base::win::ScopedComPtr<IConnectionPoint> cp;
136 cpc->FindConnectionPoint(sinks[i], cp.Receive());
137 if (cp) {
138 base::win::ScopedComPtr<IEnumConnections> connections;
139 cp->EnumConnections(connections.Receive());
140 if (connections) {
141 CONNECTDATA cd = {0};
142 DWORD fetched = 0;
143 while (connections->Next(1, &cd, &fetched) == S_OK && fetched) {
144 PatchIfBuggy(cd.pUnk, sinks[i]);
145 cd.pUnk->Release();
146 fetched = 0;
152 patched_ = true;
153 web_browser2_ = browser;
154 return hr;
157 bool BuggyBhoTls::PatchIfBuggy(IUnknown* unk, const IID& diid) {
158 DCHECK(unk);
159 PROC* methods = *reinterpret_cast<PROC**>(unk);
160 HMODULE mod = base::GetModuleFromAddress(methods[0]);
161 if (!IsBuggyBho(mod))
162 return false;
164 base::win::ScopedComPtr<IDispatch> disp;
165 HRESULT hr = unk->QueryInterface(diid,
166 reinterpret_cast<void**>(disp.Receive()));
167 if (FAILED(hr)) // Sometimes only IDispatch QI is supported
168 hr = disp.QueryFrom(unk);
169 DCHECK(SUCCEEDED(hr));
171 if (SUCCEEDED(hr)) {
172 const int kInvokeIndex = 6;
173 DCHECK(static_cast<IUnknown*>(disp) == unk);
174 if (SUCCEEDED(PatchInvokeMethod(&methods[kInvokeIndex]))) {
175 AddBuggyObject(disp);
178 return false;
181 // static
182 STDMETHODIMP BuggyBhoTls::BuggyBhoInvoke(InvokeFunc original, IDispatch* me,
183 DISPID dispid, REFIID riid, LCID lcid,
184 WORD flags, DISPPARAMS* params,
185 VARIANT* result, EXCEPINFO* ei,
186 UINT* err) {
187 DVLOG(1) << __FUNCTION__;
189 DCHECK(BuggyBhoTls::GetInstance())
190 << "You must first have an instance of BuggyBhoTls on this thread";
191 if (BuggyBhoTls::GetInstance() &&
192 BuggyBhoTls::GetInstance()->ShouldSkipInvoke(me)) {
193 // Ignore this call and avoid the bug.
194 // TODO(tommi): Maybe we should check a specific list of DISPIDs too?
195 return S_OK;
198 // No need to report crashes in those known-to-be-buggy DLLs.
199 ExceptionBarrierReportOnlyModule barrier;
200 return original(me, dispid, riid, lcid, flags, params, result, ei, err);
203 // static
204 HRESULT BuggyBhoTls::PatchInvokeMethod(PROC* invoke) {
205 CCritSecLock lock(_pAtlModule->m_csStaticDataInitAndTypeInfo.m_sec, true);
207 FunctionStub* stub = FunctionStub::FromCode(*invoke);
208 if (stub)
209 return S_FALSE;
211 DWORD flags = 0;
212 if (!::VirtualProtect(invoke, sizeof(PROC), PAGE_EXECUTE_READWRITE, &flags))
213 return AtlHresultFromLastError();
215 HRESULT hr = S_OK;
217 stub = FunctionStub::Create(reinterpret_cast<uintptr_t>(*invoke),
218 BuggyBhoInvoke);
219 if (!stub) {
220 hr = E_OUTOFMEMORY;
221 } else {
222 if (!vtable_patch::internal::ReplaceFunctionPointer(
223 reinterpret_cast<void**>(invoke), stub->code(),
224 reinterpret_cast<void*>(stub->argument()))) {
225 hr = E_UNEXPECTED;
226 FunctionStub::Destroy(stub);
227 } else {
228 PinModule(); // No backing out now.
229 ::FlushInstructionCache(::GetCurrentProcess(), invoke, sizeof(PROC));
232 ::VirtualProtect(invoke, sizeof(PROC), flags, &flags);
233 return hr;
236 } // end namespace buggy_bho