Example #1
0
def execute(statespace):

    logging.debug("Executing module: INTEGER_UNDERFLOW")

    issues = []

    for k in statespace.nodes:
        node = statespace.nodes[k]

        for instruction in node.instruction_list:

            if(instruction['opcode'] == "SUB"):

                stack = node.states[instruction['address']].stack

                op0 = stack[-1]
                op1 = stack[-2]

                constraints = copy.deepcopy(node.constraints)

                if type(op0) == int and type(op1) == int:
                    continue

                if (re.search(r'calldatasize_', str(op0))) \
                    or (re.search(r'256\*.*If\(1', str(op0), re.DOTALL) or re.search(r'256\*.*If\(1', str(op1), re.DOTALL)) \
                    or (re.search(r'32 \+.*calldata', str(op0), re.DOTALL) or re.search(r'32 \+.*calldata', str(op1), re.DOTALL)):

                    # Filter for patterns that contain possible (but apparently non-exploitable) Integer underflows.

                    # Pattern 1: (96 + calldatasize_MAIN) - (96), where (96 + calldatasize_MAIN) would underflow if calldatasize is very large.
                    # Pattern 2: (256*If(1 & storage_0 == 0, 1, 0)) - 1, this would underlow if storage_0 = 0

                    # Both seem to be standard compiler outputs that exist in many contracts.

                    continue

                logging.debug("[INTEGER_UNDERFLOW] Checking SUB " + str(op0) + ", " + str(op1) + " at address " + str(instruction['address']))

                constraints.append(UGT(op1,op0))

                try:
                    
                    model = solver.get_model(constraints)

                    issue = Issue(node.module_name, node.function_name, instruction['address'], "Integer Underflow", "Warning")

                    issue.description = "A possible integer underflow exists in the function " + node.function_name + ".\n" \
                        "The SUB instruction at address " + str(instruction['address']) + " may result in a value < 0." 

                    issue.debug = "(" + str(op0) + ") - (" + str(op1) + ").]"

                    issues.append(issue)

                    for d in model.decls():
                        logging.debug("[INTEGER_UNDERFLOW] model: %s = 0x%x" % (d.name(), model[d].as_long()))

                except UnsatError:
                    logging.debug("[INTEGER_UNDERFLOW] no model found")         

    return issues
Example #2
0
def _check_integer_overflow(statespace, state, node):
    """
    Checks for integer overflow
    :param statespace: statespace that is being examined
    :param state: state from node to examine
    :param node: node to examine
    :return: found issue
    """
    issues = []

    # Check the instruction
    instruction = state.get_current_instruction()
    if instruction['opcode'] not in ("ADD", "MUL"):
        return issues

    # Formulate overflow constraints
    stack = state.mstate.stack
    op0, op1 = stack[-1], stack[-2]

    # An integer overflow is possible if op0 + op1 or op0 * op1 > MAX_UINT
    # Do a type check
    allowed_types = [int, BitVecRef, BitVecNumRef]
    if not (type(op0) in allowed_types and type(op1) in allowed_types):
        return issues

    # Change ints to BitVec
    if type(op0) is int:
        op0 = BitVecVal(op0, 256)
    if type(op1) is int:
        op1 = BitVecVal(op1, 256)

    # Formulate expression
    if instruction['opcode'] == "ADD":
        expr = op0 + op1
    else:
        expr = op1 * op0

    # Check satisfiable
    constraint = Or(ULT(expr, op0), ULT(expr, op1))
    model = _try_constraints(node.constraints, [constraint])

    if model is None:
        logging.debug("[INTEGER_OVERFLOW] no model found")
        return issues

    if not _verify_integer_overflow(statespace, node, expr, state, model,
                                    constraint, op0, op1):
        return issues

    # Build issue
    issue = Issue(node.contract_name, node.function_name,
                  instruction['address'], "Integer Overflow ", "Warning")

    issue.description = "A possible integer overflow exists in the function `{}`.\n" \
                        "The addition or multiplication may result in a value higher than the maximum representable integer.".format(
        node.function_name)
    issue.debug = solver.pretty_print_model(model)
    issues.append(issue)

    return issues
