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 "base/logging.h"
8 #include "base/process_util.h"
9 #include "base/win/scoped_comptr.h"
11 #include "chrome_frame/exception_barrier.h"
12 #include "chrome_frame/function_stub.h"
13 #include "chrome_frame/utils.h"
14 #include "chrome_frame/vtable_patch_manager.h"
18 base::ThreadLocalPointer
<BuggyBhoTls
> BuggyBhoTls::s_bad_object_tls_
;
20 struct ModuleAndVersion
{
21 const char* module_name_
;
22 const uint32 major_version_
;
23 const uint32 minor_version_
;
26 const ModuleAndVersion kBuggyModules
[] = {
27 { "askbar.dll", 4, 1 }, // troublemaker: 4.1.0.5.
28 { "gbieh.dll", 3, 8 }, // troublemaker: 3.8.14.12
29 { "gbiehcef.dll", 3, 8 }, // troublemaker: 3.8.11.23
30 { "alot.dll", 2, 5 }, // troublemaker: 2.5.12000.509
31 { "ctbr.dll", 5, 1 }, // troublemaker: 5.1.0.95
32 { "srchbxex.dll", 1, 2 }, // troublemaker: 1.2.123.0
33 { "iedvtool32.dll", 8, 0 }, // troublemaker: 8.0.0.4
34 { "mst164.dll", 9, 1 }, // troublemaker: 9.1.3700.1
35 { "deposit_ie_com.dll", 0, 1 }, // troublemaker: 0.1.0.72
36 { "rpshell32.dll", 6, 0 }, // troublemaker: 6.0.6000.1389
37 { "msgsres.dll", 6, 0 }, // troublemaker: 6.0.6000.1389
38 { "limewireinttb.dll", 4, 1 }, // troublemaker: 4.1.1.1000
39 { "pxsecure.dll", 3, 0 }, // troublemaker: 3.0.5.220
41 // These BHOs seem to be out of the same buggy BHO factory
42 { "tbabso.dll", 4, 5 }, // troublemaker: 4.5.156.0
43 { "tbabs0.dll.dll", 4, 5 }, // troublemaker: 4.5.156.0
44 { "tbbes0.dll", 4, 5 }, // troublemaker: 4.5.153.0
45 { "tbfre0.dll", 4, 5 }, // troublemaker: 4.5.181.1
46 { "tbmypl.dll", 4, 5 }, // troublemaker: 4.5.181.3
47 { "tbmul1.dll", 4, 5 }, // troublemaker: 4.5.181.1
48 { "tbdow1.dll", 4, 5 }, // troublemaker: 4.5.167.0
49 { "tbfree.dll", 4, 5 }, // troublemaker: 4.5.178.0
52 { "msgsc2.dll", 0xffff, 0xffff }, // troublemaker: 1.2.3000.1
53 { "essclis.dll", 0xffff, 0xffff }, // troublemaker: 4.3.1800.2
54 { "snagwb.dll", 0xffff, 0xffff }, // troublemaker: 2.6.0.28
57 bool IsBuggyBho(HMODULE mod
) {
60 char path
[MAX_PATH
* 2] = {0};
61 ::GetModuleFileNameA(mod
, path
, arraysize(path
));
62 const char* file_name
= ::PathFindFileNameA(path
);
63 for (size_t i
= 0; i
< arraysize(kBuggyModules
); ++i
) {
64 if (lstrcmpiA(file_name
, kBuggyModules
[i
].module_name_
) == 0) {
66 GetModuleVersion(mod
, &version
, NULL
);
67 const ModuleAndVersion
& buggy
= kBuggyModules
[i
];
68 if (HIWORD(version
) < buggy
.major_version_
||
69 (HIWORD(version
) == buggy
.major_version_
&&
70 LOWORD(version
) <= buggy
.minor_version_
)) {
79 BuggyBhoTls::BuggyBhoTls()
81 DCHECK(s_bad_object_tls_
.Get() == NULL
);
82 s_bad_object_tls_
.Set(this);
85 BuggyBhoTls::~BuggyBhoTls() {
86 DCHECK(BuggyBhoTls::GetInstance() == this);
87 s_bad_object_tls_
.Set(NULL
);
90 void BuggyBhoTls::AddBuggyObject(IDispatch
* obj
) {
91 bad_objects_
.push_back(obj
);
94 bool BuggyBhoTls::ShouldSkipInvoke(IDispatch
* obj
) const {
95 DCHECK(web_browser2_
!= NULL
);
96 if (IsChromeFrameDocument(web_browser2_
)) {
97 return std::find(bad_objects_
.begin(), bad_objects_
.end(), obj
) !=
104 BuggyBhoTls
* BuggyBhoTls::GetInstance() {
105 BuggyBhoTls
* tls_instance
= s_bad_object_tls_
.Get();
107 tls_instance
= new BuggyBhoTls();
108 DCHECK(s_bad_object_tls_
.Get() != NULL
);
114 void BuggyBhoTls::DestroyInstance() {
115 BuggyBhoTls
* tls_instance
= s_bad_object_tls_
.Get();
118 DCHECK(s_bad_object_tls_
.Get() == NULL
);
122 HRESULT
BuggyBhoTls::PatchBuggyBHOs(IWebBrowser2
* browser
) {
127 DCHECK(web_browser2_
== NULL
);
129 base::win::ScopedComPtr
<IConnectionPointContainer
> cpc
;
130 HRESULT hr
= cpc
.QueryFrom(browser
);
132 const GUID sinks
[] = { DIID_DWebBrowserEvents2
, DIID_DWebBrowserEvents
};
133 for (size_t i
= 0; i
< arraysize(sinks
); ++i
) {
134 base::win::ScopedComPtr
<IConnectionPoint
> cp
;
135 cpc
->FindConnectionPoint(sinks
[i
], cp
.Receive());
137 base::win::ScopedComPtr
<IEnumConnections
> connections
;
138 cp
->EnumConnections(connections
.Receive());
140 CONNECTDATA cd
= {0};
142 while (connections
->Next(1, &cd
, &fetched
) == S_OK
&& fetched
) {
143 PatchIfBuggy(cd
.pUnk
, sinks
[i
]);
152 web_browser2_
= browser
;
156 bool BuggyBhoTls::PatchIfBuggy(IUnknown
* unk
, const IID
& diid
) {
158 PROC
* methods
= *reinterpret_cast<PROC
**>(unk
);
159 HMODULE mod
= base::GetModuleFromAddress(methods
[0]);
160 if (!IsBuggyBho(mod
))
163 base::win::ScopedComPtr
<IDispatch
> disp
;
164 HRESULT hr
= unk
->QueryInterface(diid
,
165 reinterpret_cast<void**>(disp
.Receive()));
166 if (FAILED(hr
)) // Sometimes only IDispatch QI is supported
167 hr
= disp
.QueryFrom(unk
);
168 DCHECK(SUCCEEDED(hr
));
171 const int kInvokeIndex
= 6;
172 DCHECK(static_cast<IUnknown
*>(disp
) == unk
);
173 if (SUCCEEDED(PatchInvokeMethod(&methods
[kInvokeIndex
]))) {
174 AddBuggyObject(disp
);
181 STDMETHODIMP
BuggyBhoTls::BuggyBhoInvoke(InvokeFunc original
, IDispatch
* me
,
182 DISPID dispid
, REFIID riid
, LCID lcid
,
183 WORD flags
, DISPPARAMS
* params
,
184 VARIANT
* result
, EXCEPINFO
* ei
,
186 DVLOG(1) << __FUNCTION__
;
188 DCHECK(BuggyBhoTls::GetInstance())
189 << "You must first have an instance of BuggyBhoTls on this thread";
190 if (BuggyBhoTls::GetInstance() &&
191 BuggyBhoTls::GetInstance()->ShouldSkipInvoke(me
)) {
192 // Ignore this call and avoid the bug.
193 // TODO(tommi): Maybe we should check a specific list of DISPIDs too?
197 // No need to report crashes in those known-to-be-buggy DLLs.
198 ExceptionBarrierReportOnlyModule barrier
;
199 return original(me
, dispid
, riid
, lcid
, flags
, params
, result
, ei
, err
);
203 HRESULT
BuggyBhoTls::PatchInvokeMethod(PROC
* invoke
) {
204 CCritSecLock
lock(_pAtlModule
->m_csStaticDataInitAndTypeInfo
.m_sec
, true);
206 FunctionStub
* stub
= FunctionStub::FromCode(*invoke
);
211 if (!::VirtualProtect(invoke
, sizeof(PROC
), PAGE_EXECUTE_READWRITE
, &flags
))
212 return AtlHresultFromLastError();
216 stub
= FunctionStub::Create(reinterpret_cast<uintptr_t>(*invoke
),
221 if (!vtable_patch::internal::ReplaceFunctionPointer(
222 reinterpret_cast<void**>(invoke
), stub
->code(),
223 reinterpret_cast<void*>(stub
->argument()))) {
225 FunctionStub::Destroy(stub
);
227 PinModule(); // No backing out now.
228 ::FlushInstructionCache(::GetCurrentProcess(), invoke
, sizeof(PROC
));
231 ::VirtualProtect(invoke
, sizeof(PROC
), flags
, &flags
);
235 } // end namespace buggy_bho