Merge pull request #2435 from crytic/fix/evm-printer

Fix bugs in the EVM printer
pull/2473/head
alpharush 6 months ago committed by GitHub
commit a46835b418
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 8
      slither/analyses/evm/convert.py
  2. 86
      slither/printers/summary/evm.py

@ -178,15 +178,14 @@ def generate_source_to_evm_ins_mapping(evm_instructions, srcmap_runtime, slither
# In order to compress these source mappings especially for bytecode, the following rules are used:
# If a field is empty, the value of the preceding element is used.
# If a : is missing, all following fields are considered empty.
mapping_item = mapping.split(":")
mapping_item += prev_mapping[len(mapping_item) :]
for i, _ in enumerate(mapping_item):
if mapping_item[i] == "":
mapping_item[i] = int(prev_mapping[i])
mapping_item[i] = prev_mapping[i]
offset, _length, file_id, *_ = mapping_item
offset, _, file_id, *_ = mapping_item
prev_mapping = mapping_item
if file_id == "-1":
@ -194,8 +193,7 @@ def generate_source_to_evm_ins_mapping(evm_instructions, srcmap_runtime, slither
# 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
line_number = file_source[0 : int(offset)].count("\n".encode("utf-8")) + 1
# Append evm instructions to the corresponding source line number
# Note: Some evm instructions in mapping are not necessarily in program execution order

@ -2,8 +2,11 @@
Module printing evm mapping of the contract
"""
import logging
from typing import Union, List, Dict
from slither.printers.abstract_printer import AbstractPrinter
from slither.core.declarations.function import Function
from slither.core.declarations.modifier import Modifier
from slither.analyses.evm import (
generate_source_to_evm_ins_mapping,
load_evm_cfg_builder,
@ -77,6 +80,33 @@ class PrinterEVM(AbstractPrinter):
WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#evm"
def build_element_node_str(
self,
element: Union["Modifier", "Function"],
contract_pcs: Dict[int, List[int]],
contract_cfg,
) -> str:
element_file = self.slither.source_code[
element.contract_declarer.source_mapping.filename.absolute
].splitlines()
return_string = ""
for node in element.nodes:
return_string += green(f"\t\tNode: {node}\n")
node_source_line = node.source_mapping.lines[0]
return_string += green(
f"\t\tSource line {node_source_line}: {element_file[node_source_line - 1].rstrip()}\n"
)
return_string += magenta("\t\tEVM Instructions:\n")
node_pcs = contract_pcs.get(node_source_line, [])
for pc in node_pcs:
return_string += magenta(
f"\t\t\t{hex(pc)}: {contract_cfg.get_instruction_at(pc)}\n"
)
return return_string
def output(self, _filename):
"""
_filename is not used
@ -99,53 +129,27 @@ class PrinterEVM(AbstractPrinter):
txt += "\tempty contract\n"
continue
contract_file = self.slither.source_code[
contract.source_mapping.filename.absolute
].encode("utf-8")
with open(contract.source_mapping.filename.absolute, "r", encoding="utf8") as f:
contract_file_lines = f.readlines()
contract_pcs = {}
contract_cfg = {}
for function in contract.functions:
txt += blue(f"\tFunction {function.canonical_name}\n")
# CFG and source mapping depend on function being constructor or not
if function.is_constructor:
contract_cfg = evm_info["cfg_init", contract.name]
contract_pcs = evm_info["mapping_init", contract.name]
else:
contract_cfg = evm_info["cfg", contract.name]
contract_pcs = evm_info["mapping", contract.name]
for node in function.nodes:
txt += green("\t\tNode: " + str(node) + "\n")
node_source_line = (
contract_file[0 : node.source_mapping.start].count("\n".encode("utf-8")) + 1
)
txt += green(
f"\t\tSource line {node_source_line}: {contract_file_lines[node_source_line - 1].rstrip()}\n"
)
txt += magenta("\t\tEVM Instructions:\n")
node_pcs = contract_pcs.get(node_source_line, [])
for pc in node_pcs:
txt += magenta(f"\t\t\t{hex(pc)}: {contract_cfg.get_instruction_at(pc)}\n")
txt += self.build_element_node_str(
function,
evm_info["mapping", contract.name]
if not function.is_constructor
else evm_info["mapping_init", contract.name],
evm_info["cfg", contract.name]
if not function.is_constructor
else evm_info["cfg_init", contract.name],
)
for modifier in contract.modifiers:
txt += blue(f"\tModifier {modifier.canonical_name}\n")
for node in modifier.nodes:
txt += green("\t\tNode: " + str(node) + "\n")
node_source_line = (
contract_file[0 : node.source_mapping.start].count("\n".encode("utf-8")) + 1
)
txt += green(
f"\t\tSource line {node_source_line}: {contract_file_lines[node_source_line - 1].rstrip()}\n"
)
txt += magenta("\t\tEVM Instructions:\n")
node_pcs = contract_pcs.get(node_source_line, [])
for pc in node_pcs:
txt += magenta(f"\t\t\t{hex(pc)}: {contract_cfg.get_instruction_at(pc)}\n")
txt += self.build_element_node_str(
modifier,
evm_info["mapping", contract.name],
evm_info["cfg", contract.name],
)
self.info(txt)
res = self.generate_output(txt)

Loading…
Cancel
Save