Example #3
0
def _check_integer_underflow(state, node):
    """
    Checks for integer underflow
    :param state: state from node to examine
    :param node: node to examine
    :return: found issue
    """
    issues = []
    instruction = state.get_current_instruction()

    if instruction['opcode'] == "SUB":

        stack = state.mstate.stack

        op0 = stack[-1]
        op1 = stack[-2]

        constraints = copy.deepcopy(node.constraints)

        # Filter for patterns that contain bening nteger underflows.

        # Pattern 1: (96 + calldatasize_MAIN) - (96), where (96 + calldatasize_MAIN) would underflow if calldatasize is very large.
        # Pattern 2: (256*If(1 & storage_0 == 0, 1, 0)) - 1, this would underlow if storage_0 = 0
        if type(op0) == int and type(op1) == int:
            return []
        if re.search(r'calldatasize_', str(op0)):
            return []
        if re.search(r'256\*.*If\(1', str(op0), re.DOTALL) or re.search(r'256\*.*If\(1', str(op1), re.DOTALL):
            return []
        if re.search(r'32 \+.*calldata', str(op0), re.DOTALL) or re.search(r'32 \+.*calldata', str(op1), re.DOTALL):
            return []

        logging.debug("[INTEGER_UNDERFLOW] Checking SUB {0}, {1} at address {2}".format(str(op0), str(op1),
                                                                                        str(instruction['address'])))

        allowed_types = [int, BitVecRef, BitVecNumRef]

        if type(op0) in allowed_types and type(op1) in allowed_types:
            constraints.append(UGT(op1, op0))

            try:

                model = solver.get_model(constraints)

                issue = Issue(node.contract_name, node.function_name, instruction['address'], "Integer Underflow",
                              "Warning")

                issue.description = "A possible integer underflow exists in the function " + node.function_name + ".\n" \
                                                                                                                  "The subtraction may result in a value < 0."

                issue.debug = solver.pretty_print_model(model)
                issues.append(issue)

            except UnsatError:
                logging.debug("[INTEGER_UNDERFLOW] no model found")
    return issues
Example #4
0
    def _handle_transaction_end(self, state: GlobalState) -> None:
        for annotation in cast(
                List[OverUnderflowStateAnnotation],
                state.get_annotations(OverUnderflowStateAnnotation),
        ):

            ostate = annotation.overflowing_state
            address = _get_address_from_state(ostate)

            if annotation.operator == "subtraction" and self._underflow_cache.get(
                    address, False):
                continue

            if annotation.operator != "subtraction" and self._overflow_cache.get(
                    address, False):
                continue

            try:
                # This check can be disabled if the contraints are to difficult for z3 to solve
                # within any reasonable time.
                if DISABLE_EFFECT_CHECK:
                    constraints = ostate.mstate.constraints + [
                        annotation.constraint
                    ]
                else:
                    constraints = state.mstate.constraints + [
                        annotation.constraint
                    ]

                transaction_sequence = solver.get_transaction_sequence(
                    state, constraints)
            except UnsatError:
                continue

            _type = "Underflow" if annotation.operator == "subtraction" else "Overflow"
            issue = Issue(
                contract=ostate.environment.active_account.contract_name,
                function_name=ostate.environment.active_function_name,
                address=ostate.get_current_instruction()["address"],
                swc_id=INTEGER_OVERFLOW_AND_UNDERFLOW,
                bytecode=ostate.environment.code.bytecode,
                title=self._get_title(_type),
                severity="High",
                description_head=self._get_description_head(annotation, _type),
                description_tail=self._get_description_tail(annotation, _type),
                gas_used=(state.mstate.min_gas_used,
                          state.mstate.max_gas_used),
            )

            issue.debug = json.dumps(transaction_sequence, indent=4)

            if annotation.operator == "subtraction":
                self._underflow_cache[address] = True
            else:
                self._overflow_cache[address] = True
            self._issues.append(issue)
