diff --git a/src/bin/urcrat.rs b/src/bin/urcrat.rs index 1836dc9..b178b16 100644 --- a/src/bin/urcrat.rs +++ b/src/bin/urcrat.rs @@ -23,9 +23,5 @@ fn main() { } let file = args.input.join("c2rust-lib.rs"); - steensgaard::analyze_path(&file); - // relational::analyze_str( - // " - // ", - // ); + relational::analyze_path(&file); } diff --git a/src/relational/analysis.rs b/src/relational/analysis.rs index 5d3a17b..7b8f1c9 100644 --- a/src/relational/analysis.rs +++ b/src/relational/analysis.rs @@ -132,7 +132,7 @@ pub struct Analyzer<'tcx, 'a> { dead_locals: Vec>, local_tys: Vec, local_ptr_tys: HashMap, - local_def_id: LocalDefId, + pub local_def_id: LocalDefId, indirect_assigns: &'a HashMap>, reachability: &'a HashMap>, alias_graph: &'a steensgaard::AliasGraph, @@ -274,9 +274,6 @@ impl TyStructure { match ty.kind() { TyKind::Adt(adt_def, generic_args) => { if adt_def.adt_kind() == AdtKind::Enum { - let def_id = adt_def.did(); - let name = tcx.def_path(def_id).to_string_no_crate_verbose(); - assert!(name.contains("::Option") && !def_id.is_local(), "{name}"); Self::Adt(vec![Self::Leaf]) } else { let variant = &adt_def.variants()[VariantIdx::from_usize(0)]; diff --git a/src/relational/domains.rs b/src/relational/domains.rs index 01e10a3..1dc0b89 100644 --- a/src/relational/domains.rs +++ b/src/relational/domains.rs @@ -93,6 +93,11 @@ impl AbsLoc { pub fn root(&self) -> NodeId { self.root } + + #[inline] + pub fn projection(&self) -> &[AccElem] { + &self.projection + } } #[derive(Clone, PartialEq, Eq)] @@ -454,7 +459,7 @@ impl Graph { *obj = Obj::Ptr(AbsLoc::new(id, vec![])); } - fn x_eq(&mut self, x: &AccPath, deref: bool) { + pub fn x_eq(&mut self, x: &AccPath, deref: bool) { let obj = self.lvalue(x, deref); *obj = Obj::default(); } @@ -473,6 +478,57 @@ impl Graph { *obj = Obj::Ptr(loc); } + pub fn x_eq_offset(&mut self, x: &AccPath, y: &AccPath, idx: OpVal) { + let (id, _) = self.get_local_node_mut(y.local); + let loc = self.get_pointed_loc_mut(id, &[]); + let mut loc = if loc.projection.is_empty() { + let Obj::Ptr(loc) = &mut self.nodes[id].obj else { panic!() }; + loc.projection.push(AccElem::Int(0)); + let loc = loc.clone(); + self.obj_at_location_mut(&loc); + loc + } else { + loc + }; + let elem = loc.projection.last_mut().unwrap(); + if let AccElem::Int(n) = elem { + match idx { + OpVal::Place(idx, idx_deref) => { + assert!(idx.projection.is_empty()); + assert!(!idx_deref); + *elem = AccElem::Symbolic(self.find_aliases(idx.local)); + let obj = self.lvalue(x, false); + *obj = Obj::Ptr(loc); + } + OpVal::Int(idx) => { + *n += idx as usize; + let obj = self.lvalue(x, false); + *obj = Obj::Ptr(loc); + } + OpVal::Other => { + let obj = self.lvalue(x, false); + *obj = Obj::default(); + } + } + } else { + let obj = self.lvalue(x, false); + *obj = Obj::default(); + } + } + + pub fn x_eq_offset_int(&mut self, x: &AccPath, y: &AccPath, idx: u128) { + let (id, _) = self.get_local_node_mut(y.local); + let mut loc = self.get_pointed_loc_mut(id, &[]); + let obj = self.lvalue(x, false); + let elem = loc.projection.last_mut().unwrap(); + if let AccElem::Int(n) = elem { + *n += idx as usize; + *obj = Obj::Ptr(loc); + } else { + *obj = Obj::default(); + } + } + pub fn filter_x_int(&mut self, x: &AccPath, deref: bool, n: u128) { let ptr = self.set_obj_ptr(|this| this.lvalue(x, deref)); assert!(ptr.projection.is_empty()); diff --git a/src/relational/semantics.rs b/src/relational/semantics.rs index c3793eb..ced6126 100644 --- a/src/relational/semantics.rs +++ b/src/relational/semantics.rs @@ -61,22 +61,24 @@ impl<'tcx> Analyzer<'tcx, '_> { _ => unreachable!(), }; state.gm().assign(&l, l_deref, &OpVal::Int(v)); - } else if matches!(ty.kind(), TyKind::Uint(UintTy::Usize)) { - state.gm().assign(&l, l_deref, &r); } else { - state.gm().assign(&l, l_deref, &OpVal::Other); + state.gm().assign(&l, l_deref, &r); } } - CastKind::PointerCoercion(coercion) => { - if *coercion == PointerCoercion::MutToConstPointer { - let r = self.transfer_op(r, state); - state.gm().assign_with_suffixes(&l, l_deref, &r, &suffixes); - } else { + CastKind::PointerCoercion(coercion) => match coercion { + PointerCoercion::ReifyFnPointer + | PointerCoercion::UnsafeFnPointer + | PointerCoercion::ClosureFnPointer(_) => { state .gm() .assign_with_suffixes(&l, l_deref, &OpVal::Other, &suffixes); } - } + PointerCoercion::MutToConstPointer | PointerCoercion::Unsize => { + let r = self.transfer_op(r, state); + state.gm().assign_with_suffixes(&l, l_deref, &r, &suffixes); + } + PointerCoercion::ArrayToPointer => todo!(), + }, _ => { state .gm() @@ -333,11 +335,12 @@ impl<'tcx> Analyzer<'tcx, '_> { block: *target, statement_index: 0, }; - match func { + let need_update = match func { Operand::Copy(func) | Operand::Move(func) => { assert!(func.projection.is_empty()); let callees = self.resolve_indirect_calls(func.local); self.transfer_intra_call(&callees, &mut state); + true } Operand::Constant(box constant) => { let ConstantKind::Val(value, ty) = constant.literal else { unreachable!() }; @@ -357,23 +360,34 @@ impl<'tcx> Analyzer<'tcx, '_> { let code = self.tcx.sess.source_map().span_to_snippet(span).unwrap(); assert_eq!(code, "BitfieldStruct"); + true } else if seg1.contains("{extern#") { - self.transfer_c_call(seg0, inputs, args, &mut state); + self.transfer_c_call(inputs, args, &mut state); + true } else { self.transfer_intra_call( &HashSet::from_iter([local_def_id]), &mut state, ); + true } } else { self.transfer_rust_call( (seg3, seg2, seg1, seg0), inputs, args, + destination, &mut state, - ); + ) } } + }; + if need_update { + let (l, l_deref) = AccPath::from_place(*destination, &state); + let suffixes = self.get_path_suffixes(&l, l_deref); + state + .gm() + .assign_with_suffixes(&l, l_deref, &OpVal::Other, &suffixes); } vec![(location, state)] } @@ -390,29 +404,51 @@ impl<'tcx> Analyzer<'tcx, '_> { } } - fn transfer_c_call( - &self, - _name: &str, - _inputs: &[Ty<'_>], - _args: &[Operand<'_>], - _state: &mut AbsMem, - ) { - todo!() + fn transfer_c_call(&self, inputs: &[Ty<'_>], args: &[Operand<'_>], state: &mut AbsMem) { + let graph = state.gm(); + for (ty, arg) in inputs.iter().zip(args) { + if ty.is_mutable_ptr() { + if let Operand::Copy(arg) | Operand::Move(arg) = arg { + for (local, depth) in self.find_may_aliases(arg.local) { + graph.invalidate_deref(local, depth, None); + } + } + } + } } fn transfer_rust_call( &self, name: (&str, &str, &str, &str), inputs: &[Ty<'_>], - _args: &[Operand<'_>], - _state: &mut AbsMem, - ) { + args: &[Operand<'_>], + dst: &Place<'_>, + state: &mut AbsMem, + ) -> bool { if inputs.iter().all(|t| t.is_primitive()) { - return; + return true; } + let (d, d_deref) = AccPath::from_place(*dst, state); + assert!(!d_deref); match name { - ("", "option", _, "unwrap") => {} - _ => todo!("{:?}", name), + ("", "option", _, "unwrap") | ("ptr", _, _, "is_null") => true, + ("", "slice", _, "as_ptr" | "as_mut_ptr") => { + let (Operand::Copy(a) | Operand::Move(a)) = args[0] else { panic!() }; + let (mut a, a_deref) = AccPath::from_place(a, state); + assert!(!a_deref); + a.projection.push(AccElem::Int(0)); + state.gm().x_eq_ref_y(&d, &a, true); + false + } + ("ptr", _, _, "offset") => { + let (Operand::Copy(ptr) | Operand::Move(ptr)) = args[0] else { panic!() }; + let (ptr, ptr_deref) = AccPath::from_place(ptr, state); + assert!(!ptr_deref); + let idx = self.transfer_op(&args[1], state); + state.gm().x_eq_offset(&d, &ptr, idx); + false + } + _ => todo!("{:?} {:?}", self.local_def_id, name), } } } diff --git a/src/relational/tests.rs b/src/relational/tests.rs index 039c695..5fe08fc 100644 --- a/src/relational/tests.rs +++ b/src/relational/tests.rs @@ -826,7 +826,7 @@ fn test_call_invalidate() { // _1 = const 0_i32 // _3 = &mut _1 // _2 = &raw mut (*_3) - // _4 = foo::f(_2) -> [return: bb1, unwind continue] + // _4 = foo::f(_2) analyze_fn( " unsafe fn f(mut x: *mut libc::c_int) { @@ -861,7 +861,7 @@ fn test_indirect_call_invalidate() { // _6 = &raw mut (*_7) // _10 = _2 // _9 = std::option::Option::::unwrap(move _10) - // _8 = move _9(_6) -> [return: bb5, unwind continue] + // _8 = move _9(_6) analyze_fn_with( "", "mut x: libc::c_int", @@ -889,3 +889,144 @@ fn test_indirect_call_invalidate() { }, ); } + +#[test] +fn test_memcpy_invalidate() { + // _1 = const 0_i32 + // _2 = const 0_i32 + // _6 = &mut _1 + // _5 = &raw mut (*_6) + // _4 = move _5 as *mut libc::c_void (PtrToPtr) + // _9 = &mut _2 + // _8 = &raw mut (*_9) + // _7 = move _8 as *const libc::c_void (PtrToPtr) + // _11 = std::mem::size_of::() + // _10 = move _11 as u64 (IntToInt) + // _3 = foo::memcpy(move _4, move _7, move _10) + analyze_fn( + " + extern \"C\" { + fn memcpy( + _: *mut libc::c_void, + _: *const libc::c_void, + _: libc::c_ulong, + ) -> *mut libc::c_void; + } + let mut x: libc::c_int = 0 as libc::c_int; + let mut y: libc::c_int = 0 as libc::c_int; + memcpy( + &mut x as *mut libc::c_int as *mut libc::c_void, + &mut y as *mut libc::c_int as *const libc::c_void, + ::std::mem::size_of::() as libc::c_ulong, + ); + ", + |g, _, _| { + assert_eq!(g.get_local_as_int(1), None); + assert_eq!(g.get_local_as_int(2), Some(0)); + }, + ); +} + +#[test] +fn test_as_mut_ptr() { + // _1 = [const 0_i32; 1] + // _4 = &mut _1 + // _3 = move _4 as &mut [i32] (PointerCoercion(Unsize)) + // _2 = core::slice::::as_mut_ptr(move _3) + // _5 = const 1_i32 + // (*_2) = move _5 + analyze_fn( + " + let mut x: [libc::c_int; 1] = [0; 1]; + let mut y: *mut libc::c_int = x.as_mut_ptr(); + *y = 1 as libc::c_int; + ", + |g, _, _| { + let n = get_nodes(&g, 1..=5); + assert_eq!(n[&1].field(0).as_ptr(), n[&5].as_ptr()); + + assert_eq!(g.get_local_as_int(5), Some(1)); + }, + ); +} + +#[test] +fn test_offset_array() { + // _1 = [const 0_i32; 2] + // _4 = &mut _1 + // _3 = move _4 as &mut [i32] (PointerCoercion(Unsize)) + // _2 = core::slice::::as_mut_ptr(move _3) + // _5 = const 1_i32 + // _7 = _2 + // _9 = const 1_i32 + // _8 = move _9 as isize (IntToInt) + // _6 = std::ptr::mut_ptr::::offset(move _7, move _8) + // (*_6) = move _5 + analyze_fn( + " + let mut x: [libc::c_int; 2] = [0; 2]; + let mut y: *mut libc::c_int = x.as_mut_ptr(); + *y.offset(1 as libc::c_int as isize) = 1 as libc::c_int; + ", + |g, _, _| { + let n = get_nodes(&g, 1..=5); + assert_eq!(n[&1].field(1).as_ptr(), n[&5].as_ptr()); + + assert_eq!(g.get_local_as_int(5), Some(1)); + }, + ); +} + +#[test] +fn test_offset_array_symbolic() { + // _2 = [const 0_i32; 1] + // _5 = &mut _2 + // _4 = move _5 as &mut [i32] (PointerCoercion(Unsize)) + // _3 = core::slice::::as_mut_ptr(move _4) -> [return: bb1, unwind continue] + // _6 = const 1_i32 + // _8 = _3 + // _9 = _1 as isize (IntToInt) + // _7 = std::ptr::mut_ptr::::offset(move _8, move _9) -> [return: bb2, unwind continue] + // (*_7) = move _6 + analyze_fn_with( + "", + "mut x: libc::c_int", + " + let mut y: [libc::c_int; 1] = [0; 1]; + let mut z: *mut libc::c_int = y.as_mut_ptr(); + *z.offset(x as isize) = 1 as libc::c_int; + ", + |g, _, _| { + let n = get_nodes(&g, 2..=6); + assert_eq!(n[&2].symbolic_obj().as_ptr(), n[&6].as_ptr()); + assert_eq!(n[&2].symbolic_index(), HashSet::from_iter([1, 9])); + + assert_eq!(g.get_local_as_int(6), Some(1)); + }, + ); +} + +#[test] +fn test_offset() { + // _2 = const 0_i32 + // _5 = const 1_i32 + // _4 = move _5 as isize (IntToInt) + // _3 = std::ptr::mut_ptr::::offset(_1, move _4) + // (*_3) = move _2 + analyze_fn_with( + "", + "mut x: *mut libc::c_int", + " + *x.offset(1 as libc::c_int as isize) = 0 as libc::c_int; + ", + |g, _, _| { + let n = get_nodes(&g, 1..=3); + assert_eq!(n[&1].obj.as_ptr().root(), n[&3].obj.as_ptr().root()); + assert_eq!(n[&1].obj.as_ptr().projection()[0], AccElem::Int(0)); + assert_eq!(n[&3].obj.as_ptr().projection()[0], AccElem::Int(1)); + + let n11 = &g.nodes[n[&3].as_ptr().root()].field(1); + assert_eq!(n11.as_ptr(), n[&2].as_ptr()); + }, + ); +}