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