New home page for developer.chrome.com
[chromium-blink-merge.git] / tools / memory_watcher / preamble_patcher.cc
blob846d88141be32b8a3602f120b9d8199f67dc4e93
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.
5 #include "preamble_patcher.h"
6 #include "memory_hook.h"
7 #include "mini_disassembler.h"
9 // compatibility shims
10 #include "base/logging.h"
12 // Definitions of assembly statements we need
13 #define ASM_JMP32REL 0xE9
14 #define ASM_INT3 0xCC
16 namespace sidestep {
18 SideStepError PreamblePatcher::RawPatchWithStubAndProtections(
19 void* target_function, void *replacement_function,
20 unsigned char* preamble_stub, unsigned long stub_size,
21 unsigned long* bytes_needed) {
22 // We need to be able to write to a process-local copy of the first
23 // MAX_PREAMBLE_STUB_SIZE bytes of target_function. We may be giving execute
24 // privilege to something that doesn't have it, but that's the price to pay
25 // for tools.
26 DWORD old_target_function_protect = 0;
27 BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function),
28 MAX_PREAMBLE_STUB_SIZE,
29 PAGE_EXECUTE_READWRITE,
30 &old_target_function_protect);
31 if (!succeeded) {
32 ASSERT(false, "Failed to make page containing target function "
33 "copy-on-write.");
34 return SIDESTEP_ACCESS_DENIED;
37 SideStepError error_code = RawPatchWithStub(target_function,
38 replacement_function,
39 preamble_stub,
40 stub_size,
41 bytes_needed);
42 if (SIDESTEP_SUCCESS != error_code) {
43 ASSERT1(false);
44 return error_code;
47 // Restore the protection of the first MAX_PREAMBLE_STUB_SIZE bytes of
48 // pTargetFunction to what they were before we started goofing around.
49 succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function),
50 MAX_PREAMBLE_STUB_SIZE,
51 old_target_function_protect,
52 &old_target_function_protect);
53 if (!succeeded) {
54 ASSERT(false, "Failed to restore protection to target function.");
55 // We must not return an error here because the function has actually
56 // been patched, and returning an error would likely cause our client
57 // code not to unpatch it. So we just keep going.
60 // Flush the instruction cache to make sure the processor doesn't execute the
61 // old version of the instructions (before our patch).
63 // FlushInstructionCache is actually a no-op at least on single-processor
64 // XP machines. I'm not sure why this is so, but it is, yet I want to keep
65 // the call to the API here for correctness in case there is a difference in
66 // some variants of Windows/hardware.
67 succeeded = ::FlushInstructionCache(::GetCurrentProcess(),
68 target_function,
69 MAX_PREAMBLE_STUB_SIZE);
70 if (!succeeded) {
71 ASSERT(false, "Failed to flush instruction cache.");
72 // We must not return an error here because the function has actually
73 // been patched, and returning an error would likely cause our client
74 // code not to unpatch it. So we just keep going.
77 return SIDESTEP_SUCCESS;
80 SideStepError PreamblePatcher::RawPatch(void* target_function,
81 void* replacement_function,
82 void** original_function_stub) {
83 if (!target_function || !replacement_function || !original_function_stub ||
84 (*original_function_stub) || target_function == replacement_function) {
85 ASSERT(false, "Preconditions not met");
86 return SIDESTEP_INVALID_PARAMETER;
89 // @see MAX_PREAMBLE_STUB_SIZE for an explanation of how we arrives at
90 // this size
91 unsigned char* preamble_stub =
92 reinterpret_cast<unsigned char*>(
93 MemoryHook::Alloc(sizeof(unsigned char) * MAX_PREAMBLE_STUB_SIZE));
94 if (!preamble_stub) {
95 ASSERT(false, "Unable to allocate preamble-stub.");
96 return SIDESTEP_INSUFFICIENT_BUFFER;
99 // Change the protection of the newly allocated preamble stub to
100 // PAGE_EXECUTE_READWRITE. This is required to work with DEP (Data
101 // Execution Prevention) which will cause an exception if code is executed
102 // from a page on which you do not have read access.
103 DWORD old_stub_protect = 0;
104 BOOL succeeded = VirtualProtect(preamble_stub, MAX_PREAMBLE_STUB_SIZE,
105 PAGE_EXECUTE_READWRITE, &old_stub_protect);
106 if (!succeeded) {
107 ASSERT(false, "Failed to make page preamble stub read-write-execute.");
108 delete[] preamble_stub;
109 return SIDESTEP_ACCESS_DENIED;
112 SideStepError error_code = RawPatchWithStubAndProtections(target_function,
113 replacement_function,
114 preamble_stub,
115 MAX_PREAMBLE_STUB_SIZE,
116 NULL);
117 if (SIDESTEP_SUCCESS != error_code) {
118 ASSERT1(false);
119 delete[] preamble_stub;
120 return error_code;
123 *original_function_stub = reinterpret_cast<void*>(preamble_stub);
125 // NOTE: For hooking malloc/free, we don't want to use streams which
126 // allocate. Basically, we've hooked malloc, but not necessarily
127 // hooked free yet. To do anything which uses the heap could crash
128 // with a mismatched malloc/free!
129 //VLOG(1) << "PreamblePatcher::RawPatch successfully patched 0x"
130 // << target_function;
132 return SIDESTEP_SUCCESS;
135 SideStepError PreamblePatcher::Unpatch(void* target_function,
136 void* replacement_function,
137 void* original_function_stub) {
138 ASSERT1(target_function && original_function_stub);
139 if (!target_function || !original_function_stub) {
140 return SIDESTEP_INVALID_PARAMETER;
143 // We disassemble the preamble of the _stub_ to see how many bytes we
144 // originally copied to the stub.
145 MiniDisassembler disassembler;
146 unsigned int preamble_bytes = 0;
147 while (preamble_bytes < 5) {
148 InstructionType instruction_type = disassembler.Disassemble(
149 reinterpret_cast<unsigned char*>(original_function_stub) +
150 preamble_bytes, preamble_bytes);
151 if (IT_GENERIC != instruction_type) {
152 ASSERT(false, "Should only have generic instructions in stub!!");
153 return SIDESTEP_UNSUPPORTED_INSTRUCTION;
157 // Before unpatching, target_function should be a JMP to
158 // replacement_function. If it's not, then either it's an error, or
159 // we're falling into the case where the original instruction was a
160 // JMP, and we patched the jumped_to address rather than the JMP
161 // itself. (For instance, if malloc() is just a JMP to __malloc(),
162 // we patched __malloc() and not malloc().)
163 unsigned char* target = reinterpret_cast<unsigned char*>(target_function);
164 while (1) { // we stop when target is a JMP to replacement_function
165 if (target[0] != ASM_JMP32REL) {
166 ASSERT(false, "target_function does not look like it was patched.");
167 return SIDESTEP_INVALID_PARAMETER;
169 int relative_offset; // Windows guarantees int is 4 bytes
170 ASSERT1(sizeof(relative_offset) == 4);
171 memcpy(reinterpret_cast<void*>(&relative_offset),
172 reinterpret_cast<void*>(target + 1), 4);
173 unsigned char* jump_to = target + 5 + relative_offset;
174 if (jump_to == replacement_function)
175 break;
176 target = jump_to; // follow the jmp
179 // We need to be able to write to a process-local copy of the first
180 // MAX_PREAMBLE_STUB_SIZE bytes of target_function. We may be giving execute
181 // privilege to something that doesn't have it, but that's the price to pay
182 // for tools.
183 DWORD old_target_function_protect = 0;
184 BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target),
185 MAX_PREAMBLE_STUB_SIZE,
186 PAGE_EXECUTE_READWRITE,
187 &old_target_function_protect);
188 if (!succeeded) {
189 ASSERT(false, "Failed to make page containing target function "
190 "copy-on-write.");
191 return SIDESTEP_ACCESS_DENIED;
194 // Replace the first few bytes of the original function with the bytes we
195 // previously moved to the preamble stub.
196 memcpy(reinterpret_cast<void*>(target),
197 original_function_stub, preamble_bytes);
199 // Stub is now useless so delete it.
200 // [csilvers: Commented out for perftools because it causes big problems
201 // when we're unpatching malloc. We just let this live on as a leak.]
202 //delete original_function_stub;
204 // Restore the protection of the first MAX_PREAMBLE_STUB_SIZE bytes of
205 // target to what they were before we started goofing around.
206 succeeded = ::VirtualProtect(reinterpret_cast<void*>(target),
207 MAX_PREAMBLE_STUB_SIZE,
208 old_target_function_protect,
209 &old_target_function_protect);
211 // Flush the instruction cache to make sure the processor doesn't execute the
212 // old version of the instructions (before our patch).
214 // See comment on FlushInstructionCache elsewhere in this file.
215 succeeded = ::FlushInstructionCache(::GetCurrentProcess(),
216 target,
217 MAX_PREAMBLE_STUB_SIZE);
218 if (!succeeded) {
219 ASSERT(false, "Failed to flush instruction cache.");
220 return SIDESTEP_UNEXPECTED;
223 VLOG(1) << "PreamblePatcher::Unpatch successfully unpatched 0x"
224 << target_function;
225 return SIDESTEP_SUCCESS;
228 }; // namespace sidestep