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

Improve Delete operator in case of ReferenceVariable #412

Merged
merged 1 commit into from
Mar 20, 2020
Merged
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
113 changes: 78 additions & 35 deletions slither/slithir/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

logger = logging.getLogger('ConvertToIR')


def convert_expression(expression, node):
# handle standlone expression
# such as return true;
Expand All @@ -57,7 +58,6 @@ def convert_expression(expression, node):
result = [cond]
return result


visitor = ExpressionToSlithIR(expression, node)
result = visitor.result()

Expand Down Expand Up @@ -94,13 +94,15 @@ def is_value(ins):
return True
return False


def is_gas(ins):
if isinstance(ins, TmpCall):
if isinstance(ins.ori, Member):
if ins.ori.variable_right == 'gas':
return True
return False


def get_sig(ir, name):
'''
Return a list of potential signature
Expand All @@ -116,6 +118,7 @@ def get_sig(ir, name):
argss = convert_arguments(ir.arguments)
return [sig.format(name, ','.join(args)) for args in argss]


def get_canonical_names(ir, function_name, contract_name):
'''
Return a list of potential signature
Expand All @@ -131,6 +134,7 @@ def get_canonical_names(ir, function_name, contract_name):
argss = convert_arguments(ir.arguments)
return [sig.format(f'{contract_name}.{function_name}', ','.join(args)) for args in argss]


def convert_arguments(arguments):
argss = [[]]
for arg in arguments:
Expand Down Expand Up @@ -158,6 +162,7 @@ def convert_arguments(arguments):
args.append(type_arg)
return argss


def is_temporary(ins):
return isinstance(ins, (Argument,
TmpNewElementaryType,
Expand All @@ -166,7 +171,6 @@ def is_temporary(ins):
TmpNewStructure))



# endregion
###################################################################################
###################################################################################
Expand Down Expand Up @@ -204,17 +208,17 @@ def integrate_value_gas(result):
ins = result[idx]
# value can be shadowed, so we check that the prev ins
# is an Argument
if is_value(ins) and isinstance(result[idx-1], Argument):
if is_value(ins) and isinstance(result[idx - 1], Argument):
was_changed = True
result[idx-1].set_type(ArgumentType.VALUE)
result[idx-1].call_id = ins.ori.variable_left.name
result[idx - 1].set_type(ArgumentType.VALUE)
result[idx - 1].call_id = ins.ori.variable_left.name
calls.append(ins.ori.variable_left)
to_remove.append(ins)
variable_to_replace[ins.lvalue.name] = ins.ori.variable_left
elif is_gas(ins) and isinstance(result[idx-1], Argument):
elif is_gas(ins) and isinstance(result[idx - 1], Argument):
was_changed = True
result[idx-1].set_type(ArgumentType.GAS)
result[idx-1].call_id = ins.ori.variable_left.name
result[idx - 1].set_type(ArgumentType.GAS)
result[idx - 1].call_id = ins.ori.variable_left.name
calls.append(ins.ori.variable_left)
to_remove.append(ins)
variable_to_replace[ins.lvalue.name] = ins.ori.variable_left
Expand All @@ -241,10 +245,11 @@ def integrate_value_gas(result):
calls_d = {}
for call in calls:
calls_d[str(call)] = idx
idx = idx+1
idx = idx + 1

return result


# endregion
###################################################################################
###################################################################################
Expand Down Expand Up @@ -306,16 +311,16 @@ def propagate_type_and_convert_call(result, node):
new_ins[1].set_node(ins.node)
del result[idx]
result.insert(idx, new_ins[0])
result.insert(idx+1, new_ins[1])
result.insert(idx + 1, new_ins[1])
idx = idx + 1
elif len(new_ins) == 3:
new_ins[0].set_node(ins.node)
new_ins[1].set_node(ins.node)
new_ins[2].set_node(ins.node)
del result[idx]
result.insert(idx, new_ins[0])
result.insert(idx+1, new_ins[1])
result.insert(idx+2, new_ins[2])
result.insert(idx + 1, new_ins[1])
result.insert(idx + 2, new_ins[2])
idx = idx + 2
else:
# Pop conversion
Expand All @@ -337,9 +342,10 @@ def propagate_type_and_convert_call(result, node):
else:
new_ins.set_node(ins.node)
result[idx] = new_ins
idx = idx +1
idx = idx + 1
return result


