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