Skip to content

Commit

Permalink
Merge pull request #49 from DiuDiu777/senryx
Browse files Browse the repository at this point in the history
Modify framework of Senryx in path analysis
  • Loading branch information
hxuhack authored Oct 11, 2024
2 parents eff9023 + 1b92416 commit 07a1d10
Show file tree
Hide file tree
Showing 13 changed files with 364 additions and 74 deletions.
22 changes: 22 additions & 0 deletions rap/src/analysis/safedrop/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,4 +457,26 @@ impl<'tcx> SafeDropGraph<'tcx> {
let mut time = 0;
self.tarjan(0, &mut stack, &mut instack, &mut dfn, &mut low, &mut time);
}

pub fn dfs_on_spanning_tree(&self, index: usize, stack: &mut Vec<usize>, paths: &mut Vec<Vec<usize>>) {
let curr_scc_index = self.scc_indices[index];
if self.blocks[curr_scc_index].next.len() == 0 {
paths.push(stack.to_vec());
} else {
for child in self.blocks[curr_scc_index].next.iter() {
stack.push(*child);
self.dfs_on_spanning_tree(*child, stack, paths);
}
}
stack.pop();
}

pub fn get_paths(&self) -> Vec<Vec<usize>> {
// rap_debug!("dfs here");
let mut paths: Vec<Vec<usize>> = Vec::new();
let mut stack: Vec<usize> = vec![0];
self.dfs_on_spanning_tree(0, &mut stack, &mut paths);

return paths;
}
}
48 changes: 23 additions & 25 deletions rap/src/analysis/senryx.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
pub mod visitor;
pub mod contracts;
pub mod matcher;

use crate::analysis::unsafety_isolation::hir_visitor::{ContainsUnsafe, RelatedFnCollector};
use crate::analysis::unsafety_isolation::{hir_visitor::{ContainsUnsafe, RelatedFnCollector}, UnsafetyIsolationCheck};
use rustc_hir::def_id::DefId;
use rustc_middle::ty::TyCtxt;
use visitor::BodyVisitor;

pub struct SenryxCheck<'tcx> {
pub tcx: TyCtxt<'tcx>,
Expand Down Expand Up @@ -34,38 +36,34 @@ impl<'tcx> SenryxCheck<'tcx>{
}

pub fn check_soundness(&self, def_id: DefId) {

self.pre_handle_type(def_id);
println!("Find unsound safe api, def_id: {:?}, location: {:?}, ",def_id, def_id);
}

pub fn annotate_safety(&self, def_id: DefId) {

self.pre_handle_type(def_id);
println!("Annotate unsafe api, def_id: {:?}, location: {:?}, ",def_id, def_id);
}

//retval: 0-constructor, 1-method, 2-function
pub fn get_type(&self,def_id: DefId) -> usize{
let tcx = self.tcx;
let mut node_type = 2;
if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
if assoc_item.fn_has_self_parameter {
node_type = 1;
} else if !assoc_item.fn_has_self_parameter {
let fn_sig = tcx.fn_sig(def_id).skip_binder();
let output = fn_sig.output().skip_binder();
if output.is_param(0) {
node_type = 0;
}
if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
if let Some(impl_id) = assoc_item.impl_container(tcx) {
let ty = tcx.type_of(impl_id).skip_binder();
if output == ty{
node_type = 0;
}
}
}
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);
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);
cons_body_visitor.path_forward_check();
// TODO: cache fields' states

// TODO: update method body's states

// analyze body's states
body_visitor.path_forward_check();
}
} else {
body_visitor.path_forward_check();
}
return node_type;
}

}
76 changes: 69 additions & 7 deletions rap/src/analysis/senryx/contracts/abstract_state.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
use std::collections::HashSet;
use std::{collections::{HashMap, HashSet}, hash::Hash};

use super::state_lattice::Lattice;

#[derive(Debug, PartialEq, PartialOrd, Copy, Clone)]
pub enum Value {
Usize(usize),
Isize(isize),
U32(u32),
Custom(),
// ...
}

