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