Home | History | Annotate | Download | only in chrome_frame
      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