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
//! This module contains the Context Object for the String Abstraction.
//! It holds all necessary information that stays unchanged during the analysis.

use std::{
    collections::{HashMap, HashSet},
    marker::PhantomData,
};

use petgraph::{graph::NodeIndex, visit::IntoNodeReferences};

use crate::{
    abstract_domain::{AbstractDomain, DomainInsertion, HasTop},
    analysis::{
        forward_interprocedural_fixpoint::Context as _, graph::Node,
        interprocedural_fixpoint_generic::NodeValue,
        pointer_inference::PointerInference as PointerInferenceComputation,
        pointer_inference::State as PointerInferenceState,
    },
    intermediate_representation::{Def, ExternSymbol, Project, Term, Tid},
};

use super::{state::State, Config};

pub mod symbol_calls;
mod trait_impls;

/// Contains all context information needed for the string abstract fixpoint computation.
///
/// The struct also implements the `interprocedural_fixpoint::Context` trait to enable the fixpoint computation.
pub struct Context<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> {
    /// A reference to the `Project` object representing the binary
    pub project: &'a Project,
    /// A pointer to the results of the pointer inference analysis.
    /// They are used to determine the targets of pointers to memory,
    /// which in turn is used to keep track of taint on the stack or on the heap.
    pub pointer_inference_results: &'a PointerInferenceComputation<'a>,
    /// Maps the TIDs of functions that shall be treated as string extern symbols to the `ExternSymbol` object representing it.
    pub string_symbol_map: HashMap<Tid, &'a ExternSymbol>,
    /// Maps the TIDs of functions that shall be treated as general extern symbols to the `ExternSymbol` object representing it.
    pub extern_symbol_map: HashMap<Tid, &'a ExternSymbol>,
    /// Maps string symbols to their corresponding format string parameter index.
    pub format_string_index_map: HashMap<String, usize>,
    /// A map to get the node index of the `BlkStart` node containing a given [`Def`] as the first `Def` of the block.
    /// The keys are of the form `(Def-TID, Current-Sub-TID)`
    /// to distinguish the nodes for blocks contained in more than one function.
    pub block_start_node_map: HashMap<(Tid, Tid), NodeIndex>,
    /// A set containing a given [`Def`] as the first `Def` of the block.
    /// The keys are of the form `(Def-TID, Current-Sub-TID)`
    /// to distinguish the nodes for blocks contained in more than one function.
    pub block_first_def_set: HashSet<(Tid, Tid)>,
    /// A map to get the node index of the `BlkEnd` node containing a given [`Jmp`](crate::intermediate_representation::Jmp).
    /// The keys are of the form `(Jmp-TID, Current-Sub-TID)`
    /// to distinguish the nodes for blocks contained in more than one function.
    pub jmp_to_blk_end_node_map: HashMap<(Tid, Tid), NodeIndex>,
    _phantom_string_domain: PhantomData<T>,
}
impl<'a, T: AbstractDomain + HasTop + Eq + From<String> + DomainInsertion> Context<'a, T> {
    /// Create a new context object for a given project.
    pub fn new(
        project: &'a Project,
        pointer_inference_results: &'a PointerInferenceComputation<'a>,
        config: Config,
    ) -> Context<'a, T> {
        let string_symbol_map =
            crate::utils::symbol_utils::get_symbol_map(project, &config.string_symbols[..]);
        let mut extern_symbol_map = HashMap::new();
        for (tid, symbol) in project.program.term.extern_symbols.iter() {
            extern_symbol_map.insert(tid.clone(), symbol);
        }

        let mut block_start_node_map: HashMap<(Tid, Tid), NodeIndex> = HashMap::new();
        let mut block_first_def_set = HashSet::new();
        let mut jmp_to_blk_end_node_map = HashMap::new();
        for (node_id, node) in pointer_inference_results.get_graph().node_references() {
            match node {
                Node::BlkStart(block, sub) => {
                    if let Some(def) = block.term.defs.first() {
                        block_start_node_map.insert((def.tid.clone(), sub.tid.clone()), node_id);
                        block_first_def_set.insert((def.tid.clone(), sub.tid.clone()));
                    }
                }
                Node::BlkEnd(block, sub) => {
                    for jmp in block.term.jmps.iter() {
                        jmp_to_blk_end_node_map.insert((jmp.tid.clone(), sub.tid.clone()), node_id);
                    }
                }
                _ => (),
            }
        }

        Context {
            project,
            pointer_inference_results,
            format_string_index_map: config.format_string_index.into_iter().collect(),
            string_symbol_map,
            extern_symbol_map,
            block_start_node_map,
            block_first_def_set,
            jmp_to_blk_end_node_map,
            _phantom_string_domain: PhantomData,
        }
    }

    /// Get the current pointer inference state (if one can be found) for the given state.
    fn get_current_pointer_inference_state(
        &self,
        state: &State<T>,
        tid: &Tid,
    ) -> Option<PointerInferenceState> {
        if let Some(pi_state) = state.get_pointer_inference_state() {
            Some(pi_state.clone())
        } else if let Some(node_id) = self
            .block_start_node_map
            .get(&(tid.clone(), state.get_current_sub().unwrap().tid.clone()))
        {
            match self.pointer_inference_results.get_node_value(*node_id) {
                Some(NodeValue::Value(val)) => Some(val.clone()),
                _ => None,
            }
        } else {
            None
        }
    }

    /// Update the pointer inference state contained in the given taint state
    /// according to the effect of the given `Def` term.
    fn update_pointer_inference_state(&self, state: &mut State<T>, def: &Term<Def>) {
        if let Some(pi_state) = self.get_current_pointer_inference_state(state, &def.tid) {
            let pi_context = self.pointer_inference_results.get_context();
            let new_pi_state = pi_context.update_def(&pi_state, def);
            state.set_pointer_inference_state(new_pi_state);
        }
    }
}

#[cfg(test)]
mod tests;