def _convert_type_contract(ir, slither):
assert isinstance(ir.variable_left.type, TypeInformation)
contract = ir.variable_left.type.type
Expand Down Expand Up @@ -460,7 +466,7 @@ def propagate_types(ir, node):
if return_type:
if len(return_type) == 1:
ir.lvalue.set_type(return_type[0])
elif len(return_type)>1:
elif len(return_type) > 1:
ir.lvalue.set_type(return_type)
else:
ir.lvalue = None
Expand All @@ -480,13 +486,15 @@ def propagate_types(ir, node):
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)):
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.set_expression(ir.expression)
length.lvalue.points_to = ir.variable_left
length.set_node(ir.node)
return length
if ir.variable_right == 'balance'and not isinstance(ir.variable_left, Contract) and isinstance(ir.variable_left.type, ElementaryType):
if ir.variable_right == 'balance' and not isinstance(ir.variable_left, Contract) and isinstance(
ir.variable_left.type, ElementaryType):
b = Balance(ir.variable_left, ir.lvalue)
b.set_expression(ir.expression)
b.set_node(ir.node)
Expand All @@ -499,7 +507,8 @@ def propagate_types(ir, node):
assignment.set_node(ir.node)
assignment.lvalue.set_type(ElementaryType('bytes4'))
return assignment
if isinstance(ir.variable_left, TemporaryVariable) and isinstance(ir.variable_left.type, TypeInformation):
if isinstance(ir.variable_left, TemporaryVariable) and isinstance(ir.variable_left.type,
TypeInformation):
return _convert_type_contract(ir, node.function.slither)
left = ir.variable_left
t = None
Expand Down Expand Up @@ -555,7 +564,7 @@ def propagate_types(ir, node):
return_type = ir.function.return_type
if len(return_type) == 1:
ir.lvalue.set_type(return_type[0])
elif len(return_type)>1:
elif len(return_type) > 1:
ir.lvalue.set_type(return_type)
elif isinstance(ir, TypeConversion):
ir.lvalue.set_type(ir.type)
Expand All @@ -566,12 +575,14 @@ def propagate_types(ir, node):
idx = ir.index
t = types[idx]
ir.lvalue.set_type(t)
elif isinstance(ir, (Argument, TmpCall, TmpNewArray, TmpNewContract, TmpNewStructure, TmpNewElementaryType)):
elif isinstance(ir,
(Argument, TmpCall, TmpNewArray, TmpNewContract, TmpNewStructure, TmpNewElementaryType)):
# temporary operation; they will be removed
pass
else:
raise SlithIRError('Not handling {} during type propgation'.format(type(ir)))


def extract_tmp_call(ins, contract):
assert isinstance(ins, TmpCall)

Expand All @@ -584,7 +595,8 @@ def extract_tmp_call(ins, contract):
# If there is a call on an inherited contract, it is an internal call or an event
if ins.ori.variable_left in contract.inheritance + [contract]:
if str(ins.ori.variable_right) in [f.name for f in contract.functions]:
internalcall = InternalCall((ins.ori.variable_right, ins.ori.variable_left.name), ins.nbr_arguments, ins.lvalue, ins.type_call)
internalcall = InternalCall((ins.ori.variable_right, ins.ori.variable_left.name), ins.nbr_arguments,
ins.lvalue, ins.type_call)
internalcall.set_expression(ins.expression)
internalcall.call_id = ins.call_id
return internalcall
Expand All @@ -600,11 +612,13 @@ def extract_tmp_call(ins, contract):
op.set_expression(ins.expression)
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 = LibraryCall(ins.ori.variable_left, ins.ori.variable_right, ins.nbr_arguments, ins.lvalue,
ins.type_call)
libcall.set_expression(ins.expression)
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 = HighLevelCall(ins.ori.variable_left, ins.ori.variable_right, ins.nbr_arguments, ins.lvalue,
ins.type_call)
msgcall.call_id = ins.call_id
msgcall.set_expression(ins.expression)
return msgcall
Expand Down Expand Up @@ -664,9 +678,9 @@ def extract_tmp_call(ins, contract):
internalcall.set_expression(ins.expression)
return internalcall


