def _arithmetic_helper( a: "BitVecFunc", b: Union[BitVec, int], operation: Callable ) -> "BitVecFunc": """ Helper function for arithmetic operations on BitVecFuncs. :param a: The BitVecFunc to perform the operation on. :param b: A BitVec or int to perform the operation on. :param operation: The arithmetic operation to perform. :return: The resulting BitVecFunc """ if isinstance(b, int): b = BitVec(z3.BitVecVal(b, a.size())) raw = operation(a.raw, b.raw) union = a.annotations.union(b.annotations) if isinstance(b, BitVecFunc): return BitVecFunc( raw=raw, func_name="Hybrid", input_=BitVec(z3.BitVec("", 256), annotations=union), nested_functions=a.nested_functions + b.nested_functions + [a, b], ) return BitVecFunc( raw=raw, func_name=a.func_name, input_=a.input_, annotations=union, nested_functions=a.nested_functions + [a], )
def _arithmetic_helper(a: "BitVecFunc", b: Union[BitVec, int], operation: Callable) -> "BitVecFunc": """ Helper function for arithmetic operations on BitVecFuncs. :param a: The BitVecFunc to perform the operation on. :param b: A BitVec or int to perform the operation on. :param operation: The arithmetic operation to perform. :return: The resulting BitVecFunc """ if isinstance(b, int): b = BitVec(z3.BitVecVal(b, a.size())) raw = operation(a.raw, b.raw) union = a.annotations.union(b.annotations) if isinstance(b, BitVecFunc): # TODO: Find better value to set input and name to in this case? input_string = "MisleadingNotationop(invhash({}) {} invhash({})".format( hash(a), operation, hash(b)) return BitVecFunc( raw=raw, func_name="Hybrid", input_=BitVec(z3.BitVec(input_string, 256), annotations=union), ) return BitVecFunc(raw=raw, func_name=a.func_name, input_=a.input_, annotations=union)
def Concat(*args: Union[BitVec, List[BitVec]]) -> BitVec: """Create a concatenation expression. :param args: :return: """ # The following statement is used if a list is provided as an argument to concat if len(args) == 1 and isinstance(args[0], list): bvs = args[0] # type: List[BitVec] else: bvs = cast(List[BitVec], args) nraw = z3.Concat([a.raw for a in bvs]) annotations = set() # type: Annotations nested_functions = [] # type: List[BitVecFunc] for bv in bvs: annotations = annotations.union(bv.annotations) if isinstance(bv, BitVecFunc): nested_functions += bv.nested_functions nested_functions += [bv] if nested_functions: return BitVecFunc( raw=nraw, func_name="Hybrid", input_=BitVec(z3.BitVec("", 256), annotations=annotations), nested_functions=nested_functions, ) return BitVec(nraw, annotations)
def BVAddNoOverflow(a: Union[BitVec, int], b: Union[BitVec, int], signed: bool) -> Bool: """Creates predicate that verifies that the addition doesn't overflow. :param a: :param b: :param signed: :return: """ if not isinstance(a, BitVec): a = BitVec(z3.BitVecVal(a, 256)) if not isinstance(b, BitVec): b = BitVec(z3.BitVecVal(b, 256)) return Bool(z3.BVAddNoOverflow(a.raw, b.raw, signed))
def __getitem__(self, item: BitVec) -> BitVec: """Gets item from the array, item can be symbolic.""" if isinstance(item, slice): raise ValueError( "Instance of BaseArray, does not support getitem with slices") return BitVec(cast(z3.BitVecRef, z3.Select(self.raw, item.raw))) # type: ignore
def _comparison_helper( a: "BitVecFunc", b: Union[BitVec, int], operation: Callable, default_value: bool, inputs_equal: bool, ) -> Bool: """ Helper function for comparison operations with BitVecFuncs. :param a: The BitVecFunc to compare. :param b: A BitVec or int to compare to. :param operation: The comparison operation to perform. :return: The resulting Bool """ # Is there some hack for gt/lt comparisons? if isinstance(b, int): b = BitVec(z3.BitVecVal(b, a.size())) union = a.annotations.union(b.annotations) if not a.symbolic and not b.symbolic: return Bool(z3.BoolVal(operation(a.value, b.value)), annotations=union) if (not isinstance(b, BitVecFunc) or not a.func_name or not a.input_ or not a.func_name == b.func_name): return Bool(z3.BoolVal(default_value), annotations=union) return And( Bool(cast(z3.BoolRef, operation(a.raw, b.raw)), annotations=union), a.input_ == b.input_ if inputs_equal else a.input_ != b.input_, ) return a.input_ == b.input_
def _arithmetic_helper(a: "BitVecFunc", b: Union[BitVec, int], operation: Callable) -> "BitVecFunc": """ Helper function for arithmetic operations on BitVecFuncs. :param a: The BitVecFunc to perform the operation on. :param b: A BitVec or int to perform the operation on. :param operation: The arithmetic operation to perform. :return: The resulting BitVecFunc """ if isinstance(b, int): b = BitVec(z3.BitVecVal(b, a.size())) raw = operation(a.raw, b.raw) union = a.annotations.union(b.annotations) if isinstance(b, BitVecFunc): # TODO: Find better value to set input and name to in this case? return BitVecFunc(raw=raw, func_name=None, input_=None, annotations=union) return BitVecFunc(raw=raw, func_name=a.func_name, input_=a.input_, annotations=union)
def __xor__(self, other: Union[int, "BitVec"]) -> "BitVecFunc": """Create a xor expression. :param other: The int or BitVec to xor with this BitVecFunc :return: The resulting BitVecFunc """ if not isinstance(other, BitVec): other = BitVec(z3.BitVecVal(other, self.size())) return _arithmetic_helper(self, other, operator.xor)
def __ge__(self, other: Union[int, "BitVec"]) -> Bool: """Create a signed greater than or equal to expression. :param other: The int or BitVec to compare to this BitVecFunc :return: The resulting Bool """ if not isinstance(other, BitVec): other = BitVec(z3.BitVecVal(other, self.size())) return Or(self > other, self == other)
def _arithmetic_helper(a: BitVec, b: BitVec, operation: Callable) -> BitVec: raw = operation(a.raw, b.raw) union = a.annotations.union(b.annotations) if isinstance(a, BitVecFunc): return _func_arithmetic_helper(a, b, operation) elif isinstance(b, BitVecFunc): return _func_arithmetic_helper(b, a, operation) return BitVec(raw, annotations=union)
def __ne__(self, other: Union[int, "BitVec"]) -> Bool: # type: ignore """Create an inequality expression. :param other: The int or BitVec to compare to this BitVecFunc :return: The resulting Bool """ if not isinstance(other, BitVec): other = BitVec(z3.BitVecVal(other, self.size())) return _comparison_helper( self, other, operator.ne, default_value=True, inputs_equal=False )
def __gt__(self, other: Union[int, "BitVec"]) -> Bool: """Create a signed greater than expression. :param other: The int or BitVec to compare to this BitVecFunc :return: The resulting Bool """ if not isinstance(other, BitVec): other = BitVec(z3.BitVecVal(other, self.size())) return _comparison_helper( self, other, operator.gt, default_value=False, inputs_equal=False )
def Extract(high: int, low: int, bv: BitVec) -> BitVec: """Create an extract expression. :param high: :param low: :param bv: :return: """ raw = z3.Extract(high, low, bv.raw) if isinstance(bv, BitVecFunc): input_string = "" # Is there a better value to set func_name and input to in this case? return BitVecFunc( raw=raw, func_name="Hybrid", input_=BitVec(z3.BitVec(input_string, 256), annotations=bv.annotations), nested_functions=bv.nested_functions + [bv], ) return BitVec(raw, annotations=bv.annotations)
def If(a: Union[Bool, bool], b: Union[BitVec, int], c: Union[BitVec, int]) -> BitVec: """Create an if-then-else expression. :param a: :param b: :param c: :return: """ # TODO: Handle BitVecFunc if not isinstance(a, Bool): a = Bool(z3.BoolVal(a)) if not isinstance(b, BitVec): b = BitVec(z3.BitVecVal(b, 256)) if not isinstance(c, BitVec): c = BitVec(z3.BitVecVal(c, 256)) union = a.annotations.union(b.annotations).union(c.annotations) bvf = [] # type: List[BitVecFunc] if isinstance(a, BitVecFunc): bvf += [a] if isinstance(b, BitVecFunc): bvf += [b] if isinstance(c, BitVecFunc): bvf += [c] if bvf: raw = z3.If(a.raw, b.raw, c.raw) nested_functions = [ nf for func in bvf for nf in func.nested_functions ] + bvf return BitVecFunc(raw, func_name="Hybrid", nested_functions=nested_functions) return BitVec(z3.If(a.raw, b.raw, c.raw), union)
def Sum(*args: BitVec) -> BitVec: """Create sum expression. :return: """ raw = z3.Sum([a.raw for a in args]) annotations = set() # type: Annotations bitvecfuncs = [] for bv in args: annotations = annotations.union(bv.annotations) if isinstance(bv, BitVecFunc): bitvecfuncs.append(bv) nested_functions = [ nf for func in bitvecfuncs for nf in func.nested_functions ] + bitvecfuncs if len(bitvecfuncs) >= 2: return BitVecFunc( raw=raw, func_name="Hybrid", input_=None, annotations=annotations, nested_functions=nested_functions, ) elif len(bitvecfuncs) == 1: return BitVecFunc( raw=raw, func_name=bitvecfuncs[0].func_name, input_=bitvecfuncs[0].input_, annotations=annotations, nested_functions=nested_functions, ) return BitVec(raw, annotations)
def BitVecSym(name: str, size: int, annotations: Annotations = None) -> BitVec: """Creates a new bit vector with a symbolic value.""" raw = z3.BitVec(name, size) return BitVec(raw, annotations)
def BitVecVal(value: int, size: int, annotations: Annotations = None) -> BitVec: """Creates a new bit vector with a concrete value.""" raw = z3.BitVecVal(value, size) return BitVec(raw, annotations)
def _comparison_helper( a: "BitVecFunc", b: Union[BitVec, int], operation: Callable, default_value: bool, inputs_equal: bool, ) -> Bool: """ Helper function for comparison operations with BitVecFuncs. :param a: The BitVecFunc to compare. :param b: A BitVec or int to compare to. :param operation: The comparison operation to perform. :return: The resulting Bool """ # Is there some hack for gt/lt comparisons? if isinstance(b, int): b = BitVec(z3.BitVecVal(b, a.size())) union = a.annotations.union(b.annotations) if not a.symbolic and not b.symbolic: if operation == z3.UGT: operation = operator.gt if operation == z3.ULT: operation = operator.lt return Bool(z3.BoolVal(operation(a.value, b.value)), annotations=union) if ( not isinstance(b, BitVecFunc) or not a.func_name or not a.input_ or not a.func_name == b.func_name or str(operation) not in ("<built-in function eq>", "<built-in function ne>") ): return Bool(z3.BoolVal(default_value), annotations=union) condition = True for a_nest, b_nest in product(a.nested_functions, b.nested_functions): if a_nest.func_name != b_nest.func_name: continue if a_nest.func_name == "Hybrid": continue # a.input (eq/neq) b.input ==> a == b if inputs_equal: condition = z3.And( condition, z3.Or( z3.Not((a_nest.input_ == b_nest.input_).raw), (a_nest.raw == b_nest.raw), ), z3.Or( z3.Not((a_nest.raw == b_nest.raw)), (a_nest.input_ == b_nest.input_).raw, ), ) else: condition = z3.And( condition, z3.Or( z3.Not((a_nest.input_ != b_nest.input_).raw), (a_nest.raw == b_nest.raw), ), z3.Or( z3.Not((a_nest.raw == b_nest.raw)), (a_nest.input_ != b_nest.input_).raw, ), ) return And( Bool(cast(z3.BoolRef, operation(a.raw, b.raw)), annotations=union), Bool(condition) if b.nested_functions else Bool(True), a.input_ == b.input_ if inputs_equal else a.input_ != b.input_, )