summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.cc
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.cc')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.cc240
1 files changed, 240 insertions, 0 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.cc b/toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.cc
new file mode 100644
index 0000000000..559022404f
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.cc
@@ -0,0 +1,240 @@
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// disassembler_x86.cc: simple x86 disassembler.
+//
+// Provides single step disassembly of x86 bytecode and flags instructions
+// that utilize known bad register values.
+//
+// Author: Cris Neckar
+
+#include "processor/disassembler_x86.h"
+
+#include <string.h>
+
+namespace google_breakpad {
+
+DisassemblerX86::DisassemblerX86(const uint8_t *bytecode,
+ uint32_t size,
+ uint32_t virtual_address) :
+ bytecode_(bytecode),
+ size_(size),
+ virtual_address_(virtual_address),
+ current_byte_offset_(0),
+ current_inst_offset_(0),
+ instr_valid_(false),
+ register_valid_(false),
+ pushed_bad_value_(false),
+ end_of_block_(false),
+ flags_(0) {
+ libdis::x86_init(libdis::opt_none, NULL, NULL);
+}
+
+DisassemblerX86::~DisassemblerX86() {
+ if (instr_valid_)
+ libdis::x86_oplist_free(&current_instr_);
+
+ libdis::x86_cleanup();
+}
+
+uint32_t DisassemblerX86::NextInstruction() {
+ if (instr_valid_)
+ libdis::x86_oplist_free(&current_instr_);
+
+ if (current_byte_offset_ >= size_) {
+ instr_valid_ = false;
+ return 0;
+ }
+ uint32_t instr_size = 0;
+ instr_size = libdis::x86_disasm((unsigned char *)bytecode_, size_,
+ virtual_address_, current_byte_offset_,
+ &current_instr_);
+ if (instr_size == 0) {
+ instr_valid_ = false;
+ return 0;
+ }
+
+ current_byte_offset_ += instr_size;
+ current_inst_offset_++;
+ instr_valid_ = libdis::x86_insn_is_valid(&current_instr_);
+ if (!instr_valid_)
+ return 0;
+
+ if (current_instr_.type == libdis::insn_return)
+ end_of_block_ = true;
+ libdis::x86_op_t *src = libdis::x86_get_src_operand(&current_instr_);
+ libdis::x86_op_t *dest = libdis::x86_get_dest_operand(&current_instr_);
+
+ if (register_valid_) {
+ switch (current_instr_.group) {
+ // Flag branches based off of bad registers and calls that occur
+ // after pushing bad values.
+ case libdis::insn_controlflow:
+ switch (current_instr_.type) {
+ case libdis::insn_jmp:
+ case libdis::insn_jcc:
+ case libdis::insn_call:
+ case libdis::insn_callcc:
+ if (dest) {
+ switch (dest->type) {
+ case libdis::op_expression:
+ if (dest->data.expression.base.id == bad_register_.id)
+ flags_ |= DISX86_BAD_BRANCH_TARGET;
+ break;
+ case libdis::op_register:
+ if (dest->data.reg.id == bad_register_.id)
+ flags_ |= DISX86_BAD_BRANCH_TARGET;
+ break;
+ default:
+ if (pushed_bad_value_ &&
+ (current_instr_.type == libdis::insn_call ||
+ current_instr_.type == libdis::insn_callcc))
+ flags_ |= DISX86_BAD_ARGUMENT_PASSED;
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ // Flag block data operations that use bad registers for src or dest.
+ case libdis::insn_string:
+ if (dest && dest->type == libdis::op_expression &&
+ dest->data.expression.base.id == bad_register_.id)
+ flags_ |= DISX86_BAD_BLOCK_WRITE;
+ if (src && src->type == libdis::op_expression &&
+ src->data.expression.base.id == bad_register_.id)
+ flags_ |= DISX86_BAD_BLOCK_READ;
+ break;
+
+ // Flag comparisons based on bad data.
+ case libdis::insn_comparison:
+ if ((dest && dest->type == libdis::op_expression &&
+ dest->data.expression.base.id == bad_register_.id) ||
+ (src && src->type == libdis::op_expression &&
+ src->data.expression.base.id == bad_register_.id) ||
+ (dest && dest->type == libdis::op_register &&
+ dest->data.reg.id == bad_register_.id) ||
+ (src && src->type == libdis::op_register &&
+ src->data.reg.id == bad_register_.id))
+ flags_ |= DISX86_BAD_COMPARISON;
+ break;
+
+ // Flag any other instruction which derefs a bad register for
+ // src or dest.
+ default:
+ if (dest && dest->type == libdis::op_expression &&
+ dest->data.expression.base.id == bad_register_.id)
+ flags_ |= DISX86_BAD_WRITE;
+ if (src && src->type == libdis::op_expression &&
+ src->data.expression.base.id == bad_register_.id)
+ flags_ |= DISX86_BAD_READ;
+ break;
+ }
+ }
+
+ // When a register is marked as tainted check if it is pushed.
+ // TODO(cdn): may also want to check for MOVs into EBP offsets.
+ if (register_valid_ && dest && current_instr_.type == libdis::insn_push) {
+ switch (dest->type) {
+ case libdis::op_expression:
+ if (dest->data.expression.base.id == bad_register_.id ||
+ dest->data.expression.index.id == bad_register_.id)
+ pushed_bad_value_ = true;
+ break;
+ case libdis::op_register:
+ if (dest->data.reg.id == bad_register_.id)
+ pushed_bad_value_ = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Check if a tainted register value is clobbered.
+ // For conditional MOVs and XCHGs assume that
+ // there is a hit.
+ if (register_valid_) {
+ switch (current_instr_.type) {
+ case libdis::insn_xor:
+ if (src && src->type == libdis::op_register &&
+ dest && dest->type == libdis::op_register &&
+ src->data.reg.id == bad_register_.id &&
+ src->data.reg.id == dest->data.reg.id)
+ register_valid_ = false;
+ break;
+ case libdis::insn_pop:
+ case libdis::insn_mov:
+ case libdis::insn_movcc:
+ if (dest && dest->type == libdis::op_register &&
+ dest->data.reg.id == bad_register_.id)
+ register_valid_ = false;
+ break;
+ case libdis::insn_popregs:
+ register_valid_ = false;
+ break;
+ case libdis::insn_xchg:
+ case libdis::insn_xchgcc:
+ if (dest && dest->type == libdis::op_register &&
+ src && src->type == libdis::op_register) {
+ if (dest->data.reg.id == bad_register_.id)
+ memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t));
+ else if (src->data.reg.id == bad_register_.id)
+ memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return instr_size;
+}
+
+bool DisassemblerX86::setBadRead() {
+ if (!instr_valid_)
+ return false;
+
+ libdis::x86_op_t *operand = libdis::x86_get_src_operand(&current_instr_);
+ if (!operand || operand->type != libdis::op_expression)
+ return false;
+
+ memcpy(&bad_register_, &operand->data.expression.base,
+ sizeof(libdis::x86_reg_t));
+ register_valid_ = true;
+ return true;
+}
+
+bool DisassemblerX86::setBadWrite() {
+ if (!instr_valid_)
+ return false;
+
+ libdis::x86_op_t *operand = libdis::x86_get_dest_operand(&current_instr_);
+ if (!operand || operand->type != libdis::op_expression)
+ return false;
+
+ memcpy(&bad_register_, &operand->data.expression.base,
+ sizeof(libdis::x86_reg_t));
+ register_valid_ = true;
+ return true;
+}
+
+} // namespace google_breakpad