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 def_id;
9pub mod preprocess;
10extern crate intervals;
11extern crate rustc_abi;
12extern crate rustc_ast;
13extern crate rustc_data_structures;
14extern crate rustc_driver;
15extern crate rustc_errors;
16extern crate rustc_hir;
17extern crate rustc_hir_pretty;
18extern crate rustc_index;
19extern crate rustc_infer;
20extern crate rustc_interface;
21extern crate rustc_metadata;
22extern crate rustc_middle;
23extern crate rustc_public;
24extern crate rustc_session;
25extern crate rustc_span;
26extern crate rustc_target;
27extern crate rustc_trait_selection;
28extern crate rustc_traits;
29extern crate rustc_type_ir;
30extern crate thin_vec;
31use crate::analysis::scan::ScanAnalysis;
32use analysis::{
33    core::{
34        alias_analysis::{default::AliasAnalyzer, AAResultMapWrapper, AliasAnalysis},
35        api_dependency::ApiDependencyAnalyzer,
36        callgraph::{default::CallGraphAnalyzer, CallGraphAnalysis, CallGraphDisplay},
37        dataflow::{
38            default::DataFlowAnalyzer, Arg2RetMapWrapper, DataFlowAnalysis, DataFlowGraphMapWrapper,
39        },
40        ownedheap_analysis::{default::OwnedHeapAnalyzer, OHAResultMapWrapper, OwnedHeapAnalysis},
41        range_analysis::{
42            default::RangeAnalyzer, PathConstraintMapWrapper, RAResultMapWrapper, RangeAnalysis,
43        },
44        ssa_transform::SSATrans,
45    },
46    opt::Opt,
47    rcanary::rCanary,
48    safedrop::SafeDrop,
49    senryx::{CheckLevel, SenryxCheck},
50    test::Test,
51    unsafety_isolation::{UigInstruction, UnsafetyIsolationCheck},
52    utils::show_mir::ShowMir,
53    Analysis,
54};
55use rustc_ast::ast;
56use rustc_driver::{Callbacks, Compilation};
57use rustc_interface::{
58    interface::{self, Compiler},
59    Config,
60};
61use rustc_middle::{ty::TyCtxt, util::Providers};
62use rustc_session::search_paths::PathKind;
63use std::path::PathBuf;
64use std::{env, sync::Arc};
65
66// Insert rustc arguments at the beginning of the argument list that RAP wants to be
67// set per default, for maximal validation power.
68pub static RAP_DEFAULT_ARGS: &[&str] = &["-Zalways-encode-mir", "-Zmir-opt-level=0"];
69
70/// This is the data structure to handle rapx options as a rustc callback.
71
72#[derive(Debug, Clone, Hash)]
73pub struct RapCallback {
74    alias: bool,
75    api_dependency: bool,
76    callgraph: bool,
77    dataflow: usize,
78    ownedheap: bool,
79    range: usize,
80    ssa: bool,
81    test: bool,
82    infer: bool,
83    opt: usize,
84    rcanary: bool,
85    safedrop: bool,
86    show_mir: bool,
87    unsafety_isolation: usize,
88    verify: bool,
89    verify_std: bool,
90    scan: bool,
91    test_crate: Option<String>,
92}
93
94#[allow(clippy::derivable_impls)]
95impl Default for RapCallback {
96    fn default() -> Self {
97        Self {
98            alias: false,
99            api_dependency: false,
100            callgraph: false,
101            dataflow: 0,
102            ownedheap: false,
103            range: 0,
104            ssa: false,
105            test: false,
106            infer: false,
107            opt: usize::MAX,
108            rcanary: false,
109            safedrop: false,
110            show_mir: false,
111            unsafety_isolation: 0,
112            verify: false,
113            verify_std: false,
114            scan: false,
115            test_crate: None,
116        }
117    }
118}
119
120impl Callbacks for RapCallback {
121    fn config(&mut self, config: &mut Config) {
122        config.override_queries = Some(|_, providers| {
123            providers.extern_queries.used_crate_source = |tcx, cnum| {
124                let mut providers = Providers::default();
125                rustc_metadata::provide(&mut providers);
126                let mut crate_source = (providers.extern_queries.used_crate_source)(tcx, cnum);
127                // HACK: rustc will emit "crate ... required to be available in rlib format, but
128                // was not found in this form" errors once we use `tcx.dependency_formats()` if
129                // there's no rlib provided, so setting a dummy path here to workaround those errors.
130                Arc::make_mut(&mut crate_source).rlib = Some((PathBuf::new(), PathKind::All));
131                crate_source
132            };
133        });
134    }
135
136    fn after_crate_root_parsing(
137        &mut self,
138        _compiler: &interface::Compiler,
139        _krate: &mut ast::Crate,
140    ) -> Compilation {
141        preprocess::ssa_preprocess::create_ssa_struct(_krate);
142        Compilation::Continue
143    }
144    fn after_analysis<'tcx>(&mut self, _compiler: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation {
145        rap_trace!("Execute after_analysis() of compiler callbacks");
146
147        rustc_public::rustc_internal::run(tcx, || {
148            def_id::init(tcx);
149            if self.is_building_test_crate() {
150                start_analyzer(tcx, self);
151            } else {
152                let package_name = std::env::var("CARGO_PKG_NAME")
153                    .expect("cannot capture env var `CARGO_PKG_NAME`");
154                rap_trace!("skip analyzing package `{}`", package_name);
155            }
156        })
157        .expect("Failed to run rustc_public.");
158        rap_trace!("analysis done");
159
160        Compilation::Continue
161    }
162}
163
164impl RapCallback {
165    fn is_building_test_crate(&self) -> bool {
166        match &self.test_crate {
167            None => true,
168            Some(test_crate) => {
169                let test_crate: &str = test_crate;
170                let package_name = std::env::var("CARGO_PKG_NAME")
171                    .expect("cannot capture env var `CARGO_PKG_NAME`");
172                package_name == test_crate
173            }
174        }
175    }
176
177    /// Enable alias analysis. The parameter is used to config the threshold of alias analysis.
178    /// Currently, we mainly use it to control the depth of field-sensitive analysis.
179    /// -alias0: set field depth limit to 10; do not distinguish different flows within a each
180    /// strongly-connected component.
181    /// -alias1: set field depth limit to 20 (this is default setting).
182    /// -alias2: set field depth limit to 30.
183    pub fn enable_alias(&mut self, arg: String) {
184        self.alias = true;
185        match arg.as_str() {
186            "-alias" => {
187                env::set_var("ALIAS", "1");
188            }
189            "-alias0" => {
190                env::set_var("ALIAS", "0");
191            }
192            "-alias1" => {
193                env::set_var("ALIAS", "1");
194            }
195            "-alias2" => {
196                env::set_var("ALIAS", "2");
197            }
198            _ => {}
199        }
200    }
201
202    /// Test if alias analysis is enabled.
203    pub fn is_alias_enabled(&self) -> bool {
204        self.alias
205    }
206
207    /// Enable API-dependency graph generation.
208    pub fn enable_api_dependency(&mut self) {
209        self.api_dependency = true;
210    }
211
212    /// Test if API-dependency graph generation is enabled.
213    pub fn is_api_dependency_enabled(&self) -> bool {
214        self.api_dependency
215    }
216
217    /// Enable call-graph analysis.
218    pub fn enable_callgraph(&mut self) {
219        self.callgraph = true;
220    }
221
222    /// Test if call-graph analysis is enabled.
223    pub fn is_callgraph_enabled(&self) -> bool {
224        self.callgraph
225    }
226
227    /// Enable owned heap analysis.
228    pub fn enable_ownedheap(&mut self) {
229        self.ownedheap = true;
230    }
231
232    /// Test if owned-heap analysis is enabled.
233    pub fn is_ownedheap_enabled(&self) -> bool {
234        self.ownedheap
235    }
236
237    /// Enable dataflow analysis.
238    pub fn enable_dataflow(&mut self, x: usize) {
239        self.dataflow = x;
240    }
241
242    /// Test if dataflow analysis is enabled.
243    pub fn is_dataflow_enabled(&self) -> usize {
244        self.dataflow
245    }
246
247    /// Enable range analysis.
248    pub fn enable_range_analysis(&mut self, x: usize) {
249        self.range = x;
250    }
251
252    /// Test if range analysis is enabled.
253    pub fn is_range_analysis_enabled(&self) -> bool {
254        self.range > 0
255    }
256
257    /// Enable test of features provided by the core analysis traits.
258    pub fn enable_test(&mut self) {
259        self.test = true;
260    }
261
262    /// Check if test is enabled.
263    pub fn is_test_enabled(&self) -> bool {
264        self.test
265    }
266
267    /// Enable ssa transformation
268    pub fn enable_ssa_transform(&mut self) {
269        self.ssa = true;
270    }
271
272    /// Test if ssa transformation is enabled.
273    pub fn is_ssa_transform_enabled(&self) -> bool {
274        self.ssa
275    }
276
277    /// Enable optimization analysis for performance bug detection.
278    pub fn enable_opt(&mut self, x: usize) {
279        self.opt = x;
280    }
281
282    /// Test if optimization analysis is enabled.
283    pub fn is_opt_enabled(&self) -> usize {
284        self.opt
285    }
286
287    /// Enable rcanary for memory leakage detection.
288    pub fn enable_rcanary(&mut self) {
289        self.rcanary = true;
290    }
291
292    /// Test if rcanary is enabled.
293    pub fn is_rcanary_enabled(&self) -> bool {
294        self.rcanary
295    }
296
297    /// Enable safedrop for use-after-free bug detection.
298    /// Similar to alias analysis, the second parameter is to control the depth threshold for
299    /// field-sensitive analysis.
300    pub fn enable_safedrop(&mut self, arg: String) {
301        self.safedrop = true;
302        match arg.as_str() {
303            "-F" => {
304                env::set_var("SAFEDROP", "1");
305                env::set_var("MOP", "1");
306            }
307            "-F0" => {
308                env::set_var("SAFEDROP", "0");
309                env::set_var("MOP", "0");
310            }
311            "-F1" => {
312                env::set_var("SAFEDROP", "1");
313                env::set_var("MOP", "1");
314            }
315            "-F2" => {
316                env::set_var("SAFEDROP", "2");
317                env::set_var("MOP", "2");
318            }
319            "-uaf" => {
320                env::set_var("SAFEDROP", "1");
321                env::set_var("MOP", "1");
322            }
323            _ => {}
324        }
325    }
326
327    /// Test if safedrop is enabled.
328    pub fn is_safedrop_enabled(&self) -> bool {
329        self.safedrop
330    }
331
332    /// Enable mir display.
333    pub fn enable_show_mir(&mut self) {
334        self.show_mir = true;
335    }
336
337    /// Test if mir display is enabled.
338    pub fn is_show_mir_enabled(&self) -> bool {
339        self.show_mir
340    }
341
342    pub fn enable_unsafety_isolation(&mut self, x: usize) {
343        self.unsafety_isolation = x;
344    }
345
346    pub fn is_unsafety_isolation_enabled(&self) -> usize {
347        self.unsafety_isolation
348    }
349
350    pub fn enable_verify(&mut self) {
351        self.verify = true;
352    }
353
354    pub fn is_verify_enabled(&self) -> bool {
355        self.verify
356    }
357
358    pub fn enable_verify_std(&mut self) {
359        self.verify_std = true;
360    }
361
362    pub fn is_verify_std_enabled(&self) -> bool {
363        self.verify_std
364    }
365
366    pub fn enable_infer(&mut self) {
367        self.infer = true;
368    }
369
370    pub fn is_infer_enabled(&self) -> bool {
371        self.infer
372    }
373
374    pub fn enable_scan(&mut self) {
375        self.scan = true;
376    }
377
378    pub fn is_scan_enabled(&self) -> bool {
379        self.scan
380    }
381
382    pub fn set_test_crate(&mut self, crate_name: impl ToString) {
383        self.test_crate = Some(crate_name.to_string())
384    }
385}
386
387/// Start the analysis with the features enabled.
388pub fn start_analyzer(tcx: TyCtxt, callback: &RapCallback) {
389    if callback.is_alias_enabled() {
390        let mut analyzer = AliasAnalyzer::new(tcx);
391        analyzer.run();
392        let alias = analyzer.get_local_fn_alias();
393        rap_info!("{}", AAResultMapWrapper(alias));
394    }
395
396    if callback.is_api_dependency_enabled() {
397        let mut analyzer = ApiDependencyAnalyzer::new(
398            tcx,
399            analysis::core::api_dependency::Config {
400                pub_only: true,
401                resolve_generic: true,
402                ignore_const_generic: true,
403            },
404        );
405        analyzer.run();
406    }
407
408    if callback.is_callgraph_enabled() {
409        let mut analyzer = CallGraphAnalyzer::new(tcx);
410        analyzer.run();
411        let callgraph = analyzer.get_callgraph();
412        rap_info!(
413            "{}",
414            CallGraphDisplay {
415                graph: &callgraph,
416                tcx
417            }
418        );
419        //analyzer.display();
420    }
421
422    match callback.is_dataflow_enabled() {
423        1 => {
424            let mut analyzer = DataFlowAnalyzer::new(tcx, false);
425            analyzer.run();
426            let result = analyzer.get_all_arg2ret();
427            rap_info!("{}", Arg2RetMapWrapper(result));
428        }
429        2 => {
430            let mut analyzer = DataFlowAnalyzer::new(tcx, true);
431            analyzer.run();
432            let result = analyzer.get_all_dataflow();
433            rap_info!("{}", DataFlowGraphMapWrapper(result));
434        }
435        _ => {}
436    }
437
438    if callback.is_ownedheap_enabled() {
439        let mut analyzer = OwnedHeapAnalyzer::new(tcx);
440        analyzer.run();
441        let result = analyzer.get_all_items();
442        rap_info!("{}", OHAResultMapWrapper(result));
443    }
444
445    if callback.is_range_analysis_enabled() {
446        match callback.range {
447            1 => {
448                let mut analyzer = RangeAnalyzer::<i128>::new(tcx, false);
449                analyzer.run();
450                let result = analyzer.get_all_fn_ranges();
451                rap_info!("{}", RAResultMapWrapper(result));
452            }
453            2 => {
454                let mut analyzer = RangeAnalyzer::<i128>::new(tcx, true);
455                analyzer.run();
456                let result = analyzer.get_all_fn_ranges();
457                rap_info!("{}", RAResultMapWrapper(result));
458            }
459            3 => {
460                let mut analyzer = RangeAnalyzer::<i128>::new(tcx, false);
461                analyzer.start_path_constraints_analysis();
462                let result = analyzer.get_all_path_constraints();
463                rap_info!("{}", PathConstraintMapWrapper(result));
464            }
465            _ => {}
466        }
467    }
468
469    if callback.is_test_enabled() {
470        let test = Test::new(tcx);
471        test.start();
472    }
473
474    match callback.is_opt_enabled() {
475        0 => Opt::new(tcx, 0).start(),
476        1 => Opt::new(tcx, 1).start(),
477        2 => Opt::new(tcx, 2).start(),
478        _ => {}
479    }
480
481    let _rcanary: Option<rCanary> = if callback.is_rcanary_enabled() {
482        let mut heap = OwnedHeapAnalyzer::new(tcx);
483        heap.run();
484        let adt_owner = heap.get_all_items();
485        let mut rcx = rCanary::new(tcx, adt_owner);
486        rcx.start();
487        Some(rcx)
488    } else {
489        None
490    };
491
492    if callback.is_safedrop_enabled() {
493        SafeDrop::new(tcx).start();
494    }
495
496    if callback.is_show_mir_enabled() {
497        ShowMir::new(tcx).start();
498    }
499
500    if callback.is_ssa_transform_enabled() {
501        SSATrans::new(tcx, false).start();
502    }
503
504    let x = callback.is_unsafety_isolation_enabled();
505    match x {
506        1 => UnsafetyIsolationCheck::new(tcx).start(UigInstruction::StdSp),
507        2 => UnsafetyIsolationCheck::new(tcx).start(UigInstruction::Doc),
508        3 => UnsafetyIsolationCheck::new(tcx).start(UigInstruction::Upg),
509        4 => UnsafetyIsolationCheck::new(tcx).start(UigInstruction::Ucons),
510        _ => {}
511    }
512
513    if callback.is_verify_enabled() {
514        let check_level = CheckLevel::Medium;
515        SenryxCheck::new(tcx, 2).start(check_level, true);
516    }
517
518    if callback.is_verify_std_enabled() {
519        SenryxCheck::new(tcx, 2).start_analyze_std_func();
520    }
521
522    if callback.is_infer_enabled() {
523        let check_level = CheckLevel::Medium;
524        SenryxCheck::new(tcx, 2).start(check_level, false);
525    }
526
527    if callback.is_scan_enabled() {
528        ScanAnalysis::new(tcx).run();
529    }
530}