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