Beispiel #1
0
    def pick_operand_value(self, op_type: OperandType) -> Optional[int]:
        '''Pick a random value for an operand

        The result will always be non-negative: if the operand is a signed
        immediate, this is encoded as 2s complement.

        '''
        if isinstance(op_type, RegOperandType):
            return self.pick_reg_operand_value(op_type)

        op_rng = op_type.get_op_val_range(self.pc)
        if op_rng is None:
            # If we don't know the width, the only immediate that we *know*
            # is going to be valid is 0.
            return 0

        if isinstance(op_type, ImmOperandType):
            shift = op_type.shift
        else:
            shift = 0

        align = 1 << shift

        lo, hi = op_rng
        sh_lo = (lo + align - 1) // align
        sh_hi = hi // align

        op_val = random.randint(sh_lo, sh_hi) << shift
        return op_type.op_val_to_enc_val(op_val, self.pc)
Beispiel #2
0
    def _pick_loopi_iterations(self, max_iters: int, op_type: OperandType,
                               model: Model) -> Optional[IterCount]:
        '''Pick the number of iterations for a LOOPI loop

        max_iters is the maximum number of iterations possible, given how much
        fuel we have left.

        Returns the encoded and decoded number of iterations.

        '''

        assert isinstance(op_type, ImmOperandType)
        iters_range = op_type.get_op_val_range(model.pc)
        assert iters_range is not None
        iters_lo, iters_hi = iters_range

        # Constrain iters_hi if the max-loop-iters configuration value was set.
        if self.cfg_max_iters is not None:
            iters_hi = min(iters_hi, self.cfg_max_iters)
            if iters_hi < iters_lo:
                return None

        # Very occasionally, generate iters_hi iterations (the maximum number
        # representable) if we've got fuel for it. We don't do this often,
        # because the instruction sequence will end up just testing loop
        # handling and be very inefficient for testing anything else.
        if max_iters >= iters_hi and random.random() < 0.01:
            enc_val = op_type.op_val_to_enc_val(iters_hi, model.pc)
            # This should never fail, because iters_hi was encodable.
            assert enc_val is not None
            return (enc_val, iters_hi, None)

        # The rest of the time, we don't usually (95%) generate more than 3
        # iterations (because the instruction sequences are rather
        # repetitive!). Also, don't generate 0 iterations here, even though
        # it's encodable. That causes an error, so we'll do that in a separate
        # generator.
        if random.random() < 0.95:
            tgt_max_iters = min(max_iters, 3)
        else:
            tgt_max_iters = 10000

        ub = min(iters_hi, max_iters, tgt_max_iters)
        lb = max(iters_lo, 1)

        if ub < lb:
            return None

        # Otherwise, pick a value uniformly in [iters_lo, iters_hi]. No need
        # for clever weighting: in the usual case, there are just 3
        # possibilities!
        num_iters = random.randint(lb, ub)
        enc_val = op_type.op_val_to_enc_val(num_iters, model.pc)
        # This should never fail: the choice should have been in the encodable
        # range.
        assert enc_val is not None
        return (enc_val, num_iters, None)
Beispiel #3
0
    def _pick_loopi_iterations(self, op_type: OperandType, pc: int) -> int:
        # Like Loop._pick_loopi_iterations but simpler because it doesn't try
        # to weight towards small counts.
        assert isinstance(op_type, ImmOperandType)
        iters_range = op_type.get_op_val_range(pc)
        assert iters_range is not None
        iters_lo, iters_hi = iters_range
        assert 1 <= iters_hi
        num_iters = random.randint(max(iters_lo, 1), iters_hi)

        enc_val = op_type.op_val_to_enc_val(num_iters, pc)
        assert enc_val is not None
        return enc_val
Beispiel #4
0
    def pick_operand_value(self, op_type: OperandType) -> Optional[int]:
        '''Pick a random value for an operand

        The result will always be non-negative: if the operand is a signed
        immediate, this is encoded as 2s complement.

        '''
        if isinstance(op_type, RegOperandType):
            return self.pick_reg_operand_value(op_type)

        op_rng = op_type.get_op_val_range(self.pc)
        if op_rng is None:
            # If we don't know the width, the only immediate that we *know*
            # is going to be valid is 0.
            return 0

        lo, hi = op_rng
        op_val = random.randrange(lo, hi + 1)
        return op_type.op_val_to_enc_val(op_val, self.pc)
Beispiel #5
0
    def pick_operand_value(self, op_type: OperandType) -> Optional[int]:
        '''Pick a random value for an operand

        The result will always be non-negative: if the operand is a signed
        immediate, this is encoded as 2s complement.

        '''
        if isinstance(op_type, RegOperandType):
            return self.pick_reg_operand_value(op_type)

        if isinstance(op_type, ImmOperandType):
            rng = op_type.get_range()
            if rng is None:
                # If we don't know the width, the only immediate that we *know*
                # is valid is 0.
                return 0

            lo, hi = rng
            value = random.randrange(lo, hi + 1)
            return op_type.encode_val(value)

        raise NotImplementedError('Unknown operand type in '
                                  'Model.pick_operand_value')
Beispiel #6
0
    def _pick_iterations(self, op_type: OperandType, bodysize: int,
                         model: Model) -> Optional[Tuple[int, int]]:
        '''Pick the number of iterations for the loop

        If this is a LOOP instruction, op_type will be a RegOperandType. In
        this case, we pick a register whose value we know and which doesn't
        give us a ridiculous number of iterations (checking model.fuel).
        Otherwise, op_type is an ImmOperandType and we pick a reasonable
        iteration count.

        '''
        assert bodysize > 0
        min_fuel_per_iter = 1 if bodysize == 1 else 2
        # model.fuel - 2 is the fuel after executing the LOOP/LOOPI instruction
        # and before executing the minimum-length single-instruction
        # continuation.
        max_iters = (model.fuel - 2) // min_fuel_per_iter

        # Never generate more than 10 iterations (because the instruction
        # sequences would be booooring). Obviously, we'll need to come back to
        # this when filling coverage holes.
        #
        # In general, we'll weight by 1/(1 + abs(iters - 2)). This makes 2 the
        # most likely iteration count (which is good, because 1 iteration is
        # boring).
        max_iters = min(max_iters, 10)

        if isinstance(op_type, RegOperandType):
            assert op_type.reg_type == 'gpr'
            # Iterate over the known registers, trying to pick a weight
            poss_pairs = []  # type: List[Tuple[int, int]]
            weights = []  # type: List[float]
            for idx, value in model.regs_with_known_vals('gpr'):
                if 0 < value <= max_iters:
                    poss_pairs.append((idx, value))
                    # Weight higher iteration counts smaller (1 / count)
                    weights.append(1 / (1 + abs(value - 2)))
            if not poss_pairs:
                return None
            return random.choices(poss_pairs, weights=weights)[0]

        assert isinstance(op_type, ImmOperandType)
        iters_range = op_type.get_op_val_range(model.pc)
        assert iters_range is not None
        iters_lo, iters_hi = iters_range
        if max_iters < max(1, iters_lo):
            return None

        iters_lo = max(iters_lo, 1)
        iters_hi = min(iters_hi, max_iters)

        # Pick a value in [iters_lo, iters_hi], weighting lower values more
        # heavily (1 / count). Since we've made sure that iters_hi <= max_iters
        # <= 10, we don't need to do any clever maths: we can just use
        # random.choices with some weights.
        values = range(iters_lo, 1 + iters_hi)
        weights = []
        for value in values:
            weights.append(1 / (1 + abs(value - 2)))
        num_iters = random.choices(values, weights=weights)[0]
        return (num_iters, num_iters)