From 24942c954943fa1b29d865a7fe9a034020618855 Mon Sep 17 00:00:00 2001 From: LemonJ <1632798336@qq.com> Date: Sat, 9 Nov 2024 09:51:49 +0800 Subject: [PATCH 1/4] add from_raw_parts's different SP tests in senryx --- .../slice_from_raw_parts/src/main.rs | 141 +++++++++++++++--- 1 file changed, 123 insertions(+), 18 deletions(-) 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 fb18e6c..5123c0f 100644 --- a/tests/senryx_tests/slice_from_raw_parts/src/main.rs +++ b/tests/senryx_tests/slice_from_raw_parts/src/main.rs @@ -1,20 +1,57 @@ +use std::ptr; use std::slice; +use std::mem::MaybeUninit; -// 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]) { +fn test1() { + let len: usize = 0; + let data = ptr::null::(); + // Fail(Allocated): 'data' is null, which violates the requirement that it must be non-null + let slice: &[i32] = unsafe { slice::from_raw_parts(data, len) }; +} + +fn test2() { + let len: usize = 3; + let uninit_data = MaybeUninit::<[i32; 3]>::uninit(); + let data = uninit_data.as_ptr() as *const i32; + // Fail(Initialized): 'data' points to uninitialized memory, which violates the initialization requirement + let slice: &[i32] = unsafe { slice::from_raw_parts(data, len) }; + println!("First element: {}", slice[0]); +} + +fn test3() { + let part1 = Box::new(1); + let part2 = Box::new(2); + let data = [Box::into_raw(part1), Box::into_raw(part2)].as_ptr() as *const i32; + let len = 2; + // Fail(Dereferencable): 'data' points across multiple allocated objects, violating the single allocation constraint + let slice: &[i32] = unsafe { slice::from_raw_parts(data, len) }; + println!("Slice elements: {:?}", slice); +} + +fn test4() { + let unaligned = [0u8; 5]; + let data = unaligned.as_ptr().wrapping_offset(1) as *const i32; + let len = 1; + // Fail(Layout): 'data' is not aligned, violating the alignment requirement + let slice: &[i32] = unsafe { slice::from_raw_parts(data, len) }; + println!("Slice elements: {:?}", slice); +} + +fn test5() { + 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 test6(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 { @@ -24,8 +61,76 @@ fn test2(a: &mut [u8], b: &[u32; 20]) { } 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 + // test2(&mut x[1..32], &y); +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test1_allocated_non_null_data() { + let len = 0; + let data = ptr::null::(); + + { + unsafe { slice::from_raw_parts(data, len) }; + } + } + + #[test] + fn test2_initialized_consecutive_values() { + // data属性 - Initialized: data必须指向已初始化的值 + let len = 3; + let uninit_data = MaybeUninit::<[i32; 3]>::uninit(); + let data = uninit_data.as_ptr() as *const i32; + + // 此处期望未初始化的值会导致未定义行为 + #[should_panic] + { + unsafe { slice::from_raw_parts(data, len) }; + } + } + + #[test] + fn test3_dereferencable_memory_range() { + // data属性 - Dereferencable: 内存范围应在单一分配的对象内 + // 通过手动分配多个内存对象以模拟不符合的情况 + let part1 = Box::new(1); + let part2 = Box::new(2); + let data = [Box::into_raw(part1), Box::into_raw(part2)].as_ptr() as *const i32; + + #[should_panic] + { + unsafe { slice::from_raw_parts(data, 2) }; + } + } + + #[test] + fn test4_layout_alignment() { + // data属性 - Layout: 数据指针必须对齐 + let unaligned = [0u8; 5]; + let data = unaligned.as_ptr().wrapping_offset(1) as *const i32; + + #[should_panic] + { + unsafe { slice::from_raw_parts(data, 1) }; + } + } + + #[test] + fn test5_len_bounded() { + // len属性 - Bounded: 切片大小不得超过isize::MAX + let len = (isize::MAX as usize / std::mem::size_of::()) + 1; + let data = ptr::null::(); + + #[should_panic] + { + unsafe { slice::from_raw_parts(data, len) }; + } + } +} From 451ce2bcc72e076d6ebd03d996e9548e38e51057 Mon Sep 17 00:00:00 2001 From: LemonJ <1632798336@qq.com> Date: Sat, 9 Nov 2024 10:17:24 +0800 Subject: [PATCH 2/4] add from_raw_parts's struct tests in senryx --- .../slice_from_raw_parts/src/main.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) 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 5123c0f..03ecb1c 100644 --- a/tests/senryx_tests/slice_from_raw_parts/src/main.rs +++ b/tests/senryx_tests/slice_from_raw_parts/src/main.rs @@ -2,6 +2,28 @@ use std::ptr; use std::slice; use std::mem::MaybeUninit; +struct MySliceWrapperTest { + data: *const T, + len: usize, +} + +impl MySliceWrapperTest { + fn new() -> Self { + let uninit_data = MaybeUninit::<[T; 10]>::uninit(); + let data = uninit_data.as_ptr() as *const T; + MySliceWrapperTest { data, len: 10 } + } + + fn get_slice(&self, offset: usize, length: usize) -> &[T] { + assert!(offset + length <= self.len, "Requested slice is out of bounds"); + let adjusted_data = unsafe { self.data.add(offset) }; + // Fail(Allocated): 'adjusted_data' points to uninit memory + // Fail(Aligned): 'adjusted_data' may be not aligned due to the offset + unsafe { slice::from_raw_parts(adjusted_data, length) } + } +} + + fn test1() { let len: usize = 0; let data = ptr::null::(); From a4fce8180b9c064dae503c159c0f82d8eb6069fe Mon Sep 17 00:00:00 2001 From: LemonJ <1632798336@qq.com> Date: Sat, 9 Nov 2024 13:25:45 +0800 Subject: [PATCH 3/4] fix: remove redundant tests --- .../slice_from_raw_parts/src/main.rs | 68 ------------------- 1 file changed, 68 deletions(-) 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 03ecb1c..eedd859 100644 --- a/tests/senryx_tests/slice_from_raw_parts/src/main.rs +++ b/tests/senryx_tests/slice_from_raw_parts/src/main.rs @@ -88,71 +88,3 @@ fn main() { let y = [0u32;20]; // test2(&mut x[1..32], &y); } - - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test1_allocated_non_null_data() { - let len = 0; - let data = ptr::null::(); - - { - unsafe { slice::from_raw_parts(data, len) }; - } - } - - #[test] - fn test2_initialized_consecutive_values() { - // data属性 - Initialized: data必须指向已初始化的值 - let len = 3; - let uninit_data = MaybeUninit::<[i32; 3]>::uninit(); - let data = uninit_data.as_ptr() as *const i32; - - // 此处期望未初始化的值会导致未定义行为 - #[should_panic] - { - unsafe { slice::from_raw_parts(data, len) }; - } - } - - #[test] - fn test3_dereferencable_memory_range() { - // data属性 - Dereferencable: 内存范围应在单一分配的对象内 - // 通过手动分配多个内存对象以模拟不符合的情况 - let part1 = Box::new(1); - let part2 = Box::new(2); - let data = [Box::into_raw(part1), Box::into_raw(part2)].as_ptr() as *const i32; - - #[should_panic] - { - unsafe { slice::from_raw_parts(data, 2) }; - } - } - - #[test] - fn test4_layout_alignment() { - // data属性 - Layout: 数据指针必须对齐 - let unaligned = [0u8; 5]; - let data = unaligned.as_ptr().wrapping_offset(1) as *const i32; - - #[should_panic] - { - unsafe { slice::from_raw_parts(data, 1) }; - } - } - - #[test] - fn test5_len_bounded() { - // len属性 - Bounded: 切片大小不得超过isize::MAX - let len = (isize::MAX as usize / std::mem::size_of::()) + 1; - let data = ptr::null::(); - - #[should_panic] - { - unsafe { slice::from_raw_parts(data, len) }; - } - } -} From 633cfb22036071f81c8395c944e43a542d2ace6e Mon Sep 17 00:00:00 2001 From: LemonJ <1632798336@qq.com> Date: Sat, 9 Nov 2024 14:02:27 +0800 Subject: [PATCH 4/4] add handle call logic in senryx --- rap/Cargo.toml | 1 + rap/src/analysis/senryx.rs | 7 +- .../senryx/contracts/abstract_state.rs | 1 + rap/src/analysis/senryx/inter_record.rs | 32 +++++++++ rap/src/analysis/senryx/visitor.rs | 66 +++++++++++-------- 5 files changed, 78 insertions(+), 29 deletions(-) create mode 100644 rap/src/analysis/senryx/inter_record.rs diff --git a/rap/Cargo.toml b/rap/Cargo.toml index 0712177..3165308 100644 --- a/rap/Cargo.toml +++ b/rap/Cargo.toml @@ -17,6 +17,7 @@ name = "cargo-rap" name = "rap" [dependencies] +lazy_static = "1.4" snafu = "0.7.0" chrono = "0.4.19" serde_json = "1.0.72" diff --git a/rap/src/analysis/senryx.rs b/rap/src/analysis/senryx.rs index 8f8a63d..1ec161f 100644 --- a/rap/src/analysis/senryx.rs +++ b/rap/src/analysis/senryx.rs @@ -1,4 +1,5 @@ pub mod contracts; +pub mod inter_record; pub mod matcher; pub mod visitor; @@ -21,7 +22,7 @@ impl<'tcx> SenryxCheck<'tcx> { } pub fn start(&self) { - let related_items = RelatedFnCollector::collect(self.tcx); + let related_items = RelatedFnCollector::collect(self.tcx); // find all func let hir_map = self.tcx.hir(); for (_, &ref vec) in &related_items { for (body_id, _span) in vec { @@ -57,11 +58,11 @@ impl<'tcx> SenryxCheck<'tcx> { pub fn pre_handle_type(&self, def_id: DefId) { let mut uig_checker = UnsafetyIsolationCheck::new(self.tcx); let func_type = uig_checker.get_type(def_id); - let mut body_visitor = BodyVisitor::new(self.tcx, def_id); + let mut body_visitor = BodyVisitor::new(self.tcx, def_id, true); if func_type == 1 { let func_cons = uig_checker.search_constructor(def_id); for func_con in func_cons { - let mut cons_body_visitor = BodyVisitor::new(self.tcx, func_con); + let mut cons_body_visitor = BodyVisitor::new(self.tcx, func_con, true); cons_body_visitor.path_forward_check(); // TODO: cache fields' states diff --git a/rap/src/analysis/senryx/contracts/abstract_state.rs b/rap/src/analysis/senryx/contracts/abstract_state.rs index c30fab7..60476ac 100644 --- a/rap/src/analysis/senryx/contracts/abstract_state.rs +++ b/rap/src/analysis/senryx/contracts/abstract_state.rs @@ -107,6 +107,7 @@ impl AbstractStateItem { } } +#[derive(PartialEq)] pub struct AbstractState { pub state_map: HashMap, } diff --git a/rap/src/analysis/senryx/inter_record.rs b/rap/src/analysis/senryx/inter_record.rs new file mode 100644 index 0000000..90086c9 --- /dev/null +++ b/rap/src/analysis/senryx/inter_record.rs @@ -0,0 +1,32 @@ +use lazy_static::lazy_static; +use rustc_hir::def_id::DefId; +use std::{collections::HashMap, sync::Mutex}; + +use super::contracts::abstract_state::AbstractStateItem; + +lazy_static! { + pub static ref GLOBAL_INTER_RECORDER: Mutex> = + Mutex::new(HashMap::new()); +} +// static mut GLOBAL_INTER_RECORDER: HashMap = HashMap::new(); + +pub struct InterAnalysisRecord { + pub pre_analysis_state: HashMap, + pub post_analysis_state: HashMap, +} + +impl InterAnalysisRecord { + pub fn new( + pre_analysis_state: HashMap, + post_analysis_state: HashMap, + ) -> Self { + Self { + pre_analysis_state, + post_analysis_state, + } + } + + pub fn is_pre_state_same(&self, other_pre_state: &HashMap) -> bool { + self.pre_analysis_state == *other_pre_state + } +} diff --git a/rap/src/analysis/senryx/visitor.rs b/rap/src/analysis/senryx/visitor.rs index 236dbf4..54a293f 100644 --- a/rap/src/analysis/senryx/visitor.rs +++ b/rap/src/analysis/senryx/visitor.rs @@ -5,6 +5,7 @@ use std::collections::{HashMap, HashSet}; use super::contracts::abstract_state::{ AbstractState, AbstractStateItem, AlignState, StateType, VType, Value, }; +use super::inter_record::{InterAnalysisRecord, GLOBAL_INTER_RECORDER}; use super::matcher::match_unsafe_api_and_check_contracts; use rustc_hir::def_id::DefId; use rustc_middle::ty::TyCtxt; @@ -23,10 +24,11 @@ pub struct BodyVisitor<'tcx> { // abstract_states records the path index and variables' ab states in this path pub abstract_states: HashMap, pub unsafe_callee_report: HashMap, + pub first_layer_flag: bool, } impl<'tcx> BodyVisitor<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, def_id: DefId) -> Self { + pub fn new(tcx: TyCtxt<'tcx>, def_id: DefId, first_layer_flag: bool) -> Self { let body = tcx.optimized_mir(def_id); Self { tcx, @@ -34,6 +36,7 @@ impl<'tcx> BodyVisitor<'tcx> { safedrop_graph: SafeDropGraph::new(body, tcx, def_id), abstract_states: HashMap::new(), unsafe_callee_report: HashMap::new(), + first_layer_flag, } } @@ -97,23 +100,25 @@ impl<'tcx> BodyVisitor<'tcx> { } => { let func_name = format!("{:?}", func); if let Operand::Constant(func_constant) = func { - if let ty::FnDef(ref _callee_def_id, raw_list) = - func_constant.const_.ty().kind() + if let ty::FnDef(ref callee_def_id, raw_list) = func_constant.const_.ty().kind() { - for generic_arg in raw_list.iter() { - match generic_arg.unpack() { - GenericArgKind::Type(ty) => { - match_unsafe_api_and_check_contracts( - func_name.as_str(), - args, - &self.abstract_states.get(&path_index).unwrap(), - ty, - ); + if self.first_layer_flag { + for generic_arg in raw_list.iter() { + match generic_arg.unpack() { + GenericArgKind::Type(ty) => { + match_unsafe_api_and_check_contracts( + func_name.as_str(), + args, + &self.abstract_states.get(&path_index).unwrap(), + ty, + ); + } + _ => {} } - _ => {} } } //TODO:path_inter_analyze + self.handle_call(callee_def_id); } } } @@ -185,19 +190,6 @@ impl<'tcx> BodyVisitor<'tcx> { ); self.insert_path_abstate(path_index, lpjc_local, abitem); } - // 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 @@ -243,6 +235,26 @@ impl<'tcx> BodyVisitor<'tcx> { } } + pub fn handle_call(&mut self, def_id: &DefId) { + let pre_analysis_state = HashMap::new(); + let mut recorder = GLOBAL_INTER_RECORDER.lock().unwrap(); + if let Some(record) = recorder.get_mut(def_id) { + if record.is_pre_state_same(&pre_analysis_state) { + // update directly + self.update_inter_state_directly(); + return; + } + } + let _inter_body_visitor = BodyVisitor::new(self.tcx, *def_id, false).path_forward_check(); + let post_analysis_state = HashMap::new(); + recorder.insert( + *def_id, + InterAnalysisRecord::new(pre_analysis_state, post_analysis_state), + ); + } + + pub fn update_inter_state_directly(&mut self) {} + 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) => { @@ -364,4 +376,6 @@ impl<'tcx> BodyVisitor<'tcx> { let size = layout.size.bytes() as usize; (align, size) } + + pub fn get_abstate_by_place(&self) {} }