Example #5
0
    def _handle_sstore(self, state):
        stack = state.mstate.stack
        value = stack[-2]

        if not isinstance(value, Expression):
            return
        for annotation in value.annotations:
            if not isinstance(annotation, OverUnderflowAnnotation):
                continue

            _type = "Underflow" if annotation.operator == "subtraction" else "Overflow"
            ostate = annotation.overflowing_state
            node = ostate.node

            issue = Issue(
                contract=node.contract_name,
                function_name=node.function_name,
                address=ostate.get_current_instruction()["address"],
                swc_id=INTEGER_OVERFLOW_AND_UNDERFLOW,
                bytecode=ostate.environment.code.bytecode,
                title=self._get_title(_type),
                severity="High",
                description_head=self._get_description_head(annotation, _type),
                description_tail=self._get_description_tail(annotation, _type),
                gas_used=(state.mstate.min_gas_used,
                          state.mstate.max_gas_used),
            )

            address = _get_address_from_state(ostate)

            if annotation.operator == "subtraction" and self._underflow_cache.get(
                    address, False):
                continue
            if annotation.operator != "subtraction" and self._overflow_cache.get(
                    address, False):
                continue

            try:

                transaction_sequence = solver.get_transaction_sequence(
                    state, node.constraints + [annotation.constraint])

                issue.debug = json.dumps(transaction_sequence, indent=4)

            except UnsatError:
                continue
            if annotation.operator == "subtraction":
                self._underflow_cache[address] = True
            else:
                self._overflow_cache[address] = True

            self._issues.append(issue)
Example #6
0
def _check_integer_overflow(statespace, state, node):
    """
    Checks for integer overflow
    :param statespace: statespace that is being examined
    :param state: state from node to examine
    :param node: node to examine
    :return: found issue
    """
    issues = []

    # Check the instruction
    instruction = state.get_current_instruction()
    if instruction['opcode'] != "ADD":
        return []

    constraints = copy.deepcopy(node.constraints)

    # Formulate overflow constraints
    stack = state.mstate.stack
    op0, op1 = stack[-1], stack[-2]

    # An integer overflow is possible if op0 + op1,
    constraints.append(UGT(op0 + op1, (2 ** 32) - 1))

    try:
        model = solver.get_model(constraints)

        # If we get to this point then there has been an integer overflow
        # Find out if the overflowed value is actually used
        interesting_usages = _search_children(statespace, node, (op0 + op1), index=node.states.index(state))

        # Stop if it isn't
        if len(interesting_usages) == 0:
            return issues

        issue = Issue(node.contract_name, node.function_name, instruction['address'], "Integer Overflow ",
                      "Warning")

        issue.description = "A possible integer overflow exists in the function {}.\n" \
                            "The addition may result in a value higher than the maximum representable integer.".format(node.function_name)
        issue.debug = solver.pretty_print_model(model)
        issues.append(issue)

    except UnsatError:
        logging.debug("[INTEGER_OVERFLOW] no model found")

    return issues
