|
|
|
@ -1,20 +1,24 @@ |
|
|
|
|
import logging |
|
|
|
|
|
|
|
|
|
from slither.core.declarations import (Contract, Enum, Event, SolidityFunction, |
|
|
|
|
Structure, SolidityVariableComposed, Function, SolidityVariable) |
|
|
|
|
from slither.core.expressions import Identifier, Literal, TupleExpression |
|
|
|
|
from slither.core.solidity_types import ElementaryType, UserDefinedType, MappingType, ArrayType, FunctionType |
|
|
|
|
from slither.core.declarations import (Contract, Enum, Event, Function, |
|
|
|
|
SolidityFunction, SolidityVariable, |
|
|
|
|
SolidityVariableComposed, Structure) |
|
|
|
|
from slither.core.expressions import Identifier, Literal |
|
|
|
|
from slither.core.solidity_types import (ArrayType, ElementaryType, |
|
|
|
|
FunctionType, MappingType, |
|
|
|
|
UserDefinedType) |
|
|
|
|
from slither.core.variables.variable import Variable |
|
|
|
|
from slither.slithir.operations import (Assignment, Binary, BinaryType, Call, |
|
|
|
|
Condition, Delete, EventCall, |
|
|
|
|
HighLevelCall, Index, InitArray, |
|
|
|
|
InternalCall, InternalDynamicCall, LibraryCall, |
|
|
|
|
LowLevelCall, Member, NewArray, |
|
|
|
|
NewContract, NewElementaryType, |
|
|
|
|
NewStructure, OperationWithLValue, |
|
|
|
|
Push, Return, Send, SolidityCall, |
|
|
|
|
Transfer, TypeConversion, Unary, |
|
|
|
|
Unpack, Length, Balance) |
|
|
|
|
from slither.slithir.operations import (Assignment, Balance, Binary, |
|
|
|
|
BinaryType, Call, Condition, Delete, |
|
|
|
|
EventCall, HighLevelCall, Index, |
|
|
|
|
InitArray, InternalCall, |
|
|
|
|
InternalDynamicCall, Length, |
|
|
|
|
LibraryCall, LowLevelCall, Member, |
|
|
|
|
NewArray, NewContract, |
|
|
|
|
NewElementaryType, NewStructure, |
|
|
|
|
OperationWithLValue, Push, Return, |
|
|
|
|
Send, SolidityCall, Transfer, |
|
|
|
|
TypeConversion, Unary, Unpack) |
|
|
|
|
from slither.slithir.tmp_operations.argument import Argument, ArgumentType |
|
|
|
|
from slither.slithir.tmp_operations.tmp_call import TmpCall |
|
|
|
|
from slither.slithir.tmp_operations.tmp_new_array import TmpNewArray |
|
|
|
@ -23,11 +27,47 @@ from slither.slithir.tmp_operations.tmp_new_elementary_type import \ |
|
|
|
|
TmpNewElementaryType |
|
|
|
|
from slither.slithir.tmp_operations.tmp_new_structure import TmpNewStructure |
|
|
|
|
from slither.slithir.variables import (Constant, ReferenceVariable, |
|
|
|
|
TemporaryVariable, TupleVariable) |
|
|
|
|
TemporaryVariable) |
|
|
|
|
from slither.visitors.slithir.expression_to_slithir import ExpressionToSlithIR |
|
|
|
|
|
|
|
|
|
logger = logging.getLogger('ConvertToIR') |
|
|
|
|
|
|
|
|
|
def convert_expression(expression, node): |
|
|
|
|
# handle standlone expression |
|
|
|
|
# such as return true; |
|
|
|
|
from slither.core.cfg.node import NodeType |
|
|
|
|
|
|
|
|
|
if isinstance(expression, Literal) and node.type in [NodeType.IF, NodeType.IFLOOP]: |
|
|
|
|
result = [Condition(Constant(expression.value))] |
|
|
|
|
return result |
|
|
|
|
if isinstance(expression, Identifier) and node.type in [NodeType.IF, NodeType.IFLOOP]: |
|
|
|
|
result = [Condition(expression.value)] |
|
|
|
|
return result |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
visitor = ExpressionToSlithIR(expression, node) |
|
|
|
|
result = visitor.result() |
|
|
|
|
|
|
|
|
|
result = apply_ir_heuristics(result, node) |
|
|
|
|
|
|
|
|
|
if result: |
|
|
|
|
if node.type in [NodeType.IF, NodeType.IFLOOP]: |
|
|
|
|
assert isinstance(result[-1], (OperationWithLValue)) |
|
|
|
|
result.append(Condition(result[-1].lvalue)) |
|
|
|
|
elif node.type == NodeType.RETURN: |
|
|
|
|
# May return None |
|
|
|
|
if isinstance(result[-1], (OperationWithLValue)): |
|
|
|
|
result.append(Return(result[-1].lvalue)) |
|
|
|
|
|
|
|
|
|
return result |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
################################################################################### |
|
|
|
|
################################################################################### |
|
|
|
|
# region Helpers |
|
|
|
|
################################################################################### |
|
|
|
|
################################################################################### |
|
|
|
|
|
|
|
|
|
def is_value(ins): |
|
|
|
|
if isinstance(ins, TmpCall): |
|
|
|
|
if isinstance(ins.ori, Member): |
|
|
|
@ -42,7 +82,65 @@ def is_gas(ins): |
|
|
|
|
return True |
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
def get_sig(ir): |
|
|
|
|
''' |
|
|
|
|
Return a list of potential signature |
|
|
|
|
It is a list, as Constant variables can be converted to int256 |
|
|
|
|
Args: |
|
|
|
|
ir (slithIR.operation) |
|
|
|
|
Returns: |
|
|
|
|
list(str) |
|
|
|
|
''' |
|
|
|
|
sig = '{}({})' |
|
|
|
|
name = ir.function_name |
|
|
|
|
|
|
|
|
|
# list of list of arguments |
|
|
|
|
argss = [[]] |
|
|
|
|
for arg in ir.arguments: |
|
|
|
|
if isinstance(arg, (list,)): |
|
|
|
|
type_arg = '{}[{}]'.format(get_type(arg[0].type), len(arg)) |
|
|
|
|
elif isinstance(arg, Function): |
|
|
|
|
type_arg = arg.signature_str |
|
|
|
|
else: |
|
|
|
|
type_arg = get_type(arg.type) |
|
|
|
|
if isinstance(arg, Constant) and arg.type == ElementaryType('uint256'): |
|
|
|
|
# If it is a constant |
|
|
|
|
# We dupplicate the existing list |
|
|
|
|
# And we add uint256 and int256 cases |
|
|
|
|
# There is no potential collision, as the compiler |
|
|
|
|
# Prevent it with a |
|
|
|
|
# "not unique after argument-dependent loopkup" issue |
|
|
|
|
argss_new = [list(args) for args in argss] |
|
|
|
|
for args in argss: |
|
|
|
|
args.append(str(ElementaryType('uint256'))) |
|
|
|
|
for args in argss_new: |
|
|
|
|
args.append(str(ElementaryType('int256'))) |
|
|
|
|
argss = argss + argss_new |
|
|
|
|
else: |
|
|
|
|
for args in argss: |
|
|
|
|
args.append(type_arg) |
|
|
|
|
return [sig.format(name, ','.join(args)) for args in argss] |
|
|
|
|
|
|
|
|
|
def is_temporary(ins): |
|
|
|
|
return isinstance(ins, (Argument, |
|
|
|
|
TmpNewElementaryType, |
|
|
|
|
TmpNewContract, |
|
|
|
|
TmpNewArray, |
|
|
|
|
TmpNewStructure)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# endregion |
|
|
|
|
################################################################################### |
|
|
|
|
################################################################################### |
|
|
|
|
# region Calls modification |
|
|
|
|
################################################################################### |
|
|
|
|
################################################################################### |
|
|
|
|
|
|
|
|
|
def integrate_value_gas(result): |
|
|
|
|
''' |
|
|
|
|
Integrate value and gas temporary arguments to call instruction |
|
|
|
|
''' |
|
|
|
|
was_changed = True |
|
|
|
|
|
|
|
|
|
calls = [] |
|
|
|
@ -110,7 +208,17 @@ def integrate_value_gas(result): |
|
|
|
|
|
|
|
|
|
return result |
|
|
|
|
|
|
|
|
|
def propage_type_and_convert_call(result, node): |
|
|
|
|
# endregion |
|
|
|
|
################################################################################### |
|
|
|
|
################################################################################### |
|
|
|
|
# region Calls modification and Type propagation |
|
|
|
|
################################################################################### |
|
|
|
|
################################################################################### |
|
|
|
|
|
|
|
|
|
def propagate_type_and_convert_call(result, node): |
|
|
|
|
''' |
|
|
|
|
Propagate the types variables and convert tmp call to real call operation |
|
|
|
|
''' |
|
|
|
|
calls_value = {} |
|
|
|
|
calls_gas = {} |
|
|
|
|
|
|
|
|
@ -179,133 +287,343 @@ def propage_type_and_convert_call(result, node): |
|
|
|
|
idx = idx +1 |
|
|
|
|
return result |
|
|
|
|
|
|
|
|
|
def convert_to_low_level(ir): |
|
|
|
|
""" |
|
|
|
|
Convert to a transfer/send/or low level call |
|
|
|
|
The funciton assume to receive a correct IR |
|
|
|
|
The checks must be done by the caller |
|
|
|
|
|
|
|
|
|
Additionally convert abi... to solidityfunction |
|
|
|
|
""" |
|
|
|
|
if ir.function_name == 'transfer': |
|
|
|
|
assert len(ir.arguments) == 1 |
|
|
|
|
ir = Transfer(ir.destination, ir.arguments[0]) |
|
|
|
|
return ir |
|
|
|
|
elif ir.function_name == 'send': |
|
|
|
|
assert len(ir.arguments) == 1 |
|
|
|
|
ir = Send(ir.destination, ir.arguments[0], ir.lvalue) |
|
|
|
|
ir.lvalue.set_type(ElementaryType('bool')) |
|
|
|
|
return ir |
|
|
|
|
elif ir.destination.name == 'abi' and ir.function_name in ['encode', |
|
|
|
|
'encodePacked', |
|
|
|
|
'encodeWithSelector', |
|
|
|
|
'encodeWithSignature', |
|
|
|
|
'decode']: |
|
|
|
|
|
|
|
|
|
call = SolidityFunction('abi.{}()'.format(ir.function_name)) |
|
|
|
|
new_ir = SolidityCall(call, ir.nbr_arguments, ir.lvalue, ir.type_call) |
|
|
|
|
new_ir.arguments = ir.arguments |
|
|
|
|
if isinstance(call.return_type, list) and len(call.return_type) == 1: |
|
|
|
|
new_ir.lvalue.set_type(call.return_type[0]) |
|
|
|
|
else: |
|
|
|
|
new_ir.lvalue.set_type(call.return_type) |
|
|
|
|
return new_ir |
|
|
|
|
elif ir.function_name in ['call', |
|
|
|
|
'delegatecall', |
|
|
|
|
'callcode', |
|
|
|
|
'staticcall']: |
|
|
|
|
new_ir = LowLevelCall(ir.destination, |
|
|
|
|
ir.function_name, |
|
|
|
|
ir.nbr_arguments, |
|
|
|
|
ir.lvalue, |
|
|
|
|
ir.type_call) |
|
|
|
|
new_ir.call_gas = ir.call_gas |
|
|
|
|
new_ir.call_value = ir.call_value |
|
|
|
|
new_ir.arguments = ir.arguments |
|
|
|
|
new_ir.lvalue.set_type(ElementaryType('bool')) |
|
|
|
|
return new_ir |
|
|
|
|
logger.error('Incorrect conversion to low level {}'.format(ir)) |
|
|
|
|
exit(-1) |
|
|
|
|
|
|
|
|
|
def convert_to_push(ir, node): |
|
|
|
|
""" |
|
|
|
|
Convert a call to a PUSH operaiton |
|
|
|
|
|
|
|
|
|
The funciton assume to receive a correct IR |
|
|
|
|
The checks must be done by the caller |
|
|
|
|
|
|
|
|
|
May necessitate to create an intermediate operation (InitArray) |
|
|
|
|
Necessitate to return the lenght (see push documentation) |
|
|
|
|
As a result, the function return may return a list |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lvalue = ir.lvalue |
|
|
|
|
if isinstance(ir.arguments[0], list): |
|
|
|
|
ret = [] |
|
|
|
|
|
|
|
|
|
val = TemporaryVariable(node) |
|
|
|
|
operation = InitArray(ir.arguments[0], val) |
|
|
|
|
ret.append(operation) |
|
|
|
|
|
|
|
|
|
ir = Push(ir.destination, val) |
|
|
|
|
|
|
|
|
|
length = Literal(len(operation.init_values)) |
|
|
|
|
t = operation.init_values[0].type |
|
|
|
|
ir.lvalue.set_type(ArrayType(t, length)) |
|
|
|
|
|
|
|
|
|
ret.append(ir) |
|
|
|
|
|
|
|
|
|
if lvalue: |
|
|
|
|
length = Length(ir.array, lvalue) |
|
|
|
|
length.lvalue.points_to = ir.lvalue |
|
|
|
|
ret.append(length) |
|
|
|
|
|
|
|
|
|
return ret |
|
|
|
|
|
|
|
|
|
ir = Push(ir.destination, ir.arguments[0]) |
|
|
|
|
|
|
|
|
|
if lvalue: |
|
|
|
|
ret = [] |
|
|
|
|
ret.append(ir) |
|
|
|
|
def propagate_types(ir, node): |
|
|
|
|
# propagate the type |
|
|
|
|
using_for = node.function.contract.using_for |
|
|
|
|
if isinstance(ir, OperationWithLValue): |
|
|
|
|
# Force assignment in case of missing previous correct type |
|
|
|
|
if not ir.lvalue.type: |
|
|
|
|
if isinstance(ir, Assignment): |
|
|
|
|
ir.lvalue.set_type(ir.rvalue.type) |
|
|
|
|
elif isinstance(ir, Binary): |
|
|
|
|
if BinaryType.return_bool(ir.type): |
|
|
|
|
ir.lvalue.set_type(ElementaryType('bool')) |
|
|
|
|
else: |
|
|
|
|
ir.lvalue.set_type(ir.variable_left.type) |
|
|
|
|
elif isinstance(ir, Delete): |
|
|
|
|
# nothing to propagate |
|
|
|
|
pass |
|
|
|
|
elif isinstance(ir, LibraryCall): |
|
|
|
|
return convert_type_library_call(ir, ir.destination) |
|
|
|
|
elif isinstance(ir, HighLevelCall): |
|
|
|
|
t = ir.destination.type |
|
|
|
|
|
|
|
|
|
length = Length(ir.array, lvalue) |
|
|
|
|
length.lvalue.points_to = ir.lvalue |
|
|
|
|
ret.append(length) |
|
|
|
|
return ret |
|
|
|
|
# Temporary operation (they are removed later) |
|
|
|
|
if t is None: |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
return ir |
|
|
|
|
# convert library |
|
|
|
|
if t in using_for or '*' in using_for: |
|
|
|
|
new_ir = convert_to_library(ir, node, using_for) |
|
|
|
|
if new_ir: |
|
|
|
|
return new_ir |
|
|
|
|
|
|
|
|
|
def look_for_library(contract, ir, node, using_for, t): |
|
|
|
|
for destination in using_for[t]: |
|
|
|
|
lib_contract = contract.slither.get_contract_from_name(str(destination)) |
|
|
|
|
if lib_contract: |
|
|
|
|
lib_call = LibraryCall(lib_contract, |
|
|
|
|
ir.function_name, |
|
|
|
|
ir.nbr_arguments, |
|
|
|
|
ir.lvalue, |
|
|
|
|
ir.type_call) |
|
|
|
|
lib_call.call_gas = ir.call_gas |
|
|
|
|
lib_call.arguments = [ir.destination] + ir.arguments |
|
|
|
|
new_ir = convert_type_library_call(lib_call, lib_contract) |
|
|
|
|
if new_ir: |
|
|
|
|
new_ir.set_node(ir.node) |
|
|
|
|
return new_ir |
|
|
|
|
return None |
|
|
|
|
if isinstance(t, UserDefinedType): |
|
|
|
|
# UserdefinedType |
|
|
|
|
t_type = t.type |
|
|
|
|
if isinstance(t_type, Contract): |
|
|
|
|
contract = node.slither.get_contract_from_name(t_type.name) |
|
|
|
|
return convert_type_of_high_level_call(ir, contract) |
|
|
|
|
|
|
|
|
|
def convert_to_library(ir, node, using_for): |
|
|
|
|
contract = node.function.contract |
|
|
|
|
t = ir.destination.type |
|
|
|
|
# Convert HighLevelCall to LowLevelCall |
|
|
|
|
if isinstance(t, ElementaryType) and t.name == 'address': |
|
|
|
|
if ir.destination.name == 'this': |
|
|
|
|
return convert_type_of_high_level_call(ir, node.function.contract) |
|
|
|
|
return convert_to_low_level(ir) |
|
|
|
|
|
|
|
|
|
if t in using_for: |
|
|
|
|
new_ir = look_for_library(contract, ir, node, using_for, t) |
|
|
|
|
if new_ir: |
|
|
|
|
return new_ir |
|
|
|
|
# Convert push operations |
|
|
|
|
# May need to insert a new operation |
|
|
|
|
# Which leads to return a list of operation |
|
|
|
|
if isinstance(t, ArrayType): |
|
|
|
|
if ir.function_name == 'push' and len(ir.arguments) == 1: |
|
|
|
|
return convert_to_push(ir, node) |
|
|
|
|
|
|
|
|
|
if '*' in using_for: |
|
|
|
|
new_ir = look_for_library(contract, ir, node, using_for, '*') |
|
|
|
|
if new_ir: |
|
|
|
|
return new_ir |
|
|
|
|
elif isinstance(ir, Index): |
|
|
|
|
if isinstance(ir.variable_left.type, MappingType): |
|
|
|
|
ir.lvalue.set_type(ir.variable_left.type.type_to) |
|
|
|
|
elif isinstance(ir.variable_left.type, ArrayType): |
|
|
|
|
ir.lvalue.set_type(ir.variable_left.type.type) |
|
|
|
|
|
|
|
|
|
elif isinstance(ir, InitArray): |
|
|
|
|
length = len(ir.init_values) |
|
|
|
|
t = ir.init_values[0].type |
|
|
|
|
ir.lvalue.set_type(ArrayType(t, length)) |
|
|
|
|
elif isinstance(ir, InternalCall): |
|
|
|
|
# if its not a tuple, return a singleton |
|
|
|
|
return_type = ir.function.return_type |
|
|
|
|
if return_type: |
|
|
|
|
if len(return_type) == 1: |
|
|
|
|
ir.lvalue.set_type(return_type[0]) |
|
|
|
|
elif len(return_type)>1: |
|
|
|
|
ir.lvalue.set_type(return_type) |
|
|
|
|
else: |
|
|
|
|
ir.lvalue = None |
|
|
|
|
elif isinstance(ir, InternalDynamicCall): |
|
|
|
|
# if its not a tuple, return a singleton |
|
|
|
|
return_type = ir.function_type.return_type |
|
|
|
|
if return_type: |
|
|
|
|
if len(return_type) == 1: |
|
|
|
|
ir.lvalue.set_type(return_type[0]) |
|
|
|
|
else: |
|
|
|
|
ir.lvalue.set_type(return_type) |
|
|
|
|
else: |
|
|
|
|
ir.lvalue = None |
|
|
|
|
elif isinstance(ir, LowLevelCall): |
|
|
|
|
# Call are not yet converted |
|
|
|
|
# This should not happen |
|
|
|
|
assert False |
|
|
|
|
elif isinstance(ir, Member): |
|
|
|
|
# TODO we should convert the reference to a temporary if the member is a length or a balance |
|
|
|
|
if ir.variable_right == 'length' and not isinstance(ir.variable_left, Contract) and isinstance(ir.variable_left.type, (ElementaryType, ArrayType)): |
|
|
|
|
length = Length(ir.variable_left, ir.lvalue) |
|
|
|
|
length.lvalue.points_to = ir.variable_left |
|
|
|
|
return length |
|
|
|
|
if ir.variable_right == 'balance'and not isinstance(ir.variable_left, Contract) and isinstance(ir.variable_left.type, ElementaryType): |
|
|
|
|
return Balance(ir.variable_left, ir.lvalue) |
|
|
|
|
left = ir.variable_left |
|
|
|
|
if isinstance(left, (Variable, SolidityVariable)): |
|
|
|
|
t = ir.variable_left.type |
|
|
|
|
elif isinstance(left, (Contract, Enum, Structure)): |
|
|
|
|
t = UserDefinedType(left) |
|
|
|
|
# can be None due to temporary operation |
|
|
|
|
if t: |
|
|
|
|
if isinstance(t, UserDefinedType): |
|
|
|
|
# UserdefinedType |
|
|
|
|
type_t = t.type |
|
|
|
|
if isinstance(type_t, Enum): |
|
|
|
|
ir.lvalue.set_type(t) |
|
|
|
|
elif isinstance(type_t, Structure): |
|
|
|
|
elems = type_t.elems |
|
|
|
|
for elem in elems: |
|
|
|
|
if elem == ir.variable_right: |
|
|
|
|
ir.lvalue.set_type(elems[elem].type) |
|
|
|
|
else: |
|
|
|
|
assert isinstance(type_t, Contract) |
|
|
|
|
elif isinstance(ir, NewArray): |
|
|
|
|
ir.lvalue.set_type(ir.array_type) |
|
|
|
|
elif isinstance(ir, NewContract): |
|
|
|
|
contract = node.slither.get_contract_from_name(ir.contract_name) |
|
|
|
|
ir.lvalue.set_type(UserDefinedType(contract)) |
|
|
|
|
elif isinstance(ir, NewElementaryType): |
|
|
|
|
ir.lvalue.set_type(ir.type) |
|
|
|
|
elif isinstance(ir, NewStructure): |
|
|
|
|
ir.lvalue.set_type(UserDefinedType(ir.structure)) |
|
|
|
|
elif isinstance(ir, Push): |
|
|
|
|
# No change required |
|
|
|
|
pass |
|
|
|
|
elif isinstance(ir, Send): |
|
|
|
|
ir.lvalue.set_type(ElementaryType('bool')) |
|
|
|
|
elif isinstance(ir, SolidityCall): |
|
|
|
|
return_type = ir.function.return_type |
|
|
|
|
if len(return_type) == 1: |
|
|
|
|
ir.lvalue.set_type(return_type[0]) |
|
|
|
|
elif len(return_type)>1: |
|
|
|
|
ir.lvalue.set_type(return_type) |
|
|
|
|
elif isinstance(ir, TypeConversion): |
|
|
|
|
ir.lvalue.set_type(ir.type) |
|
|
|
|
elif isinstance(ir, Unary): |
|
|
|
|
ir.lvalue.set_type(ir.rvalue.type) |
|
|
|
|
elif isinstance(ir, Unpack): |
|
|
|
|
types = ir.tuple.type.type |
|
|
|
|
idx = ir.index |
|
|
|
|
t = types[idx] |
|
|
|
|
ir.lvalue.set_type(t) |
|
|
|
|
elif isinstance(ir, (Argument, TmpCall, TmpNewArray, TmpNewContract, TmpNewStructure, TmpNewElementaryType)): |
|
|
|
|
# temporary operation; they will be removed |
|
|
|
|
pass |
|
|
|
|
else: |
|
|
|
|
logger.error('Not handling {} during type propgation'.format(type(ir))) |
|
|
|
|
exit(-1) |
|
|
|
|
|
|
|
|
|
def extract_tmp_call(ins): |
|
|
|
|
assert isinstance(ins, TmpCall) |
|
|
|
|
|
|
|
|
|
if isinstance(ins.called, Variable) and isinstance(ins.called.type, FunctionType): |
|
|
|
|
call = InternalDynamicCall(ins.lvalue, ins.called, ins.called.type) |
|
|
|
|
call.call_id = ins.call_id |
|
|
|
|
return call |
|
|
|
|
if isinstance(ins.ori, Member): |
|
|
|
|
if isinstance(ins.ori.variable_left, Contract): |
|
|
|
|
st = ins.ori.variable_left.get_structure_from_name(ins.ori.variable_right) |
|
|
|
|
if st: |
|
|
|
|
op = NewStructure(st, ins.lvalue) |
|
|
|
|
op.call_id = ins.call_id |
|
|
|
|
return op |
|
|
|
|
libcall = LibraryCall(ins.ori.variable_left, ins.ori.variable_right, ins.nbr_arguments, ins.lvalue, ins.type_call) |
|
|
|
|
libcall.call_id = ins.call_id |
|
|
|
|
return libcall |
|
|
|
|
msgcall = HighLevelCall(ins.ori.variable_left, ins.ori.variable_right, ins.nbr_arguments, ins.lvalue, ins.type_call) |
|
|
|
|
msgcall.call_id = ins.call_id |
|
|
|
|
return msgcall |
|
|
|
|
|
|
|
|
|
if isinstance(ins.ori, TmpCall): |
|
|
|
|
r = extract_tmp_call(ins.ori) |
|
|
|
|
return r |
|
|
|
|
if isinstance(ins.called, SolidityVariableComposed): |
|
|
|
|
if str(ins.called) == 'block.blockhash': |
|
|
|
|
ins.called = SolidityFunction('blockhash(uint256)') |
|
|
|
|
elif str(ins.called) == 'this.balance': |
|
|
|
|
return SolidityCall(SolidityFunction('this.balance()'), ins.nbr_arguments, ins.lvalue, ins.type_call) |
|
|
|
|
|
|
|
|
|
if isinstance(ins.called, SolidityFunction): |
|
|
|
|
return SolidityCall(ins.called, ins.nbr_arguments, ins.lvalue, ins.type_call) |
|
|
|
|
|
|
|
|
|
if isinstance(ins.ori, TmpNewElementaryType): |
|
|
|
|
return NewElementaryType(ins.ori.type, ins.lvalue) |
|
|
|
|
|
|
|
|
|
if isinstance(ins.ori, TmpNewContract): |
|
|
|
|
op = NewContract(Constant(ins.ori.contract_name), ins.lvalue) |
|
|
|
|
op.call_id = ins.call_id |
|
|
|
|
return op |
|
|
|
|
|
|
|
|
|
if isinstance(ins.ori, TmpNewArray): |
|
|
|
|
return NewArray(ins.ori.depth, ins.ori.array_type, ins.lvalue) |
|
|
|
|
|
|
|
|
|
if isinstance(ins.called, Structure): |
|
|
|
|
op = NewStructure(ins.called, ins.lvalue) |
|
|
|
|
op.call_id = ins.call_id |
|
|
|
|
return op |
|
|
|
|
|
|
|
|
|
if isinstance(ins.called, Event): |
|
|
|
|
return EventCall(ins.called.name) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
raise Exception('Not extracted {} {}'.format(type(ins.called), ins)) |
|
|
|
|
|
|
|
|
|
# endregion |
|
|
|
|
################################################################################### |
|
|
|
|
################################################################################### |
|
|
|
|
# region Conversion operations |
|
|
|
|
################################################################################### |
|
|
|
|
################################################################################### |
|
|
|
|
|
|
|
|
|
def convert_to_low_level(ir): |
|
|
|
|
""" |
|
|
|
|
Convert to a transfer/send/or low level call |
|
|
|
|
The funciton assume to receive a correct IR |
|
|
|
|
The checks must be done by the caller |
|
|
|
|
|
|
|
|
|
Additionally convert abi... to solidityfunction |
|
|
|
|
""" |
|
|
|
|
if ir.function_name == 'transfer': |
|
|
|
|
assert len(ir.arguments) == 1 |
|
|
|
|
ir = Transfer(ir.destination, ir.arguments[0]) |
|
|
|
|
return ir |
|
|
|
|
elif ir.function_name == 'send': |
|
|
|
|
assert len(ir.arguments) == 1 |
|
|
|
|
ir = Send(ir.destination, ir.arguments[0], ir.lvalue) |
|
|
|
|
ir.lvalue.set_type(ElementaryType('bool')) |
|
|
|
|
return ir |
|
|
|
|
elif ir.destination.name == 'abi' and ir.function_name in ['encode', |
|
|
|
|
'encodePacked', |
|
|
|
|
'encodeWithSelector', |
|
|
|
|
'encodeWithSignature', |
|
|
|
|
'decode']: |
|
|
|
|
|
|
|
|
|
call = SolidityFunction('abi.{}()'.format(ir.function_name)) |
|
|
|
|
new_ir = SolidityCall(call, ir.nbr_arguments, ir.lvalue, ir.type_call) |
|
|
|
|
new_ir.arguments = ir.arguments |
|
|
|
|
if isinstance(call.return_type, list) and len(call.return_type) == 1: |
|
|
|
|
new_ir.lvalue.set_type(call.return_type[0]) |
|
|
|
|
else: |
|
|
|
|
new_ir.lvalue.set_type(call.return_type) |
|
|
|
|
return new_ir |
|
|
|
|
elif ir.function_name in ['call', |
|
|
|
|
'delegatecall', |
|
|
|
|
'callcode', |
|
|
|
|
'staticcall']: |
|
|
|
|
new_ir = LowLevelCall(ir.destination, |
|
|
|
|
ir.function_name, |
|
|
|
|
ir.nbr_arguments, |
|
|
|
|
ir.lvalue, |
|
|
|
|
ir.type_call) |
|
|
|
|
new_ir.call_gas = ir.call_gas |
|
|
|
|
new_ir.call_value = ir.call_value |
|
|
|
|
new_ir.arguments = ir.arguments |
|
|
|
|
new_ir.lvalue.set_type(ElementaryType('bool')) |
|
|
|
|
return new_ir |
|
|
|
|
logger.error('Incorrect conversion to low level {}'.format(ir)) |
|
|
|
|
exit(-1) |
|
|
|
|
|
|
|
|
|
def convert_to_push(ir, node): |
|
|
|
|
""" |
|
|
|
|
Convert a call to a PUSH operaiton |
|
|
|
|
|
|
|
|
|
The funciton assume to receive a correct IR |
|
|
|
|
The checks must be done by the caller |
|
|
|
|
|
|
|
|
|
May necessitate to create an intermediate operation (InitArray) |
|
|
|
|
Necessitate to return the lenght (see push documentation) |
|
|
|
|
As a result, the function return may return a list |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lvalue = ir.lvalue |
|
|
|
|
if isinstance(ir.arguments[0], list): |
|
|
|
|
ret = [] |
|
|
|
|
|
|
|
|
|
val = TemporaryVariable(node) |
|
|
|
|
operation = InitArray(ir.arguments[0], val) |
|
|
|
|
ret.append(operation) |
|
|
|
|
|
|
|
|
|
ir = Push(ir.destination, val) |
|
|
|
|
|
|
|
|
|
length = Literal(len(operation.init_values)) |
|
|
|
|
t = operation.init_values[0].type |
|
|
|
|
ir.lvalue.set_type(ArrayType(t, length)) |
|
|
|
|
|
|
|
|
|
ret.append(ir) |
|
|
|
|
|
|
|
|
|
if lvalue: |
|
|
|
|
length = Length(ir.array, lvalue) |
|
|
|
|
length.lvalue.points_to = ir.lvalue |
|
|
|
|
ret.append(length) |
|
|
|
|
|
|
|
|
|
return ret |
|
|
|
|
|
|
|
|
|
ir = Push(ir.destination, ir.arguments[0]) |
|
|
|
|
|
|
|
|
|
if lvalue: |
|
|
|
|
ret = [] |
|
|
|
|
ret.append(ir) |
|
|
|
|
|
|
|
|
|
length = Length(ir.array, lvalue) |
|
|
|
|
length.lvalue.points_to = ir.lvalue |
|
|
|
|
ret.append(length) |
|
|
|
|
return ret |
|
|
|
|
|
|
|
|
|
return ir |
|
|
|
|
|
|
|
|
|
def look_for_library(contract, ir, node, using_for, t): |
|
|
|
|
for destination in using_for[t]: |
|
|
|
|
lib_contract = contract.slither.get_contract_from_name(str(destination)) |
|
|
|
|
if lib_contract: |
|
|
|
|
lib_call = LibraryCall(lib_contract, |
|
|
|
|
ir.function_name, |
|
|
|
|
ir.nbr_arguments, |
|
|
|
|
ir.lvalue, |
|
|
|
|
ir.type_call) |
|
|
|
|
lib_call.call_gas = ir.call_gas |
|
|
|
|
lib_call.arguments = [ir.destination] + ir.arguments |
|
|
|
|
new_ir = convert_type_library_call(lib_call, lib_contract) |
|
|
|
|
if new_ir: |
|
|
|
|
new_ir.set_node(ir.node) |
|
|
|
|
return new_ir |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
def convert_to_library(ir, node, using_for): |
|
|
|
|
contract = node.function.contract |
|
|
|
|
t = ir.destination.type |
|
|
|
|
|
|
|
|
|
if t in using_for: |
|
|
|
|
new_ir = look_for_library(contract, ir, node, using_for, t) |
|
|
|
|
if new_ir: |
|
|
|
|
return new_ir |
|
|
|
|
|
|
|
|
|
if '*' in using_for: |
|
|
|
|
new_ir = look_for_library(contract, ir, node, using_for, '*') |
|
|
|
|
if new_ir: |
|
|
|
|
return new_ir |
|
|
|
|
|
|
|
|
|
return None |
|
|
|
|
|
|
|
|
@ -316,47 +634,8 @@ def get_type(t): |
|
|
|
|
""" |
|
|
|
|
if isinstance(t, UserDefinedType): |
|
|
|
|
if isinstance(t.type, Contract): |
|
|
|
|
return 'address' |
|
|
|
|
return str(t) |
|
|
|
|
|
|
|
|
|
def get_sig(ir): |
|
|
|
|
''' |
|
|
|
|
Return a list of potential signature |
|
|
|
|
It is a list, as Constant variables can be converted to int256 |
|
|
|
|
Args: |
|
|
|
|
ir (slithIR.operation) |
|
|
|
|
Returns: |
|
|
|
|
list(str) |
|
|
|
|
''' |
|
|
|
|
sig = '{}({})' |
|
|
|
|
name = ir.function_name |
|
|
|
|
|
|
|
|
|
# list of list of arguments |
|
|
|
|
argss = [[]] |
|
|
|
|
for arg in ir.arguments: |
|
|
|
|
if isinstance(arg, (list,)): |
|
|
|
|
type_arg = '{}[{}]'.format(get_type(arg[0].type), len(arg)) |
|
|
|
|
elif isinstance(arg, Function): |
|
|
|
|
type_arg = arg.signature_str |
|
|
|
|
else: |
|
|
|
|
type_arg = get_type(arg.type) |
|
|
|
|
if isinstance(arg, Constant) and arg.type == ElementaryType('uint256'): |
|
|
|
|
# If it is a constant |
|
|
|
|
# We dupplicate the existing list |
|
|
|
|
# And we add uint256 and int256 cases |
|
|
|
|
# There is no potential collision, as the compiler |
|
|
|
|
# Prevent it with a |
|
|
|
|
# "not unique after argument-dependent loopkup" issue |
|
|
|
|
argss_new = [list(args) for args in argss] |
|
|
|
|
for args in argss: |
|
|
|
|
args.append(str(ElementaryType('uint256'))) |
|
|
|
|
for args in argss_new: |
|
|
|
|
args.append(str(ElementaryType('int256'))) |
|
|
|
|
argss = argss + argss_new |
|
|
|
|
else: |
|
|
|
|
for args in argss: |
|
|
|
|
args.append(type_arg) |
|
|
|
|
return [sig.format(name, ','.join(args)) for args in argss] |
|
|
|
|
return 'address' |
|
|
|
|
return str(t) |
|
|
|
|
|
|
|
|
|
def convert_type_library_call(ir, lib_contract): |
|
|
|
|
sigs = get_sig(ir) |
|
|
|
@ -448,167 +727,12 @@ def convert_type_of_high_level_call(ir, contract): |
|
|
|
|
|
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
def propagate_types(ir, node): |
|
|
|
|
# propagate the type |
|
|
|
|
using_for = node.function.contract.using_for |
|
|
|
|
if isinstance(ir, OperationWithLValue): |
|
|
|
|
# Force assignment in case of missing previous correct type |
|
|
|
|
if not ir.lvalue.type: |
|
|
|
|
if isinstance(ir, Assignment): |
|
|
|
|
ir.lvalue.set_type(ir.rvalue.type) |
|
|
|
|
elif isinstance(ir, Binary): |
|
|
|
|
if BinaryType.return_bool(ir.type): |
|
|
|
|
ir.lvalue.set_type(ElementaryType('bool')) |
|
|
|
|
else: |
|
|
|
|
ir.lvalue.set_type(ir.variable_left.type) |
|
|
|
|
elif isinstance(ir, Delete): |
|
|
|
|
# nothing to propagate |
|
|
|
|
pass |
|
|
|
|
elif isinstance(ir, LibraryCall): |
|
|
|
|
return convert_type_library_call(ir, ir.destination) |
|
|
|
|
elif isinstance(ir, HighLevelCall): |
|
|
|
|
t = ir.destination.type |
|
|
|
|
|
|
|
|
|
# Temporary operation (they are removed later) |
|
|
|
|
if t is None: |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
# convert library |
|
|
|
|
if t in using_for or '*' in using_for: |
|
|
|
|
new_ir = convert_to_library(ir, node, using_for) |
|
|
|
|
if new_ir: |
|
|
|
|
return new_ir |
|
|
|
|
|
|
|
|
|
if isinstance(t, UserDefinedType): |
|
|
|
|
# UserdefinedType |
|
|
|
|
t_type = t.type |
|
|
|
|
if isinstance(t_type, Contract): |
|
|
|
|
contract = node.slither.get_contract_from_name(t_type.name) |
|
|
|
|
return convert_type_of_high_level_call(ir, contract) |
|
|
|
|
|
|
|
|
|
# Convert HighLevelCall to LowLevelCall |
|
|
|
|
if isinstance(t, ElementaryType) and t.name == 'address': |
|
|
|
|
if ir.destination.name == 'this': |
|
|
|
|
return convert_type_of_high_level_call(ir, node.function.contract) |
|
|
|
|
return convert_to_low_level(ir) |
|
|
|
|
|
|
|
|
|
# Convert push operations |
|
|
|
|
# May need to insert a new operation |
|
|
|
|
# Which leads to return a list of operation |
|
|
|
|
if isinstance(t, ArrayType): |
|
|
|
|
if ir.function_name == 'push' and len(ir.arguments) == 1: |
|
|
|
|
return convert_to_push(ir, node) |
|
|
|
|
|
|
|
|
|
elif isinstance(ir, Index): |
|
|
|
|
if isinstance(ir.variable_left.type, MappingType): |
|
|
|
|
ir.lvalue.set_type(ir.variable_left.type.type_to) |
|
|
|
|
elif isinstance(ir.variable_left.type, ArrayType): |
|
|
|
|
ir.lvalue.set_type(ir.variable_left.type.type) |
|
|
|
|
|
|
|
|
|
elif isinstance(ir, InitArray): |
|
|
|
|
length = len(ir.init_values) |
|
|
|
|
t = ir.init_values[0].type |
|
|
|
|
ir.lvalue.set_type(ArrayType(t, length)) |
|
|
|
|
elif isinstance(ir, InternalCall): |
|
|
|
|
# if its not a tuple, return a singleton |
|
|
|
|
return_type = ir.function.return_type |
|
|
|
|
if return_type: |
|
|
|
|
if len(return_type) == 1: |
|
|
|
|
ir.lvalue.set_type(return_type[0]) |
|
|
|
|
elif len(return_type)>1: |
|
|
|
|
ir.lvalue.set_type(return_type) |
|
|
|
|
else: |
|
|
|
|
ir.lvalue = None |
|
|
|
|
elif isinstance(ir, InternalDynamicCall): |
|
|
|
|
# if its not a tuple, return a singleton |
|
|
|
|
return_type = ir.function_type.return_type |
|
|
|
|
if return_type: |
|
|
|
|
if len(return_type) == 1: |
|
|
|
|
ir.lvalue.set_type(return_type[0]) |
|
|
|
|
else: |
|
|
|
|
ir.lvalue.set_type(return_type) |
|
|
|
|
else: |
|
|
|
|
ir.lvalue = None |
|
|
|
|
elif isinstance(ir, LowLevelCall): |
|
|
|
|
# Call are not yet converted |
|
|
|
|
# This should not happen |
|
|
|
|
assert False |
|
|
|
|
elif isinstance(ir, Member): |
|
|
|
|
# TODO we should convert the reference to a temporary if the member is a length or a balance |
|
|
|
|
if ir.variable_right == 'length' and not isinstance(ir.variable_left, Contract) and isinstance(ir.variable_left.type, (ElementaryType, ArrayType)): |
|
|
|
|
length = Length(ir.variable_left, ir.lvalue) |
|
|
|
|
length.lvalue.points_to = ir.variable_left |
|
|
|
|
return length |
|
|
|
|
if ir.variable_right == 'balance'and not isinstance(ir.variable_left, Contract) and isinstance(ir.variable_left.type, ElementaryType): |
|
|
|
|
return Balance(ir.variable_left, ir.lvalue) |
|
|
|
|
left = ir.variable_left |
|
|
|
|
if isinstance(left, (Variable, SolidityVariable)): |
|
|
|
|
t = ir.variable_left.type |
|
|
|
|
elif isinstance(left, (Contract, Enum, Structure)): |
|
|
|
|
t = UserDefinedType(left) |
|
|
|
|
# can be None due to temporary operation |
|
|
|
|
if t: |
|
|
|
|
if isinstance(t, UserDefinedType): |
|
|
|
|
# UserdefinedType |
|
|
|
|
type_t = t.type |
|
|
|
|
if isinstance(type_t, Enum): |
|
|
|
|
ir.lvalue.set_type(t) |
|
|
|
|
elif isinstance(type_t, Structure): |
|
|
|
|
elems = type_t.elems |
|
|
|
|
for elem in elems: |
|
|
|
|
if elem == ir.variable_right: |
|
|
|
|
ir.lvalue.set_type(elems[elem].type) |
|
|
|
|
else: |
|
|
|
|
assert isinstance(type_t, Contract) |
|
|
|
|
elif isinstance(ir, NewArray): |
|
|
|
|
ir.lvalue.set_type(ir.array_type) |
|
|
|
|
elif isinstance(ir, NewContract): |
|
|
|
|
contract = node.slither.get_contract_from_name(ir.contract_name) |
|
|
|
|
ir.lvalue.set_type(UserDefinedType(contract)) |
|
|
|
|
elif isinstance(ir, NewElementaryType): |
|
|
|
|
ir.lvalue.set_type(ir.type) |
|
|
|
|
elif isinstance(ir, NewStructure): |
|
|
|
|
ir.lvalue.set_type(UserDefinedType(ir.structure)) |
|
|
|
|
elif isinstance(ir, Push): |
|
|
|
|
# No change required |
|
|
|
|
pass |
|
|
|
|
elif isinstance(ir, Send): |
|
|
|
|
ir.lvalue.set_type(ElementaryType('bool')) |
|
|
|
|
elif isinstance(ir, SolidityCall): |
|
|
|
|
return_type = ir.function.return_type |
|
|
|
|
if len(return_type) == 1: |
|
|
|
|
ir.lvalue.set_type(return_type[0]) |
|
|
|
|
elif len(return_type)>1: |
|
|
|
|
ir.lvalue.set_type(return_type) |
|
|
|
|
elif isinstance(ir, TypeConversion): |
|
|
|
|
ir.lvalue.set_type(ir.type) |
|
|
|
|
elif isinstance(ir, Unary): |
|
|
|
|
ir.lvalue.set_type(ir.rvalue.type) |
|
|
|
|
elif isinstance(ir, Unpack): |
|
|
|
|
types = ir.tuple.type.type |
|
|
|
|
idx = ir.index |
|
|
|
|
t = types[idx] |
|
|
|
|
ir.lvalue.set_type(t) |
|
|
|
|
elif isinstance(ir, (Argument, TmpCall, TmpNewArray, TmpNewContract, TmpNewStructure, TmpNewElementaryType)): |
|
|
|
|
# temporary operation; they will be removed |
|
|
|
|
pass |
|
|
|
|
else: |
|
|
|
|
logger.error('Not handling {} during type propgation'.format(type(ir))) |
|
|
|
|
exit(-1) |
|
|
|
|
|
|
|
|
|
def apply_ir_heuristics(irs, node): |
|
|
|
|
""" |
|
|
|
|
Apply a set of heuristic to improve slithIR |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
irs = integrate_value_gas(irs) |
|
|
|
|
|
|
|
|
|
irs = propage_type_and_convert_call(irs, node) |
|
|
|
|
irs = remove_unused(irs) |
|
|
|
|
find_references_origin(irs) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return irs |
|
|
|
|
# endregion |
|
|
|
|
################################################################################### |
|
|
|
|
################################################################################### |
|
|
|
|
# region Points to operation |
|
|
|
|
################################################################################### |
|
|
|
|
################################################################################### |
|
|
|
|
|
|
|
|
|
def find_references_origin(irs): |
|
|
|
|
""" |
|
|
|
@ -619,13 +743,12 @@ def find_references_origin(irs): |
|
|
|
|
if isinstance(ir, (Index, Member)): |
|
|
|
|
ir.lvalue.points_to = ir.variable_left |
|
|
|
|
|
|
|
|
|
def is_temporary(ins): |
|
|
|
|
return isinstance(ins, (Argument, |
|
|
|
|
TmpNewElementaryType, |
|
|
|
|
TmpNewContract, |
|
|
|
|
TmpNewArray, |
|
|
|
|
TmpNewStructure)) |
|
|
|
|
|
|
|
|
|
# endregion |
|
|
|
|
################################################################################### |
|
|
|
|
################################################################################### |
|
|
|
|
# region Operation filtering |
|
|
|
|
################################################################################### |
|
|
|
|
################################################################################### |
|
|
|
|
|
|
|
|
|
def remove_temporary(result): |
|
|
|
|
result = [ins for ins in result if not isinstance(ins, (Argument, |
|
|
|
@ -668,88 +791,24 @@ def remove_unused(result): |
|
|
|
|
result = [i for i in result if not i in to_remove] |
|
|
|
|
return result |
|
|
|
|
|
|
|
|
|
# endregion |
|
|
|
|
################################################################################### |
|
|
|
|
################################################################################### |
|
|
|
|
# region Heuristics selection |
|
|
|
|
################################################################################### |
|
|
|
|
################################################################################### |
|
|
|
|
|
|
|
|
|
def apply_ir_heuristics(irs, node): |
|
|
|
|
""" |
|
|
|
|
Apply a set of heuristic to improve slithIR |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
def extract_tmp_call(ins): |
|
|
|
|
assert isinstance(ins, TmpCall) |
|
|
|
|
|
|
|
|
|
if isinstance(ins.called, Variable) and isinstance(ins.called.type, FunctionType): |
|
|
|
|
call = InternalDynamicCall(ins.lvalue, ins.called, ins.called.type) |
|
|
|
|
call.call_id = ins.call_id |
|
|
|
|
return call |
|
|
|
|
if isinstance(ins.ori, Member): |
|
|
|
|
if isinstance(ins.ori.variable_left, Contract): |
|
|
|
|
st = ins.ori.variable_left.get_structure_from_name(ins.ori.variable_right) |
|
|
|
|
if st: |
|
|
|
|
op = NewStructure(st, ins.lvalue) |
|
|
|
|
op.call_id = ins.call_id |
|
|
|
|
return op |
|
|
|
|
libcall = LibraryCall(ins.ori.variable_left, ins.ori.variable_right, ins.nbr_arguments, ins.lvalue, ins.type_call) |
|
|
|
|
libcall.call_id = ins.call_id |
|
|
|
|
return libcall |
|
|
|
|
msgcall = HighLevelCall(ins.ori.variable_left, ins.ori.variable_right, ins.nbr_arguments, ins.lvalue, ins.type_call) |
|
|
|
|
msgcall.call_id = ins.call_id |
|
|
|
|
return msgcall |
|
|
|
|
|
|
|
|
|
if isinstance(ins.ori, TmpCall): |
|
|
|
|
r = extract_tmp_call(ins.ori) |
|
|
|
|
return r |
|
|
|
|
if isinstance(ins.called, SolidityVariableComposed): |
|
|
|
|
if str(ins.called) == 'block.blockhash': |
|
|
|
|
ins.called = SolidityFunction('blockhash(uint256)') |
|
|
|
|
elif str(ins.called) == 'this.balance': |
|
|
|
|
return SolidityCall(SolidityFunction('this.balance()'), ins.nbr_arguments, ins.lvalue, ins.type_call) |
|
|
|
|
|
|
|
|
|
if isinstance(ins.called, SolidityFunction): |
|
|
|
|
return SolidityCall(ins.called, ins.nbr_arguments, ins.lvalue, ins.type_call) |
|
|
|
|
|
|
|
|
|
if isinstance(ins.ori, TmpNewElementaryType): |
|
|
|
|
return NewElementaryType(ins.ori.type, ins.lvalue) |
|
|
|
|
|
|
|
|
|
if isinstance(ins.ori, TmpNewContract): |
|
|
|
|
op = NewContract(Constant(ins.ori.contract_name), ins.lvalue) |
|
|
|
|
op.call_id = ins.call_id |
|
|
|
|
return op |
|
|
|
|
|
|
|
|
|
if isinstance(ins.ori, TmpNewArray): |
|
|
|
|
return NewArray(ins.ori.depth, ins.ori.array_type, ins.lvalue) |
|
|
|
|
|
|
|
|
|
if isinstance(ins.called, Structure): |
|
|
|
|
op = NewStructure(ins.called, ins.lvalue) |
|
|
|
|
op.call_id = ins.call_id |
|
|
|
|
return op |
|
|
|
|
|
|
|
|
|
if isinstance(ins.called, Event): |
|
|
|
|
return EventCall(ins.called.name) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
raise Exception('Not extracted {} {}'.format(type(ins.called), ins)) |
|
|
|
|
|
|
|
|
|
def convert_expression(expression, node): |
|
|
|
|
# handle standlone expression |
|
|
|
|
# such as return true; |
|
|
|
|
from slither.core.cfg.node import NodeType |
|
|
|
|
|
|
|
|
|
if isinstance(expression, Literal) and node.type in [NodeType.IF, NodeType.IFLOOP]: |
|
|
|
|
result = [Condition(Constant(expression.value))] |
|
|
|
|
return result |
|
|
|
|
if isinstance(expression, Identifier) and node.type in [NodeType.IF, NodeType.IFLOOP]: |
|
|
|
|
result = [Condition(expression.value)] |
|
|
|
|
return result |
|
|
|
|
|
|
|
|
|
irs = integrate_value_gas(irs) |
|
|
|
|
|
|
|
|
|
visitor = ExpressionToSlithIR(expression, node) |
|
|
|
|
result = visitor.result() |
|
|
|
|
irs = propagate_type_and_convert_call(irs, node) |
|
|
|
|
irs = remove_unused(irs) |
|
|
|
|
find_references_origin(irs) |
|
|
|
|
|
|
|
|
|
result = apply_ir_heuristics(result, node) |
|
|
|
|
|
|
|
|
|
if result: |
|
|
|
|
if node.type in [NodeType.IF, NodeType.IFLOOP]: |
|
|
|
|
assert isinstance(result[-1], (OperationWithLValue)) |
|
|
|
|
result.append(Condition(result[-1].lvalue)) |
|
|
|
|
elif node.type == NodeType.RETURN: |
|
|
|
|
# May return None |
|
|
|
|
if isinstance(result[-1], (OperationWithLValue)): |
|
|
|
|
result.append(Return(result[-1].lvalue)) |
|
|
|
|
return irs |
|
|
|
|
|
|
|
|
|
return result |
|
|
|
|