diff --git a/slither/printers/all_printers.py b/slither/printers/all_printers.py index 3c6ad58d3..bf339f8fd 100644 --- a/slither/printers/all_printers.py +++ b/slither/printers/all_printers.py @@ -13,3 +13,4 @@ from .summary.variable_order import VariableOrder from .summary.data_depenency import DataDependency from .summary.modifier_calls import Modifiers from .summary.require_calls import RequireOrAssert +from .summary.evm import PrinterEVM diff --git a/slither/printers/summary/evm.py b/slither/printers/summary/evm.py new file mode 100644 index 000000000..9dbdf3fcd --- /dev/null +++ b/slither/printers/summary/evm.py @@ -0,0 +1,86 @@ +""" + Module printing evm mapping of the contract +""" + +import logging +try: + from evm_cfg_builder.cfg import CFG +except ImportError: + logger.error("ERROR: in order to use evm printer, you need to install evm-cfg-builder") + logger.error("pip install evm-cfg-builder") + sys.exit(-1) +from slither.printers.abstract_printer import AbstractPrinter +from slither.utils.colors import blue, green, magenta + +class PrinterEVM(AbstractPrinter): + + ARGUMENT = 'evm' + HELP = 'Print the evm instructions of nodes in functions' + + WIKI = 'https://github.com/trailofbits/slither/wiki/Printer-documentation#evm' + + def output(self, _filename): + """ + _filename is not used + Args: + _filename(string) + """ + + source_to_pc_mapping = self._process_evm_cfg(self.slither) + + for contract in self.slither.contracts_derived: + print('Contract {}'.format(contract.name)) + contract_file = self.slither.source_code[contract.source_mapping['filename_absolute']].encode('utf-8') + contract_pcs = source_to_pc_mapping['mapping', contract.name] + print("contract_pcs: " + str(contract_pcs)) + contract_cfg = source_to_pc_mapping['cfg', contract.name] + for function in contract.functions: + print(f'\tFunction {function.canonical_name}') + for node in function.nodes: + print("\t\tNode: " + str(node)) + node_source_line = contract_file[0:node.source_mapping['start']].count("\n".encode("utf-8")) + 1 + print('\t\t\tEVM Instructions:') + node_pcs = contract_pcs.get(node_source_line, "[]") + for pc in node_pcs: + print('\t\t\t\tINS: {}'.format(pc)) + for modifier in contract.modifiers: + print('\tModifier {}'.format(modifier.canonical_name)) + for node in modifier.nodes: + node_source_line = contract_file[0:node.source_mapping['start']].count("\n".encode("utf-8")) + 1 + print('\t\tEVM Instructions:') + node_pcs = contract_pcs[node_source_line] + for pc in node_pcs: + print('\t\t\tINS: {}'.format(pc)) + + def _process_evm_cfg(self, slither): + source_to_pc_mapping = {} + for contract in slither.contracts_derived: + contract_bytecode_runtime = slither.crytic_compile.bytecode_runtime(contract.name) + contract_srcmap_runtime = slither.crytic_compile.srcmap_runtime(contract.name) + cfg = CFG(contract_bytecode_runtime) + source_to_pc_mapping['cfg', contract.name] = cfg + source_to_pc_mapping['mapping', contract.name] = self._generate_source_to_ins_mapping(cfg.instructions, + contract_srcmap_runtime, slither, + contract.source_mapping['filename_absolute']) + return(source_to_pc_mapping) + + def _generate_source_to_ins_mapping(self, evm_instructions, srcmap_runtime, slither, filename): + source_to_pc_mapping = {} + file_source = slither.source_code[filename].encode('utf-8') + prev_mapping = [] + for idx, mapping in enumerate(srcmap_runtime): + mapping_item = mapping.split(':') + mapping_item += prev_mapping[len(mapping_item):] + for i in range(len(mapping_item)): + if mapping_item[i] == '': + mapping_item[i] = int(prev_mapping[i]) + offset, length, file_id, _ = mapping_item + if file_id == '-1': + # Internal compiler-generated code snippets to be ignored + # See https://github.com/ethereum/solidity/issues/6119#issuecomment-467797635 + continue + offset = int(offset) + line_number = file_source[0:offset].count("\n".encode("utf-8")) + 1 + prev_mapping = mapping_item + source_to_pc_mapping.setdefault(line_number, []).append(evm_instructions[idx].pc) + return(source_to_pc_mapping)