Example #7
0
def execute(statespace):

    logging.debug("Executing module: INTEGER")

    issues = []

    for k in statespace.nodes:
        node = statespace.nodes[k]

        for state in node.states:

            instruction = state.get_current_instruction()

            if (instruction['opcode'] == "SUB"):

                stack = state.mstate.stack

                op0 = stack[-1]
                op1 = stack[-2]

                constraints = copy.deepcopy(node.constraints)

                if type(op0) == int and type(op1) == int:
                    continue

                if (re.search(r'calldatasize_', str(op0))) \
                    or (re.search(r'256\*.*If\(1', str(op0), re.DOTALL) or re.search(r'256\*.*If\(1', str(op1), re.DOTALL)) \
                    or (re.search(r'32 \+.*calldata', str(op0), re.DOTALL) or re.search(r'32 \+.*calldata', str(op1), re.DOTALL)):

                    # Filter for patterns that contain bening nteger underflows.

                    # Pattern 1: (96 + calldatasize_MAIN) - (96), where (96 + calldatasize_MAIN) would underflow if calldatasize is very large.
                    # Pattern 2: (256*If(1 & storage_0 == 0, 1, 0)) - 1, this would underlow if storage_0 = 0

                    continue

                logging.debug("[INTEGER_UNDERFLOW] Checking SUB " + str(op0) +
                              ", " + str(op1) + " at address " +
                              str(instruction['address']))

                allowed_types = [int, BitVecRef, BitVecNumRef]

                if type(op0) in allowed_types and type(op1) in allowed_types:
                    constraints.append(UGT(op1, op0))

                    try:

                        model = solver.get_model(constraints)

                        issue = Issue(node.contract_name, node.function_name,
                                      instruction['address'],
                                      "Integer Underflow", "Warning")

                        issue.description = "A possible integer underflow exists in the function " + node.function_name + ".\n" \
                            "The substraction may result in a value < 0."

                        issue.debug = solver.pretty_print_model(model)
                        issues.append(issue)

                    except UnsatError:
                        logging.debug("[INTEGER_UNDERFLOW] no model found")

    return issues
Example #8
0
def _check_integer_underflow(statespace, state, node):
    """
    Checks for integer underflow
    :param state: state from node to examine
    :param node: node to examine
    :return: found issue
    """
    issues = []
    instruction = state.get_current_instruction()
    if instruction['opcode'] == "SUB":

        stack = state.mstate.stack

        op0 = stack[-1]
        op1 = stack[-2]

        constraints = copy.deepcopy(node.constraints)

        # Filter for patterns that indicate benign underflows

        # Pattern 1: (96 + calldatasize_MAIN) - (96), where (96 + calldatasize_MAIN) would underflow if calldatasize is very large.
        # Pattern 2: (256*If(1 & storage_0 == 0, 1, 0)) - 1, this would underlow if storage_0 = 0
        if type(op0) == int and type(op1) == int:
            return []
        if re.search(r'calldatasize_', str(op0)):
            return []
        if re.search(r'256\*.*If\(1', str(op0), re.DOTALL) or re.search(
                r'256\*.*If\(1', str(op1), re.DOTALL):
            return []
        if re.search(r'32 \+.*calldata', str(op0), re.DOTALL) or re.search(
                r'32 \+.*calldata', str(op1), re.DOTALL):
            return []

        logging.debug(
            "[INTEGER_UNDERFLOW] Checking SUB {0}, {1} at address {2}".format(
                str(op0), str(op1), str(instruction['address'])))
        allowed_types = [int, BitVecRef, BitVecNumRef]

        if type(op0) in allowed_types and type(op1) in allowed_types:
            constraints.append(UGT(op1, op0))

            try:

                model = solver.get_model(constraints)

                # If we get to this point then there has been an integer overflow
                # Find out if the overflowed value is actually used
                interesting_usages = _search_children(
                    statespace,
                    node, (op0 - op1),
                    index=node.states.index(state))

                # Stop if it isn't
                if len(interesting_usages) == 0:
                    return issues

                issue = Issue(contract=node.contract_name,
                              function=node.function_name,
                              address=instruction['address'],
                              swc_id=INTEGER_OVERFLOW_AND_UNDERFLOW,
                              title="Integer Underflow",
                              _type="Warning")

                issue.description = "A possible integer underflow exists in the function `" + node.function_name + "`.\n" \
                                                                                                                   "The subtraction may result in a value < 0."

                issue.debug = solver.pretty_print_model(model)
                issues.append(issue)

            except UnsatError:
                logging.debug("[INTEGER_UNDERFLOW] no model found")
    return issues
