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 mod help;
12pub(crate) mod helpers;
13pub(crate) mod preprocess;
14pub mod verify;
15
16extern crate rustc_abi;
17extern crate rustc_ast;
18extern crate rustc_data_structures;
19extern crate rustc_driver;
20
21extern crate rustc_hir;
22extern crate rustc_hir_pretty;
23extern crate rustc_index;
24extern crate rustc_infer;
25extern crate rustc_interface;
26extern crate rustc_metadata;
27extern crate rustc_middle;
28extern crate rustc_mir_dataflow;
29extern crate rustc_public;
30extern crate rustc_session;
31extern crate rustc_span;
32
33extern crate rustc_trait_selection;
34
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    owned_heap::{OHAResultMapWrapper, OwnedHeapAnalysis, default::OwnedHeapAnalyzer},
53    path::{PathMapWrapper, default::PathAnalyzer},
54    range::{
55        PathConstraintMapWrapper, RAResultMapWrapper, RangeAnalysis, default::RangeAnalyzer,
56    },
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_99))]
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_99)]
114                {
115                    Arc::make_mut(&mut crate_source).rlib = Some(PathBuf::new());
116                }
117                #[cfg(not(rapx_ge_99))]
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        preprocess::dummy_fns::create_dummy_fns(krate, build_std);
139        preprocess::ssa_preprocess::create_ssa_struct(krate, build_std);
140        Compilation::Continue
141    }
142    fn after_analysis<'tcx>(&mut self, _compiler: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation {
143        rap_trace!("Execute after_analysis() of compiler callbacks");
144        rustc_public::rustc_internal::run(tcx, || {
145            def_id::init(tcx);
146            if self.is_building_test_crate() {
147                start_analyzer(tcx, self);
148            } else {
149                let package_name = std::env::var("CARGO_PKG_NAME")
150                    .expect("cannot capture env var `CARGO_PKG_NAME`");
151                rap_trace!("skip analyzing package `{}`", package_name);
152            }
153        })
154        .expect("Failed to run rustc_public.");
155
156        rap_trace!("analysis done");
157        Compilation::Continue
158    }
159}
160
161/// Start the analysis with the features enabled.
162pub fn start_analyzer(tcx: TyCtxt, callback: &RapCallback) {
163    match &callback.args.command {
164        Commands::Check(CheckArgs { uaf, mleak }) => {
165            if uaf.is_some() {
166                SafeDrop::new(tcx).start();
167            }
168            if *mleak {
169                let mut heap = OwnedHeapAnalyzer::new(tcx);
170                heap.run();
171                let adt_owner = heap.get_all_items();
172                rCanary::new(tcx, adt_owner).start();
173            }
174        }
175
176        Commands::Opt => {
177            Opt::new(tcx, 1).start();
178        }
179
180        Commands::Analyze { kind } => match kind {
181            AnalysisKind::Alias { strategy } => {
182                let alias = match strategy {
183                    AliasStrategyKind::Mop => {
184                        let mut analyzer = AliasAnalyzer::new(tcx);
185                        analyzer.run();
186                        analyzer.get_local_fn_alias()
187                    }
188                    AliasStrategyKind::Mfp => {
189                        let mut analyzer = MfpAliasAnalyzer::new(tcx);
190                        analyzer.run();
191                        analyzer.get_local_fn_alias()
192                    }
193                };
194                rap_info!("{}", FnAliasMapWrapper(alias));
195            }
196            AnalysisKind::Adg(args) => {
197                let config = api_dependency::Config {
198                    resolve_generic: true,
199                    visit_config: api_dependency::VisitConfig {
200                        pub_only: !args.include_private,
201                        include_generic: true,
202                        ignore_const_generic: true,
203                        include_unsafe: args.include_unsafe,
204                        include_drop: args.include_drop,
205                    },
206                    max_generic_search_iteration: args.max_iteration,
207                    dump: args.dump.clone(),
208                };
209                let mut analyzer = ApiDependencyAnalyzer::new(tcx, config);
210                analyzer.run();
211            }
212            &AnalysisKind::SafetyFlow { draw } => {
213                SafetyFlowAnalysis::new(tcx)
214                    .with_draw(draw)
215                    .start(TargetCrate::Other);
216            }
217            &AnalysisKind::SafetyFlowStd { draw } => {
218                SafetyFlowAnalysis::new(tcx)
219                    .with_draw(draw)
220                    .start(TargetCrate::Std);
221            }
222            AnalysisKind::Callgraph => {
223                let mut analyzer = CallGraphAnalyzer::new(tcx);
224                analyzer.run();
225                let callgraph = analyzer.get_fn_calls();
226                rap_info!(
227                    "{}",
228                    FnCallDisplay {
229                        fn_calls: &callgraph,
230                        tcx
231                    }
232                );
233            }
234            &AnalysisKind::Dataflow { debug, draw } => {
235                let mut analyzer = DataflowAnalyzer::new(tcx, debug).with_draw(draw);
236                analyzer.run();
237                let result = analyzer.get_all_arg2ret();
238                rap_info!("{}", Arg2RetMapWrapper(result));
239            }
240            AnalysisKind::OwnedHeap => {
241                let mut analyzer = OwnedHeapAnalyzer::new(tcx);
242                analyzer.run();
243                let result = analyzer.get_all_items();
244                rap_info!("{}", OHAResultMapWrapper(result));
245            }
246            &AnalysisKind::Paths { postfix_repeat } => {
247                let mut analyzer = PathAnalyzer::new(tcx, false);
248                analyzer.run_with_repeat(postfix_repeat);
249                let result = analyzer.get_all_paths();
250                rap_info!("{}", PathMapWrapper(result, &analyzer.graphs));
251            }
252            AnalysisKind::Pathcond => {
253                let mut analyzer = RangeAnalyzer::<i64>::new(tcx, false);
254                analyzer.start_path_constraints_analysis();
255                let result = analyzer.get_all_path_constraints();
256                rap_info!("{}", PathConstraintMapWrapper(result));
257            }
258            &AnalysisKind::Range { debug } => {
259                let mut analyzer = RangeAnalyzer::<i64>::new(tcx, debug);
260                analyzer.run();
261                let result = analyzer.get_all_fn_ranges();
262                rap_info!("{}", RAResultMapWrapper(result));
263            }
264
265            AnalysisKind::Scan => {
266                ScanAnalysis::new(tcx).run();
267            }
268            AnalysisKind::Mir => {
269                ShowMir::new(tcx).start();
270            }
271            AnalysisKind::DotMir => {
272                ShowMir::new(tcx).start_generate_dot();
273            }
274            AnalysisKind::Ssa => {
275                SSATrans::new(tcx, false).start();
276            }
277        },
278
279        Commands::Verify(VerifyArgs {
280            prepare_targets,
281            postfix_repeat,
282            mode,
283            skip_invariant,
284            crate_name,
285            module,
286            debug_contracts,
287            ..
288        }) => {
289            if *prepare_targets {
290                PrepareTargets::new(
291                    tcx,
292                    *mode,
293                    *skip_invariant,
294                    crate_name.clone(),
295                    module.clone(),
296                )
297                .run();
298            } else {
299                let repeat_strategy = match postfix_repeat {
300                    PostfixRepeat::Auto => RepeatStrategy::Auto,
301                    PostfixRepeat::Fixed(n) => RepeatStrategy::Fixed(*n),
302                };
303                VerifyRun::new(
304                    tcx,
305                    repeat_strategy,
306                    *mode,
307                    *skip_invariant,
308                    crate_name.clone(),
309                    module.clone(),
310                    *debug_contracts,
311                )
312                .run();
313            }
314        }
315    }
316}