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"
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"
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
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
) {
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) {
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_
)) {
80 BuggyBhoTls::BuggyBhoTls()
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
) !=
105 BuggyBhoTls
* BuggyBhoTls::GetInstance() {
106 BuggyBhoTls
* tls_instance
= s_bad_object_tls_
.Get();
108 tls_instance
= new BuggyBhoTls();
109 DCHECK(s_bad_object_tls_
.Get() != NULL
);
115 void BuggyBhoTls::DestroyInstance() {
116 BuggyBhoTls
* tls_instance
= s_bad_object_tls_
.Get();
119 DCHECK(s_bad_object_tls_
.Get() == NULL
);
123 HRESULT
BuggyBhoTls::PatchBuggyBHOs(IWebBrowser2
* browser
) {
128 DCHECK(web_browser2_
== NULL
);
130 base::win::ScopedComPtr
<IConnectionPointContainer
> cpc
;
131 HRESULT hr
= cpc
.QueryFrom(browser
);
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());
138 base::win::ScopedComPtr
<IEnumConnections
> connections
;
139 cp
->EnumConnections(connections
.Receive());
141 CONNECTDATA cd
= {0};
143 while (connections
->Next(1, &cd
, &fetched
) == S_OK
&& fetched
) {
144 PatchIfBuggy(cd
.pUnk
, sinks
[i
]);
153 web_browser2_
= browser
;
157 bool BuggyBhoTls::PatchIfBuggy(IUnknown
* unk
, const IID
& diid
) {
159 PROC
* methods
= *reinterpret_cast<PROC
**>(unk
);
160 HMODULE mod
= base::GetModuleFromAddress(methods
[0]);
161 if (!IsBuggyBho(mod
))
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
));
172 const int kInvokeIndex
= 6;
173 DCHECK(static_cast<IUnknown
*>(disp
) == unk
);
174 if (SUCCEEDED(PatchInvokeMethod(&methods
[kInvokeIndex
]))) {
175 AddBuggyObject(disp
);
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
,
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?
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
);
204 HRESULT
BuggyBhoTls::PatchInvokeMethod(PROC
* invoke
) {
205 CCritSecLock
lock(_pAtlModule
->m_csStaticDataInitAndTypeInfo
.m_sec
, true);
207 FunctionStub
* stub
= FunctionStub::FromCode(*invoke
);
212 if (!::VirtualProtect(invoke
, sizeof(PROC
), PAGE_EXECUTE_READWRITE
, &flags
))
213 return AtlHresultFromLastError();
217 stub
= FunctionStub::Create(reinterpret_cast<uintptr_t>(*invoke
),
222 if (!vtable_patch::internal::ReplaceFunctionPointer(
223 reinterpret_cast<void**>(invoke
), stub
->code(),
224 reinterpret_cast<void*>(stub
->argument()))) {
226 FunctionStub::Destroy(stub
);
228 PinModule(); // No backing out now.
229 ::FlushInstructionCache(::GetCurrentProcess(), invoke
, sizeof(PROC
));
232 ::VirtualProtect(invoke
, sizeof(PROC
), flags
, &flags
);
236 } // end namespace buggy_bho