def break_rand(nums: list, next_num: int): n_nums = len(nums) # print(f'len nums: {n_nums}') states = { f'state_{i}': z.BitVec(f'state_{i}', 32) for i in range(1, n_nums + 2) } # print(states) output_next = z.BitVec('output_next', 32) s = z.Solver() for i in range(2, n_nums + 2): s.add(states[f'state_{i}'] == states[f'state_{i - 1}'] * 214013 + 2531011) for i in range(1, n_nums + 1): s.add( z.URem((states[f'state_{i}'] >> 16) & 0x7FFF, 100) == nums[i - 1]) s.add(output_next == z.URem((states[f'state_{n_nums + 1}'] >> 16) & 0x7FFF, 100)) # print(s) if s.check() == z.sat: print(f'For the sequence: {nums}, problem is satisfiable') print( f'We were expecting: {next_num} and got: {s.model()[output_next]}\n' ) else: print(f'For the sequence: {nums}, problem is unsatisfiable') return s, states, output_next
def urem(self, ctx, return_type, a, atype, b, btype, nuw=False, nsw=False, exact=False): assert atype == return_type assert atype == btype assert not nuw and not nsw and not exact assert util.path_condition_implies( ctx, b != z3.BitVecVal(0, btype.size())), "urem by zero" return z3.URem(a, b)
def URem(self, other): if isinstance(other, int): other = BVV(other, self.size) else: assert isinstance(other, BV) assert self.size == other.size return BVExpr(self.size, z3.URem(self.z3obj, other.z3obj), self.interval.URem(other.interval))
def from_ExprOp(self, expr): args = map(self.from_expr, expr.args) res = args[0] if len(args) > 1: for arg in args[1:]: if expr.op in self.trivial_ops: res = eval("res %s arg" % expr.op) elif expr.op == ">>": res = z3.LShR(res, arg) elif expr.op == "a>>": res = res >> arg elif expr.op == "<<<": res = z3.RotateLeft(res, arg) elif expr.op == ">>>": res = z3.RotateRight(res, arg) elif expr.op == "idiv": res = self._idivC(res, arg) elif expr.op == "udiv": res = z3.UDiv(res, arg) elif expr.op == "imod": res = res - (arg * (self._idivC(res, arg))) elif expr.op == "umod": res = z3.URem(res, arg) else: raise NotImplementedError("Unsupported OP yet: %s" % expr.op) elif expr.op == 'parity': arg = z3.Extract(7, 0, res) res = z3.BitVecVal(1, 1) for i in xrange(8): res = res ^ z3.Extract(i, i, arg) elif expr.op == '-': res = -res elif expr.op == "cnttrailzeros": size = expr.size src = res res = z3.If(src == 0, size, src) for i in xrange(size - 1, -1, -1): res = z3.If((src & (1 << i)) != 0, i, res) elif expr.op == "cntleadzeros": size = expr.size src = res res = z3.If(src == 0, size, src) for i in xrange(size, 0, -1): index = -i % size out = size - (index + 1) res = z3.If((src & (1 << index)) != 0, out, res) elif expr.op.startswith("zeroExt"): arg, = expr.args res = z3.ZeroExt(expr.size - arg.size, self.from_expr(arg)) elif expr.op.startswith("signExt"): arg, = expr.args res = z3.SignExt(expr.size - arg.size, self.from_expr(arg)) else: raise NotImplementedError("Unsupported OP yet: %s" % expr.op) return res
def URem(a: BitVec, b: BitVec) -> BitVec: """Create an unsigned remainder expression. :param a: :param b: :return: """ union = a.annotations + b.annotations return BitVec(z3.URem(a.raw, b.raw), annotations=union)
def URem(self, other): if isinstance(other, int): other = BVV(other, self.size) else: assert isinstance(other, BV) assert self.size == other.size if isinstance(other, BVV): return BVV((self.value % other.value) & self._mask, self.size) return BVExpr(self.size, z3.URem(self.z3obj, other.z3obj), self.interval.URem(other.interval))
def ast_to_z3_expression(ast: Union[AstNode, AstLeaf], use_bitvecval=False): if not Z3_INSTALLED: raise D810Z3Exception("Z3 is not installed") if isinstance(ast, AstLeaf): if ast.is_constant(): return z3.BitVecVal(ast.value, 32) return ast.z3_var if ast.opcode == m_neg: return -(ast_to_z3_expression(ast.left, use_bitvecval)) elif ast.opcode == m_lnot: return not (ast_to_z3_expression(ast.left, use_bitvecval)) elif ast.opcode == m_bnot: return ~(ast_to_z3_expression(ast.left, use_bitvecval)) elif ast.opcode == m_add: return (ast_to_z3_expression(ast.left, use_bitvecval)) + ( ast_to_z3_expression(ast.right, use_bitvecval)) elif ast.opcode == m_sub: return (ast_to_z3_expression(ast.left, use_bitvecval)) - ( ast_to_z3_expression(ast.right, use_bitvecval)) elif ast.opcode == m_mul: return (ast_to_z3_expression(ast.left, use_bitvecval)) * ( ast_to_z3_expression(ast.right, use_bitvecval)) elif ast.opcode == m_udiv: return z3.UDiv(ast_to_z3_expression(ast.left, use_bitvecval=True), ast_to_z3_expression(ast.right, use_bitvecval=True)) elif ast.opcode == m_sdiv: return (ast_to_z3_expression(ast.left, use_bitvecval)) / ( ast_to_z3_expression(ast.right, use_bitvecval)) elif ast.opcode == m_umod: return z3.URem(ast_to_z3_expression(ast.left, use_bitvecval), ast_to_z3_expression(ast.right, use_bitvecval)) elif ast.opcode == m_smod: return (ast_to_z3_expression(ast.left, use_bitvecval)) % ( ast_to_z3_expression(ast.right, use_bitvecval)) elif ast.opcode == m_or: return (ast_to_z3_expression(ast.left, use_bitvecval)) | (ast_to_z3_expression( ast.right, use_bitvecval)) elif ast.opcode == m_and: return (ast_to_z3_expression(ast.left, use_bitvecval)) & ( ast_to_z3_expression(ast.right, use_bitvecval)) elif ast.opcode == m_xor: return (ast_to_z3_expression(ast.left, use_bitvecval)) ^ ( ast_to_z3_expression(ast.right, use_bitvecval)) elif ast.opcode == m_shl: return (ast_to_z3_expression(ast.left, use_bitvecval)) << ( ast_to_z3_expression(ast.right, use_bitvecval)) elif ast.opcode == m_shr: return z3.LShR(ast_to_z3_expression(ast.left, use_bitvecval), ast_to_z3_expression(ast.right, use_bitvecval)) elif ast.opcode == m_sar: return (ast_to_z3_expression(ast.left, use_bitvecval)) >> ( ast_to_z3_expression(ast.right, use_bitvecval)) elif ast.opcode in [m_xdu, m_xds, m_low, m_high]: return ast_to_z3_expression(ast.left, use_bitvecval) raise D810Z3Exception("Z3 evaluation: Unknown opcode {0} for {1}".format( opcode_to_string(ast.opcode), ast))
def _(term, smt): x = smt.eval(term.x) with smt.local_nonpoison() as ny: y = smt.eval(term.y) smt.add_defs(y != 0, *ny) return z3.URem(x,y)
def do_P(op, stack, state): c1 = z3.BitVecVal(0x0101010101010101, SIZE) c2 = z3.BitVecVal(0x8040201008040201, SIZE) c3 = z3.BitVecVal(0x1FF, SIZE) cur = prepare(state.esil["cur"]) lsb = cur & z3.BitVecVal(0xff, SIZE) #pf = (((((lsb * c1) & c2) % c3) & ONE) != 1) pf = ((z3.URem(((lsb * c1) & c2), c3) & ONE) != ONE) stack.append(z3.If(pf, ONE, ZERO))
def make_hash_equation(pj: int, var_b: Tuple[int], b1: int): domain_bit_count = int(ceil(log2(pj))) bc = int(ceil(log2(pj + 1))) slices = get_slices(domain_bit_count) return z3.URem( z3.Sum([ z3.ZeroExt(bc - s.size(), s) * z3.BitVecVal(var_b[i], bc) for i, s in enumerate(slices) ]) + z3.BitVecVal(b1, bc), z3.BitVecVal(pj, bc)) == z3.BitVecVal(0, bc)
def from_ExprOp(self, expr): args = map(self.from_expr, expr.args) res = args[0] if len(args) > 1: for arg in args[1:]: if expr.op in self.trivial_ops: res = eval("res %s arg" % expr.op) elif expr.op == ">>": res = z3.LShR(res, arg) elif expr.op == "a>>": res = res >> arg elif expr.op == "a<<": res = res << arg elif expr.op == "<<<": res = z3.RotateLeft(res, arg) elif expr.op == ">>>": res = z3.RotateRight(res, arg) elif expr.op == "idiv": res = res / arg elif expr.op == "udiv": res = z3.UDiv(res, arg) elif expr.op == "imod": res = res % arg elif expr.op == "umod": res = z3.URem(res, arg) else: raise NotImplementedError("Unsupported OP yet: %s" % expr.op) elif expr.op == 'parity': arg = z3.Extract(7, 0, res) res = z3.BitVecVal(1, 1) for i in xrange(8): res = res ^ z3.Extract(i, i, arg) elif expr.op == '-': res = -res elif expr.op == "bsf": size = expr.size src = res res = z3.If((src & (1 << (size - 1))) != 0, size - 1, src) for i in xrange(size - 2, -1, -1): res = z3.If((src & (1 << i)) != 0, i, res) elif expr.op == "bsr": size = expr.size src = res res = z3.If((src & 1) != 0, 0, src) for i in xrange(size - 1, 0, -1): index = -i % size res = z3.If((src & (1 << index)) != 0, index, res) else: raise NotImplementedError("Unsupported OP yet: %s" % expr.op) return res
def guess_next_number(known): n_nums = len(known) states = {f'state_{i}': z3.BitVec(f'state_{i}', 32) for i in range(1, n_nums + 2)} next_number = z3.BitVec('next_number', 32) s = z3.Solver() a = 1103515245 c = 12345 m = 16 for i in range(2, n_nums + 2): s.add(states[f'state_{i}'] == states[f'state_{i - 1}'] * a + c) for i in range(1, n_nums + 1): s.add(z3.URem((states[f'state_{i}'] >> m) & 0x7fff, (0x7f - 0x21)) + 0x21 == known[i - 1]) s.add(next_number == z3.URem((states[f'state_{n_nums + 1}'] >> m) & 0x7fff, (0x7f - 0x21)) + 0x21) if s.check() == z3.sat: return s.model()[next_number] else: print(f"Couldn't satisfy constraints for {known}") sys.exit(1)
def initial(self, instance): def trim(var, type): if var.size() == type.size(): return var return z3.Extract(type.size() - 1, 0, var) def extend(var): if var.size() < self.get_types()[-1].size(): v = z3.ZeroExt(self.get_types()[2].size() - var.size(), var) return v return var ref = instance._fields[self._name][0] perm1 = lambda fn, idx: trim(z3.UDiv(idx, 16) + 1 , type=self.get_types()[1][0]) perm2 = lambda fn, idx: trim(z3.URem(idx, 16), type=self.get_types()[1][1]) perm_inv = lambda fn, owned1, owned2: extend(owned1 - 1) * 16 + extend(owned2 % 16) instance._fields[self._name] = (ref, perm1, perm2, perm_inv)
def from_ExprOp(self, expr): args = map(self.from_expr, expr.args) res = args[0] if len(args) > 1: for arg in args[1:]: if expr.op in self.trivial_ops: res = eval("res %s arg" % expr.op) elif expr.op == ">>": res = z3.LShR(res, arg) elif expr.op == "a>>": res = res >> arg elif expr.op == "a<<": res = res << arg elif expr.op == "<<<": res = z3.RotateLeft(res, arg) elif expr.op == ">>>": res = z3.RotateRight(res, arg) elif expr.op == "idiv": res = res / arg elif expr.op == "udiv": res = z3.UDiv(res, arg) elif expr.op == "imod": res = res % arg elif expr.op == "umod": res = z3.URem(res, arg) else: raise NotImplementedError("Unsupported OP yet: %s" % expr.op) elif expr.op == 'parity': arg = z3.Extract(7, 0, res) res = z3.BitVecVal(1, 1) for i in xrange(8): res = res ^ z3.Extract(i, i, arg) elif expr.op == '-': res = -res else: raise NotImplementedError("Unsupported OP yet: %s" % expr.op) return res
def walk_bv_urem(self, formula, args, **kwargs): return z3.URem(args[0], args[1])
def to_z3(self, expr): """ Transform an expression into an z3 expression :param expr: str :return: z3 expression """ stack = [] # walk over expression for e in expr.split(" "): # ternary operator if e in self.grammar.op3: op1 = stack.pop() op2 = stack.pop() op3 = stack.pop() op_type = stack.pop() if e == "sign_extend": op1 = self.to_z3_variable(op1) result = z3.SignExt(int(op3) - op1.size(), op1) elif e == "bvextract": op3 = self.to_z3_variable(op3) result = z3.Extract(int(op1), int(op2), op3) stack.append(result) # binary operator elif e in self.grammar.op2: # operands op1 = self.to_z3_variable(stack.pop()) op2 = self.to_z3_variable(stack.pop()) op_type = stack.pop() # operator if e == "bvadd": result = (op2 + op1) elif e == "bvsub": result = (op2 - op1) elif e == "bvmul": result = (op2 * op1) elif e == "bvudiv": result = z3.UDiv(op2, op1) elif e == "bvsdiv": result = (op2 / op1) elif e == "bvurem": result = z3.URem(op2, op1) elif e == "bvsrem": result = z3.SRem(op2, op1) elif e == "bvshl": if self.bitsize == 64: op1 &= 63 else: op1 &= 31 result = (op2 << op1) elif e == "bvlshr": if self.bitsize == 64: op1 &= 63 else: op1 &= 31 result = z3.LShR(op2, op1) elif e == "bvashr": if self.bitsize == 64: op1 &= 63 else: op1 &= 31 result = (op2 >> op1) elif e == "bvand": result = (op2 & op1) elif e == "bvor": result = (op2 | op1) elif e == "bvxor": result = (op2 ^ op1) elif e == "zero_extend": result = z3.ZeroExt(int(op2) - op1.size(), op1) elif e == "bvconcat": result = z3.Concat(op2, op1) stack.append(result) # unary operator elif e in self.grammar.op1: # operand op = self.to_z3_variable(stack.pop()) op_type = stack.pop() # operator if e == "bvnot": result = ~ op elif e == "bvneg": result = - op stack.append(result) else: stack.append(e) return stack.pop()
def _z3_exp(exp, size): if isinstance(exp, SymVal): if state.has_key(exp.name): return state[exp.name] else: return z3.BitVec(exp.name, _z3_size(exp.size)) elif isinstance(exp, SymConst): return z3.BitVecVal(exp.val, _z3_size(exp.size)) elif isinstance(exp, SymExp): a, b = exp.a, exp.b assert isinstance(a, SymVal) or isinstance(a, SymConst) assert b is None or isinstance(b, SymVal) or isinstance( b, SymConst) assert b is None or a.size == b.size a = a if a is None else _z3_exp(a, a.size) b = b if b is None else _z3_exp(b, b.size) # makes 1 bit bitvectors from booleans _z3_bool_to_bv = lambda exp: z3.If(exp, z3.BitVecVal(1, 1), z3.BitVecVal(0, 1)) # z3 expression from SymExp ret = { I_ADD: lambda: a + b, I_SUB: lambda: a - b, I_NEG: lambda: -a, I_MUL: lambda: a * b, I_DIV: lambda: z3.UDiv(a, b), I_MOD: lambda: z3.URem(a, b), I_SHR: lambda: z3.LShR(a, b), I_SHL: lambda: a << b, I_OR: lambda: a | b, I_AND: lambda: a & b, I_XOR: lambda: a ^ b, I_NOT: lambda: ~a, I_EQ: lambda: _z3_bool_to_bv(a == b), I_LT: lambda: _z3_bool_to_bv(z3.ULT(a, b)) }[exp.op]() size_src = _z3_size(exp.a.size) size_dst = _z3_size(size) if size_src > size_dst: # convert to smaller value return z3.Extract(size_dst - 1, 0, ret) elif size_src < size_dst: # convert to bigger value return z3.Concat(z3.BitVecVal(0, size_dst - size_src), ret) else: return ret else: assert False
def __init__(self, ns): super(Z3python, self).__init__(False, ns, False, True, True) self.ctx = z3.Context() self.ast_list = [] self.sort_list = [] self.bool_sort = self.mk_sort((esbmc.solve.smt_sort_kind.bool, )) self.solver = z3.Solver(solver=None, ctx=self.ctx) self.fresh_arr_idx = 0 # No 'int' support at this time self.func_map = { #SMT_FUNC_ADD esbmc.solve.smt_func_kind.bvadd : lambda ctx, args, asts: asts[0] + asts[1], #SMT_FUNC_SUB esbmc.solve.smt_func_kind.bvsub : lambda ctx, args, asts: asts[0] - asts[1], #SMT_FUNC_MUL, esbmc.solve.smt_func_kind.bvmul : lambda ctx, args, asts: asts[0] * asts[1], #SMT_FUNC_DIV, esbmc.solve.smt_func_kind.bvudiv : lambda ctx, args, asts: z3.UDiv(asts[0], asts[1]), esbmc.solve.smt_func_kind.bvsdiv : lambda ctx, args, asts: asts[0] / asts[1], #SMT_FUNC_MOD, esbmc.solve.smt_func_kind.bvsmod : lambda ctx, args, asts: asts[0] % asts[1], esbmc.solve.smt_func_kind.bvumod : lambda ctx, args, asts: z3.URem(asts[0], asts[1]), #SMT_FUNC_SHL, esbmc.solve.smt_func_kind.bvshl : lambda ctx, args, asts: asts[0] << asts[1], esbmc.solve.smt_func_kind.bvashr : lambda ctx, args, asts: asts[0] >> asts[1], #SMT_FUNC_NEG, esbmc.solve.smt_func_kind.bvneg : lambda ctx, args, asts: -asts[0], esbmc.solve.smt_func_kind.bvlshr : lambda ctx, args, asts: z3.LShR(asts[0], asts[1]), esbmc.solve.smt_func_kind.bvnot : lambda ctx, args, asts: ~asts[0], #SMT_FUNC_BVNXOR, These aren't actually used anywhere #SMT_FUNC_BVNOR, #SMT_FUNC_BVNAND, esbmc.solve.smt_func_kind.bvxor : lambda ctx, args, asts: asts[0] ^ asts[1], esbmc.solve.smt_func_kind.bvor : lambda ctx, args, asts: asts[0] | asts[1], esbmc.solve.smt_func_kind.bvand : lambda ctx, args, asts: asts[0] & asts[1], esbmc.solve.smt_func_kind.implies : lambda ctx, args, asts: z3.Implies(asts[0], asts[1], ctx), esbmc.solve.smt_func_kind.xor : lambda ctx, args, asts: asts[0] ^ asts[1], esbmc.solve.smt_func_kind._or : # or is a keyword in python lambda ctx, args, asts: z3.Or(asts[0], asts[1]), esbmc.solve.smt_func_kind._and : lambda ctx, args, asts: z3.And(asts[0], asts[1]), esbmc.solve.smt_func_kind._not : lambda ctx, args, asts: z3.Not(asts[0]), #SMT_FUNC_LT, esbmc.solve.smt_func_kind.bvslt : lambda ctx, args, asts: asts[0] < asts[1], esbmc.solve.smt_func_kind.bvult : lambda ctx, args, asts: z3.ULT(asts[0], asts[1]), #SMT_FUNC_GT, esbmc.solve.smt_func_kind.bvsgt : lambda ctx, args, asts: asts[0] > asts[1], esbmc.solve.smt_func_kind.bvugt : lambda ctx, args, asts: z3.UGT(asts[0], asts[1]), #SMT_FUNC_LTE, # Z3 doesn't do lte's, so invert gt esbmc.solve.smt_func_kind.bvslte : lambda ctx, args, asts: z3.Not(asts[0] > asts[1]), esbmc.solve.smt_func_kind.bvulte : lambda ctx, args, asts: z3.Not(z3.UGT(asts[0], asts[1])), #SMT_FUNC_GTE, esbmc.solve.smt_func_kind.bvsgte : lambda ctx, args, asts: z3.Not(asts[0] < asts[1]), esbmc.solve.smt_func_kind.bvugte : lambda ctx, args, asts: z3.Not(z3.ULT(asts[0], asts[1])), esbmc.solve.smt_func_kind.eq : lambda ctx, args, asts: asts[0] == asts[1], esbmc.solve.smt_func_kind.noteq : lambda ctx, args, asts: asts[0] != asts[1], esbmc.solve.smt_func_kind.ite : lambda ctx, args, asts: z3.If(asts[0], asts[1], asts[2], ctx), #SMT_FUNC_STORE, Handled via ast functions #SMT_FUNC_SELECT, esbmc.solve.smt_func_kind.concat : lambda ctx, args, asts: z3.Concat(asts[0], asts[1]), #SMT_FUNC_EXTRACT, // Not for going through mk app due to sillyness. # Int related stuff #SMT_FUNC_INT2REAL, #SMT_FUNC_REAL2INT, #SMT_FUNC_IS_INT, } # Various accounting structures for the address space modeling need to # be set up, but that needs to happen after the solver is online. Thus, # we have to call this once the object is ready to create asts. self.smt_post_init()
from rfb_mc.implementation.eamp.eamp_rfmi_z3 import EampRfmiZ3 from rfb_mc.implementation.runner_z3 import FormulaParamsZ3, RunnerZ3 from rfb_mc.types import Params from rfb_mc.implementation.aws.dynamodb_store import DynamodbStore dynamodb = boto3.resource("dynamodb") # should be replaced with used table and requires AWS CLI credentials to be setup table = dynamodb.Table("rfb_mc_store_test") RunnerZ3.register_restrictive_formula_module_implementation(EampRfmiZ3) if __name__ == "__main__": k = 20 x, y, z = z3.BitVecs("x y z", k) f = z3.And([ z3.URem(x, 200) == 0, z3.URem(y, 200) == 0, z3.ULT(z, x + y), ]) a = 100 ident = DynamodbStore.create_store_data_entry( table, Params(bit_width_counter=Counter({k: 2}), ), ) store = DynamodbStore(table, ident) scheduler = EampEdgeScheduler( store=store,
from rfb_mc.implementation.eamp.eamp_rfmi_z3 import EampRfmiZ3 from rfb_mc.implementation.multi_processing_integrator_z3 import MultiProcessingIntegratorZ3 from rfb_mc.implementation.runner_z3 import RunnerZ3, FormulaParamsZ3 from rfb_mc.implementation.in_memory_store import InMemoryStore from rfb_mc.store import StoreData from rfb_mc.types import Params RunnerZ3.register_restrictive_formula_module_implementation(EampRfmiZ3) if __name__ == "__main__": k = 18 x, y, z = z3.BitVecs("x y z", k) f = z3.And([ z3.UGE(x, 0), z3.UGE(y, 0), z3.URem(x, 4) == 0, z3.URem(y, 5) == 0, z3.ULT(z, x + y), ]) s2 = perf_counter() s = perf_counter() a = 100 store = InMemoryStore(data=StoreData(params=Params( bit_width_counter=Counter({k: 2})))) print( f"Initializing InMemoryApproxExecutionManager took {perf_counter() - s:.3f} seconds" )
import z3 from rfb_mc.implementation.direct_integrator_z3 import DirectIntegratorZ3 from rfb_mc.implementation.eamp.eamp_edge_scheduler import EampEdgeScheduler from rfb_mc.implementation.eamp.eamp_rfmi_z3 import EampRfmiZ3 from rfb_mc.implementation.runner_z3 import FormulaParamsZ3, RunnerZ3 from rfb_mc.implementation.in_memory_store import InMemoryStore from rfb_mc.store import StoreData from rfb_mc.types import Params RunnerZ3.register_restrictive_formula_module_implementation(EampRfmiZ3) if __name__ == "__main__": k = 14 x, y, z = z3.BitVecs("x y z", k) f = z3.And([ z3.URem(x, 13) == 0, z3.URem(y, 18) == 0, z3.ULT(z, x + y), ]) store = InMemoryStore(data=StoreData(params=Params( bit_width_counter=Counter({k: 2}), ), ), ) scheduler = EampEdgeScheduler( store=store, confidence=Fraction(0.99), a=100, q=1, ) integrator = DirectIntegratorZ3(formula_params=FormulaParamsZ3(
def chicken_hash(bytes, m, r): return z3.URem(chicken_hash_xor(bytes, m, r), z3.BitVecVal(m, 64))
def MOD(self, gstate, a, b): a, b = map(svm_utils.convert_to_bitvec, (a, b)) gstate.mstate.stack.append(0 if b == 0 else z3.URem(a, b))
## Compute the average of a and b. The initial computation we provided ## naively adds them and divides by two, but that is not correct. Modify ## these lines to implement your solution for both unsigned (u_avg) and ## signed (s_avg) division. ## ## Watch out for the difference between signed and unsigned integer ## operations. For example, the Z3 expression (x/2) performs signed ## division, meaning it treats the 32-bit value as a signed integer. ## Similarly, (x>>16) shifts x by 16 bits to the right, treating it ## as a signed integer. ## ## Use z3.UDiv(x, y) for unsigned division of x by y. ## Use z3.LShR(x, y) for unsigned (logical) right shift of x by y bits. # a/2 + b/2 + (a%2) * (b%2) u_avg = z3.UDiv(a, 2) + z3.UDiv(b, 2) + z3.URem(a, 2) * z3.URem(b, 2) t = (a & b) + ((a ^ b) >> 1) s_avg = t + z3.LShR(t, 31) & (a ^ b) ## Do not change the code below. ## To compute the reference answers, we extend both a and b by one ## more bit (to 33 bits), add them, divide by two, and shrink back ## down to 32 bits. You are not allowed to "cheat" in this way in ## your answer. az33 = z3.ZeroExt(1, a) bz33 = z3.ZeroExt(1, b) real_u_avg = z3.Extract(31, 0, z3.UDiv(az33 + bz33, 2)) as33 = z3.SignExt(1, a)
def bvurem(self, other): return type(self)(z3.URem(self.value, other.value))
# solution space. Proof takes about 40s on my Haswell-era Xeon. seed = z3.BitVec('seed', 66) s.add(seed >= 0, seed < mod) #s.add(seed >= 0, seed < 1000) # Start adding constraints to find the seed from a series of outputs. In my # testing, only two observed outputs were necessary to find the correct seed. num_outputs = 2 states = {0: seed} for i in range(1, num_outputs + 1): # Each intermediary state is a function of the previous state (doesn't need # an independent z3 variable): states[i] = m31_mod((states[i - 1] * mult) + add) # Add constraint based on observed output: s.add(z3.URem(states[i], outmod) == next(lcg)) # Run the solver! if s.check() == z3.unsat: print "If this happens, enable proof mode w/ z3.set_param above and re-run." print s.proof() exit() res = s.model() print "Solved model:", res # Test our model: lcg_verbose = False # Couldn't find a better way to extract an integer from a z3 BitVec ... test_model = day18_lcg(int(str( res[seed] ))) test_lcg = day18_lcg(680)
def opcode_mod(opcode_data1, opcode_data2): return z3.simplify(z3.URem(opcode_data1, opcode_data2))
def do_MOD(op, stack, state): arg1, arg2 = pop_values(stack, state, 2) stack.append(z3.URem(arg1, arg2))
def _op_mod(a, b): return z3.URem(a, b)
def run_block(s, solver, log_trace=False): def end_trace(reason, *args): s.end_type = reason s.end_info = args pass while True: op = s.code[s.pc] try: instr = vm_isa.opcodes[op] except KeyError: end_trace('invalid') return if log_trace: print('{:04}: {}'.format(s.pc, instr.name)) print('> ' + ';; '.join(str(z3.simplify(x)) for x in s.stack)) #print('> {} | {} | {}'.format(stack, mem, store)) try: instr_args = [s.stack.pop() for i in range(instr.pop)] except IndexError: end_trace('stack underflow') return def reducestack(fn): s.stack.append(fn(*instr_args)) oplen = 1 s.gas = s.gas - instr.base_gas if instr.extra_gas is not None: s.gas = s.gas - instr.extra_gas(s, *instr_args) s.gas = z3.simplify(s.gas) if op >= 0x80 and op <= 0x8f: # DUPn # instr_args[0] = old top of stack for v in reversed(instr_args): s.stack.append(v) s.stack.append(instr_args[-1]) elif op >= 0x90 and op <= 0x9f: #SWAPn # Old top of stack pushed first s.stack.append(instr_args[0]) # Then the middle section (in original order) for v in reversed(instr_args[1:-1]): s.stack.append(v) # Then bottom value on top s.stack.append(instr_args[-1]) elif op >= 0x60 and op <= 0x7f: #PUSHn npush = op - 0x60 + 1 s.stack.append(z3.BitVecVal(util.pushval(s.code, s.pc), 256)) oplen += npush elif instr.name == 'ADD': reducestack(lambda x, y: x + y) elif instr.name == 'MUL': reducestack(lambda x, y: x * y) elif instr.name == 'SUB': reducestack(lambda x, y: x - y) elif instr.name == 'DIV': reducestack(lambda x, y: z3.If(y == 0, z3.BitVecVal(0, 256), z3.UDiv(x, y))) elif instr.name == 'SDIV': reducestack(lambda x, y: z3.If( y == 0, z3.BitVecVal(0, 256), z3.If(x == -2**255 and y == -1, z3.BitVecVal(-2**255, 256), x / y))) elif instr.name == 'MOD': reducestack(lambda x, y: z3.If(y == 0, z3.BitVecVal(0, 256), z3.URem(x, y))) elif instr.name == 'SMOD': reducestack(lambda x, y: z3.If(y == 0, z3.BitVecVal(0, 256), z3.SRem(x, y))) elif instr.name == 'ADDMOD': reducestack(lambda x, y, z: z3.If( z == 0, z3.BitVecVal(0, 256), z3.Extract( 255, 0, z3.URem( z3.ZeroExt(1, x) + z3.ZeroExt(1, y), z3.ZeroExt(1, z))) )) elif instr.name == 'MULMOD': reducestack(lambda x, y, z: z3.If( z == 0, z3.BitVecVal(0, 256), z3.Extract( 255, 0, z3.URem( z3.ZeroExt(256, x) * z3.ZeroExt(256, y), z3.ZeroExt(256, z))))) elif instr.name == 'EXP': # TODO z3 currently doesn't seem to provide __pow__ on BitVecs? reducestack(lambda x, y: z3.BitVecVal( pow(x.as_long(), y.as_long(), 1 << 256), 256)) elif instr.name == 'LT': reducestack(lambda x, y: _bool_to_01(z3.ULT(x, y))) elif instr.name == 'GT': reducestack(lambda x, y: _bool_to_01(z3.UGT(x, y))) elif instr.name == 'SLT': reducestack(lambda x, y: _bool_to_01(x < y)) elif instr.name == 'SGT': reducestack(lambda x, y: _bool_to_01(x > y)) elif instr.name == 'EQ': reducestack(lambda x, y: _bool_to_01(x == y)) elif instr.name == 'ISZERO': reducestack(lambda x: _bool_to_01(x == 0)) elif instr.name == 'AND': reducestack(lambda x, y: x & y) elif instr.name == 'OR': reducestack(lambda x, y: x | y) elif instr.name == 'XOR': reducestack(lambda x, y: x ^ y) elif instr.name == 'NOT': reducestack(lambda x: ~x) elif instr.name == 'BYTE': idx, val = instr_args bidx = as_concrete(idx) if bidx <= 31: s.stack.append(z3.ZeroExt(248, get_byte(val, bidx))) else: s.stack.append(z3.BitVecVal(0, 256)) elif instr.name == 'SIGNEXTEND': idx, val = instr_args bidx = as_concrete(idx) if bidx <= 31: nbits = 8 * (bidx + 1) to_extend = z3.Extract(nbits - 1, 0, val) s.stack.append(z3.SignExt(256 - nbits, to_extend)) else: s.stack.append(val) elif instr.name == 'CODESIZE': s.stack.append(z3.BitVecVal(s.code.size(), 256)) elif instr.name == 'SHA3': start, sz = instr_args v = MemoryEmpty n = as_concrete(sz) for i in range(n): v = z3.Store(v, i, s.memory.select(start + i)) s.stack.append(sha3(v)) # TODO when n == 0 or all values are concrete, simplify! #start, sz = as_concrete(start), as_concrete(sz) #stack.append(ethereum.utils.sha3_256([as_concrete( elif instr.name in { 'GASPRICE', 'COINBASE', 'TIMESTAMP', 'NUMBER', 'DIFFICULTY', 'GASLIMIT', 'ORIGIN' }: reducestack(getattr(s.transaction, instr.name.lower())) elif instr.name in {'BALANCE', 'BLOCKHASH', 'EXTCODESIZE'}: reducestack(lambda x: (getattr(s.transaction, instr.name.lower()) ())(x)) elif instr.name == 'ADDRESS': s.stack.append(s.addr) elif instr.name == 'CALLVALUE': s.stack.append(s.callinfo.value) elif instr.name == 'CALLDATASIZE': s.stack.append(s.callinfo.calldata.size) elif instr.name == 'CALLER': s.stack.append(s.caller) elif instr.name == 'CODECOPY': # TODO handle non-concrete size start_mem, start_code, sz = instr_args start_code = as_concrete(start_code) for i in range(as_concrete(sz)): s.memory.store(start_mem + i, s.code[start_code + i]) elif instr.name == 'CALLDATACOPY': src, dest, sz = instr_args cd_mem, cd_off, cd_sz = s.callinfo.calldata # TODO cache this limited calldata memory object - this is so that # out of range calldata reads correctly return 0s limited_cdmem = mem.Memory() limited_cdmem.overlay(cd_mem, 0, cd_off, cd_sz) s.memory.overlay(limited_cdmem, dest, cd_off + src, sz) elif instr.name == 'CALLDATALOAD': addr, = instr_args cd_mem, cd_off, cd_sz, *_ = s.callinfo.calldata s.stack.append( z3.simplify( z3.Concat(*[ z3.If(addr + i < cd_sz, cd_mem.select(cd_off + addr + i), 0) for i in range(32) ]))) elif instr.name == 'RETURNDATASIZE': if hasattr(s, retdata): s.stack.append(s.retdata.size) else: s.stack.append(z3.BitVecVal(0, 256)) elif instr.name == 'RETURNDATACOPY': src, dest, sz = instr_args # TODO non-concrete length, retdata overflow (should terminate) if hasattr(s, retdata): for i in range(sz.as_long()): s.memory.store( dest + i, z3.Select(s.retdata.mem, s.retdata.offset + src + i)) elif instr.name == 'POP': pass elif instr.name == 'MLOAD': addr, = instr_args s.stack.append( z3.simplify( z3.Concat(*[s.memory.select(addr + i) for i in range(32)]))) elif instr.name == 'MSTORE': dst, word = instr_args for i in range(32): s.memory.store(dst + i, get_byte(word, i)) elif instr.name == 'MSTORE8': dst, word = instr_args s.memory.store(dst, get_byte(word, 31)) elif instr.name == 'SLOAD': addr, = instr_args s.stack.append(z3.simplify(z3.Select(s.storage, addr))) elif instr.name == 'SSTORE': addr, word = instr_args s.storage = z3.Store(s.storage, addr, word) elif instr.name == 'PC': s.stack.append(z3.BitVecVal(s.pc, 256)) elif instr.name == 'GAS': # TODO actually track gas usage? s.stack.append(z3.BitVec('{}:GAS'.format(s.pc), 256)) elif instr.name in 'STOP': end_trace('stop') return elif instr.name == 'RETURN': ret_start, ret_size = instr_args s.make_child_return(ret_start, ret_size) return elif instr.name == 'REVERT': ret_start, ret_size = instr_args s.make_child_revert(ret_start, ret_size) return elif instr.name in {'CALL', 'CALLCODE', 'DELEGATECALL'}: if instr.name in {'CALL', 'CALLCODE'}: gas, addr, value, in_off, in_sz, out_off, out_sz = instr_args caller = s.addr elif instr.name == 'DELEGATECALL': gas, addr, in_off, in_sz, out_off, out_sz = instr_args value = s.callinfo.value caller = s.caller else: assert False, instr.name addr = z3.simplify(addr) if instr.name == 'CALL': call_addr = addr code_addr = addr else: call_addr = z3.BitVecVal(s.addr, 256) code_addr = addr callres = z3.BitVec( '{}:{}({})'.format(s.pc, instr.name, z3.simplify(call_addr)), 256) s.stack.append(callres) if is_concrete(call_addr): s.make_child_call(addr=call_addr.as_long(), code_addr=code_addr.as_long(), caller=caller, retinfo=ReturnInfo(s, s.pc + 1, out_off, out_sz, callres), callinfo=CallInfo( MemRange(s.memory, in_off, in_sz), z3.BV2Int(gas), value)) return else: end_trace('call', call_addr, value, gas) s.make_child_branch( new_pc=s.pc + 1, preds=[s.gas > 0, z3.Or(callres == 1, callres == 0)]) return elif instr.name == 'CREATE': value, in_off, in_sz = instr_args res = z3.BitVec('{}:CREATE({})'.format(s.pc, value), 256) s.stack.append(res) end_trace('create', value) s.make_child_branch(new_pc=s.pc + 1, preds=[s.gas > 0, z3.Or(res == 0, res == 1)]) return elif instr.name == 'SELFDESTRUCT': to_addr = instr_args end_trace('suicide', to_addr) # No successors return elif instr.name == 'JUMPI': end_trace(None) loc, cond = instr_args fallthrough_pc = None solver.push() solver.add(cond == 0) fallthrough_state = None if solver.check() == z3.sat: # Also might not take the jump fallthrough_pc = s.pc + 1 solver.pop() solver.push() solver.add(cond != 0) if solver.check() == z3.sat: # OK, can take the jump if is_concrete(loc): loc_conc = loc.as_long() if loc_conc == fallthrough_pc: # Fuse fallthrough and jump if to same location fallthrough_pc = None s.make_child_branch(new_pc=loc_conc, preds=[s.gas > 0]) else: s.make_child_branch(new_pc=loc_conc, preds=[s.gas > 0, cond != 0]) else: for dest in s.code.all_jumpdests(): solver.push() solver.add(loc == dest) if solver.check() == z3.sat: if dest == fallthrough_pc: fallthrough_pc = None s.make_child_branch( new_pc=dest, preds=[s.gas > 0, loc == dest]) else: s.make_child_branch( new_pc=dest, preds=[s.gas > 0, cond != 0, loc == dest]) solver.pop() solver.pop() if fallthrough_pc is not None: s.make_child_branch(new_pc=fallthrough_pc, preds=[s.gas > 0, cond == 0]) return elif instr.name == 'JUMP': end_trace(None) (loc, ) = instr_args if is_concrete(loc): s.make_child_branch(new_pc=loc.as_long(), preds=[s.gas > 0]) else: successors = [] for dest in s.code.all_jumpdests(): solver.push() solver.add(loc == dest) if solver.check() == z3.sat: s.make_child_branch(new_pc=dest, preds=[s.gas > 0, loc == dest]) solver.pop() # No fallthrough return elif instr.name == 'JUMPDEST': s.jumpdests.add(s.pc) elif instr.name in {'LOG0', 'LOG1', 'LOG2', 'LOG3', 'LOG4'}: pass else: raise NotImplementedError(instr.name) if log_trace: print('< ' + ';; '.join(str(z3.simplify(x)) for x in s.stack)) s.pc += oplen