Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a simple alignment check in senryx #63

Merged
merged 5 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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_callgraph_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);
}