1 // Copyright (c) 2011 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/win/scoped_comptr.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"
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
38 { "pxsecure.dll", 3, 0 }, // troublemaker: 3.0.5.220
40 // These BHOs seem to be out of the same buggy BHO factory
41 { "tbabso.dll", 4, 5 }, // troublemaker: 4.5.156.0
42 { "tbabs0.dll.dll", 4, 5 }, // troublemaker: 4.5.156.0
43 { "tbbes0.dll", 4, 5 }, // troublemaker: 4.5.153.0
44 { "tbfre0.dll", 4, 5 }, // troublemaker: 4.5.181.1
45 { "tbmypl.dll", 4, 5 }, // troublemaker: 4.5.181.3
46 { "tbmul1.dll", 4, 5 }, // troublemaker: 4.5.181.1
47 { "tbdow1.dll", 4, 5 }, // troublemaker: 4.5.167.0
48 { "tbfree.dll", 4, 5 }, // troublemaker: 4.5.178.0
51 { "msgsc2.dll", 0xffff, 0xffff }, // troublemaker: 1.2.3000.1
52 { "essclis.dll", 0xffff, 0xffff }, // troublemaker: 4.3.1800.2
53 { "snagwb.dll", 0xffff, 0xffff }, // troublemaker: 2.6.0.28
56 bool IsBuggyBho(HMODULE mod
) {
59 char path
[MAX_PATH
* 2] = {0};
60 ::GetModuleFileNameA(mod
, path
, arraysize(path
));
61 const char* file_name
= ::PathFindFileNameA(path
);
62 for (size_t i
= 0; i
< arraysize(kBuggyModules
); ++i
) {
63 if (lstrcmpiA(file_name
, kBuggyModules
[i
].module_name_
) == 0) {
65 GetModuleVersion(mod
, &version
, NULL
);
66 const ModuleAndVersion
& buggy
= kBuggyModules
[i
];
67 if (HIWORD(version
) < buggy
.major_version_
||
68 (HIWORD(version
) == buggy
.major_version_
&&
69 LOWORD(version
) <= buggy
.minor_version_
)) {
78 BuggyBhoTls::BuggyBhoTls()
80 DCHECK(s_bad_object_tls_
.Get() == NULL
);
81 s_bad_object_tls_
.Set(this);
84 BuggyBhoTls::~BuggyBhoTls() {
85 DCHECK(BuggyBhoTls::GetInstance() == this);
86 s_bad_object_tls_
.Set(NULL
);
89 void BuggyBhoTls::AddBuggyObject(IDispatch
* obj
) {
90 bad_objects_
.push_back(obj
);
93 bool BuggyBhoTls::ShouldSkipInvoke(IDispatch
* obj
) const {
94 DCHECK(web_browser2_
!= NULL
);
95 if (IsChromeFrameDocument(web_browser2_
)) {
96 return std::find(bad_objects_
.begin(), bad_objects_
.end(), obj
) !=
103 BuggyBhoTls
* BuggyBhoTls::GetInstance() {
104 BuggyBhoTls
* tls_instance
= s_bad_object_tls_
.Get();
106 tls_instance
= new BuggyBhoTls();
107 DCHECK(s_bad_object_tls_
.Get() != NULL
);
113 void BuggyBhoTls::DestroyInstance() {
114 BuggyBhoTls
* tls_instance
= s_bad_object_tls_
.Get();
117 DCHECK(s_bad_object_tls_
.Get() == NULL
);
121 HRESULT
BuggyBhoTls::PatchBuggyBHOs(IWebBrowser2
* browser
) {
126 DCHECK(web_browser2_
== NULL
);
128 base::win::ScopedComPtr
<IConnectionPointContainer
> cpc
;
129 HRESULT hr
= cpc
.QueryFrom(browser
);
131 const GUID sinks
[] = { DIID_DWebBrowserEvents2
, DIID_DWebBrowserEvents
};
132 for (size_t i
= 0; i
< arraysize(sinks
); ++i
) {
133 base::win::ScopedComPtr
<IConnectionPoint
> cp
;
134 cpc
->FindConnectionPoint(sinks
[i
], cp
.Receive());
136 base::win::ScopedComPtr
<IEnumConnections
> connections
;
137 cp
->EnumConnections(connections
.Receive());
139 CONNECTDATA cd
= {0};
141 while (connections
->Next(1, &cd
, &fetched
) == S_OK
&& fetched
) {
142 PatchIfBuggy(cd
.pUnk
, sinks
[i
]);
151 web_browser2_
= browser
;
155 bool BuggyBhoTls::PatchIfBuggy(IUnknown
* unk
, const IID
& diid
) {
157 PROC
* methods
= *reinterpret_cast<PROC
**>(unk
);
158 HMODULE mod
= GetModuleFromAddress(methods
[0]);
159 if (!IsBuggyBho(mod
))
162 base::win::ScopedComPtr
<IDispatch
> disp
;
163 HRESULT hr
= unk
->QueryInterface(diid
,
164 reinterpret_cast<void**>(disp
.Receive()));
165 if (FAILED(hr
)) // Sometimes only IDispatch QI is supported
166 hr
= disp
.QueryFrom(unk
);
167 DCHECK(SUCCEEDED(hr
));
170 const int kInvokeIndex
= 6;
171 DCHECK(static_cast<IUnknown
*>(disp
) == unk
);
172 if (SUCCEEDED(PatchInvokeMethod(&methods
[kInvokeIndex
]))) {
173 AddBuggyObject(disp
);
180 STDMETHODIMP
BuggyBhoTls::BuggyBhoInvoke(InvokeFunc original
, IDispatch
* me
,
181 DISPID dispid
, REFIID riid
, LCID lcid
,
182 WORD flags
, DISPPARAMS
* params
,
183 VARIANT
* result
, EXCEPINFO
* ei
,
185 DVLOG(1) << __FUNCTION__
;
187 DCHECK(BuggyBhoTls::GetInstance())
188 << "You must first have an instance of BuggyBhoTls on this thread";
189 if (BuggyBhoTls::GetInstance() &&
190 BuggyBhoTls::GetInstance()->ShouldSkipInvoke(me
)) {
191 // Ignore this call and avoid the bug.
192 // TODO(tommi): Maybe we should check a specific list of DISPIDs too?
196 // No need to report crashes in those known-to-be-buggy DLLs.
197 ExceptionBarrierReportOnlyModule barrier
;
198 return original(me
, dispid
, riid
, lcid
, flags
, params
, result
, ei
, err
);
202 HRESULT
BuggyBhoTls::PatchInvokeMethod(PROC
* invoke
) {
203 CCritSecLock
lock(_pAtlModule
->m_csStaticDataInitAndTypeInfo
.m_sec
, true);
205 FunctionStub
* stub
= FunctionStub::FromCode(*invoke
);
210 if (!::VirtualProtect(invoke
, sizeof(PROC
), PAGE_EXECUTE_READWRITE
, &flags
))
211 return AtlHresultFromLastError();
215 stub
= FunctionStub::Create(reinterpret_cast<uintptr_t>(*invoke
),
220 if (!vtable_patch::internal::ReplaceFunctionPointer(
221 reinterpret_cast<void**>(invoke
), stub
->code(),
222 reinterpret_cast<void*>(stub
->argument()))) {
224 FunctionStub::Destroy(stub
);
226 PinModule(); // No backing out now.
227 ::FlushInstructionCache(::GetCurrentProcess(), invoke
, sizeof(PROC
));
230 ::VirtualProtect(invoke
, sizeof(PROC
), flags
, &flags
);
234 } // end namespace buggy_bho