blob: 7fbedd05fe563e3b8fdecc7b7d1101600d9bb09b [file] [log] [blame]
#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
import argparse
import sys
from typing import Dict, List, Optional, Set, Tuple
from shared.check import CheckResult
from shared.control_flow import program_control_graph, subroutine_control_graph
from shared.decode import decode_elf
from shared.information_flow_analysis import (get_program_iflow,
get_subroutine_iflow,
stringify_control_deps)
def main() -> int:
parser = argparse.ArgumentParser(
description='Analyze whether secret data affects the control flow of '
'an OTBN program or subroutine.')
parser.add_argument('elf', help=('The .elf file to check.'))
parser.add_argument('--verbose', action='store_true')
parser.add_argument(
'--subroutine',
required=False,
help=('The specific subroutine to check. If not provided, the start '
'point is _imem_start (whole program).'))
parser.add_argument(
'--secrets',
nargs='+',
type=str,
required=False,
help=('Initial secret information-flow nodes. If not provided, '
'assume everything is secret; check that the subroutine or '
'program has only one possible control-flow path regardless '
'of input.'))
args = parser.parse_args()
# Compute control graph and get all nodes that influence control flow.
program = decode_elf(args.elf)
if args.subroutine is None:
graph = program_control_graph(program)
to_analyze = 'entire program'
_, control_deps = get_program_iflow(program, graph)
else:
graph = subroutine_control_graph(program, args.subroutine)
to_analyze = 'subroutine {}'.format(args.subroutine)
_, _, control_deps = get_subroutine_iflow(program, graph,
args.subroutine)
if args.secrets is None:
if args.verbose:
print(
'No specific secrets provided; checking that {} has only one '
'control-flow path'.format(to_analyze))
secret_control_deps = control_deps
else:
if args.verbose:
print('Analyzing {} with initial secrets {}'.format(
to_analyze, args.secrets))
# If secrets were provided, only show the ways in which those specific
# nodes could influence control flow.
secret_control_deps = {
node: pcs
for node, pcs in control_deps.items() if node in args.secrets
}
out = CheckResult()
if len(secret_control_deps) != 0:
msg = 'The following secrets may influence control flow:\n '
msg += '\n '.join(stringify_control_deps(program,
secret_control_deps))
out.err(msg)
if args.verbose or out.has_errors() or out.has_warnings():
print(out.report())
return 0
if __name__ == "__main__":
sys.exit(main())