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

Side by Side Diff: chrome_frame/function_stub_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/function_stub.cc ('k') | chrome_frame/test/function_stub_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5
5 #include "chrome_frame/function_stub.h" 6 #include "chrome_frame/function_stub.h"
6 #include "testing/gtest/include/gtest/gtest.h" 7 #include "testing/gtest/include/gtest/gtest.h"
7 8 #include "testing/gmock/include/gmock/gmock.h"
8 #define NO_INLINE __declspec(noinline)
9 9
10 namespace { 10 namespace {
11 11
12 typedef int (__stdcall* FooPrototype)(); 12 // Test subclass to expose extra stuff.
13 13 class TestFunctionStub: public FunctionStub {
14 NO_INLINE int __stdcall Foo() { 14 public:
15 return 1; 15 static void Init(TestFunctionStub* stub) {
16 } 16 stub->FunctionStub::Init(&stub->stub_);
17 17 }
18 NO_INLINE int __stdcall PatchedFoo(FooPrototype original) { 18
19 return original() + 1; 19 // Expose the offset to our signature_ field.
20 } 20 static const size_t kSignatureOffset;
21 21
22 } // end namespace 22 void set_signature(HMODULE signature) { signature_ = signature; }
23 23 };
24 TEST(PatchTests, FunctionStub) { 24
25 EXPECT_EQ(Foo(), 1); 25 const size_t TestFunctionStub::kSignatureOffset =
26 // Create a function stub that calls PatchedFoo and supplies it with 26 FIELD_OFFSET(TestFunctionStub, signature_);
27 // a pointer to Foo. 27
28 FunctionStub* stub = FunctionStub::Create(reinterpret_cast<uintptr_t>(&Foo), 28 class FunctionStubTest: public testing::Test {
29 &PatchedFoo); 29 public:
30 EXPECT_TRUE(stub != NULL); 30 FunctionStubTest() : stub_(NULL) {
31 // Call the stub as it were Foo(). The call should get forwarded to Foo(). 31 }
32 FooPrototype patch = reinterpret_cast<FooPrototype>(stub->code()); 32
33 EXPECT_EQ(patch(), 2); 33 virtual void SetUp() {
34 // Now neutralize the stub so that it calls Foo() directly without touching 34 SYSTEM_INFO sys_info;
35 // PatchedFoo(). 35 ::GetSystemInfo(&sys_info);
36 // stub->BypassStub(&Foo); 36
37 stub->BypassStub(reinterpret_cast<void*>(stub->argument())); 37 // Playpen size is a system page.
38 EXPECT_EQ(patch(), 1); 38 playpen_size_ = sys_info.dwPageSize;
39 // We're done with the stub. 39
40 FunctionStub::Destroy(stub); 40 // Reserve two pages.
41 } 41 playpen_ = reinterpret_cast<uint8*>(
42 42 ::VirtualAlloc(NULL,
43 // Basic tests to check the validity of a stub. 43 2 * playpen_size_,
44 TEST(PatchTests, FunctionStubCompare) { 44 MEM_RESERVE,
45 EXPECT_EQ(Foo(), 1); 45 PAGE_EXECUTE_READWRITE));
46 46 ASSERT_TRUE(playpen_ != NULL);
47 // Detect the absence of a stub 47
48 FunctionStub* stub = reinterpret_cast<FunctionStub*>(&Foo); 48 // And commit the first one.
49 EXPECT_FALSE(stub->is_valid()); 49 ASSERT_TRUE(::VirtualAlloc(playpen_,
50 50 playpen_size_,
51 stub = FunctionStub::Create(reinterpret_cast<uintptr_t>(&Foo), &PatchedFoo); 51 MEM_COMMIT,
52 EXPECT_TRUE(stub != NULL); 52 PAGE_EXECUTE_READWRITE));
53 EXPECT_TRUE(stub->is_valid()); 53 }
54 54
55 FooPrototype patch = reinterpret_cast<FooPrototype>(stub->code()); 55 virtual void TearDown() {
56 EXPECT_EQ(patch(), 2); 56 if (stub_ != NULL) {
57 57 EXPECT_TRUE(FunctionStub::Destroy(stub_));
58 // See if we can get the correct absolute pointer to the hook function 58 }
59 // back from the stub. 59
60 EXPECT_EQ(stub->absolute_target(), reinterpret_cast<uintptr_t>(&PatchedFoo)); 60 if (playpen_ != NULL) {
61 61 EXPECT_TRUE(::VirtualFree(playpen_, 0, MEM_RELEASE));
62 // Also verify that the argument being passed to the hook function is indeed 62 }
63 // the pointer to the original function (again, absolute not relative). 63 }
64 EXPECT_EQ(stub->argument(), reinterpret_cast<uintptr_t>(&Foo)); 64
65 } 65 protected:
66 typedef uintptr_t (CALLBACK *FuncPtr0)();
67 typedef uintptr_t (CALLBACK *FuncPtr1)(uintptr_t arg);
68
69 MOCK_METHOD0(Foo0, uintptr_t());
70 MOCK_METHOD1(Foo1, uintptr_t(uintptr_t));
71 MOCK_METHOD0(Bar0, uintptr_t());
72 MOCK_METHOD1(Bar1, uintptr_t(uintptr_t));
73
74 static uintptr_t CALLBACK FooCallback0(FunctionStubTest* test) {
75 return test->Foo0();
76 }
77 static uintptr_t CALLBACK FooCallback1(FunctionStubTest* test, uintptr_t arg) {
78 return test->Foo1(arg);
79 }
80 static uintptr_t CALLBACK BarCallback0(FunctionStubTest* test) {
81 return test->Foo0();
82 }
83 static uintptr_t CALLBACK BarCallback1(FunctionStubTest* test, uintptr_t arg) {
84 return test->Foo1(arg);
85 }
86
87 // If a stub is allocated during testing, assigning it here
88 // will deallocate it at the end of test.
89 FunctionStub* stub_;
90
91 // playpen_[0 .. playpen_size_ - 1] is committed, writable memory.
92 // playpen_[playpen_size_] is uncommitted, defined memory.
93 uint8* playpen_;
94 size_t playpen_size_;
95 };
96
97 const uintptr_t kDivertedRetVal = 0x42;
98 const uintptr_t kFooRetVal = 0xCAFEBABE;
99 const uintptr_t kFooArg = 0xF0F0F0F0;
100
101 uintptr_t CALLBACK Foo() {
102 return kFooRetVal;
103 }
104
105 uintptr_t CALLBACK FooDivert(uintptr_t arg) {
106 return kFooRetVal;
107 }
108
109 } // namespace
110
111 TEST_F(FunctionStubTest, Accessors) {
112 uintptr_t argument = reinterpret_cast<uintptr_t>(this);
113 uintptr_t dest_fn = reinterpret_cast<uintptr_t>(FooDivert);
114 stub_ = FunctionStub::Create(argument, FooDivert);
115
116 EXPECT_FALSE(stub_->is_bypassed());
117 EXPECT_TRUE(stub_->is_valid());
118 EXPECT_TRUE(stub_->code() != NULL);
119
120 // Check that the stub code is executable.
121 MEMORY_BASIC_INFORMATION info = {};
122 EXPECT_NE(0, ::VirtualQuery(stub_->code(), &info, sizeof(info)));
123 const DWORD kExecutableMask = PAGE_EXECUTE | PAGE_EXECUTE_READ |
124 PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
125 EXPECT_NE(0, info.Protect & kExecutableMask);
126
127 EXPECT_EQ(argument, stub_->argument());
128 EXPECT_TRUE(stub_->bypass_address() != NULL);
129 EXPECT_EQ(dest_fn, stub_->destination_function());
130 }
131
132 TEST_F(FunctionStubTest, ZeroArgumentStub) {
133 stub_ = FunctionStub::Create(reinterpret_cast<uintptr_t>(this),
134 &FunctionStubTest::FooCallback0);
135
136 FuncPtr0 func = reinterpret_cast<FuncPtr0>(stub_->code());
137 EXPECT_CALL(*this, Foo0())
138 .WillOnce(testing::Return(kDivertedRetVal));
139
140 EXPECT_EQ(kDivertedRetVal, func());
141 }
142
143 TEST_F(FunctionStubTest, OneArgumentStub) {
144 stub_ = FunctionStub::Create(reinterpret_cast<uintptr_t>(this),
145 &FunctionStubTest::FooCallback1);
146
147 FuncPtr1 func = reinterpret_cast<FuncPtr1>(stub_->code());
148 EXPECT_CALL(*this, Foo1(kFooArg))
149 .WillOnce(testing::Return(kDivertedRetVal));
150
151 EXPECT_EQ(kDivertedRetVal, func(kFooArg));
152 }
153
154 TEST_F(FunctionStubTest, Bypass) {
155 stub_ = FunctionStub::Create(reinterpret_cast<uintptr_t>(this),
156 &FunctionStubTest::FooCallback0);
157
158 FuncPtr0 func = reinterpret_cast<FuncPtr0>(stub_->code());
159 EXPECT_CALL(*this, Foo0())
160 .WillOnce(testing::Return(kDivertedRetVal));
161
162 // This will call through to foo.
163 EXPECT_EQ(kDivertedRetVal, func());
164
165 // Now bypass to Foo().
166 stub_->BypassStub(Foo);
167 EXPECT_TRUE(stub_->is_bypassed());
168 EXPECT_FALSE(stub_->is_valid());
169
170 // We should not call through anymore.
171 EXPECT_CALL(*this, Foo0())
172 .Times(0);
173
174 EXPECT_EQ(kFooRetVal, func());
175 }
176
177 TEST_F(FunctionStubTest, FromCode) {
178 // We should get NULL and no crash from reserved memory.
179 EXPECT_EQ(NULL, FunctionStub::FromCode(playpen_ + playpen_size_));
180
181 // Create a FunctionStub pointer whose signature_
182 // field hangs just off the playpen.
183 TestFunctionStub* stub =
184 reinterpret_cast<TestFunctionStub*>(playpen_ + playpen_size_ -
185 TestFunctionStub::kSignatureOffset);
186 TestFunctionStub::Init(stub);
187 EXPECT_EQ(NULL, FunctionStub::FromCode(stub));
188
189 // Create a stub in committed memory.
190 stub = reinterpret_cast<TestFunctionStub*>(playpen_);
191 TestFunctionStub::Init(stub);
192 // Signature is NULL, which won't do.
193 EXPECT_EQ(NULL, FunctionStub::FromCode(stub));
194
195 const DWORD kFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
196 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
197
198 HMODULE my_module = NULL;
199 EXPECT_TRUE(::GetModuleHandleEx(kFlags,
200 reinterpret_cast<const wchar_t*>(&kDivertedRetVal),
201 &my_module));
202
203 // Set our module as signature.
204 stub->set_signature(my_module);
205 EXPECT_EQ(stub, FunctionStub::FromCode(stub));
206 }
207
OLDNEW
« no previous file with comments | « chrome_frame/function_stub.cc ('k') | chrome_frame/test/function_stub_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698