raise Exception('Not extracted {} {}'.format(type(ins.called), ins))


# endregion
###################################################################################
###################################################################################
Expand All @@ -682,6 +696,7 @@ def can_be_low_level(ir):
'callcode',
'staticcall']


def convert_to_low_level(ir):
"""
Convert to a transfer/send/or low level call
Expand Down Expand Up @@ -710,10 +725,10 @@ def convert_to_low_level(ir):
'callcode',
'staticcall']:
new_ir = LowLevelCall(ir.destination,
ir.function_name,
ir.nbr_arguments,
ir.lvalue,
ir.type_call)
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
Expand All @@ -725,11 +740,12 @@ def convert_to_low_level(ir):


def can_be_solidity_func(ir):
return ir.destination.name == 'abi' and ir.function_name in ['encode',
'encodePacked',
'encodeWithSelector',
'encodeWithSignature',
'decode']
return ir.destination.name == 'abi' and ir.function_name in ['encode',
'encodePacked',
'encodeWithSelector',
'encodeWithSignature',
'decode']


def convert_to_solidity_func(ir):
"""
Expand All @@ -748,6 +764,7 @@ def convert_to_solidity_func(ir):
new_ir.lvalue.set_type(call.return_type)
return new_ir


def convert_to_push(ir, node):
"""
Convert a call to a PUSH operaiton
Expand Down Expand Up @@ -864,6 +881,7 @@ def convert_to_pop(ir, node):

return ret


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))
Expand All @@ -883,6 +901,7 @@ def look_for_library(contract, ir, node, using_for, t):
return new_ir
return None


def convert_to_library(ir, node, using_for):
# We use contract_declarer, because Solidity resolve the library
# before resolving the inheritance.
Expand All @@ -902,6 +921,7 @@ def convert_to_library(ir, node, using_for):

return None


def get_type(t):
"""
Convert a type to a str
Expand All @@ -912,6 +932,7 @@ def get_type(t):
return 'address'
return str(t)


def convert_type_library_call(ir, lib_contract):
sigs = get_sig(ir, ir.function_name)
func = None
Expand Down Expand Up @@ -949,6 +970,7 @@ def convert_type_library_call(ir, lib_contract):
ir.lvalue = None
return ir


def convert_type_of_high_and_internal_level_call(ir, contract):
func = None
if isinstance(ir, InternalCall):
Expand Down Expand Up @@ -1018,6 +1040,7 @@ def convert_type_of_high_and_internal_level_call(ir, contract):

return None


# endregion
###################################################################################
###################################################################################
Expand All @@ -1034,6 +1057,7 @@ def find_references_origin(irs):
if isinstance(ir, (Index, Member)):
ir.lvalue.points_to = ir.variable_left


# endregion
###################################################################################
###################################################################################
Expand All @@ -1050,6 +1074,7 @@ def remove_temporary(result):

return result


def remove_unused(result):
removed = True

Expand Down Expand Up @@ -1082,10 +1107,11 @@ def remove_unused(result):
result = [i for i in result if not i in to_remove]
return result


# endregion
###################################################################################
###################################################################################
# region Constant type conversioh
# region Constant type conversion
###################################################################################
###################################################################################

Expand Down Expand Up @@ -1154,6 +1180,24 @@ def convert_constant_types(irs):
was_changed = True


# endregion
###################################################################################
###################################################################################
# region Delete handling
###################################################################################
###################################################################################

def convert_delete(irs):
"""
Convert the lvalue of the Delete to point to the variable removed
This can only be done after find_references_origin is called
:param irs:
:return:
"""
for ir in irs:
if isinstance(ir, Delete):
if isinstance(ir.lvalue, ReferenceVariable):
ir.lvalue = ir.lvalue.points_to


# endregion
Expand All @@ -1174,7 +1218,6 @@ def apply_ir_heuristics(irs, node):
irs = remove_unused(irs)
find_references_origin(irs)
convert_constant_types(irs)

convert_delete(irs)

return irs