diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/delayed_ub_visitor.rs b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/initial_target_visitor.rs similarity index 81% rename from kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/delayed_ub_visitor.rs rename to kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/initial_target_visitor.rs index cde02c8b82a8..fd48952ef366 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/delayed_ub_visitor.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/initial_target_visitor.rs @@ -18,22 +18,22 @@ use crate::kani_middle::transform::check_uninit::ty_layout::tys_layout_compatibl /// Visitor that finds initial analysis targets for delayed UB instrumentation. For our purposes, /// analysis targets are *pointers* to places reading and writing from which should be tracked. -pub struct DelayedUbVisitor { +pub struct InitialTargetVisitor { body: Body, - delayed_ub_targets: Vec, + targets: Vec, } -impl DelayedUbVisitor { +impl InitialTargetVisitor { pub fn new(body: Body) -> Self { - Self { body, delayed_ub_targets: vec![] } + Self { body, targets: vec![] } } pub fn into_targets(self) -> Vec { - self.delayed_ub_targets + self.targets } } -impl MirVisitor for DelayedUbVisitor { +impl MirVisitor for InitialTargetVisitor { fn visit_rvalue(&mut self, rvalue: &Rvalue, location: Location) { if let Rvalue::Cast(kind, operand, ty) = rvalue { let operand_ty = operand.ty(self.body.locals()).unwrap(); @@ -47,13 +47,11 @@ impl MirVisitor for DelayedUbVisitor { match operand { Operand::Copy(place) | Operand::Move(place) => { if !tys_layout_compatible(from_ty, to_ty) { - self.delayed_ub_targets.push(place.clone()); + self.targets.push(place.clone()); } } Operand::Constant(_) => { - unimplemented!( - "Delayed UB in presence of constants is not yet supported." - ) + unreachable!("cannot be a constant") } } } @@ -70,10 +68,10 @@ impl MirVisitor for DelayedUbVisitor { { match ©.dst { Operand::Copy(place) | Operand::Move(place) => { - self.delayed_ub_targets.push(place.clone()); + self.targets.push(place.clone()); } Operand::Constant(_) => { - unimplemented!("Delayed UB in presence of constants is not yet supported.") + unreachable!("cannot be a constant") } } } @@ -103,11 +101,9 @@ impl MirVisitor for DelayedUbVisitor { // Here, `dst` is the second argument. match &args[1] { Operand::Copy(place) | Operand::Move(place) => { - self.delayed_ub_targets.push(place.clone()); + self.targets.push(place.clone()); } - Operand::Constant(_) => unimplemented!( - "Delayed UB in presence of constants is not yet supported." - ), + Operand::Constant(_) => unreachable!("cannot be a constant"), } } "volatile_copy_memory" | "volatile_copy_nonoverlapping_memory" => { @@ -127,11 +123,9 @@ impl MirVisitor for DelayedUbVisitor { // Here, `dst` is the first argument. match &args[0] { Operand::Copy(place) | Operand::Move(place) => { - self.delayed_ub_targets.push(place.clone()); + self.targets.push(place.clone()); } - Operand::Constant(_) => unimplemented!( - "Delayed UB in presence of constants is not yet supported." - ), + Operand::Constant(_) => unreachable!("cannot be a constant"), } } _ => {} diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/instrumentation_visitor.rs b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/instrumentation_visitor.rs index f8d60debca10..2fb43172e05e 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/instrumentation_visitor.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/instrumentation_visitor.rs @@ -5,15 +5,21 @@ //! UB. In practice, that means collecting all instructions where the place is featured. use crate::kani_middle::transform::body::{InsertPosition, MutableBody, SourceInstruction}; +use crate::kani_middle::transform::check_uninit::delayed_ub::points_to_graph::{ + GlobalMemLoc, PointsToGraph, +}; use crate::kani_middle::transform::check_uninit::relevant_instruction::{ InitRelevantInstruction, MemoryInitOp, }; use crate::kani_middle::transform::check_uninit::TargetFinder; - +use rustc_hir::def_id::DefId as InternalDefId; +use rustc_middle::ty::TyCtxt; +use rustc_smir::rustc_internal; use stable_mir::mir::visit::{Location, PlaceContext}; -use stable_mir::mir::{BasicBlockIdx, MirVisitor, Operand, Place, ProjectionElem, Statement}; +use stable_mir::mir::{BasicBlockIdx, MirVisitor, Operand, Place, Statement}; +use std::collections::HashSet; -pub struct DelayedUbTargetVisitor<'a> { +pub struct InstrumentationVisitor<'a, 'tcx> { /// Whether we should skip the next instruction, since it might've been instrumented already. /// When we instrument an instruction, we partition the basic block, and the instruction that /// may trigger UB becomes the first instruction of the basic block, which we need to skip @@ -23,29 +29,46 @@ pub struct DelayedUbTargetVisitor<'a> { current: SourceInstruction, /// The target instruction that should be verified. pub target: Option, - /// The list of places we should be looking for, ignoring others. - place_filter: &'a [Place], + /// Aliasing analysis data. + points_to: &'a PointsToGraph<'tcx>, + /// The list of places we should be looking for, ignoring others + analysis_targets: &'a HashSet>, + current_def_id: InternalDefId, + tcx: TyCtxt<'tcx>, } -impl<'a> TargetFinder for DelayedUbTargetVisitor<'a> { +impl<'a, 'tcx> TargetFinder for InstrumentationVisitor<'a, 'tcx> { fn find_next( + &mut self, body: &MutableBody, bb: BasicBlockIdx, skip_first: bool, - place_filter: &[Place], ) -> Option { - let mut visitor = DelayedUbTargetVisitor { - skip_next: skip_first, - current: SourceInstruction::Statement { idx: 0, bb }, - target: None, - place_filter, - }; - visitor.visit_basic_block(&body.blocks()[bb]); - visitor.target + self.skip_next = skip_first; + self.current = SourceInstruction::Statement { idx: 0, bb }; + self.target = None; + self.visit_basic_block(&body.blocks()[bb]); + self.target.clone() } } -impl<'a> DelayedUbTargetVisitor<'a> { +impl<'a, 'tcx> InstrumentationVisitor<'a, 'tcx> { + pub fn new( + points_to: &'a PointsToGraph<'tcx>, + analysis_targets: &'a HashSet>, + current_def_id: InternalDefId, + tcx: TyCtxt<'tcx>, + ) -> Self { + Self { + skip_next: false, + current: SourceInstruction::Statement { idx: 0, bb: 0 }, + target: None, + points_to, + analysis_targets, + current_def_id, + tcx, + } + } fn push_target(&mut self, source_op: MemoryInitOp) { let target = self.target.get_or_insert_with(|| InitRelevantInstruction { source: self.current, @@ -56,7 +79,7 @@ impl<'a> DelayedUbTargetVisitor<'a> { } } -impl<'a> MirVisitor for DelayedUbTargetVisitor<'a> { +impl<'a, 'tcx> MirVisitor for InstrumentationVisitor<'a, 'tcx> { fn visit_statement(&mut self, stmt: &Statement, location: Location) { if self.skip_next { self.skip_next = false; @@ -70,31 +93,24 @@ impl<'a> MirVisitor for DelayedUbTargetVisitor<'a> { } fn visit_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) { - // Match the place by its local. + // Match the place by whatever it is pointing to and find an intersection with the targets. if self - .place_filter - .iter() - .any(|instrumented_place| instrumented_place.local == place.local) + .points_to + .follow_from_place(rustc_internal::internal(self.tcx, place), self.current_def_id) + .intersection(&self.analysis_targets) + .count() + != 0 { - let deref_projection_detected = place - .projection - .iter() - .any(|projection_elem| matches!(projection_elem, ProjectionElem::Deref)); - // We should only track the place itself, not whatever it gets dereferenced to. - if !deref_projection_detected { - // If we are mutating the place, initialize it. - if ptx.is_mutating() { - self.push_target(MemoryInitOp::SetRef { - operand: Operand::Copy(place.clone()), - value: true, - position: InsertPosition::After, - }); - } else { - // Otherwise, check its initialization. - self.push_target(MemoryInitOp::CheckRef { - operand: Operand::Copy(place.clone()), - }); - } + // If we are mutating the place, initialize it. + if ptx.is_mutating() { + self.push_target(MemoryInitOp::SetRef { + operand: Operand::Copy(place.clone()), + value: true, + position: InsertPosition::After, + }); + } else { + // Otherwise, check its initialization. + self.push_target(MemoryInitOp::CheckRef { operand: Operand::Copy(place.clone()) }); } } self.super_place(place, ptx, location) diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/mod.rs b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/mod.rs index 499fe95e6b15..3ab1c2b69d2e 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/mod.rs @@ -16,19 +16,20 @@ use crate::kani_middle::transform::BodyTransformation; use crate::kani_middle::transform::GlobalPass; use crate::kani_middle::transform::TransformationResult; use crate::kani_queries::QueryDb; -use delayed_ub_visitor::DelayedUbVisitor; -use instrumentation_visitor::DelayedUbTargetVisitor; +use initial_target_visitor::InitialTargetVisitor; +use instrumentation_visitor::InstrumentationVisitor; use points_to_analysis::PointsToAnalysis; -use points_to_graph::PlaceOrAlloc; +use points_to_graph::LocalMemLoc; +use points_to_graph::PointsToGraph; use rustc_middle::ty::TyCtxt; +use rustc_mir_dataflow::JoinSemiLattice; use rustc_smir::rustc_internal; use stable_mir::mir::mono::{Instance, MonoItem}; use stable_mir::mir::MirVisitor; -use stable_mir::mir::Place; use stable_mir::ty::FnDef; use stable_mir::CrateDef; -mod delayed_ub_visitor; +mod initial_target_visitor; mod instrumentation_visitor; mod points_to_analysis; mod points_to_graph; @@ -66,18 +67,19 @@ impl GlobalPass for DelayedUbPass { .flat_map(|instance| { let def_id = rustc_internal::internal(tcx, instance.def.def_id()); let body = instance.body().unwrap(); - let mut visitor = DelayedUbVisitor::new(body.clone()); + let mut visitor = InitialTargetVisitor::new(body.clone()); visitor.visit_body(&body); // Convert all places into the format of aliasing graph for later comparison. visitor.into_targets().into_iter().map(move |place| { - PlaceOrAlloc::Place(rustc_internal::internal(tcx, place)).with_def_id(def_id) + LocalMemLoc::Place(rustc_internal::internal(tcx, place)).with_def_id(def_id) }) }) .collect(); // Only perform this analysis if there is something to analyze. if !targets.is_empty() { - let mut places_need_instrumentation = HashSet::new(); + let mut analysis_targets = HashSet::new(); + let mut global_points_to_graph = PointsToGraph::empty(); // Analyze aliasing for every harness. for entry_item in starting_items { // Convert each entry function into instance, if possible. @@ -105,8 +107,9 @@ impl GlobalPass for DelayedUbPass { ); // Since analysis targets are *pointers*, need to get its followers for instrumentation. for target in targets.iter() { - places_need_instrumentation.extend(results.pointees_of(target)); + analysis_targets.extend(results.pointees_of(target)); } + global_points_to_graph.join(&results); } } @@ -118,24 +121,16 @@ impl GlobalPass for DelayedUbPass { mem_init_fn_cache: &mut self.mem_init_fn_cache, }; // Retrieve the body with all local instrumentation passes applied. - let new_body = MutableBody::from(transformer.body(tcx, instance)); - // Retrieve all places we need to instrument in the appropriate format. - let place_filter: Vec = places_need_instrumentation - .iter() - .filter(|place| { - // Make sure only places from the current instance are included. - place.has_def_id(internal_def_id) - }) - .filter_map(|global_place_or_alloc| { - match global_place_or_alloc.without_def_id() { - PlaceOrAlloc::Alloc(_) => None, // Allocations cannot be read directly, so we need not worry about them. - PlaceOrAlloc::Place(place) => Some(rustc_internal::stable(place)), // Convert back to StableMIR. - } - }) - .collect(); - // Finally, instrument. - let (instrumentation_added, body) = instrumenter - .instrument::(tcx, new_body, instance, &place_filter); + let body = MutableBody::from(transformer.body(tcx, instance)); + // Instrument for delayed UB. + let target_finder = InstrumentationVisitor::new( + &global_points_to_graph, + &analysis_targets, + internal_def_id, + tcx, + ); + let (instrumentation_added, body) = + instrumenter.instrument(tcx, body, instance, target_finder); // If some instrumentation has been performed, update the cached body in the local transformer. if instrumentation_added { transformer.cache.entry(instance).and_modify(|transformation_result| { diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/points_to_analysis.rs b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/points_to_analysis.rs index 48590c9fcea7..9cc1d591debc 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/points_to_analysis.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/points_to_analysis.rs @@ -7,9 +7,7 @@ use crate::kani_middle::{ reachability::CallGraph, transform::{ - check_uninit::delayed_ub::points_to_graph::{ - GlobalPlaceOrAlloc, PlaceOrAlloc, PointsToGraph, - }, + check_uninit::delayed_ub::points_to_graph::{GlobalMemLoc, LocalMemLoc, PointsToGraph}, internal_mir::RustcInternalMir, BodyTransformation, }, @@ -32,16 +30,16 @@ use std::collections::HashSet; /// Main points-to analysis object. Since this one will be created anew for each instance analysis, /// we need to make sure big data structures are not copied unnecessarily. -pub struct PointsToAnalysis<'a, 'b, 'c, 'tcx> { +pub struct PointsToAnalysis<'a, 'tcx> { def_id: DefId, body: Body<'tcx>, tcx: TyCtxt<'tcx>, call_graph: &'a CallGraph, - instances: &'b Vec, - transformer: &'c mut BodyTransformation, + instances: &'a Vec, + transformer: &'a mut BodyTransformation, } -impl<'a, 'b, 'c, 'tcx> PointsToAnalysis<'a, 'b, 'c, 'tcx> { +impl<'a, 'tcx> PointsToAnalysis<'a, 'tcx> { /// Perform the analysis on a body, outputting the graph containing aliasing information of the /// body itself and any body reachable from it. pub fn run( @@ -49,13 +47,13 @@ impl<'a, 'b, 'c, 'tcx> PointsToAnalysis<'a, 'b, 'c, 'tcx> { tcx: TyCtxt<'tcx>, def_id: DefId, call_graph: &'a CallGraph, - instances: &'b Vec, - transformer: &'c mut BodyTransformation, + instances: &'a Vec, + transformer: &'a mut BodyTransformation, ) -> PointsToGraph<'tcx> { let analysis = Self { body: body.clone(), tcx, def_id, call_graph, instances, transformer }; let mut cursor = analysis.into_engine(tcx, &body).iterate_to_fixpoint().into_results_cursor(&body); - let mut results = PointsToGraph::new(&body, def_id); + let mut results = PointsToGraph::from_body(&body, def_id); for (idx, _) in body.basic_blocks.iter().enumerate() { cursor.seek_to_block_end(idx.into()); results.join(cursor.get()); @@ -64,7 +62,7 @@ impl<'a, 'b, 'c, 'tcx> PointsToAnalysis<'a, 'b, 'c, 'tcx> { } } -impl<'a, 'b, 'c, 'tcx> AnalysisDomain<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tcx> { +impl<'a, 'tcx> AnalysisDomain<'tcx> for PointsToAnalysis<'a, 'tcx> { type Domain = PointsToGraph<'tcx>; type Direction = Forward; @@ -73,7 +71,7 @@ impl<'a, 'b, 'c, 'tcx> AnalysisDomain<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tc /// Dataflow state instantiated at the beginning of each basic block. fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain { - PointsToGraph::new(body, self.def_id) + PointsToGraph::from_body(body, self.def_id) } /// Dataflow state instantiated at the entry into the body, for us this coincides with the @@ -86,7 +84,7 @@ impl<'a, 'b, 'c, 'tcx> AnalysisDomain<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tc } } -impl<'a, 'b, 'c, 'tcx> Analysis<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tcx> { +impl<'a, 'tcx> Analysis<'tcx> for PointsToAnalysis<'a, 'tcx> { /// Update current dataflow state based on the information we can infer from the given /// statement. fn apply_statement_effect( @@ -108,7 +106,7 @@ impl<'a, 'b, 'c, 'tcx> Analysis<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tcx> { Rvalue::Use(operand) | Rvalue::ShallowInitBox(operand, _) | Rvalue::Cast(_, operand, _) - | Rvalue::Repeat(operand, ..) => self.find_operand_pointees(state, operand), + | Rvalue::Repeat(operand, ..) => self.follow_rvalue(state, operand), Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { // Here, a reference to a place is created, which leaves the place // unchanged. @@ -120,14 +118,7 @@ impl<'a, 'b, 'c, 'tcx> Analysis<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tcx> { // Offsetting a pointer should still be within the boundaries of the // same object, so we can simply use the operand unchanged. let (ptr, _) = *operands.clone(); - match ptr { - Operand::Copy(place) | Operand::Move(place) => { - state.follow(&state.follow_from_place(place, self.def_id)) - } - Operand::Constant(_) => { - unreachable!("Pointer in offset should not be a constant.") - } - } + self.follow_rvalue(state, ptr) } BinOp::Add | BinOp::AddUnchecked @@ -151,18 +142,8 @@ impl<'a, 'b, 'c, 'tcx> Analysis<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tcx> { // track them. We assume that even shifted addresses will be within // the same original object. let (l_operand, r_operand) = *operands.clone(); - let l_operand_set = match l_operand { - Operand::Copy(place) | Operand::Move(place) => { - state.follow(&state.follow_from_place(place, self.def_id)) - } - Operand::Constant(_) => HashSet::new(), - }; - let r_operand_set = match r_operand { - Operand::Copy(place) | Operand::Move(place) => { - state.follow(&state.follow_from_place(place, self.def_id)) - } - Operand::Constant(_) => HashSet::new(), - }; + let l_operand_set = self.follow_rvalue(state, l_operand); + let r_operand_set = self.follow_rvalue(state, r_operand); l_operand_set.union(&r_operand_set).cloned().collect() } BinOp::Eq @@ -179,12 +160,7 @@ impl<'a, 'b, 'c, 'tcx> Analysis<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tcx> { } Rvalue::UnaryOp(_, operand) => { // The same story from BinOp applies here, too. Need to track those things. - match operand { - Operand::Copy(place) | Operand::Move(place) => { - state.follow(&state.follow_from_place(place, self.def_id)) - } - Operand::Constant(_) => HashSet::new(), - } + self.follow_rvalue(state, operand) } Rvalue::Len(..) | Rvalue::NullaryOp(..) | Rvalue::Discriminant(..) => { // All of those should yield a constant. @@ -192,31 +168,16 @@ impl<'a, 'b, 'c, 'tcx> Analysis<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tcx> { } Rvalue::Aggregate(_, operands) => { // Conservatively find a union of all places mentioned here. - let places = operands + operands .into_iter() - .filter_map(|operand| { - match operand { - Operand::Copy(place) | Operand::Move(place) => { - // Simply add a constant here. - let place_or_alloc: PlaceOrAlloc = place.into(); - Some(place_or_alloc.with_def_id(self.def_id)) - } - Operand::Constant(_) => { - // This is a constant, the aliasing state is empty - None - } - } - }) - .collect(); - state.follow(&places) + .flat_map(|operand| self.follow_rvalue(state, operand)) + .collect() } Rvalue::CopyForDeref(place) => { // Use a place unchanged. state.follow(&state.follow_from_place(place, self.def_id)) } - Rvalue::ThreadLocalRef(_) => { - unimplemented!("Delayed UB analysis in Kani does not support statics.") - } + Rvalue::ThreadLocalRef(def_id) => HashSet::from([GlobalMemLoc::Global(def_id)]), }; // Create an edge between all places which could be lvalue and all places rvalue // could be pointing to. @@ -281,29 +242,12 @@ impl<'a, 'b, 'c, 'tcx> Analysis<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tcx> { args[0].node.ty(&self.body, self.tcx).kind(), TyKind::RawPtr(_, Mutability::Mut) )); - let src_set = match args[2].node { - Operand::Copy(place) | Operand::Move(place) => { - state.follow_from_place(place, self.def_id) - } - Operand::Constant(_) => HashSet::new(), - }; - let dst_set = match args[0].node { - Operand::Copy(place) | Operand::Move(place) => state - .follow_from_place( - place.project_deeper( - &[ProjectionElem::Deref], - self.tcx, - ), - self.def_id, - ), - Operand::Constant(_) => { - unreachable!("pointer cannot be a constant") - } - }; + let src_set = self.follow_rvalue(state, args[2].node.clone()); + let dst_set = self.follow_deref(state, args[0].node.clone()); let destination_set = state.follow_from_place(*destination, self.def_id); state.extend(&destination_set, &state.follow(&dst_set)); - state.extend(&dst_set, &state.follow(&src_set)); + state.extend(&dst_set, &src_set); } // All `atomic_load` intrinsics take `src` as an argument. // This is equivalent to `destination = *src`. @@ -317,19 +261,7 @@ impl<'a, 'b, 'c, 'tcx> Analysis<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tcx> { args[0].node.ty(&self.body, self.tcx).kind(), TyKind::RawPtr(_, Mutability::Not) )); - let src_set = match args[0].node { - Operand::Copy(place) | Operand::Move(place) => state - .follow_from_place( - place.project_deeper( - &[ProjectionElem::Deref], - self.tcx, - ), - self.def_id, - ), - Operand::Constant(_) => { - unreachable!("pointer cannot be a constant") - } - }; + let src_set = self.follow_deref(state, args[0].node.clone()); let destination_set = state.follow_from_place(*destination, self.def_id); state.extend(&destination_set, &state.follow(&src_set)); @@ -346,26 +278,9 @@ impl<'a, 'b, 'c, 'tcx> Analysis<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tcx> { args[0].node.ty(&self.body, self.tcx).kind(), TyKind::RawPtr(_, Mutability::Mut) )); - let dst_set = match args[0].node { - Operand::Copy(place) | Operand::Move(place) => state - .follow_from_place( - place.project_deeper( - &[ProjectionElem::Deref], - self.tcx, - ), - self.def_id, - ), - Operand::Constant(_) => { - unreachable!("pointer cannot be a constant") - } - }; - let val_set = match args[1].node { - Operand::Copy(place) | Operand::Move(place) => { - state.follow_from_place(place, self.def_id) - } - Operand::Constant(_) => HashSet::new(), - }; - state.extend(&dst_set, &state.follow(&val_set)); + let dst_set = self.follow_deref(state, args[0].node.clone()); + let val_set = self.follow_rvalue(state, args[1].node.clone()); + state.extend(&dst_set, &val_set); } // All other `atomic` intrinsics take `dst, src` as arguments. // This is equivalent to `destination = *dst; *dst = src`. @@ -379,29 +294,12 @@ impl<'a, 'b, 'c, 'tcx> Analysis<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tcx> { args[0].node.ty(&self.body, self.tcx).kind(), TyKind::RawPtr(_, Mutability::Mut) )); - let src_set = match args[1].node { - Operand::Copy(place) | Operand::Move(place) => { - state.follow_from_place(place, self.def_id) - } - Operand::Constant(_) => HashSet::new(), - }; - let dst_set = match args[0].node { - Operand::Copy(place) | Operand::Move(place) => state - .follow_from_place( - place.project_deeper( - &[ProjectionElem::Deref], - self.tcx, - ), - self.def_id, - ), - Operand::Constant(_) => { - unreachable!("pointer cannot be a constant") - } - }; + let src_set = self.follow_rvalue(state, args[1].node.clone()); + let dst_set = self.follow_deref(state, args[0].node.clone()); let destination_set = state.follow_from_place(*destination, self.def_id); state.extend(&destination_set, &state.follow(&dst_set)); - state.extend(&dst_set, &state.follow(&src_set)); + state.extend(&dst_set, &src_set); } }; } @@ -452,15 +350,7 @@ impl<'a, 'b, 'c, 'tcx> Analysis<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tcx> { )); // Destination of the return value. let lvalue_set = state.follow_from_place(*destination, self.def_id); - let rvalue_set = match args[0].node { - // Need to add an additional dereference, since the value is loaded, not pointer. - Operand::Copy(place) | Operand::Move(place) => state - .follow_from_place( - place.project_deeper(&[ProjectionElem::Deref], self.tcx), - self.def_id, - ), - Operand::Constant(_) => HashSet::new(), - }; + let rvalue_set = self.follow_deref(state, args[0].node.clone()); state.extend(&lvalue_set, &state.follow(&rvalue_set)); } // Semantically equivalent *a = b. @@ -474,24 +364,9 @@ impl<'a, 'b, 'c, 'tcx> Analysis<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tcx> { args[0].node.ty(&self.body, self.tcx).kind(), TyKind::RawPtr(_, Mutability::Mut) )); - let lvalue_set = match args[0].node { - // Need to add an additional dereference, since storing into the dereference. - Operand::Copy(place) | Operand::Move(place) => state - .follow_from_place( - place.project_deeper(&[ProjectionElem::Deref], self.tcx), - self.def_id, - ), - Operand::Constant(_) => { - unreachable!("pointer should not be a constant") - } - }; - let rvalue_set = match args[1].node { - Operand::Copy(place) | Operand::Move(place) => { - state.follow_from_place(place, self.def_id) - } - Operand::Constant(_) => HashSet::new(), - }; - state.extend(&lvalue_set, &state.follow(&rvalue_set)); + let lvalue_set = self.follow_deref(state, args[0].node.clone()); + let rvalue_set = self.follow_rvalue(state, args[1].node.clone()); + state.extend(&lvalue_set, &rvalue_set); } _ => { // TODO: go through the list of intrinsics and make sure none have @@ -514,7 +389,7 @@ impl<'a, 'b, 'c, 'tcx> Analysis<'tcx> for PointsToAnalysis<'a, 'b, 'c, 'tcx> { "alloc::alloc::__rust_alloc" | "alloc::alloc::__rust_alloc_zeroed" => { let lvalue_set = state.follow_from_place(*destination, self.def_id); let rvalue_set = HashSet::from([ - PlaceOrAlloc::new_alloc().with_def_id(self.def_id) + LocalMemLoc::new_alloc().with_def_id(self.def_id) ]); state.extend(&lvalue_set, &rvalue_set); } @@ -557,7 +432,7 @@ fn try_resolve_instance<'tcx>( } } -impl<'a, 'b, 'c, 'tcx> PointsToAnalysis<'a, 'b, 'c, 'tcx> { +impl<'a, 'tcx> PointsToAnalysis<'a, 'tcx> { // Update the analysis state according to the operation, which is semantically equivalent to `*to = *from`. fn apply_copy_effect( &self, @@ -565,41 +440,51 @@ impl<'a, 'b, 'c, 'tcx> PointsToAnalysis<'a, 'b, 'c, 'tcx> { from: Operand<'tcx>, to: Operand<'tcx>, ) { - let lvalue_set = match to { - Operand::Copy(place) | Operand::Move(place) => state.follow_from_place( - place.project_deeper(&[ProjectionElem::Deref], self.tcx), - self.def_id, - ), - Operand::Constant(_) => { - unreachable!("pointer cannot be a constant") - } - }; - let rvalue_set = match from { - Operand::Copy(place) | Operand::Move(place) => state.follow_from_place( - place.project_deeper(&[ProjectionElem::Deref], self.tcx), - self.def_id, - ), - Operand::Constant(_) => { - unreachable!("pointer cannot be a constant") - } - }; + let lvalue_set = self.follow_deref(state, to); + let rvalue_set = self.follow_deref(state, from); state.extend(&lvalue_set, &state.follow(&rvalue_set)); } // Find all places where the operand could point to at the current stage of the program. - fn find_operand_pointees( + fn follow_rvalue( &self, state: &mut PointsToGraph<'tcx>, operand: Operand<'tcx>, - ) -> HashSet> { + ) -> HashSet> { match operand { Operand::Copy(place) | Operand::Move(place) => { // Find all places which are pointed to by the place. state.follow(&state.follow_from_place(place, self.def_id)) } - Operand::Constant(_) => { - // Constants do not point to anything, the aliasing state is empty. - HashSet::new() + Operand::Constant(const_operand) => { + // Constants could point to a static, so need to check for that. + if let Some(static_def_id) = const_operand.check_static_ptr(self.tcx) { + HashSet::from([GlobalMemLoc::Global(static_def_id)]) + } else { + HashSet::new() + } + } + } + } + + // Find all places where the deref of the operand could point to at the current stage of the program. + fn follow_deref( + &self, + state: &mut PointsToGraph<'tcx>, + operand: Operand<'tcx>, + ) -> HashSet> { + match operand { + Operand::Copy(place) | Operand::Move(place) => state.follow_from_place( + place.project_deeper(&[ProjectionElem::Deref], self.tcx), + self.def_id, + ), + Operand::Constant(const_operand) => { + // Constants could point to a static, so need to check for that. + if let Some(static_def_id) = const_operand.check_static_ptr(self.tcx) { + HashSet::from([GlobalMemLoc::Global(static_def_id)]) + } else { + HashSet::new() + } } } } @@ -638,23 +523,18 @@ impl<'a, 'b, 'c, 'tcx> PointsToAnalysis<'a, 'b, 'c, 'tcx> { // TODO: this is probably wrong if the arguments are passed via spread, // as in with closures, so we would need to fix that. for (i, arg) in args.iter().enumerate() { - match &arg.node { - Operand::Copy(place) | Operand::Move(place) => { - let lvalue_set = HashSet::from([PlaceOrAlloc::Place(Place { - local: (i + 1).into(), // Since arguments in the callee are starting with 1, account for that. - projection: List::empty(), - }) - .with_def_id(instance.def_id())]); - let rvalue_set = state.follow_from_place(*place, self.def_id); - state.extend(&lvalue_set, &state.follow(&rvalue_set)); - } - Operand::Constant(_) => {} - } + let lvalue_set = HashSet::from([LocalMemLoc::Place(Place { + local: (i + 1).into(), // Since arguments in the callee are starting with 1, account for that. + projection: List::empty(), + }) + .with_def_id(instance.def_id())]); + let rvalue_set = self.follow_rvalue(state, arg.node.clone()); + state.extend(&lvalue_set, &rvalue_set); } // Similarly, need to connect the return value to the return // destination. let lvalue_set = state.follow_from_place(*destination, self.def_id); - let rvalue_set = HashSet::from([PlaceOrAlloc::Place(Place { + let rvalue_set = HashSet::from([LocalMemLoc::Place(Place { local: 0usize.into(), projection: List::empty(), }) diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/points_to_graph.rs b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/points_to_graph.rs index bc4c918b0578..2f61a8aaebb6 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/points_to_graph.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/points_to_graph.rs @@ -16,46 +16,50 @@ use std::{ /// A node in the points-to graph, which could be a place on the stack or a heap allocation. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] -pub enum PlaceOrAlloc<'tcx> { +pub enum LocalMemLoc<'tcx> { Alloc(usize), Place(Place<'tcx>), } /// A node tagged with a DefId, to differentiate between places across different functions. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] -pub struct GlobalPlaceOrAlloc<'tcx> { - def_id: DefId, - place_or_alloc: PlaceOrAlloc<'tcx>, +pub enum GlobalMemLoc<'tcx> { + Local(DefId, LocalMemLoc<'tcx>), + Global(DefId), } -impl<'tcx> GlobalPlaceOrAlloc<'tcx> { - /// Check if the node has a given DefId. - pub fn has_def_id(&self, def_id: DefId) -> bool { - self.def_id == def_id +impl<'tcx> GlobalMemLoc<'tcx> { + /// Returns DefId of the memory location. + pub fn def_id(&self) -> DefId { + match self { + GlobalMemLoc::Local(def_id, _) | GlobalMemLoc::Global(def_id) => *def_id, + } } - /// Remove DefId from the node. - pub fn without_def_id(&self) -> PlaceOrAlloc<'tcx> { - self.place_or_alloc + pub fn maybe_local_mem_loc(&self) -> Option> { + match self { + GlobalMemLoc::Local(_, mem_loc) => Some(*mem_loc), + GlobalMemLoc::Global(_) => None, + } } } -impl<'tcx> From> for PlaceOrAlloc<'tcx> { +impl<'tcx> From> for LocalMemLoc<'tcx> { fn from(value: Place<'tcx>) -> Self { - PlaceOrAlloc::Place(value) + LocalMemLoc::Place(value) } } -impl<'tcx> PlaceOrAlloc<'tcx> { +impl<'tcx> LocalMemLoc<'tcx> { /// Generate a new alloc with increasing allocation id. pub fn new_alloc() -> Self { static NEXT_ALLOC_ID: AtomicUsize = AtomicUsize::new(0); - PlaceOrAlloc::Alloc(NEXT_ALLOC_ID.fetch_add(1, Ordering::Relaxed)) + LocalMemLoc::Alloc(NEXT_ALLOC_ID.fetch_add(1, Ordering::Relaxed)) } /// Tag the node with a DefId. - pub fn with_def_id(&self, def_id: DefId) -> GlobalPlaceOrAlloc<'tcx> { - GlobalPlaceOrAlloc { def_id, place_or_alloc: *self } + pub fn with_def_id(&self, def_id: DefId) -> GlobalMemLoc<'tcx> { + GlobalMemLoc::Local(def_id, *self) } } @@ -69,14 +73,18 @@ impl<'tcx> PlaceOrAlloc<'tcx> { #[derive(Clone, Debug, PartialEq, Eq)] pub struct PointsToGraph<'tcx> { /// A hash map of node --> {nodes} edges. - edges: HashMap, HashSet>>, + edges: HashMap, HashSet>>, } impl<'tcx> PointsToGraph<'tcx> { + pub fn empty() -> Self { + Self { edges: HashMap::new() } + } + /// Create a new graph, adding all existing places without projections from a body. - pub fn new(body: &Body, def_id: DefId) -> Self { + pub fn from_body(body: &Body, def_id: DefId) -> Self { let places = (0..body.local_decls.len()).map(|local| { - let place: PlaceOrAlloc = + let place: LocalMemLoc = Place { local: local.into(), projection: List::empty() }.into(); (place.with_def_id(def_id), HashSet::new()) }); @@ -84,19 +92,12 @@ impl<'tcx> PointsToGraph<'tcx> { } /// Collect all nodes which have incoming edges from `nodes`. - pub fn follow( - &self, - nodes: &HashSet>, - ) -> HashSet> { + pub fn follow(&self, nodes: &HashSet>) -> HashSet> { nodes.iter().flat_map(|node| self.edges.get(node).cloned().unwrap_or_default()).collect() } /// For each node in `from`, add an edge to each node in `to`. - pub fn extend( - &mut self, - from: &HashSet>, - to: &HashSet>, - ) { + pub fn extend(&mut self, from: &HashSet>, to: &HashSet>) { for node in from.iter() { let node_pointees = self.edges.entry(*node).or_default(); node_pointees.extend(to.iter()); @@ -109,8 +110,8 @@ impl<'tcx> PointsToGraph<'tcx> { &self, place: Place<'tcx>, current_def_id: DefId, - ) -> HashSet> { - let place_or_alloc: PlaceOrAlloc = + ) -> HashSet> { + let place_or_alloc: LocalMemLoc = Place { local: place.local, projection: List::empty() }.into(); let mut node_set = HashSet::from([place_or_alloc.with_def_id(current_def_id)]); for projection in place.projection { @@ -137,16 +138,30 @@ impl<'tcx> PointsToGraph<'tcx> { let nodes: Vec = self .edges .keys() - .map(|from| format!("\t\"{:?}:{:?}\"", from.def_id, from.place_or_alloc)) + .map(|from| { + format!( + "\t\"{:?}:{:?}\"", + from.def_id(), + from.maybe_local_mem_loc().unwrap_or(LocalMemLoc::Alloc(0)) + ) + }) .collect(); let nodes_str = nodes.join("\n"); let edges: Vec = self .edges .iter() .flat_map(|(from, to)| { - let from = format!("\"{:?}:{:?}\"", from.def_id, from.place_or_alloc); + let from = format!( + "\"{:?}:{:?}\"", + from.def_id(), + from.maybe_local_mem_loc().unwrap_or(LocalMemLoc::Alloc(0)) + ); to.iter().map(move |to| { - let to = format!("\"{:?}:{:?}\"", to.def_id, to.place_or_alloc); + let to = format!( + "\"{:?}:{:?}\"", + to.def_id(), + to.maybe_local_mem_loc().unwrap_or(LocalMemLoc::Alloc(0)) + ); format!("\t{} -> {}", from.clone(), to) }) }) @@ -156,10 +171,7 @@ impl<'tcx> PointsToGraph<'tcx> { } /// Find a transitive closure of the graph starting from a given place. - pub fn transitive_closure( - &self, - target: &GlobalPlaceOrAlloc<'tcx>, - ) -> HashSet> { + pub fn transitive_closure(&self, target: &GlobalMemLoc<'tcx>) -> HashSet> { let mut result = HashSet::new(); let mut queue = VecDeque::from([*target]); while !queue.is_empty() { @@ -174,10 +186,7 @@ impl<'tcx> PointsToGraph<'tcx> { } /// Retrieve all places to which a given place is pointing to. - pub fn pointees_of( - &self, - target: &GlobalPlaceOrAlloc<'tcx>, - ) -> HashSet> { + pub fn pointees_of(&self, target: &GlobalMemLoc<'tcx>) -> HashSet> { self.edges .get(&target) .expect(format!("unable to retrieve {:?} from points-to graph", target).as_str()) diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs b/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs index 279229a7d9d0..7ad53b8a419e 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs @@ -30,10 +30,10 @@ mod ty_layout; /// Trait that the instrumentation target providers must implement to work with the instrumenter. trait TargetFinder { fn find_next( + &mut self, body: &MutableBody, bb: BasicBlockIdx, skip_first: bool, - place_filter: &[Place], ) -> Option; } @@ -60,12 +60,12 @@ pub struct UninitInstrumenter<'a> { impl<'a> UninitInstrumenter<'a> { /// Instrument a body with memory initialization checks, the visitor that generates /// instrumentation targets must be provided via a TF type parameter. - fn instrument( + fn instrument( &mut self, tcx: TyCtxt, mut body: MutableBody, instance: Instance, - place_filter: &[Place], + mut target_finder: impl TargetFinder, ) -> (bool, MutableBody) { // Need to break infinite recursion when memory initialization checks are inserted, so the // internal functions responsible for memory initialization are skipped. @@ -95,7 +95,7 @@ impl<'a> UninitInstrumenter<'a> { let mut bb_idx = 0; while bb_idx < body.blocks().len() { if let Some(candidate) = - TF::find_next(&body, bb_idx, skip_first.contains(&bb_idx), place_filter) + target_finder.find_next(&body, bb_idx, skip_first.contains(&bb_idx)) { self.build_check_for_instruction(tcx, &mut body, candidate, &mut skip_first); bb_idx += 1 diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/mod.rs b/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/mod.rs index 14dbfa3929c4..be400233c78f 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/mod.rs @@ -20,6 +20,7 @@ use stable_mir::CrateDef; use std::collections::HashMap; use std::fmt::Debug; use tracing::trace; +use uninit_visitor::CheckUninitVisitor; mod uninit_visitor; @@ -64,8 +65,8 @@ impl TransformPass for UninitPass { check_type: self.check_type.clone(), mem_init_fn_cache: &mut self.mem_init_fn_cache, }; - let (instrumentation_added, body) = instrumenter - .instrument::(tcx, new_body, instance, &[]); + let (instrumentation_added, body) = + instrumenter.instrument(tcx, new_body, instance, CheckUninitVisitor::new()); (changed || instrumentation_added, body.into()) } diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs b/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs index 91a7d526508f..6cede48c36cc 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs @@ -19,8 +19,8 @@ use stable_mir::mir::{ }; use stable_mir::ty::{ConstantKind, RigidTy, TyKind}; -pub struct CheckUninitVisitor<'a> { - locals: &'a [LocalDecl], +pub struct CheckUninitVisitor { + locals: Vec, /// Whether we should skip the next instruction, since it might've been instrumented already. /// When we instrument an instruction, we partition the basic block, and the instruction that /// may trigger UB becomes the first instruction of the basic block, which we need to skip @@ -34,26 +34,34 @@ pub struct CheckUninitVisitor<'a> { bb: BasicBlockIdx, } -impl<'a> TargetFinder for CheckUninitVisitor<'a> { +impl TargetFinder for CheckUninitVisitor { fn find_next( + &mut self, body: &MutableBody, bb: BasicBlockIdx, skip_first: bool, - _place_filter: &[Place], ) -> Option { - let mut visitor = CheckUninitVisitor { - locals: body.locals(), - skip_next: skip_first, - current: SourceInstruction::Statement { idx: 0, bb }, - target: None, - bb, - }; - visitor.visit_basic_block(&body.blocks()[bb]); - visitor.target + self.locals = body.locals().to_vec(); + self.skip_next = skip_first; + self.current = SourceInstruction::Statement { idx: 0, bb }; + self.target = None; + self.bb = bb; + self.visit_basic_block(&body.blocks()[bb]); + self.target.clone() } } -impl<'a> CheckUninitVisitor<'a> { +impl CheckUninitVisitor { + pub fn new() -> Self { + Self { + locals: vec![], + skip_next: false, + current: SourceInstruction::Statement { idx: 0, bb: 0 }, + target: None, + bb: 0, + } + } + fn push_target(&mut self, source_op: MemoryInitOp) { let target = self.target.get_or_insert_with(|| InitRelevantInstruction { source: self.current, @@ -64,7 +72,7 @@ impl<'a> CheckUninitVisitor<'a> { } } -impl<'a> MirVisitor for CheckUninitVisitor<'a> { +impl MirVisitor for CheckUninitVisitor { fn visit_statement(&mut self, stmt: &Statement, location: Location) { if self.skip_next { self.skip_next = false; @@ -100,7 +108,7 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { // if it points to initialized memory. if *projection_elem == ProjectionElem::Deref { if let TyKind::RigidTy(RigidTy::RawPtr(..)) = - place_to_add_projections.ty(&self.locals).unwrap().kind() + place_to_add_projections.ty(&&self.locals).unwrap().kind() { self.push_target(MemoryInitOp::Check { operand: Operand::Copy(place_to_add_projections.clone()), @@ -109,7 +117,7 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { } place_to_add_projections.projection.push(projection_elem.clone()); } - if place_without_deref.ty(&self.locals).unwrap().kind().is_raw_ptr() { + if place_without_deref.ty(&&self.locals).unwrap().kind().is_raw_ptr() { self.push_target(MemoryInitOp::Set { operand: Operand::Copy(place_without_deref), value: true, @@ -118,7 +126,7 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { } } // Check whether Rvalue creates a new initialized pointer previously not captured inside shadow memory. - if place.ty(&self.locals).unwrap().kind().is_raw_ptr() { + if place.ty(&&self.locals).unwrap().kind().is_raw_ptr() { if let Rvalue::AddressOf(..) = rvalue { self.push_target(MemoryInitOp::Set { operand: Operand::Copy(place.clone()), @@ -160,7 +168,7 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { match &term.kind { TerminatorKind::Call { func, args, destination, .. } => { self.super_terminator(term, location); - let instance = match try_resolve_instance(self.locals, func) { + let instance = match try_resolve_instance(&self.locals, func) { Ok(instance) => instance, Err(reason) => { self.super_terminator(term, location); @@ -189,7 +197,7 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { "Unexpected number of arguments for `{name}`" ); assert!(matches!( - args[0].ty(self.locals).unwrap().kind(), + args[0].ty(&self.locals).unwrap().kind(), TyKind::RigidTy(RigidTy::RawPtr(..)) )); self.push_target(MemoryInitOp::Check { @@ -203,11 +211,11 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { "Unexpected number of arguments for `compare_bytes`" ); assert!(matches!( - args[0].ty(self.locals).unwrap().kind(), + args[0].ty(&self.locals).unwrap().kind(), TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Not)) )); assert!(matches!( - args[1].ty(self.locals).unwrap().kind(), + args[1].ty(&self.locals).unwrap().kind(), TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Not)) )); self.push_target(MemoryInitOp::CheckSliceChunk { @@ -228,11 +236,11 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { "Unexpected number of arguments for `copy`" ); assert!(matches!( - args[0].ty(self.locals).unwrap().kind(), + args[0].ty(&self.locals).unwrap().kind(), TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Not)) )); assert!(matches!( - args[1].ty(self.locals).unwrap().kind(), + args[1].ty(&self.locals).unwrap().kind(), TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Mut)) )); self.push_target(MemoryInitOp::CheckSliceChunk { @@ -253,11 +261,11 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { "Unexpected number of arguments for `typed_swap`" ); assert!(matches!( - args[0].ty(self.locals).unwrap().kind(), + args[0].ty(&self.locals).unwrap().kind(), TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Mut)) )); assert!(matches!( - args[1].ty(self.locals).unwrap().kind(), + args[1].ty(&self.locals).unwrap().kind(), TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Mut)) )); self.push_target(MemoryInitOp::Check { @@ -274,7 +282,7 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { "Unexpected number of arguments for `volatile_load`" ); assert!(matches!( - args[0].ty(self.locals).unwrap().kind(), + args[0].ty(&self.locals).unwrap().kind(), TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Not)) )); self.push_target(MemoryInitOp::Check { @@ -288,7 +296,7 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { "Unexpected number of arguments for `volatile_store`" ); assert!(matches!( - args[0].ty(self.locals).unwrap().kind(), + args[0].ty(&self.locals).unwrap().kind(), TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Mut)) )); self.push_target(MemoryInitOp::Set { @@ -304,7 +312,7 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { "Unexpected number of arguments for `write_bytes`" ); assert!(matches!( - args[0].ty(self.locals).unwrap().kind(), + args[0].ty(&self.locals).unwrap().kind(), TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Mut)) )); self.push_target(MemoryInitOp::SetSliceChunk { @@ -355,13 +363,13 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { } TerminatorKind::Drop { place, .. } => { self.super_terminator(term, location); - let place_ty = place.ty(&self.locals).unwrap(); + let place_ty = place.ty(&&self.locals).unwrap(); // When drop is codegen'ed for types that could define their own dropping // behavior, a reference is taken to the place which is later implicitly coerced // to a pointer. Hence, we need to bless this pointer as initialized. match place - .ty(&self.locals) + .ty(&&self.locals) .unwrap() .kind() .rigid() @@ -403,7 +411,7 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { Place { local: place.local, projection: place.projection[..idx].to_vec() }; match elem { ProjectionElem::Deref => { - let ptr_ty = intermediate_place.ty(self.locals).unwrap(); + let ptr_ty = intermediate_place.ty(&self.locals).unwrap(); if ptr_ty.kind().is_raw_ptr() { self.push_target(MemoryInitOp::Check { operand: Operand::Copy(intermediate_place.clone()), @@ -465,7 +473,7 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { } } CastKind::Transmute => { - let operand_ty = operand.ty(&self.locals).unwrap(); + let operand_ty = operand.ty(&&self.locals).unwrap(); if !tys_layout_compatible(&operand_ty, &ty) { // If transmuting between two types of incompatible layouts, padding // bytes are exposed, which is UB.