Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bugs in the EVM printer #2435

Merged
merged 1 commit into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions slither/analyses/evm/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,24 +178,22 @@ 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":
# 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
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
Expand Down
86 changes: 45 additions & 41 deletions slither/printers/summary/evm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -99,53 +129,27 @@ def output(self, _filename):
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)
Expand Down
Loading