#![feature(rustc_private)]
#![feature(box_patterns)]
#![feature(macro_metavar_expr_concat)]
#[macro_use]
pub mod utils;
pub mod analysis;
pub mod check;
pub mod cli;
pub mod def_id;
pub mod graphs;
pub mod help;
pub mod preprocess;
pub mod verify;
extern crate rustc_abi;
extern crate rustc_ast;
extern crate rustc_data_structures;
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_hir;
extern crate rustc_hir_pretty;
extern crate rustc_index;
extern crate rustc_infer;
extern crate rustc_interface;
extern crate rustc_metadata;
extern crate rustc_middle;
extern crate rustc_mir_dataflow;
extern crate rustc_public;
extern crate rustc_session;
extern crate rustc_span;
extern crate rustc_target;
extern crate rustc_trait_selection;
extern crate rustc_traits;
extern crate rustc_type_ir;
extern crate thin_vec;
use crate::{
analysis::{alias_analysis::mfp::MfpAliasAnalyzer, api_dependency, scan::ScanAnalysis},
check::{opt::Opt, rcanary::rCanary, safedrop::SafeDrop},
cli::{AliasStrategyKind, AnalysisKind, CheckArgs, Commands, OptLevel, RapxArgs, VerifyArgs},
verify::{driver::VerifyRun, target::PrepareTargets},
};
use analysis::{
Analysis,
alias_analysis::{AliasAnalysis, FnAliasMapWrapper, default::AliasAnalyzer},
api_dependency::ApiDependencyAnalyzer,
callgraph::{CallGraphAnalysis, FnCallDisplay, default::CallGraphAnalyzer},
dataflow::{Arg2RetMapWrapper, DataflowAnalysis, default::DataflowAnalyzer},
ownedheap_analysis::{OHAResultMapWrapper, OwnedHeapAnalysis, default::OwnedHeapAnalyzer},
path_analysis::{PathMapWrapper, default::PathAnalyzer},
range_analysis::{
PathConstraintMapWrapper, RAResultMapWrapper, RangeAnalysis, default::RangeAnalyzer,
},
ssa_transform::SSATrans,
upg::{TargetCrate, UPGAnalysis},
helpers::show_mir::ShowMir,
};
use rustc_ast::ast;
use rustc_driver::{Callbacks, Compilation};
use rustc_interface::interface::{self, Compiler};
use rustc_middle::{ty::TyCtxt, util::Providers};
use rustc_session::search_paths::PathKind;
use std::path::PathBuf;
use std::sync::Arc;
pub static RAPX_DEFAULT_ARGS: &[&str] = &[
"-Zalways-encode-mir",
"-Zmir-opt-level=0",
"-Zinline-mir-threshold=0",
"-Zinline-mir-hint-threshold=0",
"-Zcross-crate-inline-threshold=0",
];
#[derive(Debug, Clone)]
pub struct RapCallback {
args: RapxArgs,
}
impl RapCallback {
pub fn new(args: RapxArgs) -> Self {
Self { args }
}
fn is_building_test_crate(&self) -> bool {
match &self.args.test_crate {
None => true,
Some(test_crate) => {
let test_crate: &str = test_crate;
let package_name = std::env::var("CARGO_PKG_NAME")
.expect("cannot capture env var `CARGO_PKG_NAME`");
package_name == test_crate
}
}
}
}
impl Callbacks for RapCallback {
fn config(&mut self, config: &mut rustc_interface::Config) {
config.override_queries = Some(|_, providers| {
providers.extern_queries.used_crate_source = |tcx, cnum| {
let mut providers = Providers::default();
rustc_metadata::provide(&mut providers);
let mut crate_source = (providers.extern_queries.used_crate_source)(tcx, cnum);
Arc::make_mut(&mut crate_source).rlib = Some((PathBuf::new(), PathKind::All));
crate_source
};
});
}
fn after_crate_root_parsing(
&mut self,
compiler: &interface::Compiler,
krate: &mut ast::Crate,
) -> Compilation {
let build_std = compiler
.sess
.opts
.crate_name
.as_deref()
.map(|s| matches!(s, "core" | "std"))
.unwrap_or(false);
preprocess::dummy_fns::create_dummy_fns(krate, build_std);
preprocess::ssa_preprocess::create_ssa_struct(krate, build_std);
Compilation::Continue
}
fn after_analysis<'tcx>(&mut self, _compiler: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation {
rap_trace!("Execute after_analysis() of compiler callbacks");
rustc_public::rustc_internal::run(tcx, || {
def_id::init(tcx);
if self.is_building_test_crate() {
start_analyzer(tcx, self);
} else {
let package_name = std::env::var("CARGO_PKG_NAME")
.expect("cannot capture env var `CARGO_PKG_NAME`");
rap_trace!("skip analyzing package `{}`", package_name);
}
})
.expect("Failed to run rustc_public.");
rap_trace!("analysis done");
Compilation::Continue
}
}
pub fn start_analyzer(tcx: TyCtxt, callback: &RapCallback) {
match &callback.args.command {
Commands::Check(CheckArgs { uaf, mleak, opt }) => {
if uaf.is_some() {
SafeDrop::new(tcx).start();
}
if *mleak {
let mut heap = OwnedHeapAnalyzer::new(tcx);
heap.run();
let adt_owner = heap.get_all_items();
rCanary::new(tcx, adt_owner).start();
}
if let Some(opt_level) = opt {
match opt_level {
OptLevel::Report => Opt::new(tcx, 0).start(),
OptLevel::Default => Opt::new(tcx, 1).start(),
OptLevel::All => Opt::new(tcx, 2).start(),
}
}
}
Commands::Analyze { kind } => match kind {
AnalysisKind::Alias { strategy } => {
let alias = match strategy {
AliasStrategyKind::Mop => {
let mut analyzer = AliasAnalyzer::new(tcx);
analyzer.run();
analyzer.get_local_fn_alias()
}
AliasStrategyKind::Mfp => {
let mut analyzer = MfpAliasAnalyzer::new(tcx);
analyzer.run();
analyzer.get_local_fn_alias()
}
};
rap_info!("{}", FnAliasMapWrapper(alias));
}
AnalysisKind::Adg(args) => {
let config = api_dependency::Config {
resolve_generic: true,
visit_config: api_dependency::VisitConfig {
pub_only: !args.include_private,
include_generic: true,
ignore_const_generic: true,
include_unsafe: args.include_unsafe,
include_drop: args.include_drop,
},
max_generic_search_iteration: args.max_iteration,
dump: args.dump.clone(),
};
let mut analyzer = ApiDependencyAnalyzer::new(tcx, config);
analyzer.run();
}
AnalysisKind::Upg => {
UPGAnalysis::new(tcx).start(TargetCrate::Other);
}
AnalysisKind::UpgStd => {
UPGAnalysis::new(tcx).start(TargetCrate::Std);
}
AnalysisKind::Callgraph => {
let mut analyzer = CallGraphAnalyzer::new(tcx);
analyzer.run();
let callgraph = analyzer.get_fn_calls();
rap_info!(
"{}",
FnCallDisplay {
fn_calls: &callgraph,
tcx
}
);
}
&AnalysisKind::Dataflow { debug, draw } => {
let mut analyzer = DataflowAnalyzer::new(tcx, debug).with_draw(draw);
analyzer.run();
let result = analyzer.get_all_arg2ret();
rap_info!("{}", Arg2RetMapWrapper(result));
}
AnalysisKind::OwnedHeap => {
let mut analyzer = OwnedHeapAnalyzer::new(tcx);
analyzer.run();
let result = analyzer.get_all_items();
rap_info!("{}", OHAResultMapWrapper(result));
}
AnalysisKind::Paths => {
let mut analyzer = PathAnalyzer::new(tcx, false);
analyzer.run();
let result = analyzer.get_all_paths();
rap_info!("{}", PathMapWrapper(result));
}
AnalysisKind::Pathcond => {
let mut analyzer = RangeAnalyzer::<i64>::new(tcx, false);
analyzer.start_path_constraints_analysis();
let result = analyzer.get_all_path_constraints();
rap_info!("{}", PathConstraintMapWrapper(result));
}
&AnalysisKind::Range { debug } => {
let mut analyzer = RangeAnalyzer::<i64>::new(tcx, debug);
analyzer.run();
let result = analyzer.get_all_fn_ranges();
rap_info!("{}", RAResultMapWrapper(result));
}
AnalysisKind::Scan => {
ScanAnalysis::new(tcx).run();
}
AnalysisKind::Mir => {
ShowMir::new(tcx).start();
}
AnalysisKind::DotMir => {
ShowMir::new(tcx).start_generate_dot();
}
AnalysisKind::Ssa => {
SSATrans::new(tcx, false).start();
}
},
Commands::Verify(VerifyArgs { prepare_targets, allow_pathseg_repeat }) => {
if *prepare_targets {
PrepareTargets::new(tcx).run();
} else {
VerifyRun::new(tcx, *allow_pathseg_repeat).run();
}
}
}
}