Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(122)

Side by Side Diff: chrome_frame/vtable_patch_manager_unittest.cc

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

Powered by Google App Engine
This is Rietveld 408576698