| OLD | NEW |
| (Empty) |
| 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 } | |
| OLD | NEW |