Exemple #1
0
    def eval(self, ctx):
        [lhs, rhs, width, signed] = match_types(try_eval(ctx, self.lhs),
                try_eval(ctx, self.rhs))

        if self.op == '+':
            result = lhs + rhs
        elif self.op == '-':
            result = lhs - rhs
        elif self.op == '*':
            # Double the width if these are bitvectors
            if width is not None:
                width *= 2
                lhs = extend(lhs, width, signed=signed)
                rhs = extend(rhs, width, signed=signed)
            result = lhs * rhs
        elif self.op == '/':
            result = lhs / rhs
        elif self.op == 'AND':
            result = lhs & rhs
        elif self.op == 'OR':
            result = lhs | rhs
        elif self.op == 'XOR':
            result = lhs ^ rhs
        elif self.op == '<<':
            # Add more bits to the left if we know the rhs
            if isinstance(rhs, int):
                width += rhs
                lhs = extend(lhs, width, signed=signed)
                rhs = extend(rhs, width, signed=signed)
            result = lhs << rhs
        elif self.op == '>>':
            result = lhs >> rhs
        elif self.op == '<':
            result = lhs < rhs
        elif self.op == '>':
            result = lhs > rhs
        elif self.op == '==':
            result = lhs == rhs
        elif self.op == '!=':
            result = lhs != rhs
        elif self.op == '%':
            result = lhs % rhs
        # Handle (a OP b): we allow OP to be assigned as a normal variable
        # (despite OP being a keyword) with the various _MM_CMPINT_* enum
        # values actually being z3 lambdas defined in intr_builtins.py. We
        # look up the current value of OP and call the function via array
        # access
        elif self.op == 'OP':
            op = ctx.get('OP')
            assert z3.is_array_sort(op), op
            lhs = z3.BV2Int(lhs, is_signed=signed)
            rhs = z3.BV2Int(rhs, is_signed=signed)
            result = z3_multi_array_index(op, lhs, rhs)
        else:
            assert False, 'unknown binop %s' % self.op

        return Value(result, width=width, signed=signed)
Exemple #2
0
    def test1(self):
        b = multiagent.Board('b1', 3, 3)
        s = b.sum_of_adj(1, 1)
        logger.debug(s)
        smt =  z3.BV2Int(z3.Extract(1, 1, b.BV)) +\
          z3.BV2Int(z3.Extract(7, 7, b.BV)) +\
          z3.BV2Int(z3.Extract(3, 3, b.BV)) +\
          z3.BV2Int(z3.Extract(5, 5, b.BV))

        self.assertTrue(z3.simplify(smt == s))
Exemple #3
0
def find_seed_nextLong(sequence_length: int,
                       slope: int = 0x5DEECE66D,
                       intercept: int = 0xB):
    # Define some constants
    addend = z.BitVecVal(intercept, 64)
    multiplier = z.BitVecVal(slope, 64)
    mask = z.BitVecVal((1 << 48) - 1, 64)

    # Note we're generating double the constraints in the sequence_length + 1
    # This is required since we'll be using seed_0 to extract the Random() instantiation value
    # Furthermore, each nextLong call consumes two outputs from next()
    next_constraints, seeds, next_outputs = make_constraints_next(
        n_constraints=2 * sequence_length + 1, gen_bits=32)

    # Define a symbolic variable that we'll use to get the value that instantiated Random()
    original_seed = z.BitVec('original_seed', 64)

    # Build a solver object
    s = z.Solver()

    # Build a constraint that relates seed_0 and the value used to instantiate Random()
    s.add(seeds[f'seed_0'] == (original_seed ^ multiplier) & mask)

    # Next let's add the constraints we've built for next()
    s.add(next_constraints)

    # Define symbolic variables for the output from nextLong
    # Notice: we're using a symbolic variable of type Int
    # Since nextLong does a sum on ints, it's easier to model this using Z3 arithmetic models
    # This differs from previous examples where we used BitVec objects exclusively
    # Consequently, we'll be using the conversion method BV2Int that takes a BitVec object and turns it into an Int object
    nextLong_outputs = {
        f'nextLong_output_{i}': z.Int(f'nextLong_output_{i}')
        for i in range(1, sequence_length + 1)
    }

    # Finally, let's add the constraints for nextLong
    for i, j in zip(range(1, sequence_length + 1),
                    range(1, sequence_length * 2 + 1, 2)):
        # Notice: we've replaced the bit shift operator in the source with an enquivalent multiplication
        # Z3 doesn't support bit shift operations on Int objects
        first_next = z.BV2Int(next_outputs[f'next_output_{j}'],
                              is_signed=True) * 2**32
        second_next = z.BV2Int(next_outputs[f'next_output_{j + 1}'],
                               is_signed=True)
        s.add(nextLong_outputs[f'nextLong_output_{i}'] == first_next +
              second_next)

    # Lastly, let's return all the objects we've constructed so far
    return s, original_seed, nextLong_outputs
