1// Copyright (c) 2012 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// Implementation of PreamblePatcher
6
7#include "sandbox/win/src/sidestep/preamble_patcher.h"
8
9#include <stddef.h>
10
11#include "sandbox/win/src/sandbox_nt_util.h"
12#include "sandbox/win/src/sidestep/mini_disassembler.h"
13
14// Definitions of assembly statements we need
15#define ASM_JMP32REL 0xE9
16#define ASM_INT3 0xCC
17
18namespace {
19
20// Very basic memcpy. We are copying 4 to 12 bytes most of the time, so there
21// is no attempt to optimize this code or have a general purpose function.
22// We don't want to call the crt from this code.
23inline void* RawMemcpy(void* destination, const void* source, size_t bytes) {
24  const char* from = reinterpret_cast<const char*>(source);
25  char* to = reinterpret_cast<char*>(destination);
26
27  for (size_t i = 0; i < bytes ; i++)
28    to[i] = from[i];
29
30  return destination;
31}
32
33// Very basic memset. We are filling 1 to 7 bytes most of the time, so there
34// is no attempt to optimize this code or have a general purpose function.
35// We don't want to call the crt from this code.
36inline void* RawMemset(void* destination, int value, size_t bytes) {
37  char* to = reinterpret_cast<char*>(destination);
38
39  for (size_t i = 0; i < bytes ; i++)
40    to[i] = static_cast<char>(value);
41
42  return destination;
43}
44
45}  // namespace
46
47#define ASSERT(a, b) DCHECK_NT(a)
48
49namespace sidestep {
50
51SideStepError PreamblePatcher::RawPatchWithStub(
52    void* target_function,
53    void* replacement_function,
54    unsigned char* preamble_stub,
55    size_t stub_size,
56    size_t* bytes_needed) {
57  if ((NULL == target_function) ||
58      (NULL == replacement_function) ||
59      (NULL == preamble_stub)) {
60    ASSERT(false, (L"Invalid parameters - either pTargetFunction or "
61                   L"pReplacementFunction or pPreambleStub were NULL."));
62    return SIDESTEP_INVALID_PARAMETER;
63  }
64
65  // TODO(V7:joi) Siggi and I just had a discussion and decided that both
66  // patching and unpatching are actually unsafe.  We also discussed a
67  // method of making it safe, which is to freeze all other threads in the
68  // process, check their thread context to see if their eip is currently
69  // inside the block of instructions we need to copy to the stub, and if so
70  // wait a bit and try again, then unfreeze all threads once we've patched.
71  // Not implementing this for now since we're only using SideStep for unit
72  // testing, but if we ever use it for production code this is what we
73  // should do.
74  //
75  // NOTE: Stoyan suggests we can write 8 or even 10 bytes atomically using
76  // FPU instructions, and on newer processors we could use cmpxchg8b or
77  // cmpxchg16b. So it might be possible to do the patching/unpatching
78  // atomically and avoid having to freeze other threads.  Note though, that
79  // doing it atomically does not help if one of the other threads happens
80  // to have its eip in the middle of the bytes you change while you change
81  // them.
82  unsigned char* target = reinterpret_cast<unsigned char*>(target_function);
83
84  // Let's disassemble the preamble of the target function to see if we can
85  // patch, and to see how much of the preamble we need to take.  We need 5
86  // bytes for our jmp instruction, so let's find the minimum number of
87  // instructions to get 5 bytes.
88  MiniDisassembler disassembler;
89  unsigned int preamble_bytes = 0;
90  while (preamble_bytes < 5) {
91    InstructionType instruction_type =
92      disassembler.Disassemble(target + preamble_bytes, &preamble_bytes);
93    if (IT_JUMP == instruction_type) {
94      ASSERT(false, (L"Unable to patch because there is a jump instruction "
95                     L"in the first 5 bytes."));
96      return SIDESTEP_JUMP_INSTRUCTION;
97    } else if (IT_RETURN == instruction_type) {
98      ASSERT(false, (L"Unable to patch because function is too short"));
99      return SIDESTEP_FUNCTION_TOO_SMALL;
100    } else if (IT_GENERIC != instruction_type) {
101      ASSERT(false, (L"Disassembler encountered unsupported instruction "
102                     L"(either unused or unknown"));
103      return SIDESTEP_UNSUPPORTED_INSTRUCTION;
104    }
105  }
106
107  if (NULL != bytes_needed)
108    *bytes_needed = preamble_bytes + 5;
109
110  // Inv: preamble_bytes is the number of bytes (at least 5) that we need to
111  // take from the preamble to have whole instructions that are 5 bytes or more
112  // in size total. The size of the stub required is cbPreamble + size of
113  // jmp (5)
114  if (preamble_bytes + 5 > stub_size) {
115    NOTREACHED_NT();
116    return SIDESTEP_INSUFFICIENT_BUFFER;
117  }
118
119  // First, copy the preamble that we will overwrite.
120  RawMemcpy(reinterpret_cast<void*>(preamble_stub),
121            reinterpret_cast<void*>(target), preamble_bytes);
122
123  // Now, make a jmp instruction to the rest of the target function (minus the
124  // preamble bytes we moved into the stub) and copy it into our preamble-stub.
125  // find address to jump to, relative to next address after jmp instruction
126#pragma warning(push)
127#pragma warning(disable:4244)
128  // This assignment generates a warning because it is 32 bit specific.
129  int relative_offset_to_target_rest
130    = ((reinterpret_cast<unsigned char*>(target) + preamble_bytes) -
131        (preamble_stub + preamble_bytes + 5));
132#pragma warning(pop)
133  // jmp (Jump near, relative, displacement relative to next instruction)
134  preamble_stub[preamble_bytes] = ASM_JMP32REL;
135  // copy the address
136  RawMemcpy(reinterpret_cast<void*>(preamble_stub + preamble_bytes + 1),
137            reinterpret_cast<void*>(&relative_offset_to_target_rest), 4);
138
139  // Inv: preamble_stub points to assembly code that will execute the
140  // original function by first executing the first cbPreamble bytes of the
141  // preamble, then jumping to the rest of the function.
142
143  // Overwrite the first 5 bytes of the target function with a jump to our
144  // replacement function.
145  // (Jump near, relative, displacement relative to next instruction)
146  target[0] = ASM_JMP32REL;
147
148  // Find offset from instruction after jmp, to the replacement function.
149#pragma warning(push)
150#pragma warning(disable:4244)
151  int offset_to_replacement_function =
152    reinterpret_cast<unsigned char*>(replacement_function) -
153    reinterpret_cast<unsigned char*>(target) - 5;
154#pragma warning(pop)
155  // complete the jmp instruction
156  RawMemcpy(reinterpret_cast<void*>(target + 1),
157            reinterpret_cast<void*>(&offset_to_replacement_function), 4);
158  // Set any remaining bytes that were moved to the preamble-stub to INT3 so
159  // as not to cause confusion (otherwise you might see some strange
160  // instructions if you look at the disassembly, or even invalid
161  // instructions). Also, by doing this, we will break into the debugger if
162  // some code calls into this portion of the code.  If this happens, it
163  // means that this function cannot be patched using this patcher without
164  // further thought.
165  if (preamble_bytes > 5) {
166    RawMemset(reinterpret_cast<void*>(target + 5), ASM_INT3,
167              preamble_bytes - 5);
168  }
169
170  // Inv: The memory pointed to by target_function now points to a relative
171  // jump instruction that jumps over to the preamble_stub.  The preamble
172  // stub contains the first stub_size bytes of the original target
173  // function's preamble code, followed by a relative jump back to the next
174  // instruction after the first cbPreamble bytes.
175
176  return SIDESTEP_SUCCESS;
177}
178
179};  // namespace sidestep
180
181#undef ASSERT
182