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.
5 #include "chrome_frame/vtable_patch_manager.h"
10 #include "base/bind_helpers.h"
11 #include "base/message_loop.h"
12 #include "base/threading/thread.h"
13 #include "base/win/scoped_handle.h"
14 #include "gtest/gtest.h"
15 #include "gmock/gmock.h"
18 // GMock names we use.
20 using testing::Return
;
22 class MockClassFactory
: public IClassFactory
{
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
));
33 // Retrieve the vtable for an interface.
34 void* GetVtable(IUnknown
* unk
) {
35 return *reinterpret_cast<void**>(unk
);
39 extern vtable_patch::MethodPatchInfo IClassFactory_PatchInfo
[];
41 class VtablePatchManagerTest
: public testing::Test
{
43 VtablePatchManagerTest() {
44 EXPECT_TRUE(current_
== NULL
);
48 ~VtablePatchManagerTest() {
49 EXPECT_TRUE(current_
== this);
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_
));
62 virtual void TearDown() {
63 // Unpatch to make sure we've restored state for subsequent test.
64 UnpatchInterfaceMethods(IClassFactory_PatchInfo
);
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
));
76 typedef HRESULT (__stdcall
* LockServerFun
)(IClassFactory
* self
, BOOL lock
);
77 MOCK_METHOD3(LockServerPatch
,
78 HRESULT(LockServerFun old_fun
, IClassFactory
* self
, BOOL lock
));
80 static HRESULT STDMETHODCALLTYPE
LockServerPatchCallback(
81 LockServerFun fun
, IClassFactory
* self
, BOOL lock
) {
82 EXPECT_TRUE(current_
!= NULL
);
84 return current_
->LockServerPatch(fun
, self
, lock
);
90 // Number of functions in the IClassFactory vtable.
91 static const size_t kFunctionCount
= 5;
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_
;
98 // The mock factory class we patch.
99 MockClassFactory factory_
;
101 // Current test running for routing the patch callback function.
102 static VtablePatchManagerTest
* current_
;
105 VtablePatchManagerTest
* VtablePatchManagerTest::current_
= NULL
;
107 BEGIN_VTABLE_PATCHES(IClassFactory
)
108 VTABLE_PATCH_ENTRY(4, &VtablePatchManagerTest::LockServerPatchCallback
)
109 END_VTABLE_PATCHES();
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);
118 using vtable_patch::internal::ReplaceFunctionPointer
;
119 // Replacing a non-writable location should fail, but not crash.
120 EXPECT_FALSE(ReplaceFunctionPointer(NULL
, kFunctionBar
, kFunctionFoo
));
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
);
128 // Replacing with the correct original should succeed.
129 EXPECT_TRUE(ReplaceFunctionPointer(&foo_entry
,
132 EXPECT_EQ(foo_entry
, kFunctionBar
);
135 TEST_F(VtablePatchManagerTest
, PatchInterfaceMethods
) {
137 EXPECT_CALL(factory_
, LockServer(TRUE
))
138 .WillOnce(Return(E_FAIL
));
139 EXPECT_EQ(E_FAIL
, factory_
.LockServer(TRUE
));
141 EXPECT_HRESULT_SUCCEEDED(
142 PatchInterfaceMethods(&factory_
, IClassFactory_PatchInfo
));
144 EXPECT_NE(0, memcmp(GetVtable(&factory_
),
146 sizeof(vtable_backup_
)));
148 // This should not be called while the patch is in effect.
149 EXPECT_CALL(factory_
, LockServer(_
))
152 EXPECT_CALL(*this, LockServerPatch(testing::_
, &factory_
, TRUE
))
153 .WillOnce(testing::Return(S_FALSE
));
155 EXPECT_EQ(S_FALSE
, factory_
.LockServer(TRUE
));
158 TEST_F(VtablePatchManagerTest
, UnpatchInterfaceMethods
) {
160 EXPECT_HRESULT_SUCCEEDED(
161 PatchInterfaceMethods(&factory_
, IClassFactory_PatchInfo
));
163 EXPECT_NE(0, memcmp(GetVtable(&factory_
),
165 sizeof(vtable_backup_
)));
167 // This should not be called while the patch is in effect.
168 EXPECT_CALL(factory_
, LockServer(testing::_
))
171 EXPECT_CALL(*this, LockServerPatch(testing::_
, &factory_
, TRUE
))
172 .WillOnce(testing::Return(S_FALSE
));
174 EXPECT_EQ(S_FALSE
, factory_
.LockServer(TRUE
));
177 EXPECT_HRESULT_SUCCEEDED(
178 UnpatchInterfaceMethods(IClassFactory_PatchInfo
));
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
));
186 TEST_F(VtablePatchManagerTest
, DoublePatch
) {
188 EXPECT_HRESULT_SUCCEEDED(
189 PatchInterfaceMethods(&factory_
, IClassFactory_PatchInfo
));
191 // Capture the VTable after patching.
192 PROC vtable
[kFunctionCount
];
193 memcpy(vtable
, GetVtable(&factory_
), sizeof(vtable
));
195 // Patch it again, this should be idempotent.
196 EXPECT_HRESULT_SUCCEEDED(
197 PatchInterfaceMethods(&factory_
, IClassFactory_PatchInfo
));
199 // Should not have changed the VTable on second call.
200 EXPECT_EQ(0, memcmp(vtable
, GetVtable(&factory_
), sizeof(vtable
)));
203 namespace vtable_patch
{
204 // Expose internal implementation detail, purely for testing.
205 extern base::Lock patch_lock_
;
207 } // namespace vtable_patch
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
213 base::Thread
background("Background Test Thread");
215 EXPECT_TRUE(background
.Start());
216 base::win::ScopedHandle
event(::CreateEvent(NULL
, TRUE
, FALSE
, NULL
));
218 // Grab the patch lock.
219 vtable_patch::patch_lock_
.Acquire();
221 // Instruct the background thread to patch factory_.
222 background
.message_loop()->PostTask(
224 base::Bind(base::IgnoreResult(&vtable_patch::PatchInterfaceMethods
),
225 &factory_
, &IClassFactory_PatchInfo
[0]));
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()));
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));
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
));
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
));
247 // We should not get called here anymore.
248 EXPECT_CALL(factory_
, LockServer(TRUE
))
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
));
256 // Same deal for unpatching.
257 ::ResetEvent(event
.Get());
259 // Grab the patch lock.
260 vtable_patch::patch_lock_
.Acquire();
262 // Instruct the background thread to unpatch.
263 background
.message_loop()->PostTask(
265 base::Bind(base::IgnoreResult(&vtable_patch::UnpatchInterfaceMethods
),
266 &IClassFactory_PatchInfo
[0]));
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()));
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));
278 // We should still be patched.
279 EXPECT_CALL(factory_
, LockServer(TRUE
))
281 EXPECT_CALL(*this, LockServerPatch(_
, &factory_
, TRUE
))
282 .WillOnce(Return(S_FALSE
));
283 EXPECT_EQ(S_FALSE
, factory_
.LockServer(TRUE
));
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
));
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
));