Exemple #4
0
 def __cast_to_numb__(self, reference):
     self.__add_assume__(None)
     if isinstance(reference, z3.BoolRef):
         return z3.If(reference, 1, 0)
     elif isinstance(reference, z3.BitVecRef) or isinstance(
             reference, z3.BitVecNumRef):
         return z3.BV2Int(reference, True)
     else:
         return reference
def opcode_exp(opcode_data1, opcode_data2):

    if not is_z3_express(opcode_data1) and not is_z3_express(opcode_data2):
        op1 = format_to_int(opcode_data1)
        op2 = format_to_int(opcode_data2)
        result = op1**op2
        return z3.BitVecVal(result, 256)

    size = 0
    if is_z3_express(
            opcode_data1) or opcode_data1.__class__ == z3.z3.BitVecNumRef:
        size = opcode_data1.size()
        opcode_data1 = z3.BV2Int(opcode_data1)

    if is_z3_express(
            opcode_data2) or opcode_data1.__class__ == z3.z3.BitVecNumRef:
        if opcode_data2.size() > size:
            size = opcode_data2.size()
        opcode_data2 = z3.BV2Int(opcode_data2)

    return (z3.simplify(z3.Int2BV(opcode_data1**opcode_data2, size)))
Exemple #6
0
    def cast(self, value, is_type, to_type):
        if is_type is Types.STRING and isinstance(to_type,
                                                  z3.z3.DatatypeSortRef):
            # the value is a string and it should be cast to a datatyperef
            # (i.e. an enum-type), just return the value because we can deal with it
            return value

        value_is_int = isinstance(value, int)
        value_is_float = isinstance(value, float)

        if is_type is to_type:  # already correct type, just return the value
            return value
            """ INT <---> INTEGER """
        elif is_type is Types.INT and to_type is Types.INTEGER:
            if value_is_int or value_is_float:
                return value  # this happens if it is an int numeral (e.g. 2)
            else:
                return z3.BV2Int(value)
        elif is_type is Types.INTEGER and to_type is Types.INT:
            if value_is_int or value_is_float:
                return value
            else:
                return z3.Int2BV(value, 32)
            """ INT <---> FLOAT """
        elif is_type is Types.FLOAT and to_type is Types.INT:
            if value_is_float:
                return value  # this happens if it is a float numeral (e.g. 3.14)
            else:
                return z3.fpToSBV(z3.RNE(), value, z3.BitVecSort(32))
        elif is_type is Types.INT and to_type is Types.FLOAT:
            if value_is_int:
                return value
            else:
                return z3.fpSignedToFP(z3.RNE(), value, z3.Float32())
            """ INTEGER <---> FLOAT """
        elif is_type is Types.FLOAT and to_type is Types.INTEGER:
            if value_is_float:
                return value  # this happens if it is a float numeral (e.g. 3.14)
            else:
                return self.cast(self.cast(value, Types.FLOAT, Types.INT),
                                 Types.INT, Types.INTEGER)
        elif is_type is Types.INTEGER and to_type is Types.FLOAT:
            if value_is_int:
                return value
            else:
                return self.cast(self.cast(value, Types.INTEGER, Types.INT),
                                 Types.INT, Types.FLOAT)
            """ from REAL """
        elif is_type is Types.REAL and to_type is Types.INTEGER:
            if value_is_float:
                return value
            else:
                return z3.ToInt(value)
        elif is_type is Types.REAL and to_type is Types.INT:
            return self.cast(self.cast(value, Types.REAL, Types.INTEGER),
                             Types.INTEGER, Types.INT)
        elif is_type is Types.REAL and to_type is Types.FLOAT:
            """
            Rounding modes: probably should make these parameterizable!
            roundNearestTiesToEven ... RNE() = default
            roundNearestTiesToAway ... RNA()
            roundTowardPositive ...... RTP()
            roundTowardNegative ...... RTN()
            roundTowardZero .......... RTZ()
            """
            if value_is_int or value_is_float:  # int numeral
                return value
            else:
                return z3.fpRealToFP(z3.RNE(), value, z3.Float32())
            """ to REAL """
        elif is_type is Types.INT and to_type is Types.REAL:
            if value_is_int or value_is_float:  # int numeral
                return value
            else:
                return z3.ToReal(self.cast(value, Types.INT, Types.INTEGER))
        elif is_type is Types.INTEGER and to_type is Types.REAL:
            if value_is_int or value_is_float:  # int numeral
                return value
            else:
                return z3.ToReal(value)
        elif is_type is Types.FLOAT and to_type is Types.REAL:
            if value_is_int or value_is_float:
                return value  # this happens if it is a float numeral (e.g. 3.14)
            else:
                return z3.fpToReal(value)
            """ FROM BOOL conversions """
        elif is_type is Types.BOOL and to_type is Types.INT:
            return z3.If(value, z3.BitVecVal(1, 32), z3.BitVecVal(0, 32))
        elif is_type is Types.BOOL and to_type is Types.INTEGER:
            return z3.If(value, 1, 0)
        elif is_type is Types.BOOL and to_type is Types.REAL:
            return z3.If(value, 1.0, 0.0)
        elif is_type is Types.BOOL and to_type is Types.FLOAT:
            return z3.If(value, z3.FPVal(1.0, z3.Float32()),
                         z3.FPVal(0.0, z3.Float32()))
            """ TO BOOL conversions """
        elif is_type is Types.INT and to_type is Types.BOOL:
            return value == 1
        elif is_type is Types.INTEGER and to_type is Types.BOOL:
            return value == 1
        elif is_type is Types.REAL and to_type is Types.BOOL:
            return value == 1
        elif is_type is Types.FLOAT and to_type is Types.BOOL:
            return value == 1

        raise TypeError(f"Don't know how to cast from {is_type} to {to_type}!")
