rapx/analysis/opt/checking/
encoding_checking.rs

1pub mod array_encoding;
2pub mod string_lowercase;
3pub mod string_push;
4pub mod vec_encoding;
5
6use std::collections::HashSet;
7
8use crate::analysis::core::dataflow::graph::{
9    DFSStatus, Direction, EdgeIdx, EdgeOp, Graph, NodeOp,
10};
11use crate::analysis::opt::OptCheck;
12use crate::utils::log::{
13    relative_pos_range, span_to_filename, span_to_line_number, span_to_source_code,
14};
15use annotate_snippets::{Level, Renderer, Snippet};
16
17use rustc_middle::mir::Local;
18use rustc_middle::ty::TyCtxt;
19use rustc_span::Span;
20
21use array_encoding::ArrayEncodingCheck;
22use string_lowercase::StringLowercaseCheck;
23use string_push::StringPushCheck;
24use vec_encoding::VecEncodingCheck;
25
26pub struct EncodingCheck {
27    vec_encoding: VecEncodingCheck,
28    array_encoding: ArrayEncodingCheck,
29    string_push: StringPushCheck,
30    string_lowercase: StringLowercaseCheck,
31}
32
33impl OptCheck for EncodingCheck {
34    fn new() -> Self {
35        Self {
36            vec_encoding: VecEncodingCheck::new(),
37            array_encoding: ArrayEncodingCheck::new(),
38            string_push: StringPushCheck::new(),
39            string_lowercase: StringLowercaseCheck::new(),
40        }
41    }
42
43    fn check(&mut self, graph: &Graph, tcx: &TyCtxt) {
44        self.vec_encoding.check(graph, tcx);
45        self.array_encoding.check(graph, tcx);
46        self.string_push.check(graph, tcx);
47        self.string_lowercase.check(graph, tcx);
48    }
49
50    fn report(&self, graph: &Graph) {
51        self.vec_encoding.report(graph);
52        self.array_encoding.report(graph);
53        self.string_push.report(graph);
54        self.string_lowercase.report(graph);
55    }
56
57    fn cnt(&self) -> usize {
58        self.vec_encoding.cnt()
59            + self.array_encoding.cnt()
60            + self.string_lowercase.cnt()
61            + self.string_push.cnt()
62    }
63}
64
65fn report_encoding_bug(graph: &Graph, span: Span) {
66    let code_source = span_to_source_code(graph.span);
67    let filename = span_to_filename(graph.span);
68    let snippet = Snippet::source(&code_source)
69        .line_start(span_to_line_number(graph.span))
70        .origin(&filename)
71        .fold(true)
72        .annotation(
73            Level::Error
74                .span(relative_pos_range(graph.span, span))
75                .label("Checked here."),
76        );
77    let message = Level::Warning
78        .title("Unnecessary encoding checkings detected")
79        .snippet(snippet)
80        .footer(Level::Help.title("Use unsafe APIs."));
81    let renderer = Renderer::styled();
82    println!("{}", renderer.render(message));
83}
84
85// Warning: WE APPROXIMATELY VIEW CONST U8s AS SAFE INPUT
86// which may cause wrong result.
87
88// todo: ascii chars are extracted from String variables
89fn value_is_from_const(graph: &Graph, value_idx: Local) -> bool {
90    let mut const_found = false;
91    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::Const(_, src_ty) = op {
95                if src_ty.contains("u8") {
96                    const_found = true;
97                    return DFSStatus::Stop;
98                }
99            }
100        }
101        DFSStatus::Continue
102    };
103    let mut edge_validator = |graph: &Graph, idx: EdgeIdx| {
104        let edge = &graph.edges[idx];
105        let dst_node = &graph.nodes[edge.dst];
106        let same_seq_edge_cnt = dst_node
107            .in_edges
108            .iter()
109            .filter(|edge_idx| graph.edges[**edge_idx].seq == edge.seq)
110            .count();
111        match same_seq_edge_cnt {
112            1 => Graph::always_true_edge_validator(graph, idx),
113            2 => {
114                if let EdgeOp::Index = edge.op {
115                    DFSStatus::Continue
116                } else {
117                    DFSStatus::Stop
118                }
119            }
120            _ => DFSStatus::Stop,
121        }
122    };
123    let mut seen = HashSet::new();
124    graph.dfs(
125        value_idx,
126        Direction::Upside,
127        &mut node_operator,
128        &mut edge_validator,
129        false,
130        &mut seen,
131    );
132    const_found
133}