Example #9
0
def _check_integer_overflow(statespace, state, node):
    """
    Checks for integer overflow
    :param statespace: statespace that is being examined
    :param state: state from node to examine
    :param node: node to examine
    :return: found issue
    """
    issues = []

    # Check the instruction
    instruction = state.get_current_instruction()
    if instruction["opcode"] not in ("ADD", "MUL"):
        return issues

    # Formulate overflow constraints
    stack = state.mstate.stack
    op0, op1 = stack[-1], stack[-2]

    # An integer overflow is possible if op0 + op1 or op0 * op1 > MAX_UINT
    # Do a type check
    allowed_types = [int, BitVecRef, BitVecNumRef]
    if not (type(op0) in allowed_types and type(op1) in allowed_types):
        return issues

    # Change ints to BitVec
    if type(op0) is int:
        op0 = BitVecVal(op0, 256)
    if type(op1) is int:
        op1 = BitVecVal(op1, 256)

    # Formulate expression
    if instruction["opcode"] == "ADD":
        expr = op0 + op1
        # constraint = Not(BVAddNoOverflow(op0, op1, signed=False))
    else:
        expr = op1 * op0
        # constraint = Not(BVMulNoOverflow(op0, op1, signed=False))

    constraint = Or(And(ULT(expr, op0), op1 != 0), And(ULT(expr, op1), op0 != 0))
    # Check satisfiable
    model = _try_constraints(node.constraints, [constraint])

    if model is None:
        logging.debug("[INTEGER_OVERFLOW] no model found")
        return issues

    if not _verify_integer_overflow(
        statespace, node, expr, state, model, constraint, op0, op1
    ):
        return issues

    # Build issue
    issue = Issue(
        contract=node.contract_name,
        function_name=node.function_name,
        address=instruction["address"],
        swc_id=INTEGER_OVERFLOW_AND_UNDERFLOW,
        bytecode=state.environment.code.bytecode,
        title="Integer Overflow",
        _type="Warning",
    )

    issue.description = "The arithmetic operation can result in integer overflow.\n"
    issue.debug = "Transaction Sequence: " + str(
        solver.get_transaction_sequence(state, node.constraints)
    )
    issues.append(issue)

    return issues
Example #10
0
    def _check_integer_overflow(self, statespace, state, node):
        """
        Checks for integer overflow
        :param statespace: statespace that is being examined
        :param state: state from node to examine
        :param node: node to examine
        :return: found issue
        """
        issues = []

        # Check the instruction
        instruction = state.get_current_instruction()
        if instruction["opcode"] not in ("ADD", "MUL"):
            return issues

        # Formulate overflow constraints
        stack = state.mstate.stack
        op0, op1 = stack[-1], stack[-2]

        # An integer overflow is possible if op0 + op1 or op0 * op1 > MAX_UINT
        # Do a type check
        allowed_types = [int, BitVecRef, BitVecNumRef]
        if not (type(op0) in allowed_types and type(op1) in allowed_types):
            return issues

        # Change ints to BitVec
        if type(op0) is int:
            op0 = BitVecVal(op0, 256)
        if type(op1) is int:
            op1 = BitVecVal(op1, 256)

        # Formulate expression
        # FIXME: handle exponentiation
        if instruction["opcode"] == "ADD":
            operator = "add"
            expr = op0 + op1
            constraint = Not(BVAddNoOverflow(op0, op1, signed=False))
        else:
            operator = "multiply"
            expr = op1 * op0
            constraint = Not(BVMulNoOverflow(op0, op1, signed=False))

        # Check satisfiable
        model = self._try_constraints(node.constraints, [constraint])

        if model is None:
            logging.debug("[INTEGER_OVERFLOW] no model found")
            return issues

        # Build issue
        issue = Issue(
            contract=node.contract_name,
            function_name=node.function_name,
            address=instruction["address"],
            swc_id=INTEGER_OVERFLOW_AND_UNDERFLOW,
            bytecode=state.environment.code.bytecode,
            title="Integer Overflow",
            _type="Warning",
            gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used),
        )

        issue.description = "This binary {} operation can result in integer overflow.\n".format(
            operator)
        try:
            issue.debug = "Transaction Sequence: " + str(
                solver.get_transaction_sequence(state, node.constraints))
        except UnsatError:
            return issues

        issues.append(issue)

        return issues