Exemple #7
0
    def bv_sampler(self, solver, exprs):
        global CON_SOL_TIME, CON_SOL_COUNT, OTHER_TIME, OTHER_COUNT,\
            NO_MODEL_COUNT, IS_MODEL_COUNT, DP_MODEL_COUNT, \
            FROM_FUZZING_COUNT, FROM_FUZZING_TIME, CINR, HIGH_FUZZING_COUNT
        # A collection of results (via constraint solving)
        #  and candidates (via bit flipping)
        # in the format of {value: level}
        mutations = {}
        target = exprs[0]
        assert len(exprs) == 1
        n = target.size()
        flippable_bits = [i for i in range(n)]
        delta = z3.BitVec('delta', n)
        result = z3.BitVec('result', n)

        # solver = self.solver()
        solver.add(result == target)
        solver.minimize(z3.BV2Int(delta))
        results = set()

        while True:
            # LOGGER.info('---------------------------')
            LOGGER.info("results len:", len(results))
            guess = z3.BitVecVal(
                random.getrandbits(min(len(results), n)) if results else 0, n)
            # LOGGER.info('------------0--------------')

            solver.push()
            for value in mutations:
                LOGGER.error(
                    "************** Expensive to generate a new sigma ****************"
                )
                solver.add(result != value)
            solver.add(result ^ delta == guess)
            LOGGER.info('------------1--------------')
            import time
            pre = time.time()
            if solver.check() != z3.sat:
                LOGGER.info("**************No solution ****************")
                break
            model = solver.model()
            result0 = model[result].as_long()
            post = time.time()
            CON_SOL_TIME += post - pre
            CON_SOL_COUNT += 1

            solver.pop()
            LOGGER.info('------------2--------------')
            results.add(result0)

            self.log_sampler_status()
            yield result0
            yield None
            LOGGER.info('------------3--------------')
            LOGGER.info('solver: ' + str(solver))
            LOGGER.info('guess: ' + str(guess))
            LOGGER.info('model: ' + str(model))
            LOGGER.info('------------4--------------')
            nresults = 0
            # From 0 to n has a low probability of finding a valid model
            # for i in range(n-1, -1, -1):
            # for i in [(n-1)*(i%2)+(i//2*(-1) if i%2 else i//2) for i in range(n)]:
            while flippable_bits:
                i = flippable_bits.pop(
                    random.randint(0,
                                   len(flippable_bits) - 1))
                # Generating a result with the ith bit flipped
                LOGGER.info('mutating bit ' + str(i))
                solver.push()
                goal = z3.BitVecVal(result0, n)
                solver.add(result ^ delta == goal)
                solver.add(z3.Extract(i, i, delta) == 0x1)
                pre = time.time()
                if solver.check() == z3.sat:
                    model = solver.model()
                else:
                    model = None
                post = time.time()
                OTHER_TIME += post - pre
                OTHER_COUNT += 1
                self.log_sampler_status()
                solver.pop()

                # Try the next bit if the model is unsat
                if not model:
                    LOGGER.info("No model found")
                    NO_MODEL_COUNT += 1
                    self.log_sampler_status()
                    continue

                # A new result is found by the model
                new_result = model[result].as_long()

                # Try the next bit if new result is a duplicate
                if new_result in mutations:
                    LOGGER.info("Duplicated new result")
                    DP_MODEL_COUNT += 1
                    self.log_sampler_status()
                    continue

                LOGGER.info("New result found ")
                IS_MODEL_COUNT += 1
                nresults += 1
                self.log_sampler_status()
                yield new_result

                # Start combining existing mutations
                new_mutations = {new_result: 1}
                # Needs at least one result in the mutations (e.g. sigma_a)
                # to combine with the new result (e.g. sigma_b)
                # When mutations is empty, this for loop will be skipped
                for existing_result in mutations:
                    pre = time.time()
                    # print("Combining with ", existing_result)
                    level = mutations[existing_result]
                    if level > MAX_LEVEL:
                        HIGH_FUZZING_COUNT += 1
                        self.log_sampler_status()
                        continue

                    candidate = (result0 ^ ((result0 ^ existing_result) |
                                            (result0 ^ new_result)))
                    LOGGER.info('yielding candidate ' + str(candidate) +
                                ' at level ' + str(level))

                    # Try the next existing result in mutations if
                    # this candidate is a duplicate
                    if candidate in mutations:
                        CINR += 1
                        self.log_sampler_status()
                        continue

                    # The candidate is new
                    new_mutations[candidate] = level + 1
                    nresults += 1
                    FROM_FUZZING_COUNT += 1
                    post = time.time()
                    FROM_FUZZING_TIME += (post - pre)
                    self.log_sampler_status()
                    yield candidate
                mutations.update(new_mutations)
                yield None  # all cheap results are generated
                # LOGGER.info("============== Looping forever===========-")
            if not nresults:
                break
