rapx/
lib.rs

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