1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use crate::abstract_domain::BricksDomain;
use crate::analysis::function_signature::FunctionSignature;
use crate::analysis::graph::Graph;
use crate::analysis::pointer_inference::PointerInference;
use crate::analysis::string_abstraction::StringAbstraction;
use crate::intermediate_representation::Project;
use crate::prelude::*;
use crate::utils::log::LogMessage;
use std::collections::BTreeMap;

/// A struct containing pointers to all known analysis results
/// that may be needed as input for other analyses and CWE checks.
#[derive(Clone, Copy)]
pub struct AnalysisResults<'a> {
    /// The content of the binary file
    pub binary: &'a [u8],
    /// The computed control flow graph of the program.
    pub control_flow_graph: &'a Graph<'a>,
    /// A pointer to the project struct
    pub project: &'a Project,
    /// The results of the function signature analysis if already computed.
    pub function_signatures: Option<&'a BTreeMap<Tid, FunctionSignature>>,
    /// The result of the pointer inference analysis if already computed.
    pub pointer_inference: Option<&'a PointerInference<'a>>,
    /// The result of the string abstraction if already computed.
    pub string_abstraction: Option<&'a StringAbstraction<'a, BricksDomain>>,
}

impl<'a> AnalysisResults<'a> {
    /// Create a new `AnalysisResults` struct with only the project itself known.
    pub fn new(
        binary: &'a [u8],
        control_flow_graph: &'a Graph<'a>,
        project: &'a Project,
    ) -> AnalysisResults<'a> {
        AnalysisResults {
            binary,
            control_flow_graph,
            project,
            function_signatures: None,
            pointer_inference: None,
            string_abstraction: None,
        }
    }

    /// Compute the function signatures for internal functions.
    pub fn compute_function_signatures(
        &self,
    ) -> (BTreeMap<Tid, FunctionSignature>, Vec<LogMessage>) {
        crate::analysis::function_signature::compute_function_signatures(
            self.project,
            self.control_flow_graph,
        )
    }

    /// Create a new `AnalysisResults` struct containing the given function signature analysis results.
    pub fn with_function_signatures(
        self,
        function_signatures: Option<&'a BTreeMap<Tid, FunctionSignature>>,
    ) -> AnalysisResults<'a> {
        AnalysisResults {
            function_signatures,
            ..self
        }
    }

    /// Compute the pointer inference analysis.
    /// The result gets returned, but not saved to the `AnalysisResults` struct itself.
    pub fn compute_pointer_inference(
        &'a self,
        config: &serde_json::Value,
        print_stats: bool,
    ) -> PointerInference<'a> {
        crate::analysis::pointer_inference::run(
            self,
            serde_json::from_value(config.clone()).unwrap(),
            false,
            print_stats,
        )
    }

    /// Create a new `AnalysisResults` struct containing the given pointer inference analysis results.
    pub fn with_pointer_inference<'b: 'a>(
        self,
        pi_results: Option<&'b PointerInference<'a>>,
    ) -> AnalysisResults<'b> {
        AnalysisResults {
            pointer_inference: pi_results,
            ..self
        }
    }

    /// Compute the string abstraction.
    /// As the string abstraction depends on the pointer inference, the
    /// pointer inference is also computed and put into the `AnalysisResults` struct.
    /// The result gets returned, but not saved to the `AnalysisResults` struct itself.
    pub fn compute_string_abstraction(
        &'a self,
        config: &serde_json::Value,
        pi_results: Option<&'a PointerInference<'a>>,
    ) -> StringAbstraction<BricksDomain> {
        crate::analysis::string_abstraction::run(
            self.project,
            self.control_flow_graph,
            pi_results.unwrap(),
            serde_json::from_value(config.clone()).unwrap(),
        )
    }

    /// Create a new `AnalysisResults` struct containing the given string abstraction results.
    pub fn with_string_abstraction<'b: 'a>(
        self,
        string_abstraction: Option<&'b StringAbstraction<'a, BricksDomain>>,
    ) -> AnalysisResults<'b> {
        AnalysisResults {
            string_abstraction,
            ..self
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::analysis::graph::get_program_cfg;

    impl<'a> AnalysisResults<'a> {
        /// Mocks the `AnalysisResults` struct with a given project.
        /// Note that the function leaks memory!
        pub fn mock_from_project(project: &'a Project) -> AnalysisResults<'a> {
            let graph = Box::new(get_program_cfg(&project.program));
            let graph: &'a Graph = Box::leak(graph);
            let binary: &'a Vec<u8> = Box::leak(Box::new(Vec::new()));
            let analysis_results = AnalysisResults::new(binary, graph, project);
            let (fn_sigs, _) = analysis_results.compute_function_signatures();
            let fn_sigs: &'a BTreeMap<_, _> = Box::leak(Box::new(fn_sigs));
            let analysis_results = analysis_results.with_function_signatures(Some(fn_sigs));
            analysis_results
        }
    }
}