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