Skip to main content

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

1use crate::analysis::dataflow::*;
2use rustc_middle::{mir::Local, ty::TyCtxt};
3use rustc_span::Span;
4
5use super::{report_encoding_bug, value_is_from_const};
6
7crate::def_paths! {
8    string_from_utf8: "std::string::String::from_utf8",
9    string_from_utf8_lossy: "std::string::String::from_utf8_lossy",
10    vec_new: "std::vec::Vec::new",
11    vec_with_capacity: "std::vec::Vec::with_capacity",
12    vec_push: "std::vec::Vec::push",
13}
14
15use crate::check::opt::OptCheck;
16
17pub struct VecEncodingCheck {
18    record: Vec<Span>,
19}
20
21fn extract_vec_if_is_string_from(graph: &Graph, node: &GraphNode) -> Option<Local> {
22    let def_paths = &DEFPATHS.get().unwrap();
23    for op in node.ops.iter() {
24        if let NodeOp::Call(def_id) = op {
25            if *def_id == def_paths.string_from_utf8.last_def_id()
26                || *def_id == def_paths.string_from_utf8_lossy.last_def_id()
27            {
28                let in_edge = &graph.edges[node.in_edges[0]];
29                return Some(in_edge.src);
30            }
31        }
32    }
33    None
34}
35
36fn find_upside_vec_new_node(graph: &Graph, node_idx: Local) -> Option<Local> {
37    let def_paths = &DEFPATHS.get().unwrap();
38    graph.find_first_node(
39        node_idx,
40        Direction::Upside,
41        &mut |graph: &Graph, idx: Local| {
42            let node = &graph.nodes[idx];
43            for op in node.ops.iter() {
44                if let NodeOp::Call(def_id) = op {
45                    if *def_id == def_paths.vec_new.last_def_id()
46                        || *def_id == def_paths.vec_with_capacity.last_def_id()
47                    {
48                        return true;
49                    }
50                }
51            }
52            false
53        },
54        &mut Graph::always_true_edge_validator,
55    )
56}
57
58fn find_downside_push_node(graph: &Graph, node_idx: Local) -> Vec<Local> {
59    let def_paths = &DEFPATHS.get().unwrap();
60    graph.find_all_nodes(
61        node_idx,
62        Direction::Downside,
63        &mut |graph: &Graph, idx: Local| {
64            let node = &graph.nodes[idx];
65            for op in node.ops.iter() {
66                if let NodeOp::Call(def_id) = op {
67                    if *def_id == def_paths.vec_push.last_def_id() {
68                        return true;
69                    }
70                }
71            }
72            false
73        },
74        &mut Graph::always_true_edge_validator,
75    )
76}
77
78impl OptCheck for VecEncodingCheck {
79    fn new() -> Self {
80        Self { record: Vec::new() }
81    }
82
83    fn check(&mut self, graph: &Graph, tcx: &TyCtxt) {
84        DEFPATHS.get_or_init(|| DefPaths::new(tcx));
85        for node in graph.nodes.iter() {
86            if let Some(vec_node_idx) = extract_vec_if_is_string_from(graph, node) {
87                if let Some(vec_new_idx) = find_upside_vec_new_node(graph, vec_node_idx) {
88                    let vec_push_indice = find_downside_push_node(graph, vec_new_idx);
89                    for vec_push_idx in vec_push_indice {
90                        let pushed_value_edge = &graph.edges[graph.nodes[vec_push_idx].in_edges[1]]; // The second parameter
91                        let pushed_value_idx = pushed_value_edge.src;
92                        if !value_is_from_const(graph, pushed_value_idx) {
93                            self.record.clear();
94                            return;
95                        }
96                    }
97                    self.record.push(node.span);
98                }
99            }
100        }
101    }
102
103    fn report(&self, graph: &Graph) {
104        for span in self.record.iter() {
105            report_encoding_bug(graph, *span);
106        }
107    }
108
109    fn cnt(&self) -> usize {
110        self.record.len()
111    }
112}