Exemple #8
0
def _cmp(cmp, a, b, **kwargs):
    [a, b, width, signed] = match_types(a, b)
    a = z3.BV2Int(a, is_signed=signed)
    b = z3.BV2Int(b, is_signed=signed)
    return z3.Int2BV(z3.If(cmp(a, b), a, b), width)
Exemple #9
0
b2 = z3.BitVec('rbx', bvl)

s = z3.Solver()

s.push()
var1 = z3.If(z3.And(x1 + cf >= 0, x1 + cf <= 4294967295), x1 + cf,
             (x1 + cf) % 4294967296)
var2 = (x1 + cf) % 4294967296

s.add(var1 != var2)
solve(s)
s.pop()

## addition of two ints followed by mod === addition of two bvs
s.push()
var1 = (z3.BV2Int(b1) + z3.BV2Int(b2)) % maxbvl
var2 = z3.BV2Int(b1 + b2)

s.add(var1 != var2)
solve(s)
s.pop()

## addition of 2 bvs  is in 2's complement
s.push()
var1 = b1 + b2
var2 = (b1 + b2) & z3.BitVecVal(maxbvl - 1, bvl)

s.add(var1 != var2)
solve(s)
s.pop()
Exemple #10
0
 def log_gas(state, _, sz, *topics):
     return z3.BV2Int(sz) * Glogdata
