diff --git a/crates/flowistry/src/pdg/construct.rs b/crates/flowistry/src/pdg/construct.rs index 52bd5a24c..a6d94d95d 100644 --- a/crates/flowistry/src/pdg/construct.rs +++ b/crates/flowistry/src/pdg/construct.rs @@ -18,7 +18,7 @@ use rustc_index::IndexVec; use rustc_middle::{ mir::{ visit::Visitor, AggregateKind, BasicBlock, Body, Location, Operand, Place, PlaceElem, - Rvalue, Statement, StatementKind, Terminator, TerminatorKind, RETURN_PLACE, + Rvalue, Statement, StatementKind, Terminator, TerminatorKind, RETURN_PLACE }, ty::{GenericArg, GenericArgsRef, List, ParamEnv, TyCtxt, TyKind}, }; @@ -682,15 +682,24 @@ impl<'tcx> GraphConstructor<'tcx> { // Find every reference to a parent-able node in the child's graph. let is_arg = |node: &DepNode<'tcx>| { node.at.leaf().function == child_constructor.def_id - && (node.place.local == RETURN_PLACE + && (node.place.local == RETURN_PLACE || node.place.is_arg(&child_constructor.body)) }; - let parentable_srcs = child_graph - .edges - .iter() - .map(|(src, _, _)| *src) - .filter(is_arg) - .filter(|node| node.at.leaf().location.is_start()); + // An attempt at getting immutable arguments to connect + let parentable_srcs = if self.params.false_call_edges { + let dummy_state = PartialGraph::default(); + let constructor_ref = &child_constructor; + Either::Right(child_constructor.body.args_iter() + .map(|local| Place::from(local)) + .flat_map(move |place| constructor_ref.find_data_inputs(&dummy_state, place))) + } else { + Either::Left(child_graph + .edges + .iter() + .map(|(src, _, _)| *src) + .filter(is_arg) + .filter(|node| node.at.leaf().location.is_start())) + }; let parentable_dsts = child_graph .edges .iter() @@ -698,26 +707,6 @@ impl<'tcx> GraphConstructor<'tcx> { .filter(is_arg) .filter(|node| node.at.leaf().location.is_end()); - if self.params.false_call_edges { - for arg in args { - if let Some(place) = arg.place() { - for mut_ref in self.place_info.reachable_values(place, Mutability::Mut) { - if mut_ref.ty(&parent_body.local_decls, tcx).ty.is_ref() { - debug!("false mutation"); - let mutated = tcx.mk_place_deref(*mut_ref); - self.apply_mutation( - state, - location, - Either::Left(mutated), - Either::Left(vec![mutated]), - MutationStatus::Possibly, - ); - } - } - } - } - } - // For each source node CHILD that is parentable to PLACE, // add an edge from PLACE -> CHILD. for child_src in parentable_srcs { @@ -874,6 +863,29 @@ impl<'tcx> GraphConstructor<'tcx> { let empty = PartialGraph::default(); let mut domains = IndexVec::from_elem_n(empty.clone(), bb_graph.len()); + + if self.params.false_call_edges { + let start_domain = &mut domains[0_u32.into()]; + for arg in self.body.args_iter() { + let place = Place::from(arg); + for mutation in self.find_data_inputs(start_domain, place) { + start_domain.last_mutation + .entry(mutation.place) + .or_default() + .insert(RichLocation::Start); + } + // for child in self.place_info.children(place).iter().copied() { + // let ty = child.ty(self.body.as_ref(), self.tcx); + // if !ty.ty.is_mutable_ptr() { + // continue; + // } + // let target = child.project_deeper(&[PlaceElem::Deref], self.tcx); + // let initial = start_domain.last_mutation.entry(target).or_default(); + // initial.insert(RichLocation::Start); + // } + } + } + for block in blocks { for parent in bb_graph.predecessors()[block].iter() { let (child, parent) = domains.pick2_mut(block, *parent); @@ -900,7 +912,7 @@ impl<'tcx> GraphConstructor<'tcx> { for block in all_returns { let return_state = &domains[block]; for (place, locations) in &return_state.last_mutation { - if place.local == RETURN_PLACE || place.is_arg(&self.body) { + if place.local == RETURN_PLACE || self.is_ptr_argument_mutation(*place) { for location in locations { let src = self.make_dep_node(*place, *location); let dst = self.make_dep_node(*place, RichLocation::End); @@ -916,6 +928,13 @@ impl<'tcx> GraphConstructor<'tcx> { final_state } + fn is_ptr_argument_mutation(&self, place: Place<'tcx>) -> bool { + place.is_arg(&self.body) + // && place.iter_projections().any(|(place_ref, projection)| + // projection == PlaceElem::Deref && place_ref.ty(self.body.as_ref(), self.tcx).ty.is_mutable_ptr() + // ) + } + fn domain_to_petgraph(self, domain: PartialGraph<'tcx>) -> DepGraph<'tcx> { let mut graph: DiGraph, DepEdge> = DiGraph::new(); let mut nodes = FxHashMap::default(); diff --git a/crates/flowistry/tests/pdg.rs b/crates/flowistry/tests/pdg.rs index 817e75430..7f24247ad 100644 --- a/crates/flowistry/tests/pdg.rs +++ b/crates/flowistry/tests/pdg.rs @@ -505,10 +505,48 @@ fn main() { }) } +#[test] +fn test_fields_passing_through() { + + let input = r#" +use std::num::Wrapping; +fn pass(x2: &Wrapping) { + println!("{}", x2); +} + +fn main() { + let ref mut i = Wrapping(0); + let y = 10; + i.0 += y; + pass(i); +} +"#; + let _ = env_logger::try_init(); + flowistry::test_utils::compile(input, move |tcx| { + let def_id = get_main(tcx); + let params = PdgParams::new(tcx, def_id); + + let with_edges = flowistry::pdg::compute_pdg(params.with_false_call_edges()); + + with_edges.generate_graphviz( + "graph.pdf" + ).unwrap(); + + + assert!(connects( + tcx, + &with_edges, + "y", + "*x2", + None + )); + }) +} + #[test] fn false_call_edges() { let input = r#" -fn does_not_mutate(x: &mut i32) {} +fn does_not_mutate(x2: &mut i32) {} fn main() { let mut x = 0; @@ -521,22 +559,91 @@ fn main() { let def_id = get_main(tcx); let params = PdgParams::new(tcx, def_id); - let without_edges = flowistry::pdg::compute_pdg(params.clone()); - assert!(!connects( - tcx, - &without_edges, - "x", - "y", - Some("does_not_mutate") - )); + // let without_edges = flowistry::pdg::compute_pdg(params.clone()); + // assert!(!connects( + // tcx, + // &without_edges, + // "x", + // "y", + // None + // )); let with_edges = flowistry::pdg::compute_pdg(params.with_false_call_edges()); + + with_edges.generate_graphviz( + "graph.pdf" + ).unwrap(); assert!(connects( tcx, &with_edges, "x", + "*x2", + None + )); + assert!(connects( + tcx, + &with_edges, + "*x2", "y", - Some("does_not_mutate") + None )); }) } + +#[test] +fn false_call_edges_2() { + let input = r#" +struct UserData { + pub data: Vec, +} +fn get_user_data() -> UserData { + return UserData { + data: vec![1, 2, 3], + }; +} +fn send_user_data(user_data: &UserData) {} +fn send2(user_data2: &UserData) {} +fn modify_vec(v: &mut [i64]) {} +fn main() { + let ref mut p = get_user_data(); + modify_vec(&mut p.data); + send_user_data(p); +} +"#; + + let _ = env_logger::try_init(); + flowistry::test_utils::compile(input, move |tcx| { + let def_id = get_main(tcx); + let params = PdgParams::new(tcx, def_id); + + let ref with_edges = flowistry::pdg::compute_pdg(params.with_false_call_edges()); + + with_edges.generate_graphviz( + "graph.pdf" + ).unwrap(); + + println!("Running in {}", std::env::current_dir().unwrap().display()); + + assert!(connects( + tcx, + with_edges, + "RETURN.data", + "*v", + None, + )); + assert!(connects( + tcx, + with_edges, + "*v", + "user_data", + None, + )); + assert!(!connects( + tcx, + with_edges, + "user_data", + "user_data2", + None, + )); + }); +}