Skip to main content

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

1use std::collections::HashSet;
2
3
4use rustc_middle::{mir::Local, ty::TyCtxt};
5use rustc_span::Span;
6
7use super::{report_encoding_bug, value_is_from_const};
8use crate::analysis::dataflow::*;
9use crate::check::opt::OptCheck;
10
11crate::def_paths! {
12    str_from_utf8: "std::str::from_utf8",
13}
14
15
16pub struct ArrayEncodingCheck {
17    record: Vec<Span>,
18}
19
20fn extract_ancestor_set_if_is_str_from(
21    graph: &Graph,
22    node_idx: Local,
23    node: &GraphNode,
24) -> Option<HashSet<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.str_from_utf8.last_def_id() {
29                return Some(graph.collect_ancestor_locals(node_idx, false));
30            }
31        }
32    }
33    None
34}
35
36fn is_valid_index_edge(graph: &Graph, edge: &GraphEdge) -> bool {
37    if let EdgeOp::Index = edge.op {
38        // must be Index edge
39        let dst_node = &graph.nodes[edge.dst];
40        if dst_node.in_edges.len() > 2 {
41            // must be the left value
42            let rvalue_edge_idx = dst_node.in_edges[2];
43            let rvalue_idx = graph.edges[rvalue_edge_idx].src;
44            if value_is_from_const(graph, rvalue_idx) {
45                return true;
46            }
47        }
48    }
49    false
50}
51
52impl OptCheck for ArrayEncodingCheck {
53    fn new() -> Self {
54        Self { record: Vec::new() }
55    }
56
57    fn check(&mut self, graph: &Graph, tcx: &TyCtxt) {
58        let _ = &DEFPATHS.get_or_init(|| DefPaths::new(tcx));
59        let common_ancestor = graph
60            .edges
61            .iter()
62            .filter_map(|edge| {
63                // The index must be an lvalue and the rvalue must come from a const
64                if is_valid_index_edge(graph, edge) {
65                    Some(graph.collect_ancestor_locals(edge.src, true))
66                } else {
67                    None
68                }
69            })
70            .reduce(|set1, set2| set1.into_iter().filter(|k| set2.contains(k)).collect());
71
72        if let Some(common_ancestor) = common_ancestor {
73            for (node_idx, node) in graph.nodes.iter_enumerated() {
74                if let Some(str_from_ancestor_set) =
75                    extract_ancestor_set_if_is_str_from(graph, node_idx, node)
76                {
77                    if !common_ancestor
78                        .intersection(&str_from_ancestor_set)
79                        .next()
80                        .is_some()
81                    {
82                        self.record.clear();
83                        return;
84                    }
85                    self.record.push(node.span);
86                }
87            }
88        }
89    }
90
91    fn report(&self, graph: &Graph) {
92        for span in self.record.iter() {
93            report_encoding_bug(graph, *span);
94        }
95    }
96
97    fn cnt(&self) -> usize {
98        self.record.len()
99    }
100}