1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#![feature(rustc_private)]

extern crate rustc_data_structures;
extern crate rustc_driver;
extern crate rustc_hir;
extern crate rustc_infer;
extern crate rustc_interface;
extern crate rustc_middle;
extern crate rustc_mir_dataflow;
extern crate rustc_span;
extern crate rustc_trait_selection;
extern crate rustc_traits;

mod analysis;

use std::{borrow::Cow, io::Write};

use analysis::IssueFound;
use flowistry::infoflow;
use rustc_hir::{
  intravisit::{self, Visitor},
  BodyId,
};
use rustc_middle::{hir::nested_filter::OnlyBodies, ty::TyCtxt};
use rustc_plugin::{CrateFilter, RustcPlugin, RustcPluginArgs};
use rustc_utils::mir::borrowck_facts;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
pub struct IfcPlugin;

impl RustcPlugin for IfcPlugin {
  type Args = ();

  fn driver_name(&self) -> Cow<'static, str> {
    "ifc-driver".into()
  }

  fn version(&self) -> Cow<'static, str> {
    env!("CARGO_PKG_VERSION").into()
  }

  fn args(&self, _target_dir: &rustc_plugin::Utf8Path) -> RustcPluginArgs<Self::Args> {
    RustcPluginArgs {
      args: (),
      filter: CrateFilter::OnlyWorkspace,
    }
  }

  fn run(
    self,
    compiler_args: Vec<String>,
    _plugin_args: Self::Args,
  ) -> rustc_interface::interface::Result<()> {
    rustc_driver::RunCompiler::new(&compiler_args, &mut Callbacks).run()
  }
}

pub struct IfcVisitor<'tcx> {
  tcx: TyCtxt<'tcx>,
  issue_found: IssueFound,
}

impl<'tcx> Visitor<'tcx> for IfcVisitor<'tcx> {
  type NestedFilter = OnlyBodies;

  fn nested_visit_map(&mut self) -> Self::Map {
    self.tcx.hir()
  }

  fn visit_nested_body(&mut self, body_id: BodyId) {
    intravisit::walk_body(self, self.tcx.hir().body(body_id));

    let tcx = self.tcx;
    let local_def_id = tcx.hir().body_owner_def_id(body_id);
    let body_with_facts = borrowck_facts::get_body_with_borrowck_facts(tcx, local_def_id);
    let flow = &infoflow::compute_flow(tcx, body_id, body_with_facts);
    if let IssueFound::Yes = analysis::analyze(&body_id, flow).unwrap() {
      self.issue_found = IssueFound::Yes;
    }
  }
}

pub struct Callbacks;
impl rustc_driver::Callbacks for Callbacks {
  fn config(&mut self, config: &mut rustc_interface::Config) {
    borrowck_facts::enable_mir_simplification();
    config.override_queries = Some(borrowck_facts::override_queries);
  }

  fn after_crate_root_parsing<'tcx>(
    &mut self,
    _compiler: &rustc_interface::interface::Compiler,
    queries: &'tcx rustc_interface::Queries<'tcx>,
  ) -> rustc_driver::Compilation {
    queries.global_ctxt().unwrap().enter(|tcx| {
      let mut visitor = IfcVisitor {
        tcx,
        issue_found: IssueFound::No,
      };
      tcx.hir().visit_all_item_likes_in_crate(&mut visitor);

      if let IssueFound::No = visitor.issue_found {
        let mut stdout = StandardStream::stderr(ColorChoice::Auto);
        let mut green_spec = ColorSpec::new();
        green_spec.set_fg(Some(Color::Green));
        stdout.set_color(&green_spec).unwrap();
        writeln!(stdout, "No security issues found!",).unwrap();
      }
    });

    rustc_driver::Compilation::Stop
  }
}