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