Source code for mythril.laser.smt.solver.independence_solver

import z3

from mythril.laser.smt.model import Model
from mythril.laser.smt.bool import Bool
from mythril.laser.smt.solver.solver_statistics import stat_smt_query

from typing import Set, Tuple, Dict, List, cast


def _get_expr_variables(expression: z3.ExprRef) -> List[z3.ExprRef]:
    """
    Gets the variables that make up the current expression
    :param expression:
    :return:
    """
    result = []
    if not expression.children() and not isinstance(expression, z3.BitVecNumRef):
        result.append(expression)
    for child in expression.children():
        c_children = _get_expr_variables(child)
        result.extend(c_children)
    return result


[docs]class DependenceBucket: """Bucket object to contain a set of conditions that are dependent on each other""" def __init__(self, variables=None, conditions=None): """ Initializes a DependenceBucket object :param variables: Variables contained in the conditions :param conditions: The conditions that are dependent on each other """ self.variables = variables or [] # type: List[z3.ExprRef] self.conditions = conditions or [] # type: List[z3.ExprRef]
[docs]class DependenceMap: """DependenceMap object that maintains a set of dependence buckets, used to separate independent smt queries""" def __init__(self): """Initializes a DependenceMap object""" self.buckets = [] # type: List[DependenceBucket] self.variable_map = {} # type: Dict[str, DependenceBucket]
[docs] def add_condition(self, condition: z3.BoolRef) -> None: """ Add condition to the dependence map :param condition: The condition that is to be added to the dependence map """ variables = set(_get_expr_variables(condition)) relevant_buckets = set() for variable in variables: try: bucket = self.variable_map[str(variable)] relevant_buckets.add(bucket) except KeyError: continue new_bucket = DependenceBucket(variables, [condition]) self.buckets.append(new_bucket) if relevant_buckets: # Merge buckets, and rewrite variable map accordingly relevant_buckets.add(new_bucket) new_bucket = self._merge_buckets(relevant_buckets) for variable in new_bucket.variables: self.variable_map[str(variable)] = new_bucket
def _merge_buckets(self, bucket_list: Set[DependenceBucket]) -> DependenceBucket: """Merges the buckets in bucket list""" variables = [] # type: List[str] conditions = [] # type: List[z3.BoolRef] for bucket in bucket_list: self.buckets.remove(bucket) variables += bucket.variables conditions += bucket.conditions new_bucket = DependenceBucket(variables, conditions) self.buckets.append(new_bucket) return new_bucket
[docs]class IndependenceSolver: """An SMT solver object that uses independence optimization""" def __init__(self): """""" self.raw = z3.Solver() self.constraints = [] self.models = []
[docs] def set_timeout(self, timeout: int) -> None: """Sets the timeout that will be used by this solver, timeout is in milliseconds. :param timeout: """ self.raw.set(timeout=timeout)
[docs] def add(self, *constraints: Tuple[Bool]) -> None: """Adds the constraints to this solver. :param constraints: constraints to add """ raw_constraints = [ c.raw for c in cast(Tuple[Bool], constraints) ] # type: List[z3.BoolRef] self.constraints.extend(raw_constraints)
[docs] def append(self, *constraints: Tuple[Bool]) -> None: """Adds the constraints to this solver. :param constraints: constraints to add """ raw_constraints = [ c.raw for c in cast(Tuple[Bool], constraints) ] # type: List[z3.BoolRef] self.constraints.extend(raw_constraints)
@stat_smt_query def check(self) -> z3.CheckSatResult: """Returns z3 smt check result.""" dependence_map = DependenceMap() for constraint in self.constraints: dependence_map.add_condition(constraint) self.models = [] for bucket in dependence_map.buckets: self.raw.reset() self.raw.append(*bucket.conditions) check_result = self.raw.check() if check_result == z3.sat: self.models.append(self.raw.model()) else: return check_result return z3.sat
[docs] def model(self) -> Model: """Returns z3 model for a solution.""" return Model(self.models)
[docs] def reset(self) -> None: """Reset this solver.""" self.constraints = []
[docs] def pop(self, num) -> None: """Pop num constraints from this solver.""" self.constraints.pop(num)