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. 4 5 #include "chrome_frame/vtable_patch_manager.h" 6 7 #include <unknwn.h> 8 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/threading/thread.h" 13 #include "base/win/scoped_handle.h" 14 #include "gmock/gmock.h" 15 #include "gtest/gtest.h" 16 17 namespace { 18 // GMock names we use. 19 using testing::_; 20 using testing::Return; 21 22 class MockClassFactory : public IClassFactory { 23 public: 24 MOCK_METHOD2_WITH_CALLTYPE(__stdcall, QueryInterface, 25 HRESULT(REFIID riid, void **object)); 26 MOCK_METHOD0_WITH_CALLTYPE(__stdcall, AddRef, ULONG()); 27 MOCK_METHOD0_WITH_CALLTYPE(__stdcall, Release, ULONG()); 28 MOCK_METHOD3_WITH_CALLTYPE(__stdcall, CreateInstance, 29 HRESULT (IUnknown *outer, REFIID riid, void **object)); 30 MOCK_METHOD1_WITH_CALLTYPE(__stdcall, LockServer, HRESULT(BOOL lock)); 31 }; 32 33 // Retrieve the vtable for an interface. 34 void* GetVtable(IUnknown* unk) { 35 return *reinterpret_cast<void**>(unk); 36 } 37 38 // Forward decl. 39 extern vtable_patch::MethodPatchInfo IClassFactory_PatchInfo[]; 40 41 class VtablePatchManagerTest: public testing::Test { 42 public: 43 VtablePatchManagerTest() { 44 EXPECT_TRUE(current_ == NULL); 45 current_ = this; 46 } 47 48 ~VtablePatchManagerTest() { 49 EXPECT_TRUE(current_ == this); 50 current_ = NULL; 51 } 52 53 virtual void SetUp() { 54 // Make a backup of the test vtable and it's page protection settings. 55 void* vtable = GetVtable(&factory_); 56 MEMORY_BASIC_INFORMATION info; 57 ASSERT_TRUE(::VirtualQuery(vtable, &info, sizeof(info))); 58 vtable_protection_ = info.Protect; 59 memcpy(vtable_backup_, vtable, sizeof(vtable_backup_)); 60 } 61 62 virtual void TearDown() { 63 // Unpatch to make sure we've restored state for subsequent test. 64 UnpatchInterfaceMethods(IClassFactory_PatchInfo); 65 66 // Restore the test vtable and its page protection settings. 67 void* vtable = GetVtable(&factory_); 68 DWORD old_protect = 0; 69 EXPECT_TRUE(::VirtualProtect(vtable, sizeof(vtable_backup_), 70 PAGE_EXECUTE_WRITECOPY, &old_protect)); 71 memcpy(vtable, vtable_backup_, sizeof(vtable_backup_)); 72 EXPECT_TRUE(::VirtualProtect(vtable, sizeof(vtable_backup_), 73 vtable_protection_, &old_protect)); 74 } 75 76 typedef HRESULT (__stdcall* LockServerFun)(IClassFactory* self, BOOL lock); 77 MOCK_METHOD3(LockServerPatch, 78 HRESULT(LockServerFun old_fun, IClassFactory* self, BOOL lock)); 79 80 static HRESULT STDMETHODCALLTYPE LockServerPatchCallback( 81 LockServerFun fun, IClassFactory* self, BOOL lock) { 82 EXPECT_TRUE(current_ != NULL); 83 if (current_ != NULL) 84 return current_->LockServerPatch(fun, self, lock); 85 else 86 return E_UNEXPECTED; 87 } 88 89 protected: 90 // Number of functions in the IClassFactory vtable. 91 static const size_t kFunctionCount = 5; 92 93 // Backup of the factory_ vtable as we found it at Setup. 94 PROC vtable_backup_[kFunctionCount]; 95 // VirtualProtect flags on the factory_ vtable as we found it at Setup. 96 DWORD vtable_protection_; 97 98 // The mock factory class we patch. 99 MockClassFactory factory_; 100 101 // Current test running for routing the patch callback function. 102 static VtablePatchManagerTest* current_; 103 }; 104 105 VtablePatchManagerTest* VtablePatchManagerTest::current_ = NULL; 106 107 BEGIN_VTABLE_PATCHES(IClassFactory) 108 VTABLE_PATCH_ENTRY(4, &VtablePatchManagerTest::LockServerPatchCallback) 109 END_VTABLE_PATCHES(); 110 111 } // namespace 112 113 TEST_F(VtablePatchManagerTest, ReplacePointer) { 114 void* const kFunctionOriginal = reinterpret_cast<void*>(0xCAFEBABE); 115 void* const kFunctionFoo = reinterpret_cast<void*>(0xF0F0F0F0); 116 void* const kFunctionBar = reinterpret_cast<void*>(0xBABABABA); 117 118 using vtable_patch::internal::ReplaceFunctionPointer; 119 // Replacing a non-writable location should fail, but not crash. 120 EXPECT_FALSE(ReplaceFunctionPointer(NULL, kFunctionBar, kFunctionFoo)); 121 122 void* foo_entry = kFunctionOriginal; 123 // Replacing with the wrong original function should 124 // fail and not change the entry. 125 EXPECT_FALSE(ReplaceFunctionPointer(&foo_entry, kFunctionBar, kFunctionFoo)); 126 EXPECT_EQ(foo_entry, kFunctionOriginal); 127 128 // Replacing with the correct original should succeed. 129 EXPECT_TRUE(ReplaceFunctionPointer(&foo_entry, 130 kFunctionBar, 131 kFunctionOriginal)); 132 EXPECT_EQ(foo_entry, kFunctionBar); 133 } 134 135 TEST_F(VtablePatchManagerTest, PatchInterfaceMethods) { 136 // Unpatched. 137 EXPECT_CALL(factory_, LockServer(TRUE)) 138 .WillOnce(Return(E_FAIL)); 139 EXPECT_EQ(E_FAIL, factory_.LockServer(TRUE)); 140 141 EXPECT_HRESULT_SUCCEEDED( 142 PatchInterfaceMethods(&factory_, IClassFactory_PatchInfo)); 143 144 EXPECT_NE(0, memcmp(GetVtable(&factory_), 145 vtable_backup_, 146 sizeof(vtable_backup_))); 147 148 // This should not be called while the patch is in effect. 149 EXPECT_CALL(factory_, LockServer(_)) 150 .Times(0); 151 152 EXPECT_CALL(*this, LockServerPatch(testing::_, &factory_, TRUE)) 153 .WillOnce(testing::Return(S_FALSE)); 154 155 EXPECT_EQ(S_FALSE, factory_.LockServer(TRUE)); 156 } 157 158 TEST_F(VtablePatchManagerTest, UnpatchInterfaceMethods) { 159 // Patch it. 160 EXPECT_HRESULT_SUCCEEDED( 161 PatchInterfaceMethods(&factory_, IClassFactory_PatchInfo)); 162 163 EXPECT_NE(0, memcmp(GetVtable(&factory_), 164 vtable_backup_, 165 sizeof(vtable_backup_))); 166 167 // This should not be called while the patch is in effect. 168 EXPECT_CALL(factory_, LockServer(testing::_)) 169 .Times(0); 170 171 EXPECT_CALL(*this, LockServerPatch(testing::_, &factory_, TRUE)) 172 .WillOnce(testing::Return(S_FALSE)); 173 174 EXPECT_EQ(S_FALSE, factory_.LockServer(TRUE)); 175 176 // Now unpatch. 177 EXPECT_HRESULT_SUCCEEDED( 178 UnpatchInterfaceMethods(IClassFactory_PatchInfo)); 179 180 // And check that the call comes through correctly. 181 EXPECT_CALL(factory_, LockServer(FALSE)) 182 .WillOnce(testing::Return(E_FAIL)); 183 EXPECT_EQ(E_FAIL, factory_.LockServer(FALSE)); 184 } 185 186 TEST_F(VtablePatchManagerTest, DoublePatch) { 187 // Patch it. 188 EXPECT_HRESULT_SUCCEEDED( 189 PatchInterfaceMethods(&factory_, IClassFactory_PatchInfo)); 190 191 // Capture the VTable after patching. 192 PROC vtable[kFunctionCount]; 193 memcpy(vtable, GetVtable(&factory_), sizeof(vtable)); 194 195 // Patch it again, this should be idempotent. 196 EXPECT_HRESULT_SUCCEEDED( 197 PatchInterfaceMethods(&factory_, IClassFactory_PatchInfo)); 198 199 // Should not have changed the VTable on second call. 200 EXPECT_EQ(0, memcmp(vtable, GetVtable(&factory_), sizeof(vtable))); 201 } 202 203 namespace vtable_patch { 204 // Expose internal implementation detail, purely for testing. 205 extern base::Lock patch_lock_; 206 207 } // namespace vtable_patch 208 209 TEST_F(VtablePatchManagerTest, ThreadSafePatching) { 210 // It's difficult to test for threadsafe patching, but as a close proxy, 211 // test for no patching happening from a background thread while the patch 212 // lock is held. 213 base::Thread background("Background Test Thread"); 214 215 EXPECT_TRUE(background.Start()); 216 base::win::ScopedHandle event(::CreateEvent(NULL, TRUE, FALSE, NULL)); 217 218 // Grab the patch lock. 219 vtable_patch::patch_lock_.Acquire(); 220 221 // Instruct the background thread to patch factory_. 222 background.message_loop()->PostTask( 223 FROM_HERE, 224 base::Bind(base::IgnoreResult(&vtable_patch::PatchInterfaceMethods), 225 &factory_, &IClassFactory_PatchInfo[0])); 226 227 // And subsequently to signal the event. Neither of these actions should 228 // occur until we've released the patch lock. 229 background.message_loop()->PostTask( 230 FROM_HERE, base::Bind(base::IgnoreResult(::SetEvent), event.Get())); 231 232 // Wait for a little while, to give the background thread time to process. 233 // We expect this wait to time out, as the background thread should end up 234 // blocking on the patch lock. 235 EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(event.Get(), 50)); 236 237 // Verify that patching did not take place yet. 238 EXPECT_CALL(factory_, LockServer(TRUE)) 239 .WillOnce(Return(S_FALSE)); 240 EXPECT_EQ(S_FALSE, factory_.LockServer(TRUE)); 241 242 // Release the lock and wait on the event again to ensure 243 // the patching has taken place now. 244 vtable_patch::patch_lock_.Release(); 245 EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(event.Get(), INFINITE)); 246 247 // We should not get called here anymore. 248 EXPECT_CALL(factory_, LockServer(TRUE)) 249 .Times(0); 250 251 // But should be diverted here. 252 EXPECT_CALL(*this, LockServerPatch(_, &factory_, TRUE)) 253 .WillOnce(Return(S_FALSE)); 254 EXPECT_EQ(S_FALSE, factory_.LockServer(TRUE)); 255 256 // Same deal for unpatching. 257 ::ResetEvent(event.Get()); 258 259 // Grab the patch lock. 260 vtable_patch::patch_lock_.Acquire(); 261 262 // Instruct the background thread to unpatch. 263 background.message_loop()->PostTask( 264 FROM_HERE, 265 base::Bind(base::IgnoreResult(&vtable_patch::UnpatchInterfaceMethods), 266 &IClassFactory_PatchInfo[0])); 267 268 // And subsequently to signal the event. Neither of these actions should 269 // occur until we've released the patch lock. 270 background.message_loop()->PostTask( 271 FROM_HERE, base::Bind(base::IgnoreResult(::SetEvent), event.Get())); 272 273 // Wait for a little while, to give the background thread time to process. 274 // We expect this wait to time out, as the background thread should end up 275 // blocking on the patch lock. 276 EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(event.Get(), 50)); 277 278 // We should still be patched. 279 EXPECT_CALL(factory_, LockServer(TRUE)) 280 .Times(0); 281 EXPECT_CALL(*this, LockServerPatch(_, &factory_, TRUE)) 282 .WillOnce(Return(S_FALSE)); 283 EXPECT_EQ(S_FALSE, factory_.LockServer(TRUE)); 284 285 // Release the patch lock and wait on the event. 286 vtable_patch::patch_lock_.Release(); 287 EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(event.Get(), INFINITE)); 288 289 // Verify that unpatching took place. 290 EXPECT_CALL(factory_, LockServer(TRUE)) 291 .WillOnce(Return(S_FALSE)); 292 EXPECT_EQ(S_FALSE, factory_.LockServer(TRUE)); 293 } 294