Skip to main content

rapx/
lib.rs

1#![feature(rustc_private)]
2
3#[macro_use]
4pub mod utils;
5pub mod analysis;
6pub mod check;
7pub mod cli;
8pub(crate) mod compat;
9pub(crate) mod def_id;
10pub(crate) mod graphs;
11pub(crate) mod limit;
12pub mod help;
13pub(crate) mod helpers;
14pub(crate) mod preprocess;
15pub(crate) mod verify;
16
17extern crate rustc_abi;
18extern crate rustc_ast;
19#[cfg(rapx_has_attr_ir)]
20extern crate rustc_attr_ir;
21extern crate rustc_data_structures;
22extern crate rustc_driver;
23extern crate rustc_hir;
24extern crate rustc_hir_pretty;
25extern crate rustc_index;
26extern crate rustc_infer;
27extern crate rustc_interface;
28extern crate rustc_metadata;
29extern crate rustc_middle;
30extern crate rustc_mir_dataflow;
31extern crate rustc_public;
32extern crate rustc_session;
33extern crate rustc_span;
34extern crate rustc_trait_selection;
35extern crate rustc_type_ir;
36extern crate thin_vec;
37
38use crate::{
39    analysis::{alias::mfp::MfpAliasAnalyzer, api_dependency, scan::ScanAnalysis},
40    check::{opt::Opt, rcanary::rCanary, safedrop::SafeDrop},
41    cli::{
42        AliasStrategyKind, AnalysisKind, CheckArgs, Commands, PostfixRepeat, RapxArgs, VerifyArgs,
43    },
44    verify::{driver::VerifyRun, loop_sensitivity::RepeatStrategy, target::PrepareTargets},
45};
46use analysis::{
47    Analysis,
48    alias::{AliasAnalysis, FnAliasMapWrapper, default::AliasAnalyzer},
49    api_dependency::ApiDependencyAnalyzer,
50    callgraph::{CallGraphAnalysis, FnCallDisplay, default::CallGraphAnalyzer},
51    dataflow::{Arg2RetMapWrapper, DataflowAnalysis, default::DataflowAnalyzer},
52    heap_ownership::{
53        HeapOwnershipAnalysis, HeapOwnershipResultMapWrapper, default::HeapOwnershipAnalyzer,
54    },
55    path::{PathMapWrapper, default::PathAnalyzer},
56    range::{PathConstraintMapWrapper, RAResultMapWrapper, RangeAnalysis, default::RangeAnalyzer},
57    safety_flow::{SafetyFlowAnalysis, TargetCrate},
58    ssa_transform::SSATrans,
59};
60use helpers::show_mir::ShowMir;
61use rustc_ast::ast;
62use rustc_driver::{Callbacks, Compilation};
63use rustc_interface::interface::{self, Compiler};
64use rustc_middle::{ty::TyCtxt, util::Providers};
65#[cfg(not(rapx_ge_95))]
66use rustc_session::search_paths::PathKind;
67use std::path::PathBuf;
68use std::sync::Arc;
69
70pub static RAPX_DEFAULT_ARGS: &[&str] = &[
71    "-Zalways-encode-mir",
72    "-Zmir-opt-level=0",
73    "-Zinline-mir-threshold=0",
74    "-Zinline-mir-hint-threshold=0",
75    "-Zcross-crate-inline-threshold=0",
76];
77
78/// This is the data structure to handle rapx options as a rustc callback.
79
80#[derive(Debug, Clone)]
81pub struct RapCallback {
82    args: RapxArgs,
83}
84
85impl RapCallback {
86    pub fn new(args: RapxArgs) -> Self {
87        Self { args }
88    }
89
90    fn is_building_test_crate(&self) -> bool {
91        match &self.args.test_crate {
92            None => true,
93            Some(test_crate) => {
94                let test_crate: &str = test_crate;
95                let package_name = std::env::var("CARGO_PKG_NAME")
96                    .expect("cannot capture env var `CARGO_PKG_NAME`");
97                package_name == test_crate
98            }
99        }
100    }
101}
102
103impl Callbacks for RapCallback {
104    fn config(&mut self, config: &mut rustc_interface::Config) {
105        config.override_queries = Some(|_, providers| {
106            providers.extern_queries.used_crate_source = |tcx, cnum| {
107                let mut providers = Providers::default();
108                rustc_metadata::provide(&mut providers);
109                let mut crate_source = (providers.extern_queries.used_crate_source)(tcx, cnum);
110                // HACK: rustc will emit "crate ... required to be available in rlib format, but
111                // was not found in this form" errors once we use `tcx.dependency_formats()` if
112                // there's no rlib provided, so setting a dummy path here to workaround those errors.
113                #[cfg(rapx_ge_95)]
114                {
115                    Arc::make_mut(&mut crate_source).rlib = Some(PathBuf::new());
116                }
117                #[cfg(not(rapx_ge_95))]
118                {
119                    Arc::make_mut(&mut crate_source).rlib = Some((PathBuf::new(), PathKind::All));
120                }
121                crate_source
122            };
123        });
124    }
125
126    fn after_crate_root_parsing(
127        &mut self,
128        compiler: &interface::Compiler,
129        krate: &mut ast::Crate,
130    ) -> Compilation {
131        let build_std = compiler
132            .sess
133            .opts
134            .crate_name
135            .as_deref()
136            .map(|s| matches!(s, "core" | "std" | "alloc" | "proc_macro" | "test"))
137            .unwrap_or(false);
138
139        preprocess::dummy_fns::create_dummy_fns(krate, build_std);
140        preprocess::ssa_preprocess::create_ssa_struct(krate, build_std);
141        Compilation::Continue
142    }
143
144    fn after_analysis<'tcx>(&mut self, _compiler: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation {
145        rap_trace!("Execute after_analysis() of compiler callbacks");
146        rustc_public::rustc_internal::run(tcx, || {
147            def_id::init(tcx);
148            if self.is_building_test_crate() {
149                start_analyzer(tcx, self);
150            } else {
151                let package_name = std::env::var("CARGO_PKG_NAME")
152                    .expect("cannot capture env var `CARGO_PKG_NAME`");
153                rap_trace!("skip analyzing package `{}`", package_name);
154            }
155        })
156        .expect("Failed to run rustc_public.");
157
158        rap_trace!("analysis done");
159        Compilation::Continue
160    }
161}
162
163/// Start the analysis with the features enabled.
164pub fn start_analyzer(tcx: TyCtxt, callback: &RapCallback) {
165    match &callback.args.command {
166        Commands::Check(CheckArgs { uaf, mleak }) => {
167            if uaf.is_some() {
168                SafeDrop::new(tcx).start();
169            }
170            if *mleak {
171                let mut heap = HeapOwnershipAnalyzer::new(tcx);
172                heap.run();
173                let adt_owner = heap.get_all_items();
174                rCanary::new(tcx, adt_owner).start();
175            }
176        }
177
178        Commands::Opt => {
179            Opt::new(tcx, 1).start();
180        }
181
182        Commands::Analyze { kind } => match kind {
183            AnalysisKind::Alias { strategy } => {
184                let alias = match strategy {
185                    AliasStrategyKind::Mop => {
186                        let mut analyzer = AliasAnalyzer::new(tcx);
187                        analyzer.run();
188                        analyzer.get_local_fn_alias()
189                    }
190                    AliasStrategyKind::Mfp => {
191                        let mut analyzer = MfpAliasAnalyzer::new(tcx);
192                        analyzer.run();
193                        analyzer.get_local_fn_alias()
194                    }
195                };
196                rap_info!("{}", FnAliasMapWrapper(alias));
197            }
198            AnalysisKind::Adg(args) => {
199                let config = api_dependency::Config {
200                    resolve_generic: true,
201                    visit_config: api_dependency::VisitConfig {
202                        pub_only: !args.include_private,
203                        include_generic: true,
204                        ignore_const_generic: true,
205                        include_unsafe: args.include_unsafe,
206                        include_drop: args.include_drop,
207                    },
208                    max_generic_search_iteration: args.max_iteration,
209                    dump: args.dump.clone(),
210                };
211                let mut analyzer = ApiDependencyAnalyzer::new(tcx, config);
212                analyzer.run();
213            }
214            &AnalysisKind::SafetyFlow { draw } => {
215                SafetyFlowAnalysis::new(tcx)
216                    .with_draw(draw)
217                    .start(TargetCrate::Other);
218            }
219            &AnalysisKind::SafetyFlowStd { draw } => {
220                SafetyFlowAnalysis::new(tcx)
221                    .with_draw(draw)
222                    .start(TargetCrate::Std);
223            }
224            AnalysisKind::Callgraph => {
225                let mut analyzer = CallGraphAnalyzer::new(tcx);
226                analyzer.run();
227                let callgraph = analyzer.get_fn_calls();
228                rap_info!(
229                    "{}",
230                    FnCallDisplay {
231                        fn_calls: &callgraph,
232                        tcx
233                    }
234                );
235            }
236            &AnalysisKind::Dataflow { debug, draw } => {
237                let mut analyzer = DataflowAnalyzer::new(tcx, debug).with_draw(draw);
238                analyzer.run();
239                let result = analyzer.get_all_arg2ret();
240                rap_info!("{}", Arg2RetMapWrapper(result));
241            }
242            AnalysisKind::HeapOwnership => {
243                let mut analyzer = HeapOwnershipAnalyzer::new(tcx);
244                analyzer.run();
245                let result = analyzer.get_all_items();
246                rap_info!("{}", HeapOwnershipResultMapWrapper(result));
247            }
248            &AnalysisKind::Paths { postfix_repeat } => {
249                let mut analyzer = PathAnalyzer::new(tcx, false);
250                analyzer.run_with_repeat(postfix_repeat);
251                let result = analyzer.get_all_paths();
252                rap_info!("{}", PathMapWrapper(result, &analyzer.graphs));
253            }
254            AnalysisKind::Pathcond => {
255                let mut analyzer = RangeAnalyzer::<i64>::new(tcx, false);
256                analyzer.start_path_constraints_analysis();
257                let result = analyzer.get_all_path_constraints();
258                rap_info!("{}", PathConstraintMapWrapper(result));
259            }
260            &AnalysisKind::Range { debug } => {
261                let mut analyzer = RangeAnalyzer::<i64>::new(tcx, debug);
262                analyzer.run();
263                let result = analyzer.get_all_fn_ranges();
264                rap_info!("{}", RAResultMapWrapper(result));
265            }
266
267            AnalysisKind::Scan => {
268                ScanAnalysis::new(tcx).run();
269            }
270            AnalysisKind::Mir => {
271                ShowMir::new(tcx).start();
272            }
273            AnalysisKind::DotMir => {
274                ShowMir::new(tcx).start_generate_dot();
275            }
276            AnalysisKind::Ssa => {
277                SSATrans::new(tcx, false).start();
278            }
279        },
280
281        Commands::Verify(VerifyArgs {
282            prepare_targets,
283            postfix_repeat,
284            mode,
285            skip_invariant,
286            crate_name,
287            module,
288            debug_contracts,
289            path_limit,
290            ..
291        }) => {
292            if let Some(n) = path_limit {
293                crate::limit::set_path_limit(*n);
294            }
295            if *prepare_targets {
296                PrepareTargets::new(
297                    tcx,
298                    *mode,
299                    *skip_invariant,
300                    crate_name.clone(),
301                    module.clone(),
302                )
303                .run();
304            } else {
305                let repeat_strategy = match postfix_repeat {
306                    PostfixRepeat::Auto => RepeatStrategy::Auto,
307                    PostfixRepeat::Fixed(n) => RepeatStrategy::Fixed(*n),
308                };
309                VerifyRun::new(
310                    tcx,
311                    repeat_strategy,
312                    *mode,
313                    *skip_invariant,
314                    crate_name.clone(),
315                    module.clone(),
316                    *debug_contracts,
317                )
318                .run();
319            }
320        }
321    }
322}