Exemple #11
0
 def words_ceil(n):
     # ceil(n / 32)
     return z3.BV2Int((n + 31) >> 5)
Exemple #12
0
assert(bar2020.same_constraint_single_variable(answer,z3.Int('y_intle:32') == -2))

#another example
bv_value2 = z3.And(ex3124 == z3.BitVecVal(-2,8),
                  ex2316 == z3.BitVecVal(-1,8),
                  ex158 == z3.BitVecVal(-1,8),
                  ex70 == z3.BitVecVal(-1,8))
answer = bar2020.from_bv_to_int_domain(bv_value2)
print("As bit-vector: " + str(bv_value2) + " in integer domain: " + str(answer))
assert(bar2020.same_constraint_single_variable(answer,z3.Int('y_intle:32') == -2))

#check two values are the same
assert(bar2020.same_value_single_variable(bv_value1, bv_value2))

#example of conversion not precise
variable_relationship = (z3.BV2Int(yconcat) == i_y)
i_value = i_y + 1
bv_value3 = yconcat + z3.BitVecVal(1, 32)
value_relationship = (z3.BV2Int(bv_value3) == i_value)
(same, model, explanation) = (
    bar2020.single_variable_bit_vector_domain_equals_integer_domain(
    i_y, y, variable_relationship,
    i_value, bv_value3, value_relationship,
    True, True, bv_type='intle:32'))
print("Is conversion precise for bit-vector:" +
      str(bv_value3) + " in integer domain:" + str(i_value))
print(explanation)

#check given constraints that should make them precise
i_value = i_y - 2
bv_value4 = yconcat + z3.BitVecVal(-2, 32)
Exemple #13
0
def interp(tree, lookup, z3_mode):
    """Evaluate the arithmetic expression.

    Pass a tree as a Lark `Tree` object for the parsed expression. For
    `lookup`, provide a function for mapping variable names to values.
    """

    op = tree.data
    if op in ('add', 'sub', 'mul', 'div', 'shl', 'shr'):  # Binary operators.
        lhs = interp(tree.children[0], lookup, z3_mode)
        rhs = interp(tree.children[1], lookup, z3_mode)
        if op == 'add':
            return lhs + rhs
        elif op == 'sub':
            return lhs - rhs
        elif op == 'mul':
            return lhs * rhs
        elif op == 'div':
            return lhs / rhs
        elif op == 'shl':
            return lhs << rhs
        elif op == 'shr':
            return lhs >> rhs
    elif op == 'neg':  # Negation.
        sub = interp(tree.children[0], lookup, z3_mode)
        return -sub
    elif op == 'int':  # Literal number.
        if z3_mode:
            return z3.BitVecVal(int(tree.children[0]), 32)
        else:
            return int(tree.children[0])
    elif op == 'float':  # Literal number.
        if z3_mode:
            return z3.BitVecVal(float(tree.children[0]), 32)
        else:
            return float(tree.children[0])
    elif op == 'var':  # Variable lookup.
        return lookup(tree.children[0])
    elif op == 'if':  # Conditional.
        cond = interp(tree.children[0], lookup, z3_mode)
        true = interp(tree.children[1], lookup, z3_mode)
        false = interp(tree.children[2], lookup, z3_mode)
        return (cond != 0) * true + (cond == 0) * false
    elif op == "intcast":
        child = interp(tree.children[0], lookup, z3_mode)
        if z3_mode:
            return z3.BV2Int(child)
        else:
            return int(child)
    elif op == "floatcast":
        child = interp(tree.children[0], lookup, z3_mode)
        if z3_mode:
            return z3.fpBVToFP(child, z3.Float32())
        else:
            return float(child)
    elif op == "int2bv":
        child = interp(tree.children[0], lookup, z3_mode)
        if z3_mode:
            return z3.Int2BV(child, 32)
        else:
            return child
    elif op == "float2bv":
        child = interp(tree.children[0], lookup, z3_mode)
        if z3_mode:
            return z3.fpToIEEEBV(child)
        else:
            return child
Exemple #14
0
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