diff --git a/rap/src/analysis/senryx.rs b/rap/src/analysis/senryx.rs index b2c81b1..8f8a63d 100644 --- a/rap/src/analysis/senryx.rs +++ b/rap/src/analysis/senryx.rs @@ -12,11 +12,12 @@ use visitor::BodyVisitor; pub struct SenryxCheck<'tcx> { pub tcx: TyCtxt<'tcx>, + pub threshhold: usize, } impl<'tcx> SenryxCheck<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>) -> Self { - Self { tcx } + pub fn new(tcx: TyCtxt<'tcx>, threshhold: usize) -> Self { + Self { tcx, threshhold } } pub fn start(&self) { diff --git a/rap/src/analysis/senryx/contracts/abstract_state.rs b/rap/src/analysis/senryx/contracts/abstract_state.rs index 257953c..c30fab7 100644 --- a/rap/src/analysis/senryx/contracts/abstract_state.rs +++ b/rap/src/analysis/senryx/contracts/abstract_state.rs @@ -11,6 +11,7 @@ pub enum Value { Isize(isize), U32(u32), Custom(), + None, // ... } @@ -54,15 +55,26 @@ pub enum InitState { PartlyInitialized, } +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] +pub enum VType { + Pointer(usize, usize), // (align, size) + // todo +} + #[derive(Debug, PartialEq, Clone)] pub struct AbstractStateItem { pub value: (Value, Value), + pub vtype: VType, pub state: HashSet, } impl AbstractStateItem { - pub fn new(value: (Value, Value), state: HashSet) -> Self { - Self { value, state } + pub fn new(value: (Value, Value), vtype: VType, state: HashSet) -> Self { + Self { + value, + vtype, + state, + } } pub fn meet_state_item(&mut self, other_state: &AbstractStateItem) { diff --git a/rap/src/analysis/senryx/contracts/checker.rs b/rap/src/analysis/senryx/contracts/checker.rs index 90ddb3e..bd6464c 100644 --- a/rap/src/analysis/senryx/contracts/checker.rs +++ b/rap/src/analysis/senryx/contracts/checker.rs @@ -25,13 +25,13 @@ impl SliceFromRawPartsChecker { map.insert( 0, vec![ - Contract::ValueCheck { + Contract::StateCheck { op: Op::GE, - value: Value::Usize(0), + state: StateType::AllocatedState(AllocatedState::Alloc), }, Contract::StateCheck { - op: Op::EQ, - state: StateType::AllocatedState(AllocatedState::Alloc), + op: Op::NE, + state: StateType::AlignState(AlignState::Small2BigCast), }, ], ); diff --git a/rap/src/analysis/senryx/matcher.rs b/rap/src/analysis/senryx/matcher.rs index ebddd93..b99d528 100644 --- a/rap/src/analysis/senryx/matcher.rs +++ b/rap/src/analysis/senryx/matcher.rs @@ -7,7 +7,6 @@ use super::contracts::{ contract::check_contract, }; -//pub fn match_unsafe_api_and_check_contracts(func_name: &str, args:&Vec, abstate:&AbstractState, _ty: T) { pub fn match_unsafe_api_and_check_contracts( func_name: &str, args: &Box<[Spanned]>, @@ -17,7 +16,9 @@ pub fn match_unsafe_api_and_check_contracts( let base_func_name = func_name.split::<&str>("<").next().unwrap_or(func_name); // println!("base name ---- {:?}",base_func_name); let checker: Option> = match base_func_name { - "std::slice::from_raw_parts::" => Some(Box::new(SliceFromRawPartsChecker::::new())), + "std::slice::from_raw_parts::" | "std::slice::from_raw_parts_mut::" => { + Some(Box::new(SliceFromRawPartsChecker::::new())) + } _ => None, }; @@ -26,7 +27,6 @@ pub fn match_unsafe_api_and_check_contracts( } } -//fn process_checker(checker: &dyn Checker, args: &Vec, abstate: &AbstractState) { fn process_checker(checker: &dyn Checker, args: &Box<[Spanned]>, abstate: &AbstractState) { for (idx, contracts_vec) in checker.variable_contracts().iter() { for contract in contracts_vec { @@ -36,7 +36,9 @@ fn process_checker(checker: &dyn Checker, args: &Box<[Spanned]>, abstat } if let Some(abstate_item) = abstate.state_map.get(&arg_place) { if !check_contract(*contract, abstate_item) { - println!("Checking contract failed! ---- {:?}", contract); + println!("Contract failed! ---- {:?}", contract); + } else { + println!("Contract passed! ---- {:?}", contract); } } } diff --git a/rap/src/analysis/senryx/visitor.rs b/rap/src/analysis/senryx/visitor.rs index 5518fd3..236dbf4 100644 --- a/rap/src/analysis/senryx/visitor.rs +++ b/rap/src/analysis/senryx/visitor.rs @@ -1,25 +1,28 @@ -use std::collections::HashMap; - use crate::analysis::safedrop::graph::SafeDropGraph; +use crate::rap_warn; +use std::collections::{HashMap, HashSet}; -use super::contracts::abstract_state::AbstractState; +use super::contracts::abstract_state::{ + AbstractState, AbstractStateItem, AlignState, StateType, VType, Value, +}; use super::matcher::match_unsafe_api_and_check_contracts; use rustc_hir::def_id::DefId; use rustc_middle::ty::TyCtxt; use rustc_middle::{ mir::{ - self, AggregateKind, BasicBlock, BasicBlockData, Operand, Place, Rvalue, Statement, + self, AggregateKind, BasicBlock, BasicBlockData, Local, Operand, Place, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }, - ty, - ty::GenericArgKind, + ty::{self, GenericArgKind, Ty, TyKind}, }; pub struct BodyVisitor<'tcx> { pub tcx: TyCtxt<'tcx>, pub def_id: DefId, pub safedrop_graph: SafeDropGraph<'tcx>, + // abstract_states records the path index and variables' ab states in this path pub abstract_states: HashMap, + pub unsafe_callee_report: HashMap, } impl<'tcx> BodyVisitor<'tcx> { @@ -30,6 +33,7 @@ impl<'tcx> BodyVisitor<'tcx> { def_id, safedrop_graph: SafeDropGraph::new(body, tcx, def_id), abstract_states: HashMap::new(), + unsafe_callee_report: HashMap::new(), } } @@ -147,9 +151,9 @@ impl<'tcx> BodyVisitor<'tcx> { &mut self, lplace: &Place<'tcx>, rvalue: &Rvalue<'tcx>, - _path_index: usize, + path_index: usize, ) { - let _lpjc_local = self + let lpjc_local = self .safedrop_graph .projection(self.tcx, false, lplace.clone()); match rvalue { @@ -169,21 +173,53 @@ impl<'tcx> BodyVisitor<'tcx> { } _ => {} }, - Rvalue::Ref(_, _, rplace) => { - let _rpjc_local = self - .safedrop_graph - .projection(self.tcx, true, rplace.clone()); - } - Rvalue::AddressOf(_, rplace) => { - let _rpjc_local = self + Rvalue::Ref(_, _, rplace) | Rvalue::AddressOf(_, rplace) => { + let rpjc_local = self .safedrop_graph .projection(self.tcx, true, rplace.clone()); + let (align, size) = self.get_layout_by_place_usize(rpjc_local); + let abitem = AbstractStateItem::new( + (Value::None, Value::None), + VType::Pointer(align, size), + HashSet::from([StateType::AlignState(AlignState::Aligned)]), + ); + self.insert_path_abstate(path_index, lpjc_local, abitem); } - Rvalue::Cast(_cast_kind, op, _ty) => match op { + // Rvalue::AddressOf(_, rplace) => { + // let align = 0; + // let size = 0; + // let abitem = AbstractStateItem::new( + // (Value::None, Value::None), + // VType::Pointer(align, size), + // HashSet::from([StateType::AlignState(AlignState::Aligned)]), + // ); + // self.insert_path_abstate(path_index, lpjc_local, abitem); + // let _rpjc_local = self + // .safedrop_graph + // .projection(self.tcx, true, rplace.clone()); + // } + Rvalue::Cast(_cast_kind, op, ty) => match op { Operand::Move(rplace) | Operand::Copy(rplace) => { - let _rpjc_local = - self.safedrop_graph - .projection(self.tcx, true, rplace.clone()); + let rpjc_local = self + .safedrop_graph + .projection(self.tcx, true, rplace.clone()); + let (src_align, _src_size) = self.get_layout_by_place_usize(rpjc_local); + let (dst_align, dst_size) = self.visit_ty_and_get_layout(*ty); + let state = match dst_align.cmp(&src_align) { + std::cmp::Ordering::Greater => { + StateType::AlignState(AlignState::Small2BigCast) + } + std::cmp::Ordering::Less => { + StateType::AlignState(AlignState::Big2SmallCast) + } + std::cmp::Ordering::Equal => StateType::AlignState(AlignState::Aligned), + }; + let abitem = AbstractStateItem::new( + (Value::None, Value::None), + VType::Pointer(dst_align, dst_size), + HashSet::from([state]), + ); + self.insert_path_abstate(path_index, lpjc_local, abitem); } _ => {} }, @@ -207,6 +243,15 @@ impl<'tcx> BodyVisitor<'tcx> { } } + pub fn visit_ty_and_get_layout(&self, ty: Ty<'tcx>) -> (usize, usize) { + match ty.kind() { + TyKind::RawPtr(ty, _) | TyKind::Ref(_, ty, _) | TyKind::Slice(ty) => { + self.get_layout_by_ty(*ty) + } + _ => (0, 0), + } + } + pub fn get_all_paths(&mut self) -> Vec> { self.safedrop_graph.solve_scc(); let results = self.safedrop_graph.get_paths(); @@ -266,4 +311,57 @@ impl<'tcx> BodyVisitor<'tcx> { } results } + + pub fn update_callee_report_level(&mut self, unsafe_callee: String, report_level: usize) { + self.unsafe_callee_report + .entry(unsafe_callee) + .and_modify(|e| { + if report_level < *e { + *e = report_level; + } + }) + .or_insert(report_level); + } + + // level: 0 bug_level, 1-3 unsound_level + // TODO: add more information about the result + pub fn output_results(&self, threshold: usize) { + for (unsafe_callee, report_level) in &self.unsafe_callee_report { + if *report_level == 0 { + rap_warn!("Find one bug in {:?}!", unsafe_callee); + } else if *report_level <= threshold { + rap_warn!("Find an unsoundness issue in {:?}!", unsafe_callee); + } + } + } + + pub fn insert_path_abstate( + &mut self, + path_index: usize, + place: usize, + abitem: AbstractStateItem, + ) { + self.abstract_states + .entry(path_index) + .or_insert_with(|| AbstractState { + state_map: HashMap::new(), + }) + .state_map + .insert(place, abitem); + } + + pub fn get_layout_by_place_usize(&self, place: usize) -> (usize, usize) { + let local_place = Place::from(Local::from_usize(place)); + let body = self.tcx.optimized_mir(self.def_id); + let place_ty = local_place.ty(body, self.tcx).ty; + self.visit_ty_and_get_layout(place_ty) + } + + pub fn get_layout_by_ty(&self, ty: Ty<'tcx>) -> (usize, usize) { + let param_env = self.tcx.param_env(self.def_id); + let layout = self.tcx.layout_of(param_env.and(ty)).unwrap(); + let align = layout.align.abi.bytes_usize(); + let size = layout.size.bytes() as usize; + (align, size) + } } diff --git a/rap/src/lib.rs b/rap/src/lib.rs index c04a73c..1e84520 100644 --- a/rap/src/lib.rs +++ b/rap/src/lib.rs @@ -224,7 +224,7 @@ pub fn start_analyzer(tcx: TyCtxt, callback: RapCallback) { } if callback.is_senryx_enabled() { - SenryxCheck::new(tcx).start(); + SenryxCheck::new(tcx, 2).start(); } if callback.is_callgraph_enabled() { diff --git a/tests/senryx_tests/slice_from_raw_parts/src/main.rs b/tests/senryx_tests/slice_from_raw_parts/src/main.rs index b7b3c39..fb18e6c 100644 --- a/tests/senryx_tests/slice_from_raw_parts/src/main.rs +++ b/tests/senryx_tests/slice_from_raw_parts/src/main.rs @@ -1,19 +1,31 @@ use std::slice; -fn test1() { - let data: *const u8 = Box::leak(Box::new(0)); - let len: usize = (isize::MAX as usize) / std::mem::size_of::() + 1; - // Pass(Allocated \ Aligned): data is allocated and aligned - // Fail(Bounded): 'len' is out of the max value - // Fail(Dereferencable \ Initialized): 'data' onnly points to the memory with a 'u8' size, but the 'len' is out of this range - let slice: &[u8] = unsafe { slice::from_raw_parts(data, len) }; - if let Some(last_element) = slice.last() { - println!("Last element: {}", last_element); - } else { - println!("Slice is empty"); +// fn test1() { +// let data: *const u8 = Box::leak(Box::new(0)); +// let len: usize = (isize::MAX as usize) / std::mem::size_of::() + 1; +// // Pass(Allocated \ Aligned): data is allocated and aligned +// // Fail(Bounded): 'len' is out of the max value +// // Fail(Dereferencable \ Initialized): 'data' onnly points to the memory with a 'u8' size, but the 'len' is out of this range +// let slice: &[u8] = unsafe { slice::from_raw_parts(data, len) }; +// if let Some(last_element) = slice.last() { +// println!("Last element: {}", last_element); +// } else { +// println!("Slice is empty"); +// } +// } + +fn test2(a: &mut [u8], b: &[u32; 20]) { + unsafe { + let c = slice::from_raw_parts_mut(a.as_mut_ptr() as *mut u32, 20); + for i in 0..20 { + c[i] ^= b[i]; + } } } fn main() { - test1(); + // test1(); + let mut x = [0u8;40]; + let y = [0u32;20]; + test2(&mut x[1..32], &y); } \ No newline at end of file