Example #1
0
def _check_loop_end_insns(program: OTBNProgram,
                          loops: List[CodeSection]) -> CheckResult:
    '''Checks that loops do not end in control flow instructions.

    Such instructions can cause LOOP software errors during execution.
    '''
    out = CheckResult()
    for loop in loops:
        loop_end_insn = program.get_insn(loop.end)
        if not loop_end_insn.straight_line:
            out.err('Control flow instruction ({}) at end of loop at PC {:#x} '
                    '(loop starting at PC {:#x})'.format(
                        loop_end_insn.mnemonic, loop.end, loop.start))
    return out
Example #2
0
def check_call_stack(program: OTBNProgram) -> CheckResult:
    '''Check that the special register x1 is used safely.

    If x1 is used for purposes unrelated to the call stack, it can trigger a
    CALL_STACK error. This check errors if x1 is used for any other instruction
    than `jal` or `jalr`.
    '''
    out = CheckResult()
    for pc, (insn, operands) in program.insns.items():
        if not _check_call_stack_insn(insn, operands):
            out.err(
                'Potentially dangerous use of the call stack register x1 at '
                'PC {:#x}: {}'.format(pc, insn.disassemble(pc, operands)))
    out.set_prefix('check_call_stack: ')
    return out
Example #3
0
def _check_loop_inclusion(program: OTBNProgram,
                          loops: List[CodeSection]) -> CheckResult:
    '''Checks that inner loops are fully contained within outer loops.

    When a loop starts within the body of another loop, it must be the case
    that the inner loop's final instruction occurs before the outer loop's.
    '''
    out = CheckResult()
    for loop in loops:
        for other in loops:
            if other.start in loop and other.end not in loop:
                out.err('Inner loop ends after outer loop (inner loop {}, '
                        'outer loop {})'.format(other, loop))

    return out
Example #4
0
def _check_loop_iterations(program: OTBNProgram,
                           loops: List[CodeSection]) -> CheckResult:
    '''Checks number of iterations for loopi.

    If the number of iterations is 0, this check fails; `loopi` requires at
    least one iteration and will raise a LOOP error otherwise. The `loop`
    instruction also has this requirement, but since the number of loop
    iterations comes from a register it's harder to check statically and is not
    considered here.
    '''
    out = CheckResult()
    for loop in loops:
        insn = program.get_insn(loop.start)
        operands = program.get_operands(loop.start)
        if insn.mnemonic == 'loopi' and operands['iterations'] <= 0:
            out.err(
                'Bad number of loop iterations ({}) at PC {:#x}: {}'.format(
                    operands['iterations'], loop.start,
                    insn.disassemble(loop.start, operands)))
    return out
Example #5
0
def _check_loop_stack(program: OTBNProgram,
                      loops: List[CodeSection]) -> CheckResult:
    '''Checks that loops will likely be properly cleared from loop stack.

    The checks here are based on the OTBN hardware IP documentation on loop
    nesting. From the docs:

        To avoid polluting the loop stack and avoid surprising behaviour, the
        programmer must ensure that:

        * Even if there are branches and jumps within a loop body, the final
          instruction of the loop body gets executed exactly once per
          iteration.

        * Nested loops have distinct end addresses.

        * The end instruction of an outer loop is not executed before an inner
          loop finishes.

    In order to avoid simulating the control flow of the entire program to
    check the first and third conditions, this check takes a conservative,
    simplistic approach and simply warns about all branching into or out of
    loop bodies, including jumps that don't use the call stack (e.g. `jal x0,
    <addr>`). Branching to locations within the same loop body is permitted.

    The second condition in the list, distinct end addresses, is checked
    separately.
    '''
    out = CheckResult()
    out += _check_loop_branching(program, loops)

    # Check that loops have unique end addresses
    end_addrs = []
    for loop in loops:
        if loop.end in end_addrs:
            out.err(
                'Loop starting at PC {:#x} shares a final instruction with '
                'another loop; consider adding a NOP instruction.'.format(
                    loop.start))

    return out