rapx/check/opt/checking/encoding_checking/
string_push.rs1use 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; 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(); 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}