#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
pub enum StateType {
Expand Down Expand Up @@ -30,23 +41,74 @@ pub enum AllocatedState {
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
pub enum AlignState {
Aligned,
Unaligned,
Small2BigCast,
Big2SmallCast,
}

#[derive(Debug, PartialEq)]
pub struct AbstractStateItem<T: std::cmp::PartialEq + std::cmp::PartialOrd> {
pub value: (T,T),
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
pub enum InitState {
FullyInitialized,
PartlyInitialized,
}

#[derive(Debug, PartialEq, Clone)]
pub struct AbstractStateItem {
pub value: (Value,Value),
pub state: HashSet<StateType>,
}

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

pub fn meet_state_item(&mut self, other_state:&AbstractStateItem) {
let mut new_state = HashSet::new();

// visit 'self.state' and 'other_state.state',matching states and calling meet method
for state_self in &self.state {
// if find the same state type in 'other_state', then meet it;
if let Some(matching_state) = other_state
.state
.iter()
.find(|state_other| std::mem::discriminant(*state_other) == std::mem::discriminant(state_self))
{
let merged_state = match (state_self, matching_state) {
(StateType::AllocatedState(s1), StateType::AllocatedState(s2)) => {
StateType::AllocatedState(s1.meet(*s2))
}
(StateType::AlignState(s1), StateType::AlignState(s2)) => {
StateType::AlignState(s1.meet(*s2))
}
_ => continue,
};
new_state.insert(merged_state);
} else {
// if 'other_state' does not have the same state,then reserve the current state
new_state.insert(*state_self);
}
}

// 更新 self 的状态
self.state = new_state;
}
}

pub struct AbstractState {
pub state_map: HashMap<usize,AbstractStateItem>,
}

impl AbstractState {
pub fn new() -> Self {
Self {

state_map: HashMap::new(),
}
}

pub fn insert_abstate(&mut self, place: usize, place_state_item: AbstractStateItem) {
self.state_map.insert(place, place_state_item);
}
}
23 changes: 17 additions & 6 deletions rap/src/analysis/senryx/contracts/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,31 @@ use super::contract::*;
use std::collections::HashMap;
use std::marker::PhantomData;

struct SliceFromRawPartsChecker<T>{
variable_contracts: HashMap<usize,Vec<Contract<isize>>>,

pub trait Checker {
fn variable_contracts(&self) -> &HashMap<usize, Vec<Contract>>;
}

pub struct SliceFromRawPartsChecker<T>{
pub variable_contracts: HashMap<usize,Vec<Contract>>,
_marker: PhantomData<T>,
}

impl<T> Checker for SliceFromRawPartsChecker<T> {
fn variable_contracts(&self) -> &HashMap<usize, Vec<Contract>> {
&self.variable_contracts
}
}

impl<T> SliceFromRawPartsChecker<T> {
pub fn new() -> Self {
let mut map = HashMap::new();
map.insert(1, vec![
Contract::ValueCheck { op: Op::GE, value: 0 },
map.insert(0, vec![
Contract::ValueCheck { op: Op::GE, value: Value::Usize(0) },
Contract::StateCheck { op: Op::EQ, state: StateType::AllocatedState(AllocatedState::Alloc) },
]);
map.insert(2, vec![
Contract::ValueCheck { op: Op::LE, value: (isize::MAX)/(mem::size_of::<T>() as isize) },
map.insert(1, vec![
Contract::ValueCheck { op: Op::LE, value: Value::Usize((isize::MAX as usize)/mem::size_of::<T>()) },
]);
Self {
variable_contracts: map,
Expand Down
10 changes: 5 additions & 5 deletions rap/src/analysis/senryx/contracts/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ use super::abstract_state::*;
use crate::analysis::senryx::contracts::state_lattice::Lattice;

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Contract<T: std::cmp::PartialEq + std::cmp::PartialOrd> {
ValueCheck { op: Op, value: T },
pub enum Contract {
ValueCheck { op: Op, value: Value },
StateCheck { op: Op, state: StateType },
}


pub fn check_contract<T: std::cmp::PartialEq + std::cmp::PartialOrd>(contract: Contract<T>, abstate: &AbstractStateItem<T>) -> bool {
pub fn check_contract(contract: Contract, abstate_item: &AbstractStateItem) -> bool {
match contract {
Contract::ValueCheck {op, value} => {
return handle_value_op(&abstate.value, op, value);
return handle_value_op(&abstate_item.value, op, value);
},
Contract::StateCheck {op, state} => {
for ab_state in &abstate.state {
for ab_state in &abstate_item.state {
if handle_state_op(*ab_state, op, state) {
return true;
}
Expand Down
48 changes: 42 additions & 6 deletions rap/src/analysis/senryx/contracts/state_lattice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,22 +114,58 @@ impl Lattice for AllocatedState {
impl Lattice for AlignState {
fn join(&self, other: Self) -> Self {
match (self, other) {
(AlignState::Aligned, AlignState::Unaligned) | (AlignState::Unaligned, AlignState::Aligned) => AlignState::Unaligned,
(AlignState::Aligned, AlignState::Aligned) => AlignState::Aligned,
(AlignState::Unaligned, AlignState::Unaligned) => AlignState::Unaligned,
(AlignState::Aligned, _) => AlignState::Aligned,
(AlignState::Big2SmallCast, AlignState::Big2SmallCast) => AlignState::Big2SmallCast,
(AlignState::Big2SmallCast, AlignState::Small2BigCast) => AlignState::Big2SmallCast,
(AlignState::Big2SmallCast, AlignState::Aligned) => AlignState::Aligned,
(AlignState::Small2BigCast, _) => other,
}
}

fn meet(&self, other: Self) -> Self {
match (self, other) {
(AlignState::Aligned, _) | (_, AlignState::Aligned) => AlignState::Aligned,
(AlignState::Unaligned, AlignState::Unaligned) => AlignState::Unaligned,
(AlignState::Aligned, _) => other,
(AlignState::Big2SmallCast, AlignState::Big2SmallCast) => AlignState::Big2SmallCast,
(AlignState::Big2SmallCast, AlignState::Small2BigCast) => AlignState::Small2BigCast,
(AlignState::Big2SmallCast, AlignState::Aligned) => AlignState::Big2SmallCast,
(AlignState::Small2BigCast, _) => AlignState::Small2BigCast,
}
}

fn less_than(&self, other: Self) -> bool {
match (self, other) {
(AlignState::Aligned, AlignState::Unaligned) => true,
(_, AlignState::Aligned) => true,
(AlignState::Small2BigCast, AlignState::Big2SmallCast) => true,
_ => false,
}
}

fn equal(&self, other: Self) -> bool {
*self == other
}
}

impl Lattice for InitState {
fn join(&self, other: Self) -> Self {
match (self, other) {
(InitState::FullyInitialized, _) => InitState::FullyInitialized,
(_, InitState::FullyInitialized) => InitState::FullyInitialized,
_ => InitState::PartlyInitialized,
}
}

fn meet(&self, other: Self) -> Self {
match (self, other) {
(InitState::FullyInitialized, _) => other,
(_, InitState::FullyInitialized) => *self,
_ => InitState::PartlyInitialized,
}
}

fn less_than(&self, other: Self) -> bool {
match (self, other) {
(InitState::FullyInitialized, InitState::FullyInitialized) => true,
(InitState::PartlyInitialized, _) => true,
_ => false,
}
}
Expand Down
42 changes: 42 additions & 0 deletions rap/src/analysis/senryx/matcher.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use rustc_middle::mir::Operand;

use super::contracts::{abstract_state::AbstractState, checker::{Checker, SliceFromRawPartsChecker}, contract::check_contract};

pub fn match_unsafe_api_and_check_contracts<T>(func_name: &str, args:&Vec<Operand>, abstate:&AbstractState, _ty: 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()))
}
_ => None,
};

if let Some(c) = checker {
process_checker(&*c, args, abstate);
}
}

fn process_checker(checker: &dyn Checker, args: &Vec<Operand>, abstate: &AbstractState) {
for (idx, contracts_vec) in checker.variable_contracts().iter() {
for contract in contracts_vec {
let arg_place = get_arg_place(&args[*idx]);
if arg_place == 0 {
return
}
if let Some(abstate_item) = abstate.state_map.get(&arg_place){
if !check_contract(*contract, abstate_item){
println!("Checking contract failed! ---- {:?}",contract);
}
}
}
}
}

pub fn get_arg_place(arg: &Operand) -> usize {
match arg {
Operand::Move(place) => { place.local.as_usize() }
Operand::Copy(place) => { place.local.as_usize() }
_ => { 0 }
}
}
Loading

0 comments on commit 07a1d10

Please sign in to comment.