Skip to main content

rapx/check/opt/checking/encoding_checking/
string_push.rs

1use super::value_is_from_const;
2use crate::{
3    analysis::dataflow::*,
4};
5use rustc_middle::{mir::Local, ty::TyCtxt};
6use rustc_span::Span;
7
8use annotate_snippets::Level;
9
10use crate::check::opt::report::OptReport;
11
12crate::def_paths! {
13    string_new: "std::string::String::new",
14    string_push: "std::string::String::push",
15}
16
17
18use crate::check::opt::OptCheck;
19
20pub struct StringPushCheck {
21    record: Vec<Span>,
22}
23
24fn extract_value_if_is_string_push(graph: &Graph, node: &GraphNode) -> Option<Local> {
25    let def_paths = DEFPATHS.get().unwrap();
26    for op in node.ops.iter() {
27        if let NodeOp::Call(def_id) = op {
28            if *def_id == def_paths.string_push.last_def_id() {
29                let push_value_idx = graph.edges[node.in_edges[1]].src; //the secod parameter
30                return Some(push_value_idx);
31            }
32        }
33    }
34    None
35}
36
37fn find_upside_string_new(graph: &Graph, node_idx: Local) -> Option<Local> {
38    let def_paths = DEFPATHS.get().unwrap();
39    graph.find_first_node(
40        node_idx,
41        Direction::Upside,
42        &mut |graph: &Graph, idx: Local| {
43            let node = &graph.nodes[idx];
44            for op in node.ops.iter() {
45                if let NodeOp::Call(def_id) = op {
46                    if *def_id == def_paths.string_new.last_def_id() {
47                        return true;
48                    }
49                }
50            }
51            false
52        },
53        &mut Graph::always_true_edge_validator,
54    )
55}
56
57impl OptCheck for StringPushCheck {
58    fn new() -> Self {
59        Self { record: Vec::new() }
60    }
61
62    fn check(&mut self, graph: &Graph, tcx: &TyCtxt) {
63        let _ = &DEFPATHS.get_or_init(|| DefPaths::new(tcx));
64        for (node_idx, node) in graph.nodes.iter_enumerated() {
65            if let Some(pushed_value_idx) = extract_value_if_is_string_push(graph, node) {
66                if find_upside_string_new(graph, node_idx).is_some() {
67                    if !value_is_from_const(graph, pushed_value_idx) {
68                        self.record.clear(); // Warning: Not rigorous, push of other string may cause clear
69                        return;
70                    }
71                    self.record.push(node.span);
72                }
73            }
74        }
75    }
76
77    fn report(&self, graph: &Graph) {
78        if !self.record.is_empty() {
79            report_string_push_bug(graph, &self.record);
80        }
81    }
82
83    fn cnt(&self) -> usize {
84        self.record.len()
85    }
86}
87
88fn report_string_push_bug(graph: &Graph, spans: &Vec<Span>) {
89    let mut report = OptReport::from_graph(graph)
90        .title("Unnecessary encoding checkings detected");
91    for span in spans.iter() {
92        report = report.annotate(Level::Error, *span, "Checked here.");
93    }
94    report.footer("Use unsafe APIs instead.").emit();
95}