rapx/check/senryx/
callsite.rs

1//! Unsafe call-site contract dispatch and reporting helpers.
2//!
3//! This module bridges MIR calls and safety-property checkers. It also owns the
4//! built-in unsafe-API matcher and a tiny MIR operand helper because both are
5//! only needed when identifying and instantiating call-site obligations.
6
7use crate::analysis::{
8    core::alias_analysis::FnAliasPairs,
9    utils::fn_info::{
10        generate_contract_from_std_annotation_json, get_cleaned_def_path_name, get_sp_tags_json,
11        reflect_generic,
12    },
13};
14use crate::check::senryx::{contract::PropertyContract, visitor::BodyVisitor};
15use rustc_data_structures::fx::FxHashMap;
16use rustc_hir::def_id::DefId;
17use rustc_middle::{
18    mir::{Const, Operand, Place},
19    ty::Ty,
20};
21use rustc_span::{Span, source_map::Spanned};
22/// Return true when a cleaned function path has built-in unsafe API metadata.
23pub(crate) fn has_unsafe_api_contract(func_name: &str) -> bool {
24    get_sp_tags_json().get(func_name).is_some()
25}
26
27/// Convert a MIR operand into either a constant value or a local place index.
28///
29/// Returns `(is_const, value_or_local)`.
30pub(crate) fn get_arg_place(arg: &Operand) -> (bool, usize) {
31    match arg {
32        Operand::Move(place) | Operand::Copy(place) => (false, place.local.as_usize()),
33        Operand::Constant(constant) => {
34            let mut val = 0;
35            match constant.const_ {
36                Const::Ty(_ty, _const_value) => {
37                    rap_warn!("const ty found!");
38                }
39                Const::Unevaluated(_unevaluated, _ty) => {}
40                Const::Val(const_value, _ty) => {
41                    if let Some(scalar) = const_value.try_to_scalar_int() {
42                        val = scalar.to_uint(scalar.size()) as usize;
43                    }
44                }
45            }
46            (true, val)
47        }
48    }
49}
50
51impl<'tcx> BodyVisitor<'tcx> {
52    /// Instantiate and verify contracts at a standard-library unsafe API callsite.
53    pub fn handle_std_unsafe_call(
54        &mut self,
55        _dst_place: &Place<'_>,
56        def_id: &DefId,
57        args: &[Spanned<Operand>],
58        _path_index: usize,
59        _fn_map: &FxHashMap<DefId, FnAliasPairs>,
60        fn_span: Span,
61        generic_mapping: FxHashMap<String, Ty<'tcx>>,
62    ) {
63        let func_name = get_cleaned_def_path_name(self.tcx, *def_id);
64        let args_with_contracts = generate_contract_from_std_annotation_json(self.tcx, *def_id);
65
66        for (idx, (base, fields, contract)) in args_with_contracts.iter().enumerate() {
67            rap_debug!("Find contract for {:?}, {base}: {:?}", def_id, contract);
68            if *base >= args.len() {
69                continue;
70            }
71
72            let arg_tuple = get_arg_place(&args[*base].node);
73            if arg_tuple.0 {
74                continue;
75            }
76
77            let arg_place = self.chains.find_var_id_with_fields_seq(arg_tuple.1, fields);
78            self.check_contract(
79                arg_place,
80                args,
81                contract.clone(),
82                &generic_mapping,
83                func_name.clone(),
84                fn_span,
85                idx,
86            );
87        }
88    }
89
90    /// Dispatch one instantiated contract to the checker for that safety tag.
91    pub fn check_contract(
92        &mut self,
93        arg: usize,
94        _args: &[Spanned<Operand>],
95        contract: PropertyContract<'tcx>,
96        generic_mapping: &FxHashMap<String, Ty<'tcx>>,
97        func_name: String,
98        fn_span: Span,
99        idx: usize,
100    ) -> bool {
101        rap_debug!("Check contract {:?} for {:?}.", contract, func_name);
102        let (sp_name, check_result) = match contract {
103            PropertyContract::Align(ty) => {
104                let contract_required_ty = reflect_generic(generic_mapping, &func_name, ty);
105                ("Align", self.check_align(arg, contract_required_ty))
106            }
107            PropertyContract::InBound(ty, contract_len) => {
108                let contract_required_ty = reflect_generic(generic_mapping, &func_name, ty);
109                (
110                    "Inbound",
111                    self.check_inbound(arg, contract_len, contract_required_ty),
112                )
113            }
114            PropertyContract::NonNull => ("NonNull", self.check_non_null(arg)),
115            PropertyContract::Typed(_ty) => ("Typed", self.check_typed(arg)),
116            PropertyContract::ValidPtr(ty, contract_len) => {
117                let contract_required_ty = reflect_generic(generic_mapping, &func_name, ty);
118                (
119                    "ValidPtr",
120                    self.check_valid_ptr(arg, contract_len, contract_required_ty),
121                )
122            }
123            PropertyContract::ValidNum(predicates) => {
124                ("ValidNum", self.check_valid_num(&predicates))
125            }
126            _ => ("Unknown", false),
127        };
128
129        self.insert_checking_result(sp_name, check_result, func_name, fn_span, idx);
130        true
131    }
132}