Source code for claripy.annotation

from __future__ import annotations

from typing import TYPE_CHECKING

from claripy.errors import ClaripyOperationError

if TYPE_CHECKING:
    import claripy


[docs] class Annotation: """ Annotations are used to achieve claripy's goal of being an arithmetic instrumentation engine. They provide a means to pass extra information to the claripy backends. """ @property def eliminatable(self) -> bool: # pylint:disable=no-self-use """ Returns whether this annotation can be eliminated in a simplification. :return: True if eliminatable, False otherwise """ return True @property def relocatable(self) -> bool: # pylint:disable=no-self-use """ Returns whether this annotation can be relocated in a simplification. :return: True if it can be relocated, false otherwise. """ return False
[docs] def relocate(self, src: claripy.ast.Base, dst: claripy.ast.Base): # pylint:disable=no-self-use,unused-argument """ This is called when an annotation has to be relocated because of simplifications. Consider the following case: x = claripy.BVS('x', 32) zero = claripy.BVV(0, 32).add_annotation(your_annotation) y = x + zero Here, one of three things can happen: 1. if your_annotation.eliminatable is True, the simplifiers will simply eliminate your_annotation along with `zero` and `y is x` will hold 2. elif your_annotation.relocatable is False, the simplifier will abort and y will never be simplified 3. elif your_annotation.relocatable is True, the simplifier will run, determine that the simplified result of `x + zero` will be `x`. It will then call your_annotation.relocate(zero, x) to move the annotation away from the AST that is about to be eliminated. :param src: the old AST that was eliminated in the simplification :param dst: the new AST (the result of a simplification) :return: the annotation that will be applied to `dst` """ if self.relocatable: return self raise ClaripyOperationError("Annotation is not relocatable")
# # Some built-in annotations #
[docs] class SimplificationAvoidanceAnnotation(Annotation): """SimplificationAvoidanceAnnotation is an annotation that prevents simplification of an AST.""" @property def eliminatable(self): return False @property def relocatable(self): return False
[docs] class StridedIntervalAnnotation(SimplificationAvoidanceAnnotation): """StridedIntervalAnnotation allows annotating a BVS to represent a strided interval.""" stride: int lower_bound: int upper_bound: int
[docs] def __init__(self, stride: int, lower_bound: int, upper_bound: int): self.stride = stride self.lower_bound = lower_bound self.upper_bound = upper_bound
def __hash__(self): return hash((self.stride, self.lower_bound, self.upper_bound)) def __eq__(self, other): return ( isinstance(other, StridedIntervalAnnotation) and self.stride == other.stride and self.lower_bound == other.lower_bound and self.upper_bound == other ) def __repr__(self): return f"<StridedIntervalAnnotation {self.stride}:{self.lower_bound} - {self.upper_bound}>"
[docs] class RegionAnnotation(SimplificationAvoidanceAnnotation): """ Use RegionAnnotation to annotate ASTs. Normally, an AST annotated by RegionAnnotations is treated as a ValueSet. """
[docs] def __init__(self, region_id, region_base_addr): self.region_id = region_id self.region_base_addr = region_base_addr
# # Overriding base methods # def __hash__(self): return hash((self.region_id, self.region_base_addr)) def __repr__(self): return f"<RegionAnnotation {self.region_id}@{self.region_base_addr:#08x}>"
[docs] class UninitializedAnnotation(Annotation): """ Use UninitializedAnnotation to annotate ASTs that are uninitialized. """ eliminatable = False relocatable = True def __hash__(self): return hash("uninitialized") def __eq__(self, other): return isinstance(other, UninitializedAnnotation) def __repr__(self): return "<UninitializedAnnotation>"