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