toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.cc

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 // copyright notice, this list of conditions and the following disclaimer
michael@0 2 // in the documentation and/or other materials provided with the
michael@0 3 // distribution.
michael@0 4 // * Neither the name of Google Inc. nor the names of its
michael@0 5 // contributors may be used to endorse or promote products derived from
michael@0 6 // this software without specific prior written permission.
michael@0 7 //
michael@0 8 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 9 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 10 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 11 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
michael@0 12 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@0 13 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@0 14 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 15 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 16 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 17 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 18 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 19
michael@0 20 // disassembler_x86.cc: simple x86 disassembler.
michael@0 21 //
michael@0 22 // Provides single step disassembly of x86 bytecode and flags instructions
michael@0 23 // that utilize known bad register values.
michael@0 24 //
michael@0 25 // Author: Cris Neckar
michael@0 26
michael@0 27 #include "processor/disassembler_x86.h"
michael@0 28
michael@0 29 #include <string.h>
michael@0 30 #include <unistd.h>
michael@0 31
michael@0 32 namespace google_breakpad {
michael@0 33
michael@0 34 DisassemblerX86::DisassemblerX86(const uint8_t *bytecode,
michael@0 35 uint32_t size,
michael@0 36 uint32_t virtual_address) :
michael@0 37 bytecode_(bytecode),
michael@0 38 size_(size),
michael@0 39 virtual_address_(virtual_address),
michael@0 40 current_byte_offset_(0),
michael@0 41 current_inst_offset_(0),
michael@0 42 instr_valid_(false),
michael@0 43 register_valid_(false),
michael@0 44 pushed_bad_value_(false),
michael@0 45 end_of_block_(false),
michael@0 46 flags_(0) {
michael@0 47 libdis::x86_init(libdis::opt_none, NULL, NULL);
michael@0 48 }
michael@0 49
michael@0 50 DisassemblerX86::~DisassemblerX86() {
michael@0 51 if (instr_valid_)
michael@0 52 libdis::x86_oplist_free(&current_instr_);
michael@0 53
michael@0 54 libdis::x86_cleanup();
michael@0 55 }
michael@0 56
michael@0 57 uint32_t DisassemblerX86::NextInstruction() {
michael@0 58 if (instr_valid_)
michael@0 59 libdis::x86_oplist_free(&current_instr_);
michael@0 60
michael@0 61 if (current_byte_offset_ >= size_) {
michael@0 62 instr_valid_ = false;
michael@0 63 return 0;
michael@0 64 }
michael@0 65 uint32_t instr_size = 0;
michael@0 66 instr_size = libdis::x86_disasm((unsigned char *)bytecode_, size_,
michael@0 67 virtual_address_, current_byte_offset_,
michael@0 68 &current_instr_);
michael@0 69 if (instr_size == 0) {
michael@0 70 instr_valid_ = false;
michael@0 71 return 0;
michael@0 72 }
michael@0 73
michael@0 74 current_byte_offset_ += instr_size;
michael@0 75 current_inst_offset_++;
michael@0 76 instr_valid_ = libdis::x86_insn_is_valid(&current_instr_);
michael@0 77 if (!instr_valid_)
michael@0 78 return 0;
michael@0 79
michael@0 80 if (current_instr_.type == libdis::insn_return)
michael@0 81 end_of_block_ = true;
michael@0 82 libdis::x86_op_t *src = libdis::x86_get_src_operand(&current_instr_);
michael@0 83 libdis::x86_op_t *dest = libdis::x86_get_dest_operand(&current_instr_);
michael@0 84
michael@0 85 if (register_valid_) {
michael@0 86 switch (current_instr_.group) {
michael@0 87 // Flag branches based off of bad registers and calls that occur
michael@0 88 // after pushing bad values.
michael@0 89 case libdis::insn_controlflow:
michael@0 90 switch (current_instr_.type) {
michael@0 91 case libdis::insn_jmp:
michael@0 92 case libdis::insn_jcc:
michael@0 93 case libdis::insn_call:
michael@0 94 case libdis::insn_callcc:
michael@0 95 if (dest) {
michael@0 96 switch (dest->type) {
michael@0 97 case libdis::op_expression:
michael@0 98 if (dest->data.expression.base.id == bad_register_.id)
michael@0 99 flags_ |= DISX86_BAD_BRANCH_TARGET;
michael@0 100 break;
michael@0 101 case libdis::op_register:
michael@0 102 if (dest->data.reg.id == bad_register_.id)
michael@0 103 flags_ |= DISX86_BAD_BRANCH_TARGET;
michael@0 104 break;
michael@0 105 default:
michael@0 106 if (pushed_bad_value_ &&
michael@0 107 (current_instr_.type == libdis::insn_call ||
michael@0 108 current_instr_.type == libdis::insn_callcc))
michael@0 109 flags_ |= DISX86_BAD_ARGUMENT_PASSED;
michael@0 110 break;
michael@0 111 }
michael@0 112 }
michael@0 113 break;
michael@0 114 default:
michael@0 115 break;
michael@0 116 }
michael@0 117 break;
michael@0 118
michael@0 119 // Flag block data operations that use bad registers for src or dest.
michael@0 120 case libdis::insn_string:
michael@0 121 if (dest && dest->type == libdis::op_expression &&
michael@0 122 dest->data.expression.base.id == bad_register_.id)
michael@0 123 flags_ |= DISX86_BAD_BLOCK_WRITE;
michael@0 124 if (src && src->type == libdis::op_expression &&
michael@0 125 src->data.expression.base.id == bad_register_.id)
michael@0 126 flags_ |= DISX86_BAD_BLOCK_READ;
michael@0 127 break;
michael@0 128
michael@0 129 // Flag comparisons based on bad data.
michael@0 130 case libdis::insn_comparison:
michael@0 131 if ((dest && dest->type == libdis::op_expression &&
michael@0 132 dest->data.expression.base.id == bad_register_.id) ||
michael@0 133 (src && src->type == libdis::op_expression &&
michael@0 134 src->data.expression.base.id == bad_register_.id) ||
michael@0 135 (dest && dest->type == libdis::op_register &&
michael@0 136 dest->data.reg.id == bad_register_.id) ||
michael@0 137 (src && src->type == libdis::op_register &&
michael@0 138 src->data.reg.id == bad_register_.id))
michael@0 139 flags_ |= DISX86_BAD_COMPARISON;
michael@0 140 break;
michael@0 141
michael@0 142 // Flag any other instruction which derefs a bad register for
michael@0 143 // src or dest.
michael@0 144 default:
michael@0 145 if (dest && dest->type == libdis::op_expression &&
michael@0 146 dest->data.expression.base.id == bad_register_.id)
michael@0 147 flags_ |= DISX86_BAD_WRITE;
michael@0 148 if (src && src->type == libdis::op_expression &&
michael@0 149 src->data.expression.base.id == bad_register_.id)
michael@0 150 flags_ |= DISX86_BAD_READ;
michael@0 151 break;
michael@0 152 }
michael@0 153 }
michael@0 154
michael@0 155 // When a register is marked as tainted check if it is pushed.
michael@0 156 // TODO(cdn): may also want to check for MOVs into EBP offsets.
michael@0 157 if (register_valid_ && dest && current_instr_.type == libdis::insn_push) {
michael@0 158 switch (dest->type) {
michael@0 159 case libdis::op_expression:
michael@0 160 if (dest->data.expression.base.id == bad_register_.id ||
michael@0 161 dest->data.expression.index.id == bad_register_.id)
michael@0 162 pushed_bad_value_ = true;
michael@0 163 break;
michael@0 164 case libdis::op_register:
michael@0 165 if (dest->data.reg.id == bad_register_.id)
michael@0 166 pushed_bad_value_ = true;
michael@0 167 break;
michael@0 168 default:
michael@0 169 break;
michael@0 170 }
michael@0 171 }
michael@0 172
michael@0 173 // Check if a tainted register value is clobbered.
michael@0 174 // For conditional MOVs and XCHGs assume that
michael@0 175 // there is a hit.
michael@0 176 if (register_valid_) {
michael@0 177 switch (current_instr_.type) {
michael@0 178 case libdis::insn_xor:
michael@0 179 if (src && src->type == libdis::op_register &&
michael@0 180 dest && dest->type == libdis::op_register &&
michael@0 181 src->data.reg.id == bad_register_.id &&
michael@0 182 src->data.reg.id == dest->data.reg.id)
michael@0 183 register_valid_ = false;
michael@0 184 break;
michael@0 185 case libdis::insn_pop:
michael@0 186 case libdis::insn_mov:
michael@0 187 case libdis::insn_movcc:
michael@0 188 if (dest && dest->type == libdis::op_register &&
michael@0 189 dest->data.reg.id == bad_register_.id)
michael@0 190 register_valid_ = false;
michael@0 191 break;
michael@0 192 case libdis::insn_popregs:
michael@0 193 register_valid_ = false;
michael@0 194 break;
michael@0 195 case libdis::insn_xchg:
michael@0 196 case libdis::insn_xchgcc:
michael@0 197 if (dest && dest->type == libdis::op_register &&
michael@0 198 src && src->type == libdis::op_register) {
michael@0 199 if (dest->data.reg.id == bad_register_.id)
michael@0 200 memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t));
michael@0 201 else if (src->data.reg.id == bad_register_.id)
michael@0 202 memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t));
michael@0 203 }
michael@0 204 break;
michael@0 205 default:
michael@0 206 break;
michael@0 207 }
michael@0 208 }
michael@0 209
michael@0 210 return instr_size;
michael@0 211 }
michael@0 212
michael@0 213 bool DisassemblerX86::setBadRead() {
michael@0 214 if (!instr_valid_)
michael@0 215 return false;
michael@0 216
michael@0 217 libdis::x86_op_t *operand = libdis::x86_get_src_operand(&current_instr_);
michael@0 218 if (!operand || operand->type != libdis::op_expression)
michael@0 219 return false;
michael@0 220
michael@0 221 memcpy(&bad_register_, &operand->data.expression.base,
michael@0 222 sizeof(libdis::x86_reg_t));
michael@0 223 register_valid_ = true;
michael@0 224 return true;
michael@0 225 }
michael@0 226
michael@0 227 bool DisassemblerX86::setBadWrite() {
michael@0 228 if (!instr_valid_)
michael@0 229 return false;
michael@0 230
michael@0 231 libdis::x86_op_t *operand = libdis::x86_get_dest_operand(&current_instr_);
michael@0 232 if (!operand || operand->type != libdis::op_expression)
michael@0 233 return false;
michael@0 234
michael@0 235 memcpy(&bad_register_, &operand->data.expression.base,
michael@0 236 sizeof(libdis::x86_reg_t));
michael@0 237 register_valid_ = true;
michael@0 238 return true;
michael@0 239 }
michael@0 240
michael@0 241 } // namespace google_breakpad

mercurial