rapx/
lib.rs

1#![feature(rustc_private)]
2#![feature(box_patterns)]
3#![feature(macro_metavar_expr_concat)]
4
5#[macro_use]
6pub mod utils;
7pub mod analysis;
8pub mod check;
9pub mod cli;
10pub mod def_id;
11pub mod help;
12pub mod preprocess;
13pub mod verify;
14
15extern crate rustc_abi;
16extern crate rustc_ast;
17extern crate rustc_data_structures;
18extern crate rustc_driver;
19extern crate rustc_errors;
20extern crate rustc_hir;
21extern crate rustc_hir_pretty;
22extern crate rustc_index;
23extern crate rustc_infer;
24extern crate rustc_interface;
25extern crate rustc_metadata;
26extern crate rustc_middle;
27extern crate rustc_mir_dataflow;
28extern crate rustc_public;
29extern crate rustc_session;
30extern crate rustc_span;
31extern crate rustc_target;
32extern crate rustc_trait_selection;
33extern crate rustc_traits;
34extern crate rustc_type_ir;
35extern crate thin_vec;
36
37use crate::{
38    analysis::{
39        core::{alias_analysis::mfp::MfpAliasAnalyzer, api_dependency},
40        scan::ScanAnalysis,
41    },
42    check::{
43        opt::Opt,
44        rcanary::rCanary,
45        safedrop::SafeDrop,
46        senryx::{CheckLevel, SenryxCheck},
47    },
48    cli::{AliasStrategyKind, AnalysisKind, CheckArgs, Commands, OptLevel, RapxArgs, VerifyArgs},
49    verify::target::PrepareTargets,
50};
51use analysis::{
52    Analysis,
53    core::{
54        alias_analysis::{AliasAnalysis, FnAliasMapWrapper, default::AliasAnalyzer},
55        api_dependency::ApiDependencyAnalyzer,
56        callgraph::{CallGraphAnalysis, FnCallDisplay, default::CallGraphAnalyzer},
57        dataflow::{
58            Arg2RetMapWrapper, DataFlowAnalysis, DataFlowGraphMapWrapper, default::DataFlowAnalyzer,
59        },
60        ownedheap_analysis::{OHAResultMapWrapper, OwnedHeapAnalysis, default::OwnedHeapAnalyzer},
61        range_analysis::{
62            PathConstraintMapWrapper, RAResultMapWrapper, RangeAnalysis, default::RangeAnalyzer,
63        },
64        ssa_transform::SSATrans,
65    },
66    upg::{TargetCrate, UPGAnalysis},
67    utils::show_mir::ShowMir,
68};
69use rustc_ast::ast;
70use rustc_driver::{Callbacks, Compilation};
71use rustc_interface::interface::{self, Compiler};
72use rustc_middle::{ty::TyCtxt, util::Providers};
73use rustc_session::search_paths::PathKind;
74use std::path::PathBuf;
75use std::sync::Arc;
76
77pub static RAP_DEFAULT_ARGS: &[&str] = &[
78    "-Zalways-encode-mir",
79    "-Zmir-opt-level=0",
80    "-Zinline-mir-threshold=0",
81    "-Zinline-mir-hint-threshold=0",
82    "-Zcross-crate-inline-threshold=0",
83];
84
85/// This is the data structure to handle rapx options as a rustc callback.
86
87#[derive(Debug, Clone)]
88pub struct RapCallback {
89    args: RapxArgs,
90}
91
92impl RapCallback {
93    pub fn new(args: RapxArgs) -> Self {
94        Self { args }
95    }
96
97    fn is_building_test_crate(&self) -> bool {
98        match &self.args.test_crate {
99            None => true,
100            Some(test_crate) => {
101                let test_crate: &str = test_crate;
102                let package_name = std::env::var("CARGO_PKG_NAME")
103                    .expect("cannot capture env var `CARGO_PKG_NAME`");
104                package_name == test_crate
105            }
106        }
107    }
108}
109
110impl Callbacks for RapCallback {
111    fn config(&mut self, config: &mut rustc_interface::Config) {
112        config.override_queries = Some(|_, providers| {
113            providers.extern_queries.used_crate_source = |tcx, cnum| {
114                let mut providers = Providers::default();
115                rustc_metadata::provide(&mut providers);
116                let mut crate_source = (providers.extern_queries.used_crate_source)(tcx, cnum);
117                // HACK: rustc will emit "crate ... required to be available in rlib format, but
118                // was not found in this form" errors once we use `tcx.dependency_formats()` if
119                // there's no rlib provided, so setting a dummy path here to workaround those errors.
120                Arc::make_mut(&mut crate_source).rlib = Some((PathBuf::new(), PathKind::All));
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"))
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 {
165            uaf,
166            mleak,
167            opt,
168            infer,
169            verify,
170            verify_std,
171        }) => {
172            if uaf.is_some() {
173                SafeDrop::new(tcx).start();
174            }
175            if *mleak {
176                let mut heap = OwnedHeapAnalyzer::new(tcx);
177                heap.run();
178                let adt_owner = heap.get_all_items();
179                rCanary::new(tcx, adt_owner).start();
180            }
181            if let Some(opt_level) = opt {
182                match opt_level {
183                    OptLevel::Report => Opt::new(tcx, 0).start(),
184                    OptLevel::Default => Opt::new(tcx, 1).start(),
185                    OptLevel::All => Opt::new(tcx, 2).start(),
186                }
187            }
188            if *infer {
189                let check_level = CheckLevel::Medium;
190                SenryxCheck::new(tcx, 2).start(check_level, false);
191            }
192            if *verify {
193                let check_level = CheckLevel::Medium;
194                SenryxCheck::new(tcx, 2).start(check_level, true);
195            }
196
197            if *verify_std {
198                SenryxCheck::new(tcx, 2).start_analyze_std_func();
199                // SenryxCheck::new(tcx, 2).generate_uig_by_def_id();
200            }
201        }
202
203        Commands::Analyze { kind } => match kind {
204            AnalysisKind::Alias { strategy } => {
205                let alias = match strategy {
206                    AliasStrategyKind::Mop => {
207                        let mut analyzer = AliasAnalyzer::new(tcx);
208                        analyzer.run();
209                        analyzer.get_local_fn_alias()
210                    }
211                    AliasStrategyKind::Mfp => {
212                        let mut analyzer = MfpAliasAnalyzer::new(tcx);
213                        analyzer.run();
214                        analyzer.get_local_fn_alias()
215                    }
216                };
217                rap_info!("{}", FnAliasMapWrapper(alias));
218            }
219            AnalysisKind::Adg(args) => {
220                let config = api_dependency::Config {
221                    resolve_generic: true,
222                    visit_config: api_dependency::VisitConfig {
223                        pub_only: !args.include_private,
224                        include_generic: true,
225                        ignore_const_generic: true,
226                        include_unsafe: args.include_unsafe,
227                        include_drop: args.include_drop,
228                    },
229                    max_generic_search_iteration: args.max_iteration,
230                    dump: args.dump.clone(),
231                };
232                let mut analyzer = ApiDependencyAnalyzer::new(tcx, config);
233                analyzer.run();
234            }
235            AnalysisKind::Upg => {
236                UPGAnalysis::new(tcx).start(TargetCrate::Other);
237            }
238            AnalysisKind::UpgStd => {
239                UPGAnalysis::new(tcx).start(TargetCrate::Std);
240            }
241            AnalysisKind::Callgraph => {
242                let mut analyzer = CallGraphAnalyzer::new(tcx);
243                analyzer.run();
244                let callgraph = analyzer.get_fn_calls();
245                rap_info!(
246                    "{}",
247                    FnCallDisplay {
248                        fn_calls: &callgraph,
249                        tcx
250                    }
251                );
252            }
253            &AnalysisKind::Dataflow { debug } => {
254                if debug {
255                    let mut analyzer = DataFlowAnalyzer::new(tcx, true);
256                    analyzer.run();
257                    let result = analyzer.get_all_dataflow();
258                    rap_info!("{}", DataFlowGraphMapWrapper(result));
259                } else {
260                    let mut analyzer = DataFlowAnalyzer::new(tcx, false);
261                    analyzer.run();
262                    let result = analyzer.get_all_arg2ret();
263                    rap_info!("{}", Arg2RetMapWrapper(result));
264                }
265            }
266            AnalysisKind::OwnedHeap => {
267                let mut analyzer = OwnedHeapAnalyzer::new(tcx);
268                analyzer.run();
269                let result = analyzer.get_all_items();
270                rap_info!("{}", OHAResultMapWrapper(result));
271            }
272            AnalysisKind::Pathcond => {
273                let mut analyzer = RangeAnalyzer::<i64>::new(tcx, false);
274                analyzer.start_path_constraints_analysis();
275                let result = analyzer.get_all_path_constraints();
276                rap_info!("{}", PathConstraintMapWrapper(result));
277            }
278            &AnalysisKind::Range { debug } => {
279                let mut analyzer = RangeAnalyzer::<i64>::new(tcx, debug);
280                analyzer.run();
281                let result = analyzer.get_all_fn_ranges();
282                rap_info!("{}", RAResultMapWrapper(result));
283            }
284
285            AnalysisKind::Scan => {
286                ScanAnalysis::new(tcx).run();
287            }
288            AnalysisKind::Mir => {
289                ShowMir::new(tcx).start();
290            }
291            AnalysisKind::DotMir => {
292                ShowMir::new(tcx).start_generate_dot();
293            }
294            AnalysisKind::Ssa => {
295                SSATrans::new(tcx, false).start();
296            }
297        },
298
299        Commands::Verify(VerifyArgs { prepare_targets }) => {
300            if *prepare_targets {
301                PrepareTargets::new(tcx).run();
302            }
303        }
304    }
305}