1use crate::{
2 analysis::{
3 Analysis,
4 core::{
5 alias_analysis::AAResult,
6 ownedheap_analysis::OHAResultMap,
7 range_analysis::{RangeAnalysis, default::RangeAnalyzer},
8 },
9 graphs::scc::Scc,
10 safedrop::graph::SafeDropGraph,
11 senryx::{
12 contracts::{
13 abstract_state::AlignState,
14 property::{CisRangeItem, PropertyContract},
15 },
16 dominated_graph::FunctionSummary,
17 symbolic_analysis::{AnaOperand, SymbolicDef, ValueDomain},
18 },
19 utils::{draw_dot::render_dot_string, fn_info::*, show_mir::display_mir},
20 },
21 rap_debug, rap_warn,
22};
23use rustc_middle::ty::GenericParamDefKind;
24use serde::de;
25use std::{
26 collections::{HashMap, HashSet},
27 fmt::Debug,
28 hash::Hash,
29};
30use syn::Constraint;
31
32use super::dominated_graph::DominatedGraph;
33use super::dominated_graph::InterResultNode;
34use super::generic_check::GenericChecker;
35use super::matcher::UnsafeApi;
36use super::matcher::{get_arg_place, parse_unsafe_api};
37use rustc_data_structures::fx::FxHashMap;
38use rustc_hir::def_id::DefId;
39use rustc_middle::{
40 mir::{
41 self, AggregateKind, BasicBlock, BasicBlockData, BinOp, CastKind, Local, Operand, Place,
42 ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
43 },
44 ty::{self, GenericArgKind, PseudoCanonicalInput, Ty, TyCtxt, TyKind},
45};
46use rustc_span::{Span, source_map::Spanned};
47
48#[derive(Debug, PartialEq, Eq, Clone)]
49pub struct CheckResult {
50 pub func_name: String,
51 pub func_span: Span,
52 pub failed_contracts: HashMap<usize, HashSet<String>>,
53 pub passed_contracts: HashMap<usize, HashSet<String>>,
54}
55
56impl CheckResult {
57 pub fn new(func_name: &str, func_span: Span) -> Self {
60 Self {
61 func_name: func_name.to_string(),
62 func_span,
63 failed_contracts: HashMap::new(),
64 passed_contracts: HashMap::new(),
65 }
66 }
67}
68
69#[derive(Debug, PartialEq, Eq, Clone)]
70pub enum PlaceTy<'tcx> {
71 Ty(usize, usize), GenericTy(String, HashSet<Ty<'tcx>>, HashSet<(usize, usize)>), Unknown,
74}
75
76impl<'tcx> PlaceTy<'tcx> {
77 pub fn possible_aligns(&self) -> HashSet<usize> {
82 match self {
83 PlaceTy::Ty(align, _size) => {
84 let mut set = HashSet::new();
85 set.insert(*align);
86 set
87 }
88 PlaceTy::GenericTy(_, _, tys) => tys.iter().map(|ty| ty.0).collect(),
89 _ => HashSet::new(),
90 }
91 }
92}
93
94impl<'tcx> Hash for PlaceTy<'tcx> {
95 fn hash<H: std::hash::Hasher>(&self, _state: &mut H) {}
99}
100
101pub struct BodyVisitor<'tcx> {
104 pub tcx: TyCtxt<'tcx>,
105 pub def_id: DefId,
106 pub safedrop_graph: SafeDropGraph<'tcx>,
107 pub local_ty: HashMap<usize, PlaceTy<'tcx>>,
108 pub visit_time: usize,
109 pub check_results: Vec<CheckResult>,
110 pub generic_map: HashMap<String, HashSet<Ty<'tcx>>>,
111 pub proj_ty: HashMap<usize, Ty<'tcx>>,
112 pub chains: DominatedGraph<'tcx>,
113 pub value_domains: HashMap<usize, ValueDomain<'tcx>>,
114 pub path_constraints: Vec<SymbolicDef<'tcx>>,
115}
116
117impl<'tcx> BodyVisitor<'tcx> {
121 pub fn new(tcx: TyCtxt<'tcx>, def_id: DefId, visit_time: usize) -> Self {
124 let body = tcx.optimized_mir(def_id);
125 let param_env = tcx.param_env(def_id);
126 let satisfied_ty_map_for_generic =
127 GenericChecker::new(tcx, param_env).get_satisfied_ty_map();
128 let mut chains = DominatedGraph::new(tcx, def_id);
129 chains.init_arg();
130 Self {
131 tcx,
132 def_id,
133 safedrop_graph: SafeDropGraph::new(tcx, def_id, OHAResultMap::default()),
134 local_ty: HashMap::new(),
135 visit_time,
136 check_results: Vec::new(),
137 generic_map: satisfied_ty_map_for_generic,
138 proj_ty: HashMap::new(),
139 chains,
140 value_domains: HashMap::new(),
141 path_constraints: Vec::new(),
142 }
143 }
144}
145
146impl<'tcx> BodyVisitor<'tcx> {
149 pub fn path_forward_check(
153 &mut self,
154 fn_map: &FxHashMap<DefId, AAResult>,
155 ) -> InterResultNode<'tcx> {
156 let mut inter_return_value =
157 InterResultNode::construct_from_var_node(self.chains.clone(), 0);
158 if self.visit_time >= 1000 {
159 return inter_return_value;
160 }
161 let paths = self.get_all_paths();
163 let body = self.tcx.optimized_mir(self.def_id);
164 let target_name = get_cleaned_def_path_name(self.tcx, self.def_id);
165 let locals = body.local_decls.clone();
167 for (idx, local) in locals.iter().enumerate() {
168 let local_ty = local.ty;
169 let layout = self.visit_ty_and_get_layout(local_ty);
170 self.local_ty.insert(idx, layout);
171 }
172
173 let tmp_chain = self.chains.clone();
175 for (index, (path, constraint)) in paths.iter().enumerate() {
176 self.value_domains.clear();
178 for (arg_index, _) in body.args_iter().enumerate() {
179 let local = arg_index + 1;
180 self.record_value_def(local, SymbolicDef::Param(local));
181 }
182 self.path_constraints = Vec::new();
183 self.chains = tmp_chain.clone();
184 self.set_constraint(constraint);
185 for (i, block_index) in path.iter().enumerate() {
186 if block_index >= &body.basic_blocks.len() {
187 continue;
188 }
189 let next_block = path.get(i + 1).cloned();
190 self.path_analyze_block(
191 &body.basic_blocks[BasicBlock::from_usize(*block_index)].clone(),
192 index,
193 *block_index,
194 next_block,
195 fn_map,
196 );
197 let tem_basic_blocks = &self.safedrop_graph.mop_graph.blocks[*block_index]
198 .scc
199 .nodes
200 .clone();
201 if tem_basic_blocks.len() > 0 {
203 for sub_block in tem_basic_blocks {
204 self.path_analyze_block(
205 &body.basic_blocks[BasicBlock::from_usize(*sub_block)].clone(),
206 index,
207 *block_index,
208 next_block,
209 fn_map,
210 );
211 }
212 }
213 }
214
215 if self.visit_time == 0 {
218 }
220
221 let curr_path_inter_return_value =
223 InterResultNode::construct_from_var_node(self.chains.clone(), 0);
224 inter_return_value.merge(curr_path_inter_return_value);
225 }
226
227 inter_return_value
228 }
229
230 pub fn path_analyze_block(
232 &mut self,
233 block: &BasicBlockData<'tcx>,
234 path_index: usize,
235 bb_index: usize,
236 next_block: Option<usize>,
237 fn_map: &FxHashMap<DefId, AAResult>,
238 ) {
239 for statement in block.statements.iter() {
240 self.path_analyze_statement(statement, path_index);
241 }
242 self.path_analyze_terminator(
243 &block.terminator(),
244 path_index,
245 bb_index,
246 next_block,
247 fn_map,
248 );
249 }
250
251 pub fn get_all_paths(&mut self) -> HashMap<Vec<usize>, Vec<(Place<'tcx>, Place<'tcx>, BinOp)>> {
254 let mut range_analyzer = RangeAnalyzer::<i64>::new(self.tcx, false);
255 let path_constraints_option =
256 range_analyzer.start_path_constraints_analysis_for_defid(self.def_id); let mut path_constraints: HashMap<Vec<usize>, Vec<(_, _, _)>> =
258 if path_constraints_option.is_none() {
259 let mut results = HashMap::new();
260 let paths: Vec<Vec<usize>> = self.safedrop_graph.mop_graph.get_paths();
261 for path in paths {
262 results.insert(path, Vec::new());
263 }
264 results
265 } else {
266 path_constraints_option.unwrap()
267 };
268 self.safedrop_graph.mop_graph.find_scc();
269 if self.visit_time == 0 {
271 let contains_unsafe_blocks = get_all_std_unsafe_callees_block_id(self.tcx, self.def_id);
272 path_constraints.retain(|path, cons| {
273 path.iter()
274 .any(|block_id| contains_unsafe_blocks.contains(block_id))
275 });
276 }
277 path_constraints
278 }
279}
280
281impl<'tcx> BodyVisitor<'tcx> {
284 pub fn path_analyze_statement(&mut self, statement: &Statement<'tcx>, _path_index: usize) {
286 match statement.kind {
288 StatementKind::Assign(box (ref lplace, ref rvalue)) => {
289 self.path_analyze_assign(lplace, rvalue, _path_index);
290 }
291 StatementKind::Intrinsic(box ref intrinsic) => match intrinsic {
292 mir::NonDivergingIntrinsic::CopyNonOverlapping(cno) => {
293 if cno.src.place().is_some() && cno.dst.place().is_some() {
294 let _src_pjc_local =
295 self.handle_proj(true, cno.src.place().unwrap().clone());
296 let _dst_pjc_local =
297 self.handle_proj(true, cno.dst.place().unwrap().clone());
298 }
299 }
300 _ => {}
301 },
302 StatementKind::StorageDead(local) => {}
303 _ => {}
304 }
305 }
306
307 pub fn path_analyze_assign(
309 &mut self,
310 lplace: &Place<'tcx>,
311 rvalue: &Rvalue<'tcx>,
312 path_index: usize,
313 ) {
314 let lpjc_local = self.handle_proj(false, lplace.clone());
315 match rvalue {
316 Rvalue::Use(op) => {
317 if let Some(ana_op) = self.lift_operand(op) {
318 let def = match ana_op {
319 AnaOperand::Local(src) => SymbolicDef::Use(src),
320 AnaOperand::Const(val) => SymbolicDef::Constant(val),
321 };
322 self.record_value_def(lpjc_local, def);
323 }
324 match op {
325 Operand::Move(rplace) => {
326 let rpjc_local = self.handle_proj(true, rplace.clone());
327 self.chains.merge(lpjc_local, rpjc_local);
328 }
329 Operand::Copy(rplace) => {
330 let rpjc_local = self.handle_proj(true, rplace.clone());
331 self.chains.copy_node(lpjc_local, rpjc_local);
332 }
333 _ => {}
334 }
335 }
336 Rvalue::Repeat(op, _const) => match op {
337 Operand::Move(rplace) | Operand::Copy(rplace) => {
338 let _rpjc_local = self.handle_proj(true, rplace.clone());
339 }
340 _ => {}
341 },
342 Rvalue::Ref(_, _, rplace) | Rvalue::RawPtr(_, rplace) => {
343 let rpjc_local = self.handle_proj(true, rplace.clone());
345 self.record_value_def(lpjc_local, SymbolicDef::Ref(rpjc_local));
346 self.chains.point(lpjc_local, rpjc_local);
347 }
348 Rvalue::ThreadLocalRef(_def_id) => {
350 }
352 Rvalue::Cast(cast_kind, op, ty) => {
354 if let Some(AnaOperand::Local(src_idx)) = self.lift_operand(op) {
355 self.record_value_def(
356 lpjc_local,
357 SymbolicDef::Cast(src_idx, format!("{:?}", cast_kind)),
358 );
359 }
360 match op {
361 Operand::Move(rplace) | Operand::Copy(rplace) => {
362 let rpjc_local = self.handle_proj(true, rplace.clone());
363 let r_point_to = self.chains.get_point_to_id(rpjc_local);
364 if r_point_to == rpjc_local {
365 self.chains.merge(lpjc_local, rpjc_local);
366 } else {
367 self.chains.point(lpjc_local, r_point_to);
368 }
369 }
370 _ => {}
371 }
372 }
373 Rvalue::BinaryOp(bin_op, box (op1, op2)) => {
374 if let (Some(ana_op1), Some(ana_op2)) =
376 (self.lift_operand(op1), self.lift_operand(op2))
377 {
378 if *bin_op == BinOp::Offset {
380 if let AnaOperand::Local(base) = ana_op1 {
381 let base_ty = self.get_ptr_pointee_layout(base);
382 self.record_value_def(
383 lpjc_local,
384 SymbolicDef::PtrOffset(*bin_op, base, ana_op2, base_ty),
385 );
386 return;
387 }
388 }
389 let def = match (ana_op1.clone(), ana_op2) {
391 (AnaOperand::Local(l), rhs) => Some(SymbolicDef::Binary(*bin_op, l, rhs)),
392 (AnaOperand::Const(_), AnaOperand::Local(l)) => match bin_op {
393 BinOp::Add
394 | BinOp::Mul
395 | BinOp::BitAnd
396 | BinOp::BitOr
397 | BinOp::BitXor
398 | BinOp::Eq
399 | BinOp::Ne => Some(SymbolicDef::Binary(*bin_op, l, ana_op1)),
400 _ => None,
401 },
402 _ => None,
403 };
404
405 if let Some(d) = def {
406 self.record_value_def(lpjc_local, d);
407 } else if let (AnaOperand::Const(c), AnaOperand::Local(l)) = (
408 self.lift_operand(op1).unwrap(),
409 self.lift_operand(op2).unwrap(),
410 ) {
411 if matches!(bin_op, BinOp::Add | BinOp::Mul | BinOp::Eq) {
412 self.record_value_def(
413 lpjc_local,
414 SymbolicDef::Binary(*bin_op, l, AnaOperand::Const(c)),
415 );
416 }
417 }
418 }
419 }
420 Rvalue::NullaryOp(_null_op) => {
422 }
424 Rvalue::UnaryOp(un_op, op) => {
426 self.record_value_def(lpjc_local, SymbolicDef::UnOp(*un_op));
428 }
429 Rvalue::Discriminant(_place) => {
431 }
433 Rvalue::Aggregate(box agg_kind, op_vec) => match agg_kind {
435 AggregateKind::Array(_ty) => {}
436 AggregateKind::Adt(_adt_def_id, _, _, _, _) => {
437 for (idx, op) in op_vec.into_iter().enumerate() {
438 let (is_const, val) = get_arg_place(op);
439 if is_const {
440 self.chains.insert_field_node(
441 lpjc_local,
442 idx,
443 Some(Ty::new_uint(self.tcx, rustc_middle::ty::UintTy::Usize)),
444 );
445 } else {
446 let node = self.chains.get_var_node_mut(lpjc_local).unwrap();
447 node.field.insert(idx, val);
448 }
449 }
450 }
451 _ => {}
452 },
453 Rvalue::ShallowInitBox(op, _ty) => match op {
454 Operand::Move(rplace) | Operand::Copy(rplace) => {
455 let _rpjc_local = self.handle_proj(true, rplace.clone());
456 }
457 _ => {}
458 },
459 Rvalue::CopyForDeref(p) => {
460 let op = Operand::Copy(p.clone());
461 if let Some(ana_op) = self.lift_operand(&op) {
462 let def = match ana_op {
463 AnaOperand::Local(src) => SymbolicDef::Use(src),
464 AnaOperand::Const(val) => SymbolicDef::Constant(val),
465 };
466 self.record_value_def(lpjc_local, def);
467 }
468 }
469 _ => {}
470 }
471 }
472
473 pub fn get_ptr_pointee_layout(&self, ptr_local: usize) -> PlaceTy<'tcx> {
475 if let Some(node) = self.chains.get_var_node(ptr_local) {
476 if let Some(ty) = node.ty {
477 if is_ptr(ty) || is_ref(ty) {
478 let pointee = get_pointee(ty);
479 return self.visit_ty_and_get_layout(pointee);
480 }
481 }
482 }
483 PlaceTy::Unknown
484 }
485}
486
487impl<'tcx> BodyVisitor<'tcx> {
490 pub fn path_analyze_terminator(
493 &mut self,
494 terminator: &Terminator<'tcx>,
495 path_index: usize,
496 bb_index: usize,
497 next_block: Option<usize>,
498 fn_map: &FxHashMap<DefId, AAResult>,
499 ) {
500 match &terminator.kind {
501 TerminatorKind::Call {
502 func,
503 args,
504 destination: dst_place,
505 target: _,
506 unwind: _,
507 call_source: _,
508 fn_span,
509 } => {
510 if let Operand::Constant(func_constant) = func {
511 if let ty::FnDef(callee_def_id, raw_list) = func_constant.const_.ty().kind() {
512 let mut mapping = FxHashMap::default();
513 self.get_generic_mapping(raw_list.as_slice(), callee_def_id, &mut mapping);
514 rap_debug!(
515 "func {:?}, generic type mapping {:?}",
516 callee_def_id,
517 mapping
518 );
519 self.handle_call(
520 dst_place,
521 callee_def_id,
522 args,
523 path_index,
524 fn_map,
525 *fn_span,
526 mapping,
527 );
528 }
529 }
530 }
531 TerminatorKind::Drop {
532 place,
533 target: _,
534 unwind: _,
535 replace: _,
536 drop: _,
537 async_fut: _,
538 } => {
539 let drop_local = self.handle_proj(false, *place);
541 if !self.chains.set_drop(drop_local) {
542 }
548 }
549 TerminatorKind::SwitchInt { discr, targets } => {
550 if let Some(next_bb) = next_block {
551 self.handle_switch_int(discr, targets, next_bb);
552 }
553 }
554 _ => {}
555 }
556 }
557
558 pub fn get_ty_by_place(&self, p: usize) -> Ty<'tcx> {
560 let body = self.tcx.optimized_mir(self.def_id);
561 let locals = body.local_decls.clone();
562 return locals[Local::from(p)].ty;
563 }
564
565 pub fn update_fields_states(&mut self, inter_result: InterResultNode<'tcx>) {
567 self.chains.init_self_with_inter(inter_result);
568 }
569
570 fn get_generic_mapping(
575 &self,
576 raw_list: &[rustc_middle::ty::GenericArg<'tcx>],
577 def_id: &DefId,
578 generic_mapping: &mut FxHashMap<String, Ty<'tcx>>,
579 ) {
580 let generics = self.tcx.generics_of(def_id);
581 for param in &generics.own_params {
582 if let GenericParamDefKind::Type {
583 has_default: _,
584 synthetic: _,
585 } = param.kind
586 {
587 if let Some(ty) = raw_list.get(param.index as usize) {
588 if let GenericArgKind::Type(actual_ty) = (*ty).kind() {
589 let param_name = param.name.to_string();
590 generic_mapping.insert(param_name, actual_ty);
591 }
592 }
593 }
594 }
595 if generics.own_params.len() == 0 && generics.parent.is_some() {
596 let parent_def_id = generics.parent.unwrap();
597 self.get_generic_mapping(raw_list, &parent_def_id, generic_mapping);
598 }
599 }
600
601 pub fn handle_call(
603 &mut self,
604 dst_place: &Place<'tcx>,
605 def_id: &DefId,
606 args: &Box<[Spanned<Operand<'tcx>>]>,
607 path_index: usize,
608 fn_map: &FxHashMap<DefId, AAResult>,
609 fn_span: Span,
610 generic_mapping: FxHashMap<String, Ty<'tcx>>,
611 ) {
612 let dst_local = self.handle_proj(false, *dst_place);
614 let func_name = get_cleaned_def_path_name(self.tcx, *def_id);
615 let mut call_arg_indices = Vec::new();
616 for arg in args.iter() {
617 if let Some(ana_op) = self.lift_operand(&arg.node) {
618 call_arg_indices.push(ana_op);
619 }
620 }
621 self.record_value_def(dst_local, SymbolicDef::Call(func_name, call_arg_indices));
622
623 if !self.tcx.is_mir_available(def_id) {
624 return;
625 }
626
627 if let Some(fn_result) =
629 parse_unsafe_api(get_cleaned_def_path_name(self.tcx, *def_id).as_str())
630 {
631 self.handle_std_unsafe_call(
632 dst_place,
633 def_id,
634 args,
635 path_index,
636 fn_map,
637 fn_span,
638 fn_result,
639 generic_mapping,
640 );
641 }
642
643 self.handle_offset_call(dst_place, def_id, args);
644
645 self.set_bound(def_id, dst_place, args);
646
647 self.handle_ret_alias(dst_place, def_id, fn_map, args);
649 }
650
651 fn set_bound(
653 &mut self,
654 def_id: &DefId,
655 dst_place: &Place<'tcx>,
656 args: &Box<[Spanned<Operand>]>,
657 ) {
658 if args.len() == 0 || !get_cleaned_def_path_name(self.tcx, *def_id).contains("slice::len") {
659 return;
660 }
661 let d_local = self.handle_proj(false, dst_place.clone());
662 let ptr_local = get_arg_place(&args[0].node).1;
663 let mem_local = self.chains.get_point_to_id(ptr_local);
664 let mem_var = self.chains.get_var_node_mut(mem_local).unwrap();
665 for cis in &mut mem_var.cis.contracts {
666 if let PropertyContract::InBound(cis_ty, len) = cis {
667 *len = CisRangeItem::new_var(d_local);
668 }
669 }
670 }
671
672 pub fn handle_ret_alias(
675 &mut self,
676 dst_place: &Place<'tcx>,
677 def_id: &DefId,
678 fn_map: &FxHashMap<DefId, AAResult>,
679 args: &Box<[Spanned<Operand>]>,
680 ) {
681 let d_local = self.handle_proj(false, dst_place.clone());
682 if let Some(retalias) = fn_map.get(def_id) {
683 for alias_set in retalias.aliases() {
684 let (l, r) = (alias_set.lhs_no, alias_set.rhs_no);
685 let (l_fields, r_fields) =
686 (alias_set.lhs_fields.clone(), alias_set.rhs_fields.clone());
687 let (l_place, r_place) = (
688 if l != 0 {
689 get_arg_place(&args[l - 1].node)
690 } else {
691 (false, d_local)
692 },
693 if r != 0 {
694 get_arg_place(&args[r - 1].node)
695 } else {
696 (false, d_local)
697 },
698 );
699 if l_place.0 {
701 let snd_var = self
702 .chains
703 .find_var_id_with_fields_seq(r_place.1, &r_fields);
704 self.chains
705 .update_value(self.chains.get_point_to_id(snd_var), l_place.1);
706 continue;
707 }
708 if r_place.0 {
710 let fst_var = self
711 .chains
712 .find_var_id_with_fields_seq(l_place.1, &l_fields);
713 self.chains
714 .update_value(self.chains.get_point_to_id(fst_var), r_place.1);
715 continue;
716 }
717 let (fst_var, snd_var) = (
718 self.chains
719 .find_var_id_with_fields_seq(l_place.1, &l_fields),
720 self.chains
721 .find_var_id_with_fields_seq(r_place.1, &r_fields),
722 );
723 let fst_to = self.chains.get_point_to_id(fst_var);
725 let snd_to = self.chains.get_point_to_id(snd_var);
726 let is_fst_point = fst_to != fst_var;
727 let is_snd_point = snd_to != snd_var;
728 let fst_node = self.chains.get_var_node(fst_var).unwrap();
729 let snd_node = self.chains.get_var_node(snd_var).unwrap();
730 let is_fst_ptr = is_ptr(fst_node.ty.unwrap()) || is_ref(fst_node.ty.unwrap());
731 let is_snd_ptr = is_ptr(snd_node.ty.unwrap()) || is_ref(snd_node.ty.unwrap());
732 rap_debug!(
733 "{:?}: {fst_var},{fst_to},{is_fst_ptr} -- {snd_var},{snd_to},{is_snd_ptr}",
734 def_id
735 );
736 match (is_fst_ptr, is_snd_ptr) {
737 (false, true) => {
738 if is_snd_point {
740 self.chains.point(snd_var, fst_var);
741 } else {
742 self.chains.merge(fst_var, snd_to);
743 }
744 }
745 (false, false) => {
746 self.chains.merge(fst_var, snd_var);
748 }
749 (true, true) => {
750 if is_fst_point && is_snd_point {
752 self.chains.merge(fst_to, snd_to);
753 } else if !is_fst_point && is_snd_point {
754 self.chains.point(fst_var, snd_to);
755 } else if is_fst_point && !is_snd_point {
756 self.chains.point(snd_var, fst_to);
757 } else {
758 self.chains.merge(fst_var, snd_var);
759 }
760 }
761 (true, false) => {
762 if is_fst_point {
764 self.chains.point(fst_var, snd_var);
765 } else {
766 self.chains.merge(snd_var, fst_to);
767 }
768 }
769 }
770 }
771 }
772 else {
774 let d_ty = self.chains.get_local_ty_by_place(d_local);
775 if d_ty.is_some() && (is_ptr(d_ty.unwrap()) || is_ref(d_ty.unwrap())) {
776 self.chains
777 .generate_ptr_with_obj_node(d_ty.unwrap(), d_local);
778 }
779 }
780 }
781
782 pub fn compute_function_summary(&self) -> FunctionSummary<'tcx> {
785 if let Some(domain) = self.value_domains.get(&0) {
786 if let Some(def) = &domain.def {
787 let resolved_def = self.resolve_symbolic_def(def, 0); return FunctionSummary::new(resolved_def);
789 }
790 }
791 FunctionSummary::new(None)
792 }
793
794 fn resolve_symbolic_def(
797 &self,
798 def: &SymbolicDef<'tcx>,
799 depth: usize,
800 ) -> Option<SymbolicDef<'tcx>> {
801 if depth > 10 {
803 return None;
804 }
805
806 match def {
807 SymbolicDef::Param(_) | SymbolicDef::Constant(_) => Some(def.clone()),
809 SymbolicDef::Use(local_idx) | SymbolicDef::Ref(local_idx) => {
811 self.resolve_local(*local_idx, depth + 1)
812 }
813 SymbolicDef::Cast(src_idx, _ty) => self.resolve_local(*src_idx, depth + 1),
814 SymbolicDef::Binary(op, lhs_idx, rhs_op) => {
816 let lhs_resolved = self.resolve_local(*lhs_idx, depth + 1)?;
817 let rhs_resolved_op = match rhs_op {
818 AnaOperand::Const(c) => AnaOperand::Const(*c),
819 AnaOperand::Local(l) => match self.resolve_local(*l, depth + 1) {
820 Some(SymbolicDef::Constant(c)) => AnaOperand::Const(c),
821 Some(SymbolicDef::Param(p)) => AnaOperand::Local(p),
822 _ => return None,
823 },
824 };
825 match lhs_resolved {
826 SymbolicDef::Param(p_idx) => {
828 Some(SymbolicDef::Binary(*op, p_idx, rhs_resolved_op))
829 }
830 _ => None,
831 }
832 }
833 _ => None,
834 }
835 }
836
837 fn resolve_local(&self, local_idx: usize, depth: usize) -> Option<SymbolicDef<'tcx>> {
839 if let Some(domain) = self.value_domains.get(&local_idx) {
840 if let Some(def) = &domain.def {
841 return self.resolve_symbolic_def(def, depth);
842 }
843 }
844 None
845 }
846
847 pub fn handle_offset_call(
849 &mut self,
850 dst_place: &Place<'tcx>,
851 def_id: &DefId,
852 args: &Box<[Spanned<Operand<'tcx>>]>,
853 ) {
854 let func_name = get_cleaned_def_path_name(self.tcx, *def_id);
855
856 let is_ptr_op = func_name.contains("ptr") || func_name.contains("slice");
857 if !is_ptr_op {
858 return;
859 }
860
861 let is_byte_sub =
862 func_name.ends_with("::byte_sub") || func_name.ends_with("::wrapping_byte_sub"); let is_sub =
865 is_byte_sub || func_name.ends_with("::sub") || func_name.ends_with("::wrapping_sub");
866
867 let is_byte_add =
868 func_name.ends_with("::byte_add") || func_name.ends_with("::wrapping_byte_add");
869
870 let is_add =
871 is_byte_add || func_name.ends_with("::add") || func_name.ends_with("::wrapping_add");
872
873 let is_byte_offset =
874 func_name.ends_with("::byte_offset") || func_name.ends_with("::wrapping_byte_offset");
875
876 let is_offset = is_byte_offset
877 || func_name.ends_with("::offset")
878 || func_name.ends_with("::wrapping_offset");
879
880 if !is_sub && !is_add && !is_offset {
881 return;
882 }
883 if args.len() < 2 {
884 return;
885 }
886
887 let dst_local = self.handle_proj(false, *dst_place);
888
889 let bin_op = if is_sub {
890 BinOp::Sub
891 } else if is_add {
892 BinOp::Add
893 } else {
894 BinOp::Offset
895 };
896
897 let mut arg_indices = Vec::new();
898 for arg in args.iter() {
899 if let Some(ana_op) = self.lift_operand(&arg.node) {
900 match ana_op {
901 AnaOperand::Local(l) => arg_indices.push(l),
902 AnaOperand::Const(_) => arg_indices.push(0),
903 }
904 } else {
905 arg_indices.push(0);
906 }
907 }
908 let base_op = &args[0].node;
909 let base_local = if let Some(AnaOperand::Local(l)) = self.lift_operand(base_op) {
910 l
911 } else {
912 return;
913 };
914
915 let is_byte_op = is_byte_sub || is_byte_add || is_byte_offset;
917
918 let place_ty = if is_byte_op {
919 PlaceTy::Ty(1, 1)
921 } else {
922 self.get_ptr_pointee_layout(base_local)
924 };
925
926 let summary_def = SymbolicDef::PtrOffset(bin_op, 1, AnaOperand::Local(2), place_ty);
928 let summary = FunctionSummary::new(Some(summary_def));
929
930 self.apply_function_summary(dst_local, &summary, &arg_indices);
932 }
933
934 fn handle_switch_int(
941 &mut self,
942 discr: &Operand<'tcx>,
943 targets: &mir::SwitchTargets,
944 next_bb: usize,
945 ) {
946 let discr_op = match self.lift_operand(discr) {
947 Some(op) => op,
948 None => return,
949 };
950
951 let discr_local_idx = match discr_op {
952 AnaOperand::Local(idx) => idx,
953 _ => return,
954 };
955
956 let mut matched_val = None;
957 for (val, target_bb) in targets.iter() {
958 if target_bb.as_usize() == next_bb {
959 matched_val = Some(val);
960 break;
961 }
962 }
963
964 if let Some(val) = matched_val {
965 self.refine_state_by_condition(discr_local_idx, val);
968
969 let constraint =
971 SymbolicDef::Binary(BinOp::Eq, discr_local_idx, AnaOperand::Const(val));
972 self.path_constraints.push(constraint);
973 } else if targets.otherwise().as_usize() == next_bb {
974 let mut explicit_has_zero = false;
977 let mut explicit_has_one = false;
978
979 for (val, _) in targets.iter() {
980 if val == 0 {
981 explicit_has_zero = true;
982 }
983 if val == 1 {
984 explicit_has_one = true;
985 }
986
987 let constraint =
989 SymbolicDef::Binary(BinOp::Ne, discr_local_idx, AnaOperand::Const(val));
990 self.path_constraints.push(constraint);
991 }
992
993 if explicit_has_zero && !explicit_has_one {
997 self.refine_state_by_condition(discr_local_idx, 1);
999 } else if explicit_has_one && !explicit_has_zero {
1000 self.refine_state_by_condition(discr_local_idx, 0);
1003 }
1004 }
1005 }
1006
1007 fn refine_state_by_condition(&mut self, cond_local: usize, matched_val: u128) {
1009 let domain = match self.value_domains.get(&cond_local).cloned() {
1011 Some(d) => d,
1012 None => return,
1013 };
1014
1015 if let Some(SymbolicDef::Call(func_name, args)) = &domain.def {
1017 self.apply_condition_refinement(func_name, args, matched_val);
1018 }
1019 }
1020
1021 fn apply_condition_refinement(
1023 &mut self,
1024 func_name: &str,
1025 args: &Vec<AnaOperand>,
1026 matched_val: u128,
1027 ) {
1028 if func_name.ends_with("is_aligned") || func_name.contains("is_aligned") {
1030 if let Some(AnaOperand::Local(ptr_local)) = args.get(0) {
1031 let is_aligned_state = if matched_val == 1 {
1033 Some(true)
1034 } else if matched_val == 0 {
1035 Some(false)
1036 } else {
1037 None
1038 };
1039
1040 if let Some(aligned) = is_aligned_state {
1041 self.update_align_state(*ptr_local, aligned);
1044
1045 let root_local = self.find_source_var(*ptr_local);
1048 if root_local != *ptr_local {
1049 self.update_align_state(root_local, aligned);
1050 }
1051 }
1052 }
1053 }
1054 }
1055
1056 pub fn find_source_var(&self, start_local: usize) -> usize {
1058 let mut curr = start_local;
1059 let mut depth = 0;
1060 while depth < 20 {
1061 if let Some(domain) = self.value_domains.get(&curr) {
1062 match &domain.def {
1063 Some(SymbolicDef::Use(src)) | Some(SymbolicDef::Cast(src, _)) => {
1064 curr = *src;
1065 }
1066 _ => return curr,
1067 }
1068 } else {
1069 return curr;
1070 }
1071 depth += 1;
1072 }
1073 curr
1074 }
1075
1076 pub fn apply_function_summary(
1078 &mut self,
1079 dst_local: usize,
1080 summary: &FunctionSummary<'tcx>,
1081 args: &Vec<usize>, ) {
1083 if let Some(def) = &summary.return_def {
1084 if let Some(resolved_def) = self.resolve_summary_def(def, args) {
1086 self.record_value_def(dst_local, resolved_def.clone());
1089
1090 match resolved_def {
1092 SymbolicDef::PtrOffset(_, base_local, _, _) => {
1093 self.chains
1095 .update_from_offset_def(dst_local, base_local, resolved_def);
1096 }
1097 _ => {}
1098 }
1099 }
1100 }
1101 }
1102
1103 fn resolve_summary_def(
1106 &self,
1107 def: &SymbolicDef<'tcx>,
1108 args: &Vec<usize>,
1109 ) -> Option<SymbolicDef<'tcx>> {
1110 match def {
1111 SymbolicDef::Param(idx) => {
1113 if *idx > 0 && idx - 1 < args.len() {
1115 Some(SymbolicDef::Use(args[idx - 1]))
1116 } else {
1117 None
1118 }
1119 }
1120 SymbolicDef::PtrOffset(op, base_param_idx, offset_op, ty) => {
1122 if *base_param_idx > 0 && base_param_idx - 1 < args.len() {
1123 let base_local = args[base_param_idx - 1];
1124
1125 let real_offset = match offset_op {
1127 AnaOperand::Local(idx) => {
1128 if *idx > 0 && idx - 1 < args.len() {
1129 AnaOperand::Local(args[idx - 1])
1130 } else {
1131 AnaOperand::Const(0) }
1133 }
1134 AnaOperand::Const(c) => AnaOperand::Const(*c),
1135 };
1136
1137 Some(SymbolicDef::PtrOffset(
1138 *op,
1139 base_local,
1140 real_offset,
1141 ty.clone(),
1142 ))
1143 } else {
1144 None
1145 }
1146 }
1147 SymbolicDef::Constant(c) => Some(SymbolicDef::Constant(*c)),
1149 _ => None,
1150 }
1151 }
1152
1153 pub fn set_constraint(&mut self, constraint: &Vec<(Place<'tcx>, Place<'tcx>, BinOp)>) {
1158 for (p1, p2, op) in constraint {
1159 let p1_num = self.handle_proj(false, p1.clone());
1160 let p2_num = self.handle_proj(false, p2.clone());
1161 self.chains.insert_patial_op(p1_num, p2_num, op);
1162
1163 if let BinOp::Eq = op {
1164 let maybe_const = self.value_domains.get(&p2_num).and_then(|d| {
1166 if let Some(SymbolicDef::Constant(c)) = d.def {
1167 Some(c)
1168 } else {
1169 None
1170 }
1171 });
1172
1173 if let Some(c) = maybe_const {
1174 self.value_domains
1175 .entry(p1_num)
1176 .and_modify(|d| d.value_constraint = Some(c))
1177 .or_insert(ValueDomain {
1178 def: None,
1179 value_constraint: Some(c),
1180 });
1181 }
1182
1183 let maybe_const_p1 = self.value_domains.get(&p1_num).and_then(|d| {
1185 if let Some(SymbolicDef::Constant(c)) = d.def {
1186 Some(c)
1187 } else {
1188 None
1189 }
1190 });
1191
1192 if let Some(c) = maybe_const_p1 {
1193 self.value_domains
1194 .entry(p2_num)
1195 .and_modify(|d| d.value_constraint = Some(c))
1196 .or_insert(ValueDomain {
1197 def: None,
1198 value_constraint: Some(c),
1199 });
1200 }
1201 }
1202 }
1203 }
1204
1205 pub fn get_layout_by_place_usize(&self, place: usize) -> PlaceTy<'tcx> {
1207 if let Some(ty) = self.chains.get_obj_ty_through_chain(place) {
1208 return self.visit_ty_and_get_layout(ty);
1209 } else {
1210 return PlaceTy::Unknown;
1211 }
1212 }
1213
1214 pub fn visit_ty_and_get_layout(&self, ty: Ty<'tcx>) -> PlaceTy<'tcx> {
1216 match ty.kind() {
1217 TyKind::RawPtr(ty, _)
1218 | TyKind::Ref(_, ty, _)
1219 | TyKind::Slice(ty)
1220 | TyKind::Array(ty, _) => self.visit_ty_and_get_layout(*ty),
1221 TyKind::Param(param_ty) => {
1222 let generic_name = param_ty.name.to_string();
1223 let mut layout_set: HashSet<(usize, usize)> = HashSet::new();
1224 let ty_set = self.generic_map.get(&generic_name.clone());
1225 if ty_set.is_none() {
1226 if self.visit_time == 0 {
1227 rap_warn!(
1228 "Can not get generic type set: {:?}, def_id:{:?}",
1229 generic_name,
1230 self.def_id
1231 );
1232 }
1233 return PlaceTy::GenericTy(generic_name, HashSet::new(), layout_set);
1234 }
1235 for ty in ty_set.unwrap().clone() {
1236 if let PlaceTy::Ty(align, size) = self.visit_ty_and_get_layout(ty) {
1237 layout_set.insert((align, size));
1238 }
1239 }
1240 return PlaceTy::GenericTy(generic_name, ty_set.unwrap().clone(), layout_set);
1241 }
1242 TyKind::Adt(def, _list) => {
1243 if def.is_enum() {
1244 return PlaceTy::Unknown;
1245 } else {
1246 PlaceTy::Unknown
1247 }
1248 }
1249 TyKind::Closure(_, _) => PlaceTy::Unknown,
1250 TyKind::Alias(_, ty) => {
1251 return self.visit_ty_and_get_layout(ty.self_ty());
1253 }
1254 _ => {
1255 let param_env = self.tcx.param_env(self.def_id);
1256 let ty_env = ty::TypingEnv::post_analysis(self.tcx, self.def_id);
1257 let input = PseudoCanonicalInput {
1258 typing_env: ty_env,
1259 value: ty,
1260 };
1261 if let Ok(layout) = self.tcx.layout_of(input) {
1262 let align = layout.align.abi.bytes_usize();
1264 let size = layout.size.bytes() as usize;
1265 return PlaceTy::Ty(align, size);
1266 } else {
1267 PlaceTy::Unknown
1269 }
1270 }
1271 }
1272 }
1273
1274 pub fn handle_binary_op(
1276 &mut self,
1277 first_op: &Operand,
1278 bin_op: &BinOp,
1279 second_op: &Operand,
1280 path_index: usize,
1281 ) {
1282 match bin_op {
1284 BinOp::Offset => {
1285 let _first_place = get_arg_place(first_op);
1286 let _second_place = get_arg_place(second_op);
1287 }
1288 _ => {}
1289 }
1290 }
1291
1292 pub fn handle_proj(&mut self, is_right: bool, place: Place<'tcx>) -> usize {
1295 let mut proj_id = place.local.as_usize();
1296 for proj in place.projection {
1297 match proj {
1298 ProjectionElem::Deref => {
1299 let point_to = self.chains.get_point_to_id(place.local.as_usize());
1300 if point_to == proj_id {
1301 proj_id = self.chains.check_ptr(proj_id);
1302 } else {
1303 proj_id = point_to;
1304 }
1305 }
1306 ProjectionElem::Field(field, ty) => {
1307 proj_id = self
1308 .chains
1309 .get_field_node_id(proj_id, field.as_usize(), Some(ty));
1310 }
1311 _ => {}
1312 }
1313 }
1314 proj_id
1315 }
1316
1317 fn record_value_def(&mut self, local_idx: usize, def: SymbolicDef<'tcx>) {
1319 self.value_domains
1320 .entry(local_idx)
1321 .and_modify(|d| d.def = Some(def.clone()))
1322 .or_insert(ValueDomain {
1323 def: Some(def),
1324 value_constraint: None,
1325 });
1326 }
1327
1328 fn lift_operand(&mut self, op: &Operand<'tcx>) -> Option<AnaOperand> {
1330 match op {
1331 Operand::Copy(place) | Operand::Move(place) => {
1332 Some(AnaOperand::Local(self.handle_proj(true, place.clone())))
1333 }
1334 Operand::Constant(box c) => match c.const_ {
1335 rustc_middle::mir::Const::Ty(_ty, const_value) => {
1336 if let Some(val) = const_value.try_to_target_usize(self.tcx) {
1337 Some(AnaOperand::Const(val as u128))
1338 } else {
1339 None
1340 }
1341 }
1342 rustc_middle::mir::Const::Unevaluated(_unevaluated, _ty) => None,
1343 rustc_middle::mir::Const::Val(const_value, _ty) => {
1344 if let Some(scalar) = const_value.try_to_scalar_int() {
1345 let val = scalar.to_uint(scalar.size());
1346 Some(AnaOperand::Const(val))
1347 } else {
1348 None
1349 }
1350 }
1351 },
1352 }
1353 }
1354
1355 pub fn trace_base_ptr(&self, local: usize) -> Option<(usize, u64)> {
1361 let mut curr = local;
1363 let mut total_offset = 0;
1364 let mut depth = 0;
1365
1366 loop {
1367 if depth > 10 {
1368 return None;
1369 }
1370 depth += 1;
1371
1372 if let Some(domain) = self.value_domains.get(&curr) {
1373 match &domain.def {
1374 Some(SymbolicDef::Binary(BinOp::Offset, base, offset_op)) => {
1375 if let AnaOperand::Const(off) = offset_op {
1376 total_offset += *off as u64;
1377 curr = *base;
1378 } else {
1379 return None;
1380 }
1381 }
1382 Some(SymbolicDef::Use(src)) | Some(SymbolicDef::Cast(src, _)) => {
1383 curr = *src;
1384 }
1385 Some(SymbolicDef::Ref(src)) => {
1386 return Some((*src, total_offset));
1387 }
1388 Some(SymbolicDef::Param(_)) => {
1389 return Some((curr, total_offset));
1390 }
1391 _ => return None,
1392 }
1393 } else {
1394 return None;
1395 }
1396 }
1397 }
1398}
1399
1400impl<'tcx> BodyVisitor<'tcx> {
1403 pub fn display_combined_debug_info(&self) {
1406 const TABLE_WIDTH: usize = 200; println!(
1408 "\n{:=^width$}",
1409 " Combined Analysis State Report ",
1410 width = TABLE_WIDTH
1411 );
1412
1413 let mut all_ids: HashSet<usize> = self.value_domains.keys().cloned().collect();
1415 all_ids.extend(self.chains.variables.keys().cloned());
1416 let mut sorted_ids: Vec<usize> = all_ids.into_iter().collect();
1417 sorted_ids.sort();
1418
1419 if sorted_ids.is_empty() {
1420 println!(" [Empty Analysis State]");
1421 println!("{:=^width$}\n", "", width = TABLE_WIDTH);
1422 return;
1423 }
1424
1425 let sep = format!(
1427 "+{:-^6}+{:-^25}+{:-^8}+{:-^15}+{:-^30}+{:-^25}+{:-^40}+{:-^15}+",
1428 "", "", "", "", "", "", "", ""
1429 );
1430 println!("{}", sep);
1431 println!(
1432 "| {:^6} | {:^25} | {:^8} | {:^15} | {:^30} | {:^25} | {:^40} | {:^15} |",
1433 "ID", "Type", "Pt-To", "Fields", "States", "Graph Offset", "Sym Def", "Sym Val"
1434 );
1435 println!("{}", sep);
1436
1437 for id in sorted_ids {
1439 let (ty_str, pt_str, fields_str, states_str, g_offset_str) =
1441 if let Some(node) = self.chains.variables.get(&id) {
1442 let t = node
1444 .ty
1445 .map(|t| format!("{:?}", t))
1446 .unwrap_or_else(|| "None".to_string());
1447
1448 let p = node
1450 .points_to
1451 .map(|p| format!("_{}", p))
1452 .unwrap_or_else(|| "-".to_string());
1453
1454 let f = if node.field.is_empty() {
1456 "-".to_string()
1457 } else {
1458 let mut fs: Vec<String> = node
1459 .field
1460 .iter()
1461 .map(|(k, v)| format!(".{}->_{}", k, v))
1462 .collect();
1463 fs.sort();
1464 fs.join(", ")
1465 };
1466
1467 let mut s_vec = Vec::new();
1469 match &node.ots.align {
1470 AlignState::Aligned(ty) => {
1471 if let Some(node_ty) = node.ty {
1472 if is_ptr(node_ty) || is_ref(node_ty) {
1473 s_vec.push(format!("Align({:?})", ty));
1474 }
1475 }
1476 }
1477 AlignState::Unaligned(ty) => s_vec.push(format!("Unalign({:?})", ty)),
1478 AlignState::Unknown => {} }
1480 let s = if s_vec.is_empty() {
1481 "-".to_string()
1482 } else {
1483 s_vec.join(", ")
1484 };
1485
1486 let off = if let Some(def) = &node.offset_from {
1488 match def {
1489 SymbolicDef::PtrOffset(op, base, idx, _) => {
1490 let op_str = self.binop_to_symbol(op);
1491 let idx_str = match idx {
1492 AnaOperand::Local(l) => format!("_{}", l),
1493 AnaOperand::Const(c) => format!("{}", c),
1494 };
1495 format!("_{} {} {}", base, op_str, idx_str)
1496 }
1497 SymbolicDef::Binary(BinOp::Offset, base, idx) => {
1498 let idx_str = match idx {
1499 AnaOperand::Local(l) => format!("_{}", l),
1500 AnaOperand::Const(c) => format!("{}", c),
1501 };
1502 format!("_{} + {}", base, idx_str)
1503 }
1504 _ => format!("{:?}", def),
1505 }
1506 } else {
1507 "-".to_string()
1508 };
1509
1510 (t, p, f, s, off)
1511 } else {
1512 (
1513 "-".to_string(),
1514 "-".to_string(),
1515 "-".to_string(),
1516 "-".to_string(),
1517 "-".to_string(),
1518 )
1519 };
1520
1521 let (sym_def_str, sym_val_str) = if let Some(domain) = self.value_domains.get(&id) {
1523 let d = self
1524 .format_symbolic_def(domain.def.as_ref())
1525 .replace('\n', " ");
1526 let v = match domain.value_constraint {
1527 Some(val) => format!("== {}", val),
1528 None => "-".to_string(),
1529 };
1530 (d, v)
1531 } else {
1532 ("-".to_string(), "-".to_string())
1533 };
1534
1535 println!(
1537 "| {:<6} | {:<25} | {:<8} | {:<15} | {:<30} | {:<25} | {:<40} | {:<15} |",
1538 id,
1539 self.safe_truncate(&ty_str, 25),
1540 pt_str,
1541 self.safe_truncate(&fields_str, 15),
1542 self.safe_truncate(&states_str, 30),
1543 self.safe_truncate(&g_offset_str, 25),
1544 self.safe_truncate(&sym_def_str, 40),
1545 self.safe_truncate(&sym_val_str, 15)
1546 );
1547 }
1548
1549 println!("{}", sep);
1550 println!("{:=^width$}\n", " End Report ", width = TABLE_WIDTH);
1551 }
1552
1553 pub fn display_path_constraints(&self) {
1556 const TABLE_WIDTH: usize = 86;
1557 println!(
1558 "\n{:=^width$}",
1559 " Path Constraints Report ",
1560 width = TABLE_WIDTH
1561 );
1562
1563 if self.path_constraints.is_empty() {
1564 println!(" [Empty Path Constraints]");
1565 println!("{:=^width$}\n", "", width = TABLE_WIDTH);
1566 return;
1567 }
1568
1569 println!("| {:^6} | {:^73} |", "Index", "Constraint Expression");
1570 let sep = format!("+{:-^6}+{:-^73}+", "", "");
1571 println!("{}", sep);
1572
1573 for (i, constraint) in self.path_constraints.iter().enumerate() {
1574 let def_raw = self.format_symbolic_def(Some(constraint));
1575 let def_str = def_raw.replace('\n', " ").replace('\t', " ");
1576
1577 println!("| {:<6} | {:<73} |", i, self.safe_truncate(&def_str, 73));
1578 }
1579
1580 println!("{}", sep);
1581 println!("{:=^width$}\n", " End Report ", width = TABLE_WIDTH);
1582 }
1583
1584 pub fn display_value_domains(&self) {
1587 const TABLE_WIDTH: usize = 86;
1588 println!(
1589 "\n{:=^width$}",
1590 " Value Domain Analysis Report ",
1591 width = TABLE_WIDTH
1592 );
1593
1594 let mut locals: Vec<&usize> = self.value_domains.keys().collect();
1595 locals.sort();
1596
1597 if locals.is_empty() {
1598 println!(" [Empty Value Domains]");
1599 println!("{:=^width$}\n", "", width = TABLE_WIDTH);
1600 return;
1601 }
1602
1603 let print_row = |c1: &str, c2: &str, c3: &str, is_header: bool| {
1604 if is_header {
1605 println!("| {:^6} | {:^40} | {:^15} |", c1, c2, c3);
1606 } else {
1607 println!(
1608 "| {:<6} | {:<40} | {:<15} |",
1609 c1,
1610 self.safe_truncate(c2, 40),
1611 c3,
1612 );
1613 }
1614 };
1615
1616 let sep = format!("+{:-^6}+{:-^40}+{:-^15}+", "", "", "");
1617 println!("{}", sep);
1618 print_row("Local", "Symbolic Definition", "Constraint", true);
1619 println!("{}", sep);
1620
1621 for local_idx in locals {
1622 let domain = &self.value_domains[local_idx];
1623
1624 let local_str = format!("_{}", local_idx);
1625
1626 let def_raw = self.format_symbolic_def(domain.def.as_ref());
1627 let def_str = def_raw.replace('\n', " ").replace('\t', " ");
1628
1629 let constraint_str = match domain.value_constraint {
1630 Some(v) => format!("== {}", v),
1631 None => String::from("-"),
1632 };
1633
1634 print_row(&local_str, &def_str, &constraint_str, false);
1635 }
1636
1637 println!("{}", sep);
1638 println!("{:=^width$}\n", " End Report ", width = TABLE_WIDTH);
1639 }
1640
1641 fn safe_truncate(&self, s: &str, max_width: usize) -> String {
1644 let char_count = s.chars().count();
1645 if char_count <= max_width {
1646 return s.to_string();
1647 }
1648 let truncated: String = s.chars().take(max_width - 2).collect();
1649 format!("{}..", truncated)
1650 }
1651
1652 fn format_symbolic_def(&self, def: Option<&SymbolicDef<'tcx>>) -> String {
1654 match def {
1655 None => String::from("Top (Unknown)"),
1656 Some(d) => match d {
1657 SymbolicDef::Param(idx) => format!("Param(arg_{})", idx),
1658 SymbolicDef::Constant(val) => format!("Const({})", val),
1659 SymbolicDef::Use(idx) => format!("Copy(_{})", idx),
1660 SymbolicDef::Ref(idx) => format!("&_{}", idx),
1661 SymbolicDef::Cast(idx, ty_str) => format!("_{} as {}", idx, ty_str),
1662 SymbolicDef::UnOp(op) => format!("{:?}(op)", op), SymbolicDef::Binary(op, lhs, rhs) => {
1664 let op_str = self.binop_to_symbol(op);
1665 let rhs_str = match rhs {
1666 AnaOperand::Local(i) => format!("_{}", i),
1667 AnaOperand::Const(c) => format!("{}", c),
1668 };
1669 format!("_{} {} {}", lhs, op_str, rhs_str)
1670 }
1671 SymbolicDef::Call(func_name, args) => {
1672 let args_str: Vec<String> = args.iter().map(|a| format!("_{:?}", a)).collect();
1673 format!("{}({})", func_name, args_str.join(", "))
1674 }
1675 SymbolicDef::PtrOffset(binop, ptr, offset, size) => {
1676 let op_str = self.binop_to_symbol(&binop);
1677 format!("ptr_offset({}, _{}, {:?}, {:?})", op_str, ptr, offset, size)
1678 }
1679 },
1680 }
1681 }
1682
1683 fn binop_to_symbol(&self, op: &BinOp) -> &'static str {
1685 match op {
1686 BinOp::Add => "+",
1687 BinOp::Sub => "-",
1688 BinOp::Mul => "*",
1689 BinOp::Div => "/",
1690 BinOp::Rem => "%",
1691 BinOp::BitXor => "^",
1692 BinOp::BitAnd => "&",
1693 BinOp::BitOr => "|",
1694 BinOp::Shl => "<<",
1695 BinOp::Shr => ">>",
1696 BinOp::Eq => "==",
1697 BinOp::Ne => "!=",
1698 BinOp::Lt => "<",
1699 BinOp::Le => "<=",
1700 BinOp::Gt => ">",
1701 BinOp::Ge => ">=",
1702 BinOp::Offset => "ptr_offset",
1703 _ => "",
1704 }
1705 }
1706
1707 pub fn display_path_dot(&self, path: &[usize]) {
1709 let base_name = get_cleaned_def_path_name(self.tcx, self.def_id);
1710 let path_suffix = path
1711 .iter()
1712 .map(|b| b.to_string())
1713 .collect::<Vec<String>>()
1714 .join("_");
1715
1716 let name = format!("{}_path_{}", base_name, path_suffix);
1717 let dot_string = self.chains.to_dot_graph();
1718 render_dot_string(name, dot_string);
1719 }
1720}