1 // Copyright (c) 2010 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/scoped_comptr_win.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
39 // These BHOs seem to be out of the same buggy BHO factory
40 { "tbabso.dll", 4, 5 }, // troublemaker: 4.5.156.0
41 { "tbabs0.dll.dll", 4, 5 }, // troublemaker: 4.5.156.0
42 { "tbbes0.dll", 4, 5 }, // troublemaker: 4.5.153.0
43 { "tbfre0.dll", 4, 5 }, // troublemaker: 4.5.181.1
44 { "tbmypl.dll", 4, 5 }, // troublemaker: 4.5.181.3
45 { "tbmul1.dll", 4, 5 }, // troublemaker: 4.5.181.1
46 { "tbdow1.dll", 4, 5 }, // troublemaker: 4.5.167.0
47 { "tbfree.dll", 4, 5 }, // troublemaker: 4.5.178.0
50 { "msgsc2.dll", 0xffff, 0xffff }, // troublemaker: 1.2.3000.1
51 { "essclis.dll", 0xffff, 0xffff }, // troublemaker: 4.3.1800.2
52 { "snagwb.dll", 0xffff, 0xffff }, // troublemaker: 2.6.0.28
55 bool IsBuggyBho(HMODULE mod
) {
58 char path
[MAX_PATH
* 2] = {0};
59 ::GetModuleFileNameA(mod
, path
, arraysize(path
));
60 const char* file_name
= ::PathFindFileNameA(path
);
61 for (size_t i
= 0; i
< arraysize(kBuggyModules
); ++i
) {
62 if (lstrcmpiA(file_name
, kBuggyModules
[i
].module_name_
) == 0) {
64 GetModuleVersion(mod
, &version
, NULL
);
65 const ModuleAndVersion
& buggy
= kBuggyModules
[i
];
66 if (HIWORD(version
) < buggy
.major_version_
||
67 (HIWORD(version
) == buggy
.major_version_
&&
68 LOWORD(version
) <= buggy
.minor_version_
)) {
77 BuggyBhoTls::BuggyBhoTls() : previous_instance_(s_bad_object_tls_
.Get()) {
78 s_bad_object_tls_
.Set(this);
81 BuggyBhoTls::~BuggyBhoTls() {
82 DCHECK(FromCurrentThread() == this);
83 s_bad_object_tls_
.Set(previous_instance_
);
86 void BuggyBhoTls::AddBuggyObject(IDispatch
* obj
) {
87 bad_objects_
.push_back(obj
);
90 bool BuggyBhoTls::IsBuggyObject(IDispatch
* obj
) const {
91 return std::find(bad_objects_
.begin(), bad_objects_
.end(), obj
) !=
96 BuggyBhoTls
* BuggyBhoTls::FromCurrentThread() {
97 return s_bad_object_tls_
.Get();
101 STDMETHODIMP
BuggyBhoTls::BuggyBhoInvoke(InvokeFunc original
, IDispatch
* me
,
102 DISPID dispid
, REFIID riid
, LCID lcid
,
103 WORD flags
, DISPPARAMS
* params
,
104 VARIANT
* result
, EXCEPINFO
* ei
,
106 DVLOG(1) << __FUNCTION__
;
108 const BuggyBhoTls
* tls
= BuggyBhoTls::FromCurrentThread();
109 if (tls
&& tls
->IsBuggyObject(me
)) {
110 // Ignore this call and avoid the bug.
111 // TODO(tommi): Maybe we should check a specific list of DISPIDs too?
115 // No need to report crashes in those known-to-be-buggy DLLs.
116 ExceptionBarrierReportOnlyModule barrier
;
117 return original(me
, dispid
, riid
, lcid
, flags
, params
, result
, ei
, err
);
121 HRESULT
BuggyBhoTls::PatchInvokeMethod(PROC
* invoke
) {
122 CCritSecLock
lock(_pAtlModule
->m_csStaticDataInitAndTypeInfo
.m_sec
, true);
124 FunctionStub
* stub
= FunctionStub::FromCode(*invoke
);
129 if (!::VirtualProtect(invoke
, sizeof(PROC
), PAGE_EXECUTE_READWRITE
, &flags
))
130 return AtlHresultFromLastError();
134 stub
= FunctionStub::Create(reinterpret_cast<uintptr_t>(*invoke
),
139 if (!vtable_patch::internal::ReplaceFunctionPointer(
140 reinterpret_cast<void**>(invoke
), stub
->code(),
141 reinterpret_cast<void*>(stub
->argument()))) {
143 FunctionStub::Destroy(stub
);
145 PinModule(); // No backing out now.
146 ::FlushInstructionCache(::GetCurrentProcess(), invoke
, sizeof(PROC
));
150 ::VirtualProtect(invoke
, sizeof(PROC
), flags
, &flags
);
156 bool BuggyBhoTls::PatchIfBuggy(CONNECTDATA
* cd
, const IID
& diid
) {
158 PROC
* methods
= *reinterpret_cast<PROC
**>(cd
->pUnk
);
159 HMODULE mod
= GetModuleFromAddress(methods
[0]);
160 if (!IsBuggyBho(mod
))
163 ScopedComPtr
<IDispatch
> disp
;
164 HRESULT hr
= cd
->pUnk
->QueryInterface(diid
,
165 reinterpret_cast<void**>(disp
.Receive()));
166 if (FAILED(hr
)) // Sometimes only IDispatch QI is supported
167 hr
= disp
.QueryFrom(cd
->pUnk
);
168 DCHECK(SUCCEEDED(hr
));
171 const int kInvokeIndex
= 6;
172 DCHECK(static_cast<IUnknown
*>(disp
) == cd
->pUnk
);
173 if (SUCCEEDED(PatchInvokeMethod(&methods
[kInvokeIndex
]))) {
174 BuggyBhoTls
* tls
= BuggyBhoTls::FromCurrentThread();
177 tls
->AddBuggyObject(disp
);
186 HRESULT
BuggyBhoTls::PatchBuggyBHOs(IWebBrowser2
* browser
) {
188 DCHECK(BuggyBhoTls::FromCurrentThread())
189 << "You must first have an instance of BuggyBhoTls on this thread";
191 ScopedComPtr
<IConnectionPointContainer
> cpc
;
192 HRESULT hr
= cpc
.QueryFrom(browser
);
194 const GUID sinks
[] = { DIID_DWebBrowserEvents2
, DIID_DWebBrowserEvents
};
195 for (size_t i
= 0; i
< arraysize(sinks
); ++i
) {
196 ScopedComPtr
<IConnectionPoint
> cp
;
197 cpc
->FindConnectionPoint(sinks
[i
], cp
.Receive());
199 ScopedComPtr
<IEnumConnections
> connections
;
200 cp
->EnumConnections(connections
.Receive());
202 CONNECTDATA cd
= {0};
204 while (connections
->Next(1, &cd
, &fetched
) == S_OK
&& fetched
) {
205 PatchIfBuggy(&cd
, sinks
[i
]);
217 } // end namespace buggy_bho