Example #11
0
    def _check_integer_underflow(self, statespace, state, node):
        """
        Checks for integer underflow
        :param state: state from node to examine
        :param node: node to examine
        :return: found issue
        """
        issues = []
        instruction = state.get_current_instruction()
        if instruction["opcode"] == "SUB":

            stack = state.mstate.stack

            op0 = stack[-1]
            op1 = stack[-2]

            constraints = copy.deepcopy(node.constraints)

            # Filter for patterns that indicate benign underflows

            # Pattern 1: (96 + calldatasize_MAIN) - (96), where (96 + calldatasize_MAIN) would underflow if calldatasize is very large.
            # Pattern 2: (256*If(1 & storage_0 == 0, 1, 0)) - 1, this would underlow if storage_0 = 0
            if type(op0) == int and type(op1) == int:
                return []
            if re.search(r"calldatasize_", str(op0)):
                return []
            if re.search(r"256\*.*If\(1", str(op0), re.DOTALL) or re.search(
                    r"256\*.*If\(1", str(op1), re.DOTALL):
                return []
            if re.search(r"32 \+.*calldata", str(op0), re.DOTALL) or re.search(
                    r"32 \+.*calldata", str(op1), re.DOTALL):
                return []

            logging.debug(
                "[INTEGER_UNDERFLOW] Checking SUB {0}, {1} at address {2}".
                format(str(op0), str(op1), str(instruction["address"])))
            allowed_types = [int, BitVecRef, BitVecNumRef]

            if type(op0) in allowed_types and type(op1) in allowed_types:
                constraints.append(UGT(op1, op0))

                try:

                    model = solver.get_model(constraints)

                    # If we get to this point then there has been an integer overflow
                    # Find out if the overflowed value is actually used
                    interesting_usages = self._search_children(
                        statespace,
                        node, (op0 - op1),
                        index=node.states.index(state))

                    # Stop if it isn't
                    if len(interesting_usages) == 0:
                        return issues

                    issue = Issue(
                        contract=node.contract_name,
                        function_name=node.function_name,
                        address=instruction["address"],
                        swc_id=INTEGER_OVERFLOW_AND_UNDERFLOW,
                        bytecode=state.environment.code.bytecode,
                        title="Integer Underflow",
                        _type="Warning",
                        gas_used=(state.mstate.min_gas_used,
                                  state.mstate.max_gas_used),
                    )

                    issue.description = (
                        "The subtraction can result in an integer underflow.\n"
                    )

                    issue.debug = "Transaction Sequence: " + str(
                        solver.get_transaction_sequence(
                            state, node.constraints))
                    issues.append(issue)

                except UnsatError:
                    logging.debug("[INTEGER_UNDERFLOW] no model found")
        return issues
