Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(28)

Side by Side Diff: src/processor/disassembler_x86.cc

Issue 1821293002: Replace libdisasm with capstone Base URL: https://chromium.googlesource.com/breakpad/breakpad.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // copyright notice, this list of conditions and the following disclaimer 1 // copyright notice, this list of conditions and the following disclaimer
2 // in the documentation and/or other materials provided with the 2 // in the documentation and/or other materials provided with the
3 // distribution. 3 // distribution.
4 // * Neither the name of Google Inc. nor the names of its 4 // * Neither the name of Google Inc. nor the names of its
5 // contributors may be used to endorse or promote products derived from 5 // contributors may be used to endorse or promote products derived from
6 // this software without specific prior written permission. 6 // this software without specific prior written permission.
7 // 7 //
8 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 8 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
9 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 9 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
10 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 10 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
(...skipping 18 matching lines...) Expand all
29 #include <string.h> 29 #include <string.h>
30 30
31 namespace google_breakpad { 31 namespace google_breakpad {
32 32
33 DisassemblerX86::DisassemblerX86(const uint8_t *bytecode, 33 DisassemblerX86::DisassemblerX86(const uint8_t *bytecode,
34 uint32_t size, 34 uint32_t size,
35 uint32_t virtual_address) : 35 uint32_t virtual_address) :
36 bytecode_(bytecode), 36 bytecode_(bytecode),
37 size_(size), 37 size_(size),
38 virtual_address_(virtual_address), 38 virtual_address_(virtual_address),
39 current_byte_offset_(0), 39 handle_(0),
40 current_inst_offset_(0),
41 instr_valid_(false), 40 instr_valid_(false),
41 current_instr_(nullptr),
42 register_valid_(false), 42 register_valid_(false),
43 bad_register_(X86_REG_INVALID),
43 pushed_bad_value_(false), 44 pushed_bad_value_(false),
44 end_of_block_(false), 45 end_of_block_(false),
45 flags_(0) { 46 flags_(0) {
46 libdis::x86_init(libdis::opt_none, NULL, NULL); 47 //TODO: allow selecting 32 or 64-bit.
48 if (cs_open(CS_ARCH_X86, CS_MODE_32, &handle_) == CS_ERR_OK) {
49 // Enable detailed instruction information.
50 cs_option(handle_, CS_OPT_DETAIL, CS_OPT_ON);
51 current_instr_ = cs_malloc(handle_);
52 }
47 } 53 }
48 54
49 DisassemblerX86::~DisassemblerX86() { 55 DisassemblerX86::~DisassemblerX86() {
50 if (instr_valid_) 56 if (current_instr_) {
51 libdis::x86_oplist_free(&current_instr_); 57 cs_free(current_instr_, 1);
58 }
52 59
53 libdis::x86_cleanup(); 60 cs_close(&handle_);
54 } 61 }
55 62
56 uint32_t DisassemblerX86::NextInstruction() { 63 // Return a pointer to the `nth` (zero-based) operand in `detail`, or NULL if
57 if (instr_valid_) 64 // no such operand exists.
58 libdis::x86_oplist_free(&current_instr_); 65 static cs_x86_op* get_operand(cs_x86& detail, size_t nth) {
66 if (nth > detail.op_count) {
67 return nullptr;
68 }
69 return &detail.operands[nth];
70 }
59 71
60 if (current_byte_offset_ >= size_) { 72 size_t DisassemblerX86::NextInstruction() {
61 instr_valid_ = false; 73 instr_valid_ = false;
62 return 0; 74 if (size_ == 0) {
63 }
64 uint32_t instr_size = 0;
65 instr_size = libdis::x86_disasm((unsigned char *)bytecode_, size_,
66 virtual_address_, current_byte_offset_,
67 &current_instr_);
68 if (instr_size == 0) {
69 instr_valid_ = false;
70 return 0; 75 return 0;
71 } 76 }
72 77
73 current_byte_offset_ += instr_size; 78 if (!cs_disasm_iter(handle_, &bytecode_, &size_, &virtual_address_,
74 current_inst_offset_++; 79 current_instr_)) {
75 instr_valid_ = libdis::x86_insn_is_valid(&current_instr_);
76 if (!instr_valid_)
77 return 0; 80 return 0;
81 }
78 82
79 if (current_instr_.type == libdis::insn_return) 83 instr_valid_ = true;
84 if (cs_insn_group(handle_, current_instr_, CS_GRP_RET))
80 end_of_block_ = true; 85 end_of_block_ = true;
81 libdis::x86_op_t *src = libdis::x86_get_src_operand(&current_instr_); 86 cs_x86_op* src = get_operand(current_instr_->detail->x86, 1);
82 libdis::x86_op_t *dest = libdis::x86_get_dest_operand(&current_instr_); 87 cs_x86_op* dest = get_operand(current_instr_->detail->x86, 0);
83 88
84 if (register_valid_) { 89 if (register_valid_) {
85 switch (current_instr_.group) { 90 if (cs_insn_group(handle_, current_instr_, CS_GRP_JUMP) ||
91 cs_insn_group(handle_, current_instr_, CS_GRP_CALL)) {
86 // Flag branches based off of bad registers and calls that occur 92 // Flag branches based off of bad registers and calls that occur
87 // after pushing bad values. 93 // after pushing bad values.
88 case libdis::insn_controlflow: 94 if (dest) {
89 switch (current_instr_.type) { 95 switch (dest->type) {
90 case libdis::insn_jmp: 96 case X86_OP_MEM:
91 case libdis::insn_jcc: 97 if (dest->mem.base == bad_register_)
92 case libdis::insn_call: 98 flags_ |= DISX86_BAD_BRANCH_TARGET;
93 case libdis::insn_callcc: 99 break;
94 if (dest) { 100 case X86_OP_REG:
95 switch (dest->type) { 101 if (dest->reg == bad_register_)
96 case libdis::op_expression: 102 flags_ |= DISX86_BAD_BRANCH_TARGET;
97 if (dest->data.expression.base.id == bad_register_.id) 103 break;
98 flags_ |= DISX86_BAD_BRANCH_TARGET; 104 default:
99 break; 105 if (pushed_bad_value_ &&
100 case libdis::op_register: 106 cs_insn_group(handle_, current_instr_, CS_GRP_CALL)) {
101 if (dest->data.reg.id == bad_register_.id) 107 flags_ |= DISX86_BAD_ARGUMENT_PASSED;
102 flags_ |= DISX86_BAD_BRANCH_TARGET; 108 }
103 break; 109 break;
104 default: 110 }
105 if (pushed_bad_value_ && 111 }
106 (current_instr_.type == libdis::insn_call || 112 } else if(currentInstructionIsBlockData()) {
107 current_instr_.type == libdis::insn_callcc)) 113 // Flag block data operations that use bad registers for src or dest.
108 flags_ |= DISX86_BAD_ARGUMENT_PASSED; 114 if (dest && dest->type == X86_OP_MEM &&
109 break; 115 dest->mem.base == bad_register_) {
110 } 116 flags_ |= DISX86_BAD_BLOCK_WRITE;
111 } 117 }
112 break; 118 if (src && src->type == X86_OP_MEM &&
113 default: 119 src->mem.base == bad_register_) {
114 break; 120 flags_ |= DISX86_BAD_BLOCK_READ;
121 }
122 } else {
123 switch (current_instr_->id) {
124 // Flag comparisons based on bad data.
125 case X86_INS_CMP:
126 case X86_INS_CMPPS:
127 case X86_INS_PCMPEQD:
128 case X86_INS_PCMPEQW:
129 case X86_INS_PFCMPEQ:
130 case X86_INS_PFCMPGE:
131 case X86_INS_PFCMPGT:
132 case X86_INS_TEST:
133 if ((dest && dest->type == X86_OP_MEM &&
134 dest->mem.base == bad_register_) ||
135 (src && src->type == X86_OP_MEM &&
136 src->mem.base == bad_register_) ||
137 (dest && dest->type == X86_OP_REG &&
138 dest->reg == bad_register_) ||
139 (src && src->type == X86_OP_REG &&
140 src->reg == bad_register_)) {
141 flags_ |= DISX86_BAD_COMPARISON;
115 } 142 }
116 break; 143 break;
117 144
118 // Flag block data operations that use bad registers for src or dest. 145 // Flag any other instruction which derefs a bad register for
119 case libdis::insn_string: 146 // src or dest.
120 if (dest && dest->type == libdis::op_expression && 147 default:
121 dest->data.expression.base.id == bad_register_.id) 148 if (dest && dest->type == X86_OP_MEM &&
122 flags_ |= DISX86_BAD_BLOCK_WRITE; 149 dest->mem.base == bad_register_) {
123 if (src && src->type == libdis::op_expression && 150 flags_ |= DISX86_BAD_WRITE;
124 src->data.expression.base.id == bad_register_.id) 151 }
125 flags_ |= DISX86_BAD_BLOCK_READ; 152 if (src && src->type == X86_OP_MEM &&
153 src->mem.base == bad_register_) {
154 flags_ |= DISX86_BAD_READ;
155 }
126 break; 156 break;
127 157 }
128 // Flag comparisons based on bad data.
129 case libdis::insn_comparison:
130 if ((dest && dest->type == libdis::op_expression &&
131 dest->data.expression.base.id == bad_register_.id) ||
132 (src && src->type == libdis::op_expression &&
133 src->data.expression.base.id == bad_register_.id) ||
134 (dest && dest->type == libdis::op_register &&
135 dest->data.reg.id == bad_register_.id) ||
136 (src && src->type == libdis::op_register &&
137 src->data.reg.id == bad_register_.id))
138 flags_ |= DISX86_BAD_COMPARISON;
139 break;
140
141 // Flag any other instruction which derefs a bad register for
142 // src or dest.
143 default:
144 if (dest && dest->type == libdis::op_expression &&
145 dest->data.expression.base.id == bad_register_.id)
146 flags_ |= DISX86_BAD_WRITE;
147 if (src && src->type == libdis::op_expression &&
148 src->data.expression.base.id == bad_register_.id)
149 flags_ |= DISX86_BAD_READ;
150 break;
151 } 158 }
152 } 159 }
153 160
154 // When a register is marked as tainted check if it is pushed. 161 // When a register is marked as tainted check if it is pushed.
155 // TODO(cdn): may also want to check for MOVs into EBP offsets. 162 // TODO(cdn): may also want to check for MOVs into EBP offsets.
156 if (register_valid_ && dest && current_instr_.type == libdis::insn_push) { 163 if (register_valid_ && dest && current_instr_->id == X86_INS_PUSH) {
157 switch (dest->type) { 164 switch (dest->type) {
158 case libdis::op_expression: 165 case X86_OP_MEM:
159 if (dest->data.expression.base.id == bad_register_.id || 166 if (dest->mem.base == bad_register_ ||
160 dest->data.expression.index.id == bad_register_.id) 167 dest->mem.index == bad_register_)
161 pushed_bad_value_ = true; 168 pushed_bad_value_ = true;
162 break; 169 break;
163 case libdis::op_register: 170 case X86_OP_REG:
164 if (dest->data.reg.id == bad_register_.id) 171 if (dest->reg == bad_register_)
165 pushed_bad_value_ = true; 172 pushed_bad_value_ = true;
166 break; 173 break;
167 default: 174 default:
168 break; 175 break;
169 } 176 }
170 } 177 }
171 178
172 // Check if a tainted register value is clobbered. 179 // Check if a tainted register value is clobbered.
173 // For conditional MOVs and XCHGs assume that 180 // For conditional MOVs and XCHGs assume that
174 // there is a hit. 181 // there is a hit.
175 if (register_valid_) { 182 if (register_valid_) {
176 switch (current_instr_.type) { 183 switch (current_instr_->id) {
177 case libdis::insn_xor: 184 case X86_INS_PXOR:
178 if (src && src->type == libdis::op_register && 185 case X86_INS_XOR:
179 dest && dest->type == libdis::op_register && 186 case X86_INS_XORPD:
180 src->data.reg.id == bad_register_.id && 187 case X86_INS_XORPS:
181 src->data.reg.id == dest->data.reg.id) 188 if (src && src->type == X86_OP_REG &&
189 dest && dest->type == X86_OP_REG &&
190 src->reg == bad_register_ &&
191 src->reg == dest->reg) {
182 register_valid_ = false; 192 register_valid_ = false;
193 }
183 break; 194 break;
184 case libdis::insn_pop: 195 case X86_INS_POP:
185 case libdis::insn_mov: 196 case X86_INS_CVTDQ2PS:
186 case libdis::insn_movcc: 197 case X86_INS_CVTPI2PS:
187 if (dest && dest->type == libdis::op_register && 198 case X86_INS_CVTPS2PD:
188 dest->data.reg.id == bad_register_.id) 199 case X86_INS_CVTPS2PI:
200 case X86_INS_CVTTPS2PI:
201 case X86_INS_LAHF:
202 case X86_INS_LDS:
203 case X86_INS_LEA:
204 case X86_INS_LES:
205 case X86_INS_LFS:
206 case X86_INS_LGS:
207 case X86_INS_LSS:
208 case X86_INS_MASKMOVQ:
209 case X86_INS_MOVD:
210 case X86_INS_MOVQ:
211 case X86_INS_MOV:
212 case X86_INS_MOVAPD:
213 case X86_INS_MOVAPS:
214 case X86_INS_MOVHLPS:
215 case X86_INS_MOVHPD:
216 case X86_INS_MOVLHPS:
217 case X86_INS_MOVLPD:
218 case X86_INS_MOVLPS:
219 case X86_INS_MOVMSKPS:
220 case X86_INS_MOVNTI:
221 case X86_INS_MOVNTPS:
222 case X86_INS_MOVNTQ:
223 case X86_INS_MOVSX:
224 case X86_INS_MOVSXD:
225 case X86_INS_MOVUPD:
226 case X86_INS_MOVUPS:
227 case X86_INS_MOVZX:
228 case X86_INS_PSHUFW:
229 case X86_INS_SAHF:
230 case X86_INS_CMOVA:
231 case X86_INS_CMOVBE:
232 case X86_INS_CMOVG:
233 case X86_INS_CMOVGE:
234 case X86_INS_CMOVL:
235 case X86_INS_CMOVLE:
236 case X86_INS_CMOVNO:
237 case X86_INS_CMOVNP:
238 case X86_INS_CMOVNS:
239 case X86_INS_CMOVO:
240 case X86_INS_CMOVP:
241 case X86_INS_CMOVS:
242 case X86_INS_SETA:
243 case X86_INS_SETBE:
244 case X86_INS_SETG:
245 case X86_INS_SETGE:
246 case X86_INS_SETL:
247 case X86_INS_SETLE:
248 case X86_INS_SETNO:
249 case X86_INS_SETNS:
250 case X86_INS_SETO:
251 case X86_INS_SETS:
252 if (dest && dest->type == X86_OP_REG &&
253 dest->reg == bad_register_) {
189 register_valid_ = false; 254 register_valid_ = false;
255 }
190 break; 256 break;
191 case libdis::insn_popregs: 257 case X86_INS_POPAW:
192 register_valid_ = false; 258 register_valid_ = false;
193 break; 259 break;
194 case libdis::insn_xchg: 260 case X86_INS_BSWAP:
195 case libdis::insn_xchgcc: 261 case X86_INS_XCHG:
196 if (dest && dest->type == libdis::op_register && 262 case X86_INS_CMPXCHG:
197 src && src->type == libdis::op_register) { 263 case X86_INS_CMPXCHG8B:
198 if (dest->data.reg.id == bad_register_.id) 264 if (dest && dest->type == X86_OP_REG &&
199 memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t)); 265 src && src->type == X86_OP_REG) {
200 else if (src->data.reg.id == bad_register_.id) 266 if (dest->reg == bad_register_)
201 memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t)); 267 bad_register_ = src->reg;
268 else if (src->reg == bad_register_)
269 bad_register_ = dest->reg;
202 } 270 }
203 break; 271 break;
204 default: 272 default:
205 break; 273 break;
206 } 274 }
207 } 275 }
208 276
209 return instr_size; 277 return current_instr_->size;
278 }
279
280 bool DisassemblerX86::currentInstructionIsBlockData() {
281 if (!instr_valid_)
282 return false;
283
284 switch (current_instr_->id) {
285 case X86_INS_CMPSB:
286 case X86_INS_CMPSD:
287 case X86_INS_CMPSQ:
288 case X86_INS_CMPSW:
289 case X86_INS_LODSB:
290 case X86_INS_LODSD:
291 case X86_INS_LODSQ:
292 case X86_INS_LODSW:
293 case X86_INS_MOVSB:
294 case X86_INS_MOVSD:
295 case X86_INS_MOVSQ:
296 case X86_INS_MOVSW:
297 case X86_INS_SCASB:
298 case X86_INS_SCASD:
299 case X86_INS_SCASQ:
300 case X86_INS_SCASW:
301 case X86_INS_STOSB:
302 case X86_INS_STOSD:
303 case X86_INS_STOSQ:
304 case X86_INS_STOSW:
305 case X86_INS_XLATB:
306 return true;
307 default:
308 return false;
309 }
210 } 310 }
211 311
212 bool DisassemblerX86::setBadRead() { 312 bool DisassemblerX86::setBadRead() {
213 if (!instr_valid_) 313 if (!instr_valid_)
214 return false; 314 return false;
215 315
216 libdis::x86_op_t *operand = libdis::x86_get_src_operand(&current_instr_); 316 cs_x86_op* operand = get_operand(current_instr_->detail->x86, 1);
217 if (!operand || operand->type != libdis::op_expression) 317 if (!operand || operand->type != X86_OP_MEM)
218 return false; 318 return false;
219 319
220 memcpy(&bad_register_, &operand->data.expression.base, 320 bad_register_ = static_cast<x86_reg>(operand->mem.base);
221 sizeof(libdis::x86_reg_t));
222 register_valid_ = true; 321 register_valid_ = true;
322
223 return true; 323 return true;
224 } 324 }
225 325
226 bool DisassemblerX86::setBadWrite() { 326 bool DisassemblerX86::setBadWrite() {
327
227 if (!instr_valid_) 328 if (!instr_valid_)
228 return false; 329 return false;
229 330
230 libdis::x86_op_t *operand = libdis::x86_get_dest_operand(&current_instr_); 331 cs_x86_op* operand = get_operand(current_instr_->detail->x86, 0);
231 if (!operand || operand->type != libdis::op_expression) 332 if (!operand || operand->type != X86_OP_MEM)
232 return false; 333 return false;
233 334
234 memcpy(&bad_register_, &operand->data.expression.base, 335 bad_register_ = static_cast<x86_reg>(operand->mem.base);
235 sizeof(libdis::x86_reg_t));
236 register_valid_ = true; 336 register_valid_ = true;
337
237 return true; 338 return true;
238 } 339 }
239 340
240 } // namespace google_breakpad 341 } // namespace google_breakpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698