rapx/check/opt/checking/encoding_checking/
vec_encoding.rs1use std::collections::HashSet;
2
3use crate::analysis::dataflow::*;
4use crate::helpers::def_path::DefPath;
5use once_cell::sync::OnceCell;
6use rustc_middle::{mir::Local, ty::TyCtxt};
7use rustc_span::Span;
8
9use super::{report_encoding_bug, value_is_from_const};
10
11static DEFPATHS: OnceCell<DefPaths> = OnceCell::new();
12
13struct DefPaths {
14 string_from_utf8: DefPath,
15 string_from_utf8_lossy: DefPath,
16 vec_new: DefPath,
17 vec_with_capacity: DefPath,
18 vec_push: DefPath,
19}
20
21impl DefPaths {
22 pub fn new(tcx: &TyCtxt<'_>) -> Self {
24 Self {
25 string_from_utf8: DefPath::new("std::string::String::from_utf8", tcx),
26 string_from_utf8_lossy: DefPath::new("std::string::String::from_utf8_lossy", tcx),
27 vec_new: DefPath::new("std::vec::Vec::new", tcx),
28 vec_with_capacity: DefPath::new("std::vec::Vec::with_capacity", tcx),
29 vec_push: DefPath::new("std::vec::Vec::push", tcx),
30 }
31 }
32}
33
34use crate::check::opt::OptCheck;
35
36pub struct VecEncodingCheck {
37 record: Vec<Span>,
38}
39
40fn extract_vec_if_is_string_from(graph: &Graph, node: &GraphNode) -> Option<Local> {
41 let def_paths = &DEFPATHS.get().unwrap();
42 for op in node.ops.iter() {
43 if let NodeOp::Call(def_id) = op {
44 if *def_id == def_paths.string_from_utf8.last_def_id()
45 || *def_id == def_paths.string_from_utf8_lossy.last_def_id()
46 {
47 let in_edge = &graph.edges[node.in_edges[0]];
48 return Some(in_edge.src);
49 }
50 }
51 }
52 None
53}
54
55fn find_upside_vec_new_node(graph: &Graph, node_idx: Local) -> Option<Local> {
56 let mut vec_new_node_idx = None;
57 let def_paths = &DEFPATHS.get().unwrap();
58 let mut node_operator = |graph: &Graph, idx: Local| -> DFSStatus {
60 let node = &graph.nodes[idx];
61 for op in node.ops.iter() {
62 if let NodeOp::Call(def_id) = op {
63 if *def_id == def_paths.vec_new.last_def_id()
64 || *def_id == def_paths.vec_with_capacity.last_def_id()
65 {
66 vec_new_node_idx = Some(idx);
67 return DFSStatus::Stop;
68 }
69 }
70 }
71 DFSStatus::Continue
72 };
73 let mut seen = HashSet::new();
74 graph.dfs(
75 node_idx,
76 Direction::Upside,
77 &mut node_operator,
78 &mut Graph::always_true_edge_validator,
79 false,
80 &mut seen,
81 );
82 vec_new_node_idx
83}
84
85fn find_downside_push_node(graph: &Graph, node_idx: Local) -> Vec<Local> {
88 let mut push_node_idxs: Vec<Local> = Vec::new();
89 let def_paths = &DEFPATHS.get().unwrap();
90 let mut node_operator = |graph: &Graph, idx: Local| -> DFSStatus {
92 let node = &graph.nodes[idx];
93 for op in node.ops.iter() {
94 if let NodeOp::Call(def_id) = op {
95 if *def_id == def_paths.vec_push.last_def_id() {
96 push_node_idxs.push(idx);
97 break;
98 }
99 }
100 }
101 DFSStatus::Continue
102 };
103 let mut seen = HashSet::new();
104 graph.dfs(
105 node_idx,
106 Direction::Downside,
107 &mut node_operator,
108 &mut Graph::always_true_edge_validator,
109 true,
110 &mut seen,
111 );
112 push_node_idxs
113}
114
115impl OptCheck for VecEncodingCheck {
116 fn new() -> Self {
117 Self { record: Vec::new() }
118 }
119
120 fn check(&mut self, graph: &Graph, tcx: &TyCtxt) {
121 let _ = &DEFPATHS.get_or_init(|| DefPaths::new(tcx));
122 for node in graph.nodes.iter() {
123 if let Some(vec_node_idx) = extract_vec_if_is_string_from(graph, node) {
124 if let Some(vec_new_idx) = find_upside_vec_new_node(graph, vec_node_idx) {
125 let vec_push_indice = find_downside_push_node(graph, vec_new_idx);
126 for vec_push_idx in vec_push_indice {
127 let pushed_value_edge = &graph.edges[graph.nodes[vec_push_idx].in_edges[1]]; let pushed_value_idx = pushed_value_edge.src;
129 if !value_is_from_const(graph, pushed_value_idx) {
130 self.record.clear();
131 return;
132 }
133 }
134 self.record.push(node.span);
135 }
136 }
137 }
138 }
139
140 fn report(&self, graph: &Graph) {
141 for span in self.record.iter() {
142 report_encoding_bug(graph, *span);
143 }
144 }
145
146 fn cnt(&self) -> usize {
147 self.record.len()
148 }
149}