Example #12
0
def execute(statespace):

    issues = []

    for k in statespace.nodes:
        node = statespace.nodes[k]

        for instruction in node.instruction_list:
            ''' This generates a lot of noise.

            if(instruction['opcode'] == "ADD"):

                stack = node.states[instruction['address']].stack

                op0 = stack[-1]
                op1 = stack[-2]

                if type(op0) == int and type(op1) == int:
                    continue

                logging.debug("[INTEGER_OVERFLOW] Checking ADD " + str(op0) + ", " + str(op1) + " at address " + str(instruction['address']))

                constraints = copy.deepcopy(node.constraints)

                constraints.append(UGT(op0, UINT_MAX - op1))

                try:
                    
                    model = solver.get_model(constraints)

                    issue = Issue(node.module_name, node.function_name, instruction['address'], "Integer Overflow", "Warning")

                    issue.description = "A possible integer overflow exists in the function " + node.function_name + ".\n" \
                        "The addition at address " + str(instruction['address']) + " may result in a value greater than UINT_MAX." 

                    issue.debug = "(" + str(op0) + ") + (" + str(op1) + ") > (" + hex(UINT_MAX.as_long()) + ")"

                    issues.append(issue)

                    for d in model.decls():
                        logging.debug("[INTEGER_OVERFLOW] model: %s = 0x%x" % (d.name(), model[d].as_long()))

                except UnsatError:
                    logging.debug("[INTEGER_OVERFLOW] no model found")   

            '''

            if (instruction['opcode'] == "MUL"):

                stack = node.states[instruction['address']].stack

                op0 = stack[-1]
                op1 = stack[-2]

                if (type(op0) == int and type(op1) == int
                    ) or type(op0) == BoolRef or type(op1) == BoolRef:
                    continue

                logging.debug("[INTEGER_OVERFLOW] Checking MUL " + str(op0) +
                              ", " + str(op1) + " at address " +
                              str(instruction['address']))

                if re.search(r'146150163733',
                             str(op0), re.DOTALL) or re.search(
                                 r'146150163733', str(op1),
                                 re.DOTALL) or "(2 << 160 - 1)" in str(
                                     op0) or "(2 << 160 - 1)" in str(op1):
                    continue

                constraints = copy.deepcopy(node.constraints)

                constraints.append(UGT(op0, UDiv(UINT_MAX, op1)))

                try:

                    model = solver.get_model(constraints)

                    issue = Issue(node.module_name, node.function_name,
                                  instruction['address'], "Integer Overflow",
                                  "Warning")

                    issue.description = "A possible integer overflow exists in the function " + node.function_name + ".\n" \
                        "The multiplication at address " + str(instruction['address']) + " may result in a value greater than UINT_MAX."

                    issue.debug = "(" + str(op0) + ") * (" + str(
                        op1) + ") > (" + hex(UINT_MAX.as_long()) + ")"

                    issues.append(issue)

                    logging.debug("Constraints: " + str(constraints))

                    for d in model.decls():
                        logging.debug("[INTEGER_OVERFLOW] model: %s = 0x%x" %
                                      (d.name(), model[d].as_long()))

                except UnsatError:
                    logging.debug("[INTEGER_OVERFLOW] no model found")

    return issues
Example #13
0
    def _check_integer_underflow(self, statespace, state, node):
        """
        Checks for integer underflow
        :param state: state from node to examine
        :param node: node to examine
        :return: found issue
        """
        issues = []
        instruction = state.get_current_instruction()
        if instruction["opcode"] == "SUB":

            stack = state.mstate.stack

            op0 = stack[-1]
            op1 = stack[-2]

            constraints = copy.deepcopy(node.constraints)

            if type(op0) == int and type(op1) == int:
                return []

            logging.debug(
                "[INTEGER_UNDERFLOW] Checking SUB {0}, {1} at address {2}".
                format(str(op0), str(op1), str(instruction["address"])))

            allowed_types = [int, BitVecRef, BitVecNumRef]

            if type(op0) in allowed_types and type(op1) in allowed_types:
                constraints.append(
                    Not(BVSubNoUnderflow(op0, op1, signed=False)))
                try:

                    model = solver.get_model(constraints)

                    # If we get to this point then there has been an integer overflow
                    # Find out if the overflowed value is actually used
                    interesting_usages = self._search_children(
                        statespace,
                        node, (op0 - op1),
                        index=node.states.index(state))

                    # Stop if it isn't
                    if len(interesting_usages) == 0:
                        return issues

                    issue = Issue(
                        contract=node.contract_name,
                        function_name=node.function_name,
                        address=instruction["address"],
                        swc_id=INTEGER_OVERFLOW_AND_UNDERFLOW,
                        bytecode=state.environment.code.bytecode,
                        title="Integer Underflow",
                        _type="Warning",
                        gas_used=(state.mstate.min_gas_used,
                                  state.mstate.max_gas_used),
                    )

                    issue.description = (
                        "The subtraction can result in an integer underflow.\n"
                    )

                    issue.debug = str(
                        solver.get_transaction_sequence(
                            state, node.constraints))
                    issues.append(issue)

                except UnsatError:
                    logging.debug("[INTEGER_UNDERFLOW] no model found")
        return issues