|
|
|
@ -5,12 +5,15 @@ |
|
|
|
|
import logging |
|
|
|
|
from slither.printers.abstract_printer import AbstractPrinter |
|
|
|
|
try: |
|
|
|
|
# Avoiding the addition of evm_cfg_builder as permanent dependency |
|
|
|
|
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") |
|
|
|
|
logger.error("To use evm printer, you need to install evm-cfg-builder from ToB") |
|
|
|
|
logger.error("Documentation: https://github.com/crytic/evm_cfg_builder") |
|
|
|
|
logger.error("Installation: pip install evm-cfg-builder") |
|
|
|
|
sys.exit(-1) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PrinterEVM(AbstractPrinter): |
|
|
|
|
|
|
|
|
|
ARGUMENT = 'evm' |
|
|
|
@ -18,6 +21,7 @@ class PrinterEVM(AbstractPrinter): |
|
|
|
|
|
|
|
|
|
WIKI = 'https://github.com/trailofbits/slither/wiki/Printer-documentation#evm' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def output(self, _filename): |
|
|
|
|
""" |
|
|
|
|
_filename is not used |
|
|
|
@ -29,10 +33,12 @@ class PrinterEVM(AbstractPrinter): |
|
|
|
|
|
|
|
|
|
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_file_lines = open(contract.source_mapping['filename_absolute'],'r').readlines() |
|
|
|
|
contract_cfg = evm_info['cfg', contract.name] |
|
|
|
|
contract_pcs = evm_info['mapping', contract.name] |
|
|
|
|
|
|
|
|
|
for function in contract.functions: |
|
|
|
|
print(f'\tFunction {function.canonical_name}') |
|
|
|
|
for node in function.nodes: |
|
|
|
@ -43,6 +49,7 @@ class PrinterEVM(AbstractPrinter): |
|
|
|
|
node_pcs = contract_pcs.get(node_source_line, []) |
|
|
|
|
for pc in node_pcs: |
|
|
|
|
print('\t\t\t0x{:x}: {}'.format(int(pc), contract_cfg.get_instruction_at(pc))) |
|
|
|
|
|
|
|
|
|
for modifier in contract.modifiers: |
|
|
|
|
print(f'\tModifier {modifier.canonical_name}') |
|
|
|
|
for node in modifier.nodes: |
|
|
|
@ -53,13 +60,16 @@ class PrinterEVM(AbstractPrinter): |
|
|
|
|
for pc in node_pcs: |
|
|
|
|
print('\t\t\t0x{:x}: {}'.format(int(pc), contract_cfg.get_instruction_at(pc))) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _extract_evm_info(self, slither): |
|
|
|
|
''' |
|
|
|
|
""" |
|
|
|
|
Extract evm information for all derived contracts using evm_cfg_builder |
|
|
|
|
|
|
|
|
|
Returns: evm CFG and Solidity source to Program Counter (pc) mapping |
|
|
|
|
''' |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
evm_info = {} |
|
|
|
|
|
|
|
|
|
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) |
|
|
|
@ -69,34 +79,45 @@ class PrinterEVM(AbstractPrinter): |
|
|
|
|
contract_srcmap_runtime, slither, |
|
|
|
|
contract.source_mapping['filename_absolute']) |
|
|
|
|
return(evm_info) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _generate_source_to_evm_ins_mapping(self, evm_instructions, srcmap_runtime, slither, filename): |
|
|
|
|
''' |
|
|
|
|
""" |
|
|
|
|
Generate Solidity source to EVM instruction mapping using evm_cfg_builder:cfg.instructions and solc:srcmap_runtime |
|
|
|
|
|
|
|
|
|
Returns: Solidity source to EVM instruction mapping |
|
|
|
|
''' |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
source_to_evm_mapping = {} |
|
|
|
|
file_source = slither.source_code[filename].encode('utf-8') |
|
|
|
|
prev_mapping = [] |
|
|
|
|
|
|
|
|
|
for idx, mapping in enumerate(srcmap_runtime): |
|
|
|
|
# Parse srcmap_runtime according to its format |
|
|
|
|
# See https://solidity.readthedocs.io/en/v0.5.9/miscellaneous.html#source-mappings |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
# Append evm instructions to the corresponding source line number |
|
|
|
|
# Note: Some evm instructions in mapping are not necessarily in program execution order |
|
|
|
|
# Note: The order depends on how solc creates the srcmap_runtime |
|
|
|
|
source_to_evm_mapping.setdefault(line_number, []).append(evm_instructions[idx].pc) |
|
|
|
|
|
|
|
|
|
prev_mapping = mapping_item |
|
|
|
|
|
|
|
|
|
return(source_to_evm_mapping) |
|
|
|
|