Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome_frame/buggy_bho_handling.h" | 5 #include "chrome_frame/buggy_bho_handling.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/lazy_instance.h" |
| 8 #include "base/scoped_comptr_win.h" | 8 #include "base/scoped_comptr_win.h" |
| 9 | |
| 10 #include "chrome_frame/exception_barrier.h" | 9 #include "chrome_frame/exception_barrier.h" |
| 11 #include "chrome_frame/function_stub.h" | 10 #include "chrome_frame/function_stub.h" |
| 11 #include "chrome_frame/urlmon_moniker.h" | |
| 12 #include "chrome_frame/utils.h" | 12 #include "chrome_frame/utils.h" |
| 13 #include "chrome_frame/vtable_patch_manager.h" | 13 #include "chrome_frame/vtable_patch_manager.h" |
| 14 | 14 |
| 15 namespace buggy_bho { | 15 namespace buggy_bho { |
| 16 | 16 |
| 17 base::ThreadLocalPointer<BuggyBhoTls> BuggyBhoTls::s_bad_object_tls_; | 17 base::LazyInstance<BuggyBhoHandler> |
| 18 g_buggy_bho_handler(base::LINKER_INITIALIZED); | |
| 18 | 19 |
| 19 struct ModuleAndVersion { | 20 struct ModuleAndVersion { |
| 20 const char* module_name_; | 21 const char* module_name_; |
| 21 const uint32 major_version_; | 22 const uint32 major_version_; |
| 22 const uint32 minor_version_; | 23 const uint32 minor_version_; |
| 23 }; | 24 }; |
| 24 | 25 |
| 25 const ModuleAndVersion kBuggyModules[] = { | 26 const ModuleAndVersion kBuggyModules[] = { |
| 26 { "askbar.dll", 4, 1 }, // troublemaker: 4.1.0.5. | 27 { "askbar.dll", 4, 1 }, // troublemaker: 4.1.0.5. |
| 27 { "gbieh.dll", 3, 8 }, // troublemaker: 3.8.14.12 | 28 { "gbieh.dll", 3, 8 }, // troublemaker: 3.8.14.12 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 68 (HIWORD(version) == buggy.major_version_ && | 69 (HIWORD(version) == buggy.major_version_ && |
| 69 LOWORD(version) <= buggy.minor_version_)) { | 70 LOWORD(version) <= buggy.minor_version_)) { |
| 70 return true; | 71 return true; |
| 71 } | 72 } |
| 72 } | 73 } |
| 73 } | 74 } |
| 74 | 75 |
| 75 return false; | 76 return false; |
| 76 } | 77 } |
| 77 | 78 |
| 78 BuggyBhoTls::BuggyBhoTls() : previous_instance_(s_bad_object_tls_.Get()) { | 79 BuggyBhoHandler::BuggyBhoHandler() { |
| 79 s_bad_object_tls_.Set(this); | |
| 80 } | 80 } |
| 81 | 81 |
| 82 BuggyBhoTls::~BuggyBhoTls() { | 82 BuggyBhoHandler::~BuggyBhoHandler() { |
| 83 DCHECK(FromCurrentThread() == this); | |
| 84 s_bad_object_tls_.Set(previous_instance_); | |
| 85 } | 83 } |
| 86 | 84 |
| 87 void BuggyBhoTls::AddBuggyObject(IDispatch* obj) { | 85 BuggyBhoHandler* BuggyBhoHandler::GetInstance() { |
| 88 bad_objects_.push_back(obj); | 86 return g_buggy_bho_handler.Pointer(); |
| 89 } | 87 } |
| 90 | 88 |
| 91 bool BuggyBhoTls::IsBuggyObject(IDispatch* obj) const { | 89 void BuggyBhoHandler::ClearBuggyObjectsForThread( |
| 92 return std::find(bad_objects_.begin(), bad_objects_.end(), obj) != | 90 base::PlatformThreadId thread_id) { |
| 93 bad_objects_.end(); | 91 DCHECK(GetInstance()) |
| 92 << "You must first have an instance of BuggyBhoHandler."; | |
| 93 | |
| 94 base::AutoLock lock(GetInstance()->lock_); | |
|
tommi (sloooow) - chröme
2011/02/11 14:49:02
If you store the object list in the tls, you would
| |
| 95 | |
| 96 std::vector<ObjectInfo>::iterator index = | |
| 97 GetInstance()->bad_objects_.begin(); | |
| 98 while (index != GetInstance()->bad_objects_.end()) { | |
| 99 if (index->object_thread_id == thread_id) { | |
| 100 index = GetInstance()->bad_objects_.erase(index); | |
| 101 } else { | |
| 102 ++index; | |
| 103 } | |
| 104 } | |
| 105 } | |
| 106 | |
| 107 void BuggyBhoHandler::AddBuggyObject(IDispatch* obj) { | |
| 108 base::AutoLock lock(lock_); | |
| 109 ObjectInfo object_info; | |
| 110 object_info.disp_interface = obj; | |
| 111 object_info.object_thread_id = base::PlatformThread::CurrentId(); | |
| 112 bad_objects_.push_back(object_info); | |
| 113 } | |
| 114 | |
| 115 bool BuggyBhoHandler::ShouldSkipInvoke(IDispatch* obj) { | |
| 116 NavigationManager* navigation_mgr = NavigationManager::GetThreadInstance(); | |
| 117 DCHECK(navigation_mgr); | |
| 118 if (navigation_mgr && IsChromeFrameDocument(navigation_mgr->web_browser())) { | |
| 119 base::AutoLock lock(lock_); | |
| 120 std::vector<ObjectInfo>::iterator index; | |
| 121 for (index = bad_objects_.begin(); index != bad_objects_.end(); ++index) { | |
| 122 if (index->disp_interface == obj) | |
| 123 return true; | |
| 124 } | |
| 125 } | |
| 126 return false; | |
| 94 } | 127 } |
| 95 | 128 |
| 96 // static | 129 // static |
| 97 BuggyBhoTls* BuggyBhoTls::FromCurrentThread() { | 130 STDMETHODIMP BuggyBhoHandler::BuggyBhoInvoke(InvokeFunc original, IDispatch* me, |
| 98 return s_bad_object_tls_.Get(); | |
| 99 } | |
| 100 | |
| 101 // static | |
| 102 STDMETHODIMP BuggyBhoTls::BuggyBhoInvoke(InvokeFunc original, IDispatch* me, | |
| 103 DISPID dispid, REFIID riid, LCID lcid, | 131 DISPID dispid, REFIID riid, LCID lcid, |
| 104 WORD flags, DISPPARAMS* params, | 132 WORD flags, DISPPARAMS* params, |
| 105 VARIANT* result, EXCEPINFO* ei, | 133 VARIANT* result, EXCEPINFO* ei, |
| 106 UINT* err) { | 134 UINT* err) { |
| 107 DVLOG(1) << __FUNCTION__; | 135 DVLOG(1) << __FUNCTION__; |
| 108 | 136 |
| 109 const BuggyBhoTls* tls = BuggyBhoTls::FromCurrentThread(); | 137 DCHECK(GetInstance()) |
| 110 if (tls && tls->IsBuggyObject(me)) { | 138 << "You must first have an instance of BuggyBhoHandler."; |
| 139 | |
| 140 if (GetInstance()->ShouldSkipInvoke(me)) { | |
| 111 // Ignore this call and avoid the bug. | 141 // Ignore this call and avoid the bug. |
| 112 // TODO(tommi): Maybe we should check a specific list of DISPIDs too? | 142 // TODO(tommi): Maybe we should check a specific list of DISPIDs too? |
| 113 return S_OK; | 143 return S_OK; |
| 114 } | 144 } |
| 115 | 145 |
| 116 // No need to report crashes in those known-to-be-buggy DLLs. | 146 // No need to report crashes in those known-to-be-buggy DLLs. |
| 117 ExceptionBarrierReportOnlyModule barrier; | 147 ExceptionBarrierReportOnlyModule barrier; |
| 118 return original(me, dispid, riid, lcid, flags, params, result, ei, err); | 148 return original(me, dispid, riid, lcid, flags, params, result, ei, err); |
| 119 } | 149 } |
| 120 | 150 |
| 121 // static | 151 // static |
| 122 HRESULT BuggyBhoTls::PatchInvokeMethod(PROC* invoke) { | 152 HRESULT BuggyBhoHandler::PatchInvokeMethod(PROC* invoke) { |
| 123 CCritSecLock lock(_pAtlModule->m_csStaticDataInitAndTypeInfo.m_sec, true); | 153 CCritSecLock lock(_pAtlModule->m_csStaticDataInitAndTypeInfo.m_sec, true); |
| 124 | 154 |
| 125 FunctionStub* stub = FunctionStub::FromCode(*invoke); | 155 FunctionStub* stub = FunctionStub::FromCode(*invoke); |
| 126 if (stub) | 156 if (stub) |
| 127 return S_FALSE; | 157 return S_FALSE; |
| 128 | 158 |
| 129 DWORD flags = 0; | 159 DWORD flags = 0; |
| 130 if (!::VirtualProtect(invoke, sizeof(PROC), PAGE_EXECUTE_READWRITE, &flags)) | 160 if (!::VirtualProtect(invoke, sizeof(PROC), PAGE_EXECUTE_READWRITE, &flags)) |
| 131 return AtlHresultFromLastError(); | 161 return AtlHresultFromLastError(); |
| 132 | 162 |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 147 ::FlushInstructionCache(::GetCurrentProcess(), invoke, sizeof(PROC)); | 177 ::FlushInstructionCache(::GetCurrentProcess(), invoke, sizeof(PROC)); |
| 148 } | 178 } |
| 149 } | 179 } |
| 150 | 180 |
| 151 ::VirtualProtect(invoke, sizeof(PROC), flags, &flags); | 181 ::VirtualProtect(invoke, sizeof(PROC), flags, &flags); |
| 152 | 182 |
| 153 return hr; | 183 return hr; |
| 154 } | 184 } |
| 155 | 185 |
| 156 // static | 186 // static |
| 157 bool BuggyBhoTls::PatchIfBuggy(CONNECTDATA* cd, const IID& diid) { | 187 bool BuggyBhoHandler::PatchIfBuggy(CONNECTDATA* cd, const IID& diid) { |
| 158 DCHECK(cd); | 188 DCHECK(cd); |
| 159 PROC* methods = *reinterpret_cast<PROC**>(cd->pUnk); | 189 PROC* methods = *reinterpret_cast<PROC**>(cd->pUnk); |
| 160 HMODULE mod = GetModuleFromAddress(methods[0]); | 190 HMODULE mod = GetModuleFromAddress(methods[0]); |
| 161 if (!IsBuggyBho(mod)) | 191 if (!IsBuggyBho(mod)) |
| 162 return false; | 192 return false; |
| 163 | 193 |
| 164 ScopedComPtr<IDispatch> disp; | 194 ScopedComPtr<IDispatch> disp; |
| 165 HRESULT hr = cd->pUnk->QueryInterface(diid, | 195 HRESULT hr = cd->pUnk->QueryInterface(diid, |
| 166 reinterpret_cast<void**>(disp.Receive())); | 196 reinterpret_cast<void**>(disp.Receive())); |
| 167 if (FAILED(hr)) // Sometimes only IDispatch QI is supported | 197 if (FAILED(hr)) // Sometimes only IDispatch QI is supported |
| 168 hr = disp.QueryFrom(cd->pUnk); | 198 hr = disp.QueryFrom(cd->pUnk); |
| 169 DCHECK(SUCCEEDED(hr)); | 199 DCHECK(SUCCEEDED(hr)); |
| 170 | 200 |
| 171 if (SUCCEEDED(hr)) { | 201 if (SUCCEEDED(hr)) { |
| 172 const int kInvokeIndex = 6; | 202 const int kInvokeIndex = 6; |
| 173 DCHECK(static_cast<IUnknown*>(disp) == cd->pUnk); | 203 DCHECK(static_cast<IUnknown*>(disp) == cd->pUnk); |
| 174 if (SUCCEEDED(PatchInvokeMethod(&methods[kInvokeIndex]))) { | 204 if (SUCCEEDED(PatchInvokeMethod(&methods[kInvokeIndex]))) { |
| 175 BuggyBhoTls* tls = BuggyBhoTls::FromCurrentThread(); | 205 BuggyBhoHandler* instance = GetInstance(); |
| 176 DCHECK(tls); | 206 DCHECK(instance); |
| 177 if (tls) { | 207 instance->AddBuggyObject(disp); |
| 178 tls->AddBuggyObject(disp); | |
| 179 } | |
| 180 } | 208 } |
| 181 } | 209 } |
| 182 | 210 |
| 183 return false; | 211 return false; |
| 184 } | 212 } |
| 185 | 213 |
| 186 // static | 214 // static |
| 187 HRESULT BuggyBhoTls::PatchBuggyBHOs(IWebBrowser2* browser) { | 215 HRESULT BuggyBhoHandler::PatchBuggyBHOs(IWebBrowser2* browser) { |
| 188 DCHECK(browser); | 216 DCHECK(browser); |
| 189 DCHECK(BuggyBhoTls::FromCurrentThread()) | 217 DCHECK(GetInstance()) |
| 190 << "You must first have an instance of BuggyBhoTls on this thread"; | 218 << "You must first have an instance of BuggyBhoHandler."; |
| 219 // Serialized as we could be called from multiple threads and the same BHO | |
| 220 // could be loaded in multiple IE windows. | |
| 221 base::AutoLock lock(GetInstance()->lock_); | |
| 191 | 222 |
| 192 ScopedComPtr<IConnectionPointContainer> cpc; | 223 ScopedComPtr<IConnectionPointContainer> cpc; |
| 193 HRESULT hr = cpc.QueryFrom(browser); | 224 HRESULT hr = cpc.QueryFrom(browser); |
| 194 if (SUCCEEDED(hr)) { | 225 if (SUCCEEDED(hr)) { |
| 195 const GUID sinks[] = { DIID_DWebBrowserEvents2, DIID_DWebBrowserEvents }; | 226 const GUID sinks[] = { DIID_DWebBrowserEvents2, DIID_DWebBrowserEvents }; |
| 196 for (size_t i = 0; i < arraysize(sinks); ++i) { | 227 for (size_t i = 0; i < arraysize(sinks); ++i) { |
| 197 ScopedComPtr<IConnectionPoint> cp; | 228 ScopedComPtr<IConnectionPoint> cp; |
| 198 cpc->FindConnectionPoint(sinks[i], cp.Receive()); | 229 cpc->FindConnectionPoint(sinks[i], cp.Receive()); |
| 199 if (cp) { | 230 if (cp) { |
| 200 ScopedComPtr<IEnumConnections> connections; | 231 ScopedComPtr<IEnumConnections> connections; |
| 201 cp->EnumConnections(connections.Receive()); | 232 cp->EnumConnections(connections.Receive()); |
| 202 if (connections) { | 233 if (connections) { |
| 203 CONNECTDATA cd = {0}; | 234 CONNECTDATA cd = {0}; |
| 204 DWORD fetched = 0; | 235 DWORD fetched = 0; |
| 205 while (connections->Next(1, &cd, &fetched) == S_OK && fetched) { | 236 while (connections->Next(1, &cd, &fetched) == S_OK && fetched) { |
| 206 PatchIfBuggy(&cd, sinks[i]); | 237 PatchIfBuggy(&cd, sinks[i]); |
| 207 cd.pUnk->Release(); | 238 cd.pUnk->Release(); |
| 208 fetched = 0; | 239 fetched = 0; |
| 209 } | 240 } |
| 210 } | 241 } |
| 211 } | 242 } |
| 212 } | 243 } |
| 213 } | 244 } |
| 214 | |
| 215 return hr; | 245 return hr; |
| 216 } | 246 } |
| 217 | 247 |
| 218 } // end namespace buggy_bho | 248 } // end namespace buggy_bho |
| OLD | NEW |