Skip to content

Commit

Permalink
Callgraph added and merge to latest version
Browse files Browse the repository at this point in the history
  • Loading branch information
SeanXunX committed Oct 23, 2024
2 parents 0b90d88 + 4981196 commit d7b3301
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 44 deletions.
5 changes: 3 additions & 2 deletions rap/src/analysis/senryx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
16 changes: 14 additions & 2 deletions rap/src/analysis/senryx/contracts/abstract_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub enum Value {
Isize(isize),
U32(u32),
Custom(),
None,
// ...
}

Expand Down Expand Up @@ -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<StateType>,
}

impl AbstractStateItem {
pub fn new(value: (Value, Value), state: HashSet<StateType>) -> Self {
Self { value, state }
pub fn new(value: (Value, Value), vtype: VType, state: HashSet<StateType>) -> Self {
Self {
value,
vtype,
state,
}
}

pub fn meet_state_item(&mut self, other_state: &AbstractStateItem) {
Expand Down
8 changes: 4 additions & 4 deletions rap/src/analysis/senryx/contracts/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ impl<T> SliceFromRawPartsChecker<T> {
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),
},
],
);
Expand Down
10 changes: 6 additions & 4 deletions rap/src/analysis/senryx/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use super::contracts::{
contract::check_contract,
};

//pub fn match_unsafe_api_and_check_contracts<T>(func_name: &str, args:&Vec<Operand>, abstate:&AbstractState, _ty: T) {
pub fn match_unsafe_api_and_check_contracts<T>(
func_name: &str,
args: &Box<[Spanned<Operand>]>,
Expand All @@ -17,7 +16,9 @@ pub fn match_unsafe_api_and_check_contracts<T>(
let base_func_name = func_name.split::<&str>("<").next().unwrap_or(func_name);
// println!("base name ---- {:?}",base_func_name);
let checker: Option<Box<dyn Checker>> = match base_func_name {
"std::slice::from_raw_parts::" => Some(Box::new(SliceFromRawPartsChecker::<T>::new())),
"std::slice::from_raw_parts::" | "std::slice::from_raw_parts_mut::" => {
Some(Box::new(SliceFromRawPartsChecker::<T>::new()))
}
_ => None,
};

Expand All @@ -26,7 +27,6 @@ pub fn match_unsafe_api_and_check_contracts<T>(
}
}

//fn process_checker(checker: &dyn Checker, args: &Vec<Operand>, abstate: &AbstractState) {
fn process_checker(checker: &dyn Checker, args: &Box<[Spanned<Operand>]>, abstate: &AbstractState) {
for (idx, contracts_vec) in checker.variable_contracts().iter() {
for contract in contracts_vec {
Expand All @@ -36,7 +36,9 @@ fn process_checker(checker: &dyn Checker, args: &Box<[Spanned<Operand>]>, 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);
}
}
}
Expand Down
136 changes: 117 additions & 19 deletions rap/src/analysis/senryx/visitor.rs
Original file line number Diff line number Diff line change
@@ -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<usize, AbstractState>,
pub unsafe_callee_report: HashMap<String, usize>,
}

impl<'tcx> BodyVisitor<'tcx> {
Expand All @@ -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(),
}
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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);
}
_ => {}
},
Expand All @@ -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<Vec<usize>> {
self.safedrop_graph.solve_scc();
let results = self.safedrop_graph.get_paths();
Expand Down Expand Up @@ -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)
}
}
2 changes: 1 addition & 1 deletion rap/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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_show_mir_enabled() {
Expand Down
36 changes: 24 additions & 12 deletions tests/senryx_tests/slice_from_raw_parts/src/main.rs
Original file line number Diff line number Diff line change
@@ -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::<u8>() + 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::<u8>() + 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);
}

0 comments on commit d7b3301

Please sign in to comment.