1use crate::error::JingleError;
2
3use crate::varnode::ResolvedVarnode::{Direct, Indirect};
4use crate::varnode::{ResolvedIndirectVarNode, ResolvedVarnode};
5use jingle_sleigh::{
6 GeneralizedVarNode, PcodeOperation, SleighArchInfo, SpaceType, create_varnode,
7};
8use std::cmp::{Ordering, min};
9use std::collections::HashSet;
10use std::fmt::Debug;
11use std::hash::{DefaultHasher, Hash, Hasher};
12use std::ops::{Add, Neg};
13use tracing::instrument;
14use z3::ast::{Ast, BV, Bool};
15
16mod block;
17mod branch;
18mod concretize;
19pub mod expression;
20mod instruction;
21pub mod machine;
22mod slice;
23mod state;
24pub mod tactics;
25
26pub use block::ModeledBlock;
27pub use branch::*;
28pub use instruction::ModeledInstruction;
29pub use state::State;
30
31pub trait ModelingContext: Debug + Sized {
37 fn get_arch_info(&self) -> &SleighArchInfo;
39
40 fn get_address(&self) -> u64;
44
45 fn get_original_state(&self) -> &State;
47 fn get_final_state(&self) -> &State;
49
50 fn get_ops(&self) -> Vec<&PcodeOperation>;
54 fn get_inputs(&self) -> HashSet<ResolvedVarnode>;
60 fn get_outputs(&self) -> HashSet<ResolvedVarnode>;
66
67 fn get_branch_constraint(&self) -> &BranchConstraint;
70
71 fn should_varnode_constrain(&self, v: &ResolvedVarnode) -> bool {
77 match v {
78 Direct(d) => self
79 .get_final_state()
80 .arch_info()
81 .get_space(d.space_index())
82 .map(|o| o._type == SpaceType::IPTR_PROCESSOR)
83 .unwrap_or(false),
84 Indirect(_) => true,
85 }
86 }
87
88 fn upholds_postcondition<T: ModelingContext>(&self, other: &T) -> Result<Bool, JingleError> {
94 let mut output_terms = vec![];
95 for vn in other
96 .get_outputs()
97 .iter()
98 .filter(|v| self.should_varnode_constrain(v))
99 {
100 let ours = self.get_final_state().read_resolved(vn)?;
101 let other_bv = other.get_final_state().read_resolved(vn)?;
102 output_terms.push(ours.eq(&other_bv).simplify());
103 if let Indirect(a) = vn {
104 let ours = self.get_final_state().read_varnode(&a.pointer_location)?;
105 let other = other.get_final_state().read_varnode(&a.pointer_location)?;
106 output_terms.push(ours.eq(&other).simplify());
107 }
108 }
109 let imp_terms: Vec<&Bool> = output_terms.iter().collect();
110 let outputs_pairwise_equal = Bool::and(imp_terms.as_slice());
111 Ok(outputs_pairwise_equal)
112 }
113
114 fn assert_concat<T: ModelingContext>(&self, other: &T) -> Result<Bool, JingleError> {
117 self.get_final_state()._eq(other.get_original_state())
118 }
119
120 #[deprecated]
124 #[expect(deprecated)]
125 fn branch_comparison<T: ModelingContext>(
126 &self,
127 other: &T,
128 ) -> Result<Option<Bool>, JingleError> {
129 if !self.get_branch_constraint().has_branch() {
130 Ok(None)
131 } else {
132 if !self.get_branch_constraint().conditional_branches.is_empty()
133 || !other
134 .get_branch_constraint()
135 .conditional_branches
136 .is_empty()
137 {
138 return Ok(Some(Bool::from_bool(false)));
139 }
140 let self_bv = self.get_branch_constraint().build_bv(self)?;
141 let other_bv = other.get_branch_constraint().build_bv(other)?;
142 let self_bv = zext_to_match(self_bv, &other_bv);
143 let other_bv = zext_to_match(other_bv, &self_bv);
144 let self_bv_metadata = self.get_branch_constraint().build_bv_metadata(self)?;
145 let other_bv_metadata = other.get_branch_constraint().build_bv_metadata(other)?;
146 let self_bv_metadata =
147 zext_to_match(self_bv_metadata.simplify(), &other_bv_metadata.simplify());
148 let other_bv_metadata = zext_to_match(other_bv_metadata, &self_bv_metadata);
149 Ok(Some(Bool::and(&[
150 self_bv._eq(&other_bv).simplify(),
151 self_bv_metadata._eq(&other_bv_metadata).simplify(),
152 ])))
153 }
154 }
155
156 #[expect(deprecated)]
159 fn can_branch_to_address(&self, addr: u64) -> Result<Bool, JingleError> {
160 let branch_constraint = self.get_branch_constraint().build_bv(self)?;
161 let addr_bv = BV::from_i64(addr as i64, branch_constraint.get_size());
162 Ok(branch_constraint._eq(&addr_bv))
163 }
164}
165
166pub(crate) trait TranslationContext: ModelingContext {
171 fn track_input(&mut self, input: &ResolvedVarnode);
175
176 fn track_output(&mut self, output: &ResolvedVarnode);
180
181 fn get_final_state_mut(&mut self) -> &mut State;
183
184 fn get_branch_builder(&mut self) -> &mut BranchConstraint;
186
187 fn read_and_track(&mut self, gen_varnode: GeneralizedVarNode) -> Result<BV, JingleError> {
189 match gen_varnode {
190 GeneralizedVarNode::Direct(d) => {
191 self.track_input(&Direct(d.clone()));
192 self.get_final_state().read_varnode(&d)
193 }
194 GeneralizedVarNode::Indirect(indirect) => {
195 self.track_input(&Direct(indirect.pointer_location().clone()));
196 let pointer = self
197 .get_final_state()
198 .read_varnode(indirect.pointer_location())?
199 .clone();
200 self.track_input(&Indirect(ResolvedIndirectVarNode {
201 pointer,
202 pointer_location: indirect.pointer_location().clone(),
203 access_size_bytes: indirect.access_size_bytes(),
204 pointer_space_idx: indirect.pointer_space_index(),
205 }));
206 self.get_final_state().read_varnode_indirect(&indirect)
207 }
208 }
209 }
210
211 fn write(&mut self, r#gen: &GeneralizedVarNode, val: BV) -> Result<(), JingleError> {
212 match r#gen {
213 GeneralizedVarNode::Direct(d) => {
214 self.track_output(&Direct(d.clone()));
215 self.get_final_state_mut().write_varnode(d, val)?;
216 }
217 GeneralizedVarNode::Indirect(indirect) => {
218 let pointer = self.read_and_track(indirect.pointer_location().clone().into())?;
219 self.track_output(&Indirect(ResolvedIndirectVarNode {
220 pointer,
221 pointer_location: indirect.pointer_location().clone(),
222 access_size_bytes: indirect.access_size_bytes(),
223 pointer_space_idx: indirect.pointer_space_index(),
224 }));
225 self.get_final_state_mut()
226 .write_varnode_indirect(indirect, val)?;
227 }
228 }
229 Ok(())
230 }
231
232 #[instrument(skip_all)]
234 fn model_pcode_op(&mut self, op: &PcodeOperation) -> Result<(), JingleError>
235 where
236 Self: Sized,
237 {
238 match op {
239 PcodeOperation::Copy { input, output } => {
240 let val = self.read_and_track(input.into())?;
241 let metadata = self.get_original_state().read_varnode_metadata(input)?;
242 self.get_final_state_mut()
243 .write_varnode_metadata(output, metadata)?;
244 self.write(&output.into(), val)
245 }
246 PcodeOperation::IntZExt { input, output } => {
247 let diff = (output.size() - input.size()) as u32;
248 let val = self.read_and_track(input.into())?;
249 let zext = val.zero_ext(diff * 8);
250 self.write(&output.into(), zext)
251 }
252 PcodeOperation::IntSExt { input, output } => {
253 let diff = (output.size() - input.size()) as u32;
254 let val = self.read_and_track(input.into())?;
255 let zext = val.sign_ext(diff * 8);
256 self.write(&output.into(), zext)
257 }
258 PcodeOperation::Store { output, input } => {
259 let bv = self.read_and_track(input.into())?;
261 self.write(&output.into(), bv)
263 }
264 PcodeOperation::Load { input, output } => {
265 let bv = self.read_and_track(input.into())?;
267 self.write(&output.into(), bv)
270 }
271 PcodeOperation::IntAdd {
272 input0,
273 input1,
274 output,
275 } => {
276 let bv1 = self.read_and_track(input0.into())?;
277 let bv2 = self.read_and_track(input1.into())?;
278 let add = bv1 + bv2;
279 self.write(&output.into(), add)
280 }
281 PcodeOperation::IntSub {
282 input0,
283 input1,
284 output,
285 } => {
286 let bv1 = self.read_and_track(input0.into())?;
287 let bv2 = self.read_and_track(input1.into())?;
288 let add = bv1 - bv2;
289 self.write(&output.into(), add)
290 }
291 PcodeOperation::IntAnd {
292 input0,
293 input1,
294 output,
295 } => {
296 let bv1 = self.read_and_track(input0.into())?;
297 let bv2 = self.read_and_track(input1.into())?;
298 let and = bv1.bvand(&bv2);
299 self.write(&output.into(), and)
300 }
301 PcodeOperation::IntXor {
302 input0,
303 input1,
304 output,
305 } => {
306 let bv1 = self.read_and_track(input0.into())?;
307 let bv2 = self.read_and_track(input1.into())?;
308 let and = bv1.bvxor(&bv2);
309 self.write(&output.into(), and)
310 }
311 PcodeOperation::IntOr {
312 input0,
313 input1,
314 output,
315 } => {
316 let bv1 = self.read_and_track(input0.into())?;
317 let bv2 = self.read_and_track(input1.into())?;
318 let or = bv1.bvor(&bv2);
319 self.write(&output.into(), or)
320 }
321 PcodeOperation::IntNegate { input, output } => {
322 let bv = self.read_and_track(input.into())?;
323 let neg = bv.neg();
324 self.write(&output.into(), neg)
325 }
326 PcodeOperation::IntMult {
327 input0,
328 input1,
329 output,
330 } => {
331 let bv1 = self.read_and_track(input0.into())?;
332 let bv2 = self.read_and_track(input1.into())?;
333 let mul = bv1.bvmul(&bv2);
334 self.write(&output.into(), mul)
335 }
336 PcodeOperation::IntDiv {
337 input0,
338 input1,
339 output,
340 } => {
341 let bv1 = self.read_and_track(input0.into())?;
342 let bv2 = self.read_and_track(input1.into())?;
343 let mul = bv1.bvudiv(&bv2);
344 self.write(&output.into(), mul)
345 }
346 PcodeOperation::IntSignedDiv {
347 input0,
348 input1,
349 output,
350 } => {
351 let bv1 = self.read_and_track(input0.into())?;
352 let bv2 = self.read_and_track(input1.into())?;
353 let mul = bv1.bvsdiv(&bv2);
354 self.write(&output.into(), mul)
355 }
356 PcodeOperation::IntRem {
357 input0,
358 input1,
359 output,
360 } => {
361 let bv1 = self.read_and_track(input0.into())?;
362 let bv2 = self.read_and_track(input1.into())?;
363 let mul = bv1.bvurem(&bv2);
364 self.write(&output.into(), mul)
365 }
366 PcodeOperation::IntSignedRem {
367 input0,
368 input1,
369 output,
370 } => {
371 let bv1 = self.read_and_track(input0.into())?;
372 let bv2 = self.read_and_track(input1.into())?;
373 let mul = bv1.bvsrem(&bv2);
374 self.write(&output.into(), mul)
375 }
376 PcodeOperation::IntRightShift {
377 input0,
378 input1,
379 output,
380 } => {
381 let bv1 = self.read_and_track(input0.into())?;
382 let mut bv2 = self.read_and_track(input1.into())?;
383 match bv1.get_size().cmp(&bv2.get_size()) {
384 Ordering::Less => bv2 = bv2.extract(bv1.get_size() - 1, 0),
385 Ordering::Greater => bv2 = bv2.zero_ext(bv1.get_size() - bv2.get_size()),
386 _ => {}
387 }
388 let rshift = bv1.bvlshr(&bv2);
389 self.write(&output.into(), rshift)
390 }
391 PcodeOperation::IntSignedRightShift {
392 input0,
393 input1,
394 output,
395 } => {
396 let bv1 = self.read_and_track(input0.into())?;
397 let mut bv2 = self.read_and_track(input1.into())?;
398 match bv1.get_size().cmp(&bv2.get_size()) {
399 Ordering::Less => bv2 = bv2.extract(bv1.get_size() - 1, 0),
400 Ordering::Greater => bv2 = bv2.zero_ext(bv1.get_size() - bv2.get_size()),
401 _ => {}
402 }
403 let rshift = bv1.bvashr(&bv2);
404 self.write(&output.into(), rshift)
405 }
406 PcodeOperation::IntLeftShift {
407 input0,
408 input1,
409 output,
410 } => {
411 let mut bv1 = self.read_and_track(input0.into())?;
412 let mut bv2 = self.read_and_track(input1.into())?;
413 match bv1.get_size().cmp(&bv2.get_size()) {
414 Ordering::Less => bv1 = bv1.zero_ext(bv2.get_size() - bv1.get_size()),
415 Ordering::Greater => bv2 = bv2.zero_ext(bv1.get_size() - bv2.get_size()),
416 _ => {}
417 }
418 let lshift = bv1.bvshl(&bv2);
419 self.write(&output.into(), lshift)
420 }
421 PcodeOperation::IntCarry {
422 input0,
423 input1,
424 output,
425 } => {
426 let in0 = self.read_and_track(input0.into())?;
427 let in1 = self.read_and_track(input1.into())?;
428 let carry_bool = in0.bvadd_no_overflow(&in1, false);
430 let out_bv = carry_bool.ite(&BV::from_i64(0, 8), &BV::from_i64(1, 8));
431 self.write(&output.into(), out_bv)
432 }
433 PcodeOperation::IntSignedCarry {
434 input0,
435 input1,
436 output,
437 } => {
438 let in0 = self.read_and_track(input0.into())?;
439 let in1 = self.read_and_track(input1.into())?;
440 let carry_bool = in0.bvadd_no_overflow(&in1, true);
442 let out_bv = carry_bool.ite(&BV::from_i64(0, 8), &BV::from_i64(1, 8));
443 self.write(&output.into(), out_bv)
444 }
445 PcodeOperation::IntSignedBorrow {
446 input0,
447 input1,
448 output,
449 } => {
450 let in0 = self.read_and_track(input0.into())?;
451 let in1 = self.read_and_track(input1.into())?;
452 let borrow_bool = in0.bvsub_no_underflow(&in1, true);
455 let out_bv = borrow_bool.ite(&BV::from_i64(0, 8), &BV::from_i64(1, 8));
456 self.write(&output.into(), out_bv)
457 }
458 PcodeOperation::Int2Comp { input, output } => {
459 let in0 = self.read_and_track(input.into())?;
460 let flipped = in0.bvneg().add(BV::from_u64(1, in0.get_size()));
461 self.write(&output.into(), flipped)
462 }
463 PcodeOperation::IntSignedLess {
464 input0,
465 input1,
466 output,
467 } => {
468 let in0 = self.read_and_track(input0.into())?;
469 let in1 = self.read_and_track(input1.into())?;
470 let out_bool = in0.bvslt(&in1);
471 let out_bv = out_bool.ite(&BV::from_i64(1, 8), &BV::from_i64(0, 8));
472 self.write(&output.into(), out_bv)
473 }
474 PcodeOperation::IntSignedLessEqual {
475 input0,
476 input1,
477 output,
478 } => {
479 let in0 = self.read_and_track(input0.into())?;
480 let in1 = self.read_and_track(input1.into())?;
481 let out_bool = in0.bvsle(&in1);
482 let out_bv = out_bool.ite(&BV::from_i64(1, 8), &BV::from_i64(0, 8));
483 self.write(&output.into(), out_bv)
484 }
485 PcodeOperation::IntLess {
486 input0,
487 input1,
488 output,
489 } => {
490 let in0 = self.read_and_track(input0.into())?;
491 let in1 = self.read_and_track(input1.into())?;
492 let out_bool = in0.bvult(&in1);
493 let out_bv = out_bool.ite(&BV::from_i64(1, 8), &BV::from_i64(0, 8));
494 self.write(&output.into(), out_bv)
495 }
496 PcodeOperation::IntLessEqual {
497 input0,
498 input1,
499 output,
500 } => {
501 let in0 = self.read_and_track(input0.into())?;
502 let in1 = self.read_and_track(input1.into())?;
503 let out_bool = in0.bvule(&in1);
504 let out_bv = out_bool.ite(&BV::from_i64(1, 8), &BV::from_i64(0, 8));
505 self.write(&output.into(), out_bv)
506 }
507 PcodeOperation::IntEqual {
508 input0,
509 input1,
510 output,
511 } => {
512 let in0 = self.read_and_track(input0.into())?;
513 let in1 = self.read_and_track(input1.into())?;
514 let outsize = output.size() as u32;
515 let out_bool = in0.eq(&in1);
516 let out_bv =
517 out_bool.ite(&BV::from_i64(1, outsize * 8), &BV::from_i64(0, outsize * 8));
518 self.write(&output.into(), out_bv)
519 }
520 PcodeOperation::IntNotEqual {
521 input0,
522 input1,
523 output,
524 } => {
525 let in0 = self.read_and_track(input0.into())?;
526 let in1 = self.read_and_track(input1.into())?;
527 let outsize = output.size() as u32;
528 let out_bool = in0.eq(&in1).not();
529 let out_bv =
530 out_bool.ite(&BV::from_i64(1, outsize * 8), &BV::from_i64(0, outsize * 8));
531 self.write(&output.into(), out_bv)
532 }
533 PcodeOperation::BoolAnd {
534 input0,
535 input1,
536 output,
537 } => {
538 let i0 = self.read_and_track(input0.into())?;
539 let i1 = self.read_and_track(input1.into())?;
540 let result = i0.bvand(&i1).bvand(1);
541 self.write(&output.into(), result)
542 }
543 PcodeOperation::BoolNegate { input, output } => {
544 let val = self.read_and_track(input.into())?;
545 let negated = val.bvneg().bvand(1);
546 self.write(&output.into(), negated)
547 }
548 PcodeOperation::BoolOr {
549 input0,
550 input1,
551 output,
552 } => {
553 let i0 = self.read_and_track(input0.into())?;
554 let i1 = self.read_and_track(input1.into())?;
555 let result = (i0 | i1) & 1;
556 self.write(&output.into(), result)
557 }
558 PcodeOperation::BoolXor {
559 input0,
560 input1,
561 output,
562 } => {
563 let i0 = self.read_and_track(input0.into())?;
564 let i1 = self.read_and_track(input1.into())?;
565 let result = i0.bvxor(&i1).bvand(1);
566 self.write(&output.into(), result)
567 }
568 PcodeOperation::PopCount { input, output } => {
569 let size = output.size() as u32;
570 let in0 = self.read_and_track(input.into())?;
571 let mut outbv = BV::from_i64(0, output.size() as u32 * 8);
572 for i in 0..size * 8 {
573 let extract = in0.extract(i, i);
574 let extend = extract.zero_ext((size * 8) - 1);
575 outbv = outbv.bvadd(&extend);
576 }
577
578 self.write(&output.into(), outbv)
579 }
580 PcodeOperation::Branch { input } => {
581 self.get_branch_builder()
582 .set_last(&GeneralizedVarNode::from(input));
583 self.read_and_track(GeneralizedVarNode::from(input))?;
584 Ok(())
585 }
586 PcodeOperation::BranchInd { input } => {
587 self.get_branch_builder()
588 .set_last(&GeneralizedVarNode::from(input));
589 self.read_and_track(GeneralizedVarNode::from(input.pointer_location()))?;
590 Ok(())
591 }
592 PcodeOperation::Call { dest, .. } => {
593 self.get_branch_builder().set_last(&dest.into());
594 self.read_and_track(dest.into())?;
595 Ok(())
596 }
597 PcodeOperation::CBranch { input0, input1 } => {
598 self.get_branch_builder()
599 .push_conditional(&BlockConditionalBranchInfo {
600 condition: input1.clone(),
601 destination: input0.into(),
602 });
603 self.read_and_track(input0.into())?;
604 self.read_and_track(input1.into())?;
605 Ok(())
606 }
607 PcodeOperation::SubPiece {
608 input0,
609 input1,
610 output,
611 } => {
612 let bv0 = self.read_and_track(input0.into())?;
613 let input_low_byte = input1.offset() as u32;
615 let input_size = (input0.size() as u32) - input_low_byte;
616 let output_size = output.size() as u32;
617 let size = min(input_size, output_size);
618 let input = bv0.extract((input_low_byte + size) * 8 - 1, input_low_byte * 8);
619 match size.cmp(&output_size) {
620 Ordering::Less => {
621 self.write(&output.into(), input.zero_ext((output_size - size) * 8))
622 }
623 Ordering::Greater => {
624 self.write(&output.into(), input.extract(output_size * 8 - 1, 0))
625 }
626 Ordering::Equal => self.write(&output.into(), input),
627 }
628 }
629 PcodeOperation::CallOther { inputs, output, .. } => {
630 let mut hasher = DefaultHasher::new();
631 for vn in inputs {
632 vn.hash(&mut hasher);
633 }
634 let hash = hasher.finish();
635 for input in inputs.iter() {
636 self.read_and_track(input.into())?;
637 }
638 let size = self
639 .get_final_state()
640 .get_default_code_space_info()
641 .index_size_bytes;
642 let hash_vn = create_varnode(self.get_arch_info(), "const", hash, size)?;
643 let metadata = self
644 .get_final_state()
645 .immediate_metadata_array(true, hash_vn.size());
646 self.get_final_state_mut()
647 .write_varnode_metadata(&hash_vn, metadata)?;
648 self.get_branch_builder().set_last(&hash_vn.into());
649 if let Some(out) = output {
650 let size = out.size() * 8;
651 let hash_bv = BV::from_u64(hash, size as u32);
652 let metadata = self
653 .get_final_state()
654 .immediate_metadata_array(true, out.size());
655 self.get_final_state_mut()
656 .write_varnode_metadata(out, metadata)?;
657 self.write(&out.into(), hash_bv)?;
658 }
659 Ok(())
660 }
661 PcodeOperation::CallInd { input } => {
662 self.get_branch_builder()
663 .set_last(&GeneralizedVarNode::from(input));
664 self.read_and_track(GeneralizedVarNode::from(input.pointer_location()))?;
665 Ok(())
666 }
667 PcodeOperation::Return { input } => {
668 self.get_branch_builder()
669 .set_last(&GeneralizedVarNode::from(input));
670 self.read_and_track(GeneralizedVarNode::from(input.pointer_location()))?;
671 Ok(())
672 }
673 v => Err(JingleError::UnmodeledInstruction(Box::new(v.clone()))),
674 }
675 }
676}
677
678fn zext_to_match(bv1: BV, bv2: &BV) -> BV {
679 if bv1.get_size() < bv2.get_size() {
680 bv1.zero_ext(bv2.get_size() - bv1.get_size())
681 } else {
682 bv1
683 }
684}