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/memory.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/pin_module.h"
15 #include "chrome_frame/utils.h"
16 #include "chrome_frame/vtable_patch_manager.h"
20 base::ThreadLocalPointer
<BuggyBhoTls
> BuggyBhoTls::s_bad_object_tls_
;
22 struct ModuleAndVersion
{
23 const char* module_name_
;
24 const uint32 major_version_
;
25 const uint32 minor_version_
;
28 const ModuleAndVersion kBuggyModules
[] = {
29 { "askbar.dll", 4, 1 }, // troublemaker: 4.1.0.5.
30 { "gbieh.dll", 3, 8 }, // troublemaker: 3.8.14.12
31 { "gbiehcef.dll", 3, 8 }, // troublemaker: 3.8.11.23
32 { "alot.dll", 2, 5 }, // troublemaker: 2.5.12000.509
33 { "ctbr.dll", 5, 1 }, // troublemaker: 5.1.0.95
34 { "srchbxex.dll", 1, 2 }, // troublemaker: 1.2.123.0
35 { "iedvtool32.dll", 8, 0 }, // troublemaker: 8.0.0.4
36 { "mst164.dll", 9, 1 }, // troublemaker: 9.1.3700.1
37 { "deposit_ie_com.dll", 0, 1 }, // troublemaker: 0.1.0.72
38 { "rpshell32.dll", 6, 0 }, // troublemaker: 6.0.6000.1389
39 { "msgsres.dll", 6, 0 }, // troublemaker: 6.0.6000.1389
40 { "limewireinttb.dll", 4, 1 }, // troublemaker: 4.1.1.1000
41 { "pxsecure.dll", 3, 0 }, // troublemaker: 3.0.5.220
43 // These BHOs seem to be out of the same buggy BHO factory
44 { "tbabso.dll", 4, 5 }, // troublemaker: 4.5.156.0
45 { "tbabs0.dll.dll", 4, 5 }, // troublemaker: 4.5.156.0
46 { "tbbes0.dll", 4, 5 }, // troublemaker: 4.5.153.0
47 { "tbfre0.dll", 4, 5 }, // troublemaker: 4.5.181.1
48 { "tbmypl.dll", 4, 5 }, // troublemaker: 4.5.181.3
49 { "tbmul1.dll", 4, 5 }, // troublemaker: 4.5.181.1
50 { "tbdow1.dll", 4, 5 }, // troublemaker: 4.5.167.0
51 { "tbfree.dll", 4, 5 }, // troublemaker: 4.5.178.0
54 { "msgsc2.dll", 0xffff, 0xffff }, // troublemaker: 1.2.3000.1
55 { "essclis.dll", 0xffff, 0xffff }, // troublemaker: 4.3.1800.2
56 { "snagwb.dll", 0xffff, 0xffff }, // troublemaker: 2.6.0.28
59 bool IsBuggyBho(HMODULE mod
) {
62 char path
[MAX_PATH
* 2] = {0};
63 ::GetModuleFileNameA(mod
, path
, arraysize(path
));
64 const char* file_name
= ::PathFindFileNameA(path
);
65 for (size_t i
= 0; i
< arraysize(kBuggyModules
); ++i
) {
66 if (lstrcmpiA(file_name
, kBuggyModules
[i
].module_name_
) == 0) {
68 GetModuleVersion(mod
, &version
, NULL
);
69 const ModuleAndVersion
& buggy
= kBuggyModules
[i
];
70 if (HIWORD(version
) < buggy
.major_version_
||
71 (HIWORD(version
) == buggy
.major_version_
&&
72 LOWORD(version
) <= buggy
.minor_version_
)) {
81 BuggyBhoTls::BuggyBhoTls()
83 DCHECK(s_bad_object_tls_
.Get() == NULL
);
84 s_bad_object_tls_
.Set(this);
87 BuggyBhoTls::~BuggyBhoTls() {
88 DCHECK(BuggyBhoTls::GetInstance() == this);
89 s_bad_object_tls_
.Set(NULL
);
92 void BuggyBhoTls::AddBuggyObject(IDispatch
* obj
) {
93 bad_objects_
.push_back(obj
);
96 bool BuggyBhoTls::ShouldSkipInvoke(IDispatch
* obj
) const {
97 DCHECK(web_browser2_
!= NULL
);
98 if (IsChromeFrameDocument(web_browser2_
)) {
99 return std::find(bad_objects_
.begin(), bad_objects_
.end(), obj
) !=
106 BuggyBhoTls
* BuggyBhoTls::GetInstance() {
107 BuggyBhoTls
* tls_instance
= s_bad_object_tls_
.Get();
109 tls_instance
= new BuggyBhoTls();
110 DCHECK(s_bad_object_tls_
.Get() != NULL
);
116 void BuggyBhoTls::DestroyInstance() {
117 BuggyBhoTls
* tls_instance
= s_bad_object_tls_
.Get();
120 DCHECK(s_bad_object_tls_
.Get() == NULL
);
124 HRESULT
BuggyBhoTls::PatchBuggyBHOs(IWebBrowser2
* browser
) {
129 DCHECK(web_browser2_
== NULL
);
131 base::win::ScopedComPtr
<IConnectionPointContainer
> cpc
;
132 HRESULT hr
= cpc
.QueryFrom(browser
);
134 const GUID sinks
[] = { DIID_DWebBrowserEvents2
, DIID_DWebBrowserEvents
};
135 for (size_t i
= 0; i
< arraysize(sinks
); ++i
) {
136 base::win::ScopedComPtr
<IConnectionPoint
> cp
;
137 cpc
->FindConnectionPoint(sinks
[i
], cp
.Receive());
139 base::win::ScopedComPtr
<IEnumConnections
> connections
;
140 cp
->EnumConnections(connections
.Receive());
142 CONNECTDATA cd
= {0};
144 while (connections
->Next(1, &cd
, &fetched
) == S_OK
&& fetched
) {
145 PatchIfBuggy(cd
.pUnk
, sinks
[i
]);
154 web_browser2_
= browser
;
158 bool BuggyBhoTls::PatchIfBuggy(IUnknown
* unk
, const IID
& diid
) {
160 PROC
* methods
= *reinterpret_cast<PROC
**>(unk
);
161 HMODULE mod
= base::GetModuleFromAddress(methods
[0]);
162 if (!IsBuggyBho(mod
))
165 base::win::ScopedComPtr
<IDispatch
> disp
;
166 HRESULT hr
= unk
->QueryInterface(diid
,
167 reinterpret_cast<void**>(disp
.Receive()));
168 if (FAILED(hr
)) // Sometimes only IDispatch QI is supported
169 hr
= disp
.QueryFrom(unk
);
170 DCHECK(SUCCEEDED(hr
));
173 const int kInvokeIndex
= 6;
174 DCHECK(static_cast<IUnknown
*>(disp
) == unk
);
175 if (SUCCEEDED(PatchInvokeMethod(&methods
[kInvokeIndex
]))) {
176 AddBuggyObject(disp
);
183 STDMETHODIMP
BuggyBhoTls::BuggyBhoInvoke(InvokeFunc original
, IDispatch
* me
,
184 DISPID dispid
, REFIID riid
, LCID lcid
,
185 WORD flags
, DISPPARAMS
* params
,
186 VARIANT
* result
, EXCEPINFO
* ei
,
188 DVLOG(1) << __FUNCTION__
;
190 DCHECK(BuggyBhoTls::GetInstance())
191 << "You must first have an instance of BuggyBhoTls on this thread";
192 if (BuggyBhoTls::GetInstance() &&
193 BuggyBhoTls::GetInstance()->ShouldSkipInvoke(me
)) {
194 // Ignore this call and avoid the bug.
195 // TODO(tommi): Maybe we should check a specific list of DISPIDs too?
199 // No need to report crashes in those known-to-be-buggy DLLs.
200 ExceptionBarrierReportOnlyModule barrier
;
201 return original(me
, dispid
, riid
, lcid
, flags
, params
, result
, ei
, err
);
205 HRESULT
BuggyBhoTls::PatchInvokeMethod(PROC
* invoke
) {
206 CCritSecLock
lock(_pAtlModule
->m_csStaticDataInitAndTypeInfo
.m_sec
, true);
208 FunctionStub
* stub
= FunctionStub::FromCode(*invoke
);
213 if (!::VirtualProtect(invoke
, sizeof(PROC
), PAGE_EXECUTE_READWRITE
, &flags
))
214 return AtlHresultFromLastError();
218 stub
= FunctionStub::Create(reinterpret_cast<uintptr_t>(*invoke
),
223 if (!vtable_patch::internal::ReplaceFunctionPointer(
224 reinterpret_cast<void**>(invoke
), stub
->code(),
225 reinterpret_cast<void*>(stub
->argument()))) {
227 FunctionStub::Destroy(stub
);
229 chrome_frame::PinModule(); // No backing out now.
230 ::FlushInstructionCache(::GetCurrentProcess(), invoke
, sizeof(PROC
));
233 ::VirtualProtect(invoke
, sizeof(PROC
), flags
, &flags
);
237 } // end namespace buggy_bho