def _convert_type_contract(ir, slither): assert isinstance(ir.variable_left.type, TypeInformation) contract = ir.variable_left.type.type if ir.variable_right == 'creationCode': if slither.crytic_compile: bytecode = slither.crytic_compile.bytecode_init(contract.name) else: logger.info( 'The codebase uses type(x).creationCode, but crytic-compile was not used. As a result, the bytecode cannot be found' ) bytecode = "MISSING_BYTECODE" assignment = Assignment(ir.lvalue, Constant(str(bytecode)), ElementaryType('bytes')) assignment.lvalue.set_type(ElementaryType('bytes')) return assignment if ir.variable_right == 'runtimeCode': if slither.crytic_compile: bytecode = slither.crytic_compile.bytecode_runtime(contract.name) else: logger.info( 'The codebase uses type(x).runtimeCode, but crytic-compile was not used. As a result, the bytecode cannot be found' ) bytecode = "MISSING_BYTECODE" assignment = Assignment(ir.lvalue, Constant(str(bytecode)), ElementaryType('bytes')) assignment.lvalue.set_type(ElementaryType('bytes')) return assignment if ir.variable_right == 'name': assignment = Assignment(ir.lvalue, Constant(contract.name), ElementaryType('string')) assignment.lvalue.set_type(ElementaryType('string')) return assignment raise SlithIRError(f'type({contract.name}).{ir.variable_right} is unknown')
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 == NodeType.RETURN: result = [Return(Constant(expression.value))] return result if isinstance(expression, Identifier) and node.type == NodeType.RETURN: result = [Return(expression.value)] return result 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) 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
def _post_member_access(self, expression): expr = get(expression.expression) # Look for type(X).max / min # Because we looked at the AST structure, we need to look into the nested expression # Hopefully this is always on a direct sub field, and there is no weird construction if isinstance(expression.expression, CallExpression) and expression.member_name in [ "min", "max", ]: if isinstance(expression.expression.called, Identifier): if expression.expression.called.value == SolidityFunction("type()"): assert len(expression.expression.arguments) == 1 val = TemporaryVariable(self._node) type_expression_found = expression.expression.arguments[0] assert isinstance(type_expression_found, ElementaryTypeNameExpression) type_found = type_expression_found.type if expression.member_name == "min:": op = Assignment(val, Constant(str(type_found.min), type_found), type_found,) else: op = Assignment(val, Constant(str(type_found.max), type_found), type_found,) self._result.append(op) set_val(expression, val) return val = ReferenceVariable(self._node) member = Member(expr, Constant(expression.member_name), val) member.set_expression(expression) self._result.append(member) set_val(expression, val)
def _post_unary_operation(self, expression): value = get(expression.expression) if expression.type in [ UnaryOperationType.BANG, UnaryOperationType.TILD ]: lvalue = TemporaryVariable(self._node) operation = Unary(lvalue, value, expression.type) operation.set_expression(expression) self._result.append(operation) set_val(expression, lvalue) elif expression.type in [UnaryOperationType.DELETE]: operation = Delete(value, value) operation.set_expression(expression) self._result.append(operation) set_val(expression, value) elif expression.type in [UnaryOperationType.PLUSPLUS_PRE]: operation = Binary(value, value, Constant("1", value.type), BinaryType.ADDITION) operation.set_expression(expression) self._result.append(operation) set_val(expression, value) elif expression.type in [UnaryOperationType.MINUSMINUS_PRE]: operation = Binary(value, value, Constant("1", value.type), BinaryType.SUBTRACTION) operation.set_expression(expression) self._result.append(operation) set_val(expression, value) elif expression.type in [UnaryOperationType.PLUSPLUS_POST]: lvalue = TemporaryVariable(self._node) operation = Assignment(lvalue, value, value.type) operation.set_expression(expression) self._result.append(operation) operation = Binary(value, value, Constant("1", value.type), BinaryType.ADDITION) operation.set_expression(expression) self._result.append(operation) set_val(expression, lvalue) elif expression.type in [UnaryOperationType.MINUSMINUS_POST]: lvalue = TemporaryVariable(self._node) operation = Assignment(lvalue, value, value.type) operation.set_expression(expression) self._result.append(operation) operation = Binary(value, value, Constant("1", value.type), BinaryType.SUBTRACTION) operation.set_expression(expression) self._result.append(operation) set_val(expression, lvalue) elif expression.type in [UnaryOperationType.PLUS_PRE]: set_val(expression, value) elif expression.type in [UnaryOperationType.MINUS_PRE]: lvalue = TemporaryVariable(self._node) operation = Binary(lvalue, Constant("0", value.type), value, BinaryType.SUBTRACTION) operation.set_expression(expression) self._result.append(operation) set_val(expression, lvalue) else: raise SlithIRError( 'Unary operation to IR not supported {}'.format(expression))
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 _post_member_access(self, expression): expr = get(expression.expression) val = ReferenceVariable(self._node) member = Member(expr, Constant(expression.member_name), val) member.set_expression(expression) self._result.append(member) set_val(expression, val)
def convert_to_pop(ir, node): """ Convert pop operators Return a list of 6 operations """ ret = [] arr = ir.destination length = ReferenceVariable(node) length.set_type(ElementaryType('uint256')) ir_length = Length(arr, length) ir_length.set_expression(ir.expression) ir_length.set_node(ir.node) ir_length.lvalue.points_to = arr ret.append(ir_length) val = TemporaryVariable(node) ir_sub_1 = Binary(val, length, Constant("1", ElementaryType('uint256')), BinaryType.SUBTRACTION) ir_sub_1.set_expression(ir.expression) ir_sub_1.set_node(ir.node) ret.append(ir_sub_1) element_to_delete = ReferenceVariable(node) ir_assign_element_to_delete = Index(element_to_delete, arr, val, ElementaryType('uint256')) ir_length.lvalue.points_to = arr element_to_delete.set_type(ElementaryType('uint256')) ir_assign_element_to_delete.set_expression(ir.expression) ir_assign_element_to_delete.set_node(ir.node) ret.append(ir_assign_element_to_delete) ir_delete = Delete(element_to_delete, element_to_delete) ir_delete.set_expression(ir.expression) ir_delete.set_node(ir.node) ret.append(ir_delete) length_to_assign = ReferenceVariable(node) length_to_assign.set_type(ElementaryType('uint256')) ir_length = Length(arr, length_to_assign) ir_length.set_expression(ir.expression) ir_length.lvalue.points_to = arr ir_length.set_node(ir.node) ret.append(ir_length) ir_assign_length = Assignment(length_to_assign, val, ElementaryType('uint256')) ir_assign_length.set_expression(ir.expression) ir_assign_length.set_node(ir.node) ret.append(ir_assign_length) return ret
def _detect_boolean_constant_misuses(contract): # pylint: disable=too-many-branches """ Detects and returns all nodes which misuse a Boolean constant. :param contract: Contract to detect assignment within. :return: A list of misusing nodes. """ # Create our result set. results = [] # Loop for each function and modifier. for function in contract.functions_declared: # pylint: disable=too-many-nested-blocks f_results = set() # Loop for every node in this function, looking for boolean constants for node in function.nodes: # Do not report "while(true)" if node.type == NodeType.IFLOOP: if node.irs: if len(node.irs) == 1: ir = node.irs[0] if isinstance(ir, Condition) and ir.value == Constant( "True", ElementaryType("bool") ): continue for ir in node.irs: if isinstance(ir, (Assignment, Call, Return, InitArray)): # It's ok to use a bare boolean constant in these contexts continue if isinstance(ir, Binary): if ir.type in [ BinaryType.ADDITION, BinaryType.EQUAL, BinaryType.NOT_EQUAL, ]: # Comparing to a Boolean constant is dubious style, but harmless # Equal is catch by another detector (informational severity) continue for r in ir.read: if isinstance(r, Constant): if isinstance(r.value, bool): f_results.add(node) results.append((function, f_results)) # Return the resulting set of nodes with improper uses of Boolean constants return results
def _post_literal(self, expression): cst = Constant(expression.value, expression.type, expression.subdenomination) set_val(expression, cst)
def _post_call_expression(self, expression): called = get(expression.called) args = [get(a) for a in expression.arguments if a] for arg in args: arg_ = Argument(arg) arg_.set_expression(expression) self._result.append(arg_) if isinstance(called, Function): # internal call # If tuple if expression.type_call.startswith('tuple(') and expression.type_call != 'tuple()': val = TupleVariable(self._node) else: val = TemporaryVariable(self._node) internal_call = InternalCall(called, len(args), val, expression.type_call) internal_call.set_expression(expression) self._result.append(internal_call) set_val(expression, val) else: # yul things if called.name == 'caller()': val = TemporaryVariable(self._node) var = Assignment(val, SolidityVariableComposed('msg.sender'), 'uint256') self._result.append(var) set_val(expression, val) elif called.name == 'origin()': val = TemporaryVariable(self._node) var = Assignment(val, SolidityVariableComposed('tx.origin'), 'uint256') self._result.append(var) set_val(expression, val) elif called.name == 'extcodesize(uint256)': val = ReferenceVariable(self._node) var = Member(args[0], Constant('codesize'), val) self._result.append(var) set_val(expression, val) elif called.name == 'selfbalance()': val = TemporaryVariable(self._node) var = TypeConversion(val, SolidityVariable('this'), ElementaryType('address')) self._result.append(var) val1 = ReferenceVariable(self._node) var1 = Member(val, Constant('balance'), val1) self._result.append(var1) set_val(expression, val1) elif called.name == 'address()': val = TemporaryVariable(self._node) var = TypeConversion(val, SolidityVariable('this'), ElementaryType('address')) self._result.append(var) set_val(expression, val) elif called.name == 'callvalue()': val = TemporaryVariable(self._node) var = Assignment(val, SolidityVariableComposed('msg.value'), 'uint256') self._result.append(var) set_val(expression, val) else: # If tuple if expression.type_call.startswith('tuple(') and expression.type_call != 'tuple()': val = TupleVariable(self._node) else: val = TemporaryVariable(self._node) message_call = TmpCall(called, len(args), val, expression.type_call) message_call.set_expression(expression) # Gas/value are only accessible here if the syntax {gas: , value: } # Is used over .gas().value() if expression.call_gas: call_gas = get(expression.call_gas) message_call.call_gas = call_gas if expression.call_value: call_value = get(expression.call_value) message_call.call_value = call_value if expression.call_salt: call_salt = get(expression.call_salt) message_call.call_salt = call_salt self._result.append(message_call) set_val(expression, val)
def _post_literal(self, expression): set_val(expression, Constant(expression.value))
def extract_tmp_call(ins, contract): 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.set_expression(ins.expression) call.call_id = ins.call_id return call if isinstance(ins.ori, Member): # 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.set_expression(ins.expression) internalcall.call_id = ins.call_id return internalcall if str(ins.ori.variable_right) in [f.name for f in contract.events]: eventcall = EventCall(ins.ori.variable_right) eventcall.set_expression(ins.expression) eventcall.call_id = ins.call_id return eventcall 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.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.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.call_id = ins.call_id if ins.call_gas: msgcall.call_gas = ins.call_gas if ins.call_value: msgcall.call_value = ins.call_value msgcall.set_expression(ins.expression) return msgcall if isinstance(ins.ori, TmpCall): r = extract_tmp_call(ins.ori, contract) r.set_node(ins.node) return r if isinstance(ins.called, SolidityVariableComposed): if str(ins.called) == 'block.blockhash': ins.called = SolidityFunction('blockhash(uint256)') elif str(ins.called) == 'this.balance': s = SolidityCall(SolidityFunction('this.balance()'), ins.nbr_arguments, ins.lvalue, ins.type_call) s.set_expression(ins.expression) return s if isinstance(ins.called, SolidityFunction): s = SolidityCall(ins.called, ins.nbr_arguments, ins.lvalue, ins.type_call) s.set_expression(ins.expression) return s if isinstance(ins.ori, TmpNewElementaryType): n = NewElementaryType(ins.ori.type, ins.lvalue) n.set_expression(ins.expression) return n if isinstance(ins.ori, TmpNewContract): op = NewContract(Constant(ins.ori.contract_name), ins.lvalue) op.set_expression(ins.expression) op.call_id = ins.call_id return op if isinstance(ins.ori, TmpNewArray): n = NewArray(ins.ori.depth, ins.ori.array_type, ins.lvalue) n.set_expression(ins.expression) return n if isinstance(ins.called, Structure): op = NewStructure(ins.called, ins.lvalue) op.set_expression(ins.expression) op.call_id = ins.call_id op.set_expression(ins.expression) return op if isinstance(ins.called, Event): e = EventCall(ins.called.name) e.set_expression(ins.expression) return e if isinstance(ins.called, Contract): # Called a base constructor, where there is no constructor if ins.called.constructor is None: return Nop() # Case where: # contract A{ constructor(uint) } # contract B is A {} # contract C is B{ constructor() A(10) B() {} # C calls B(), which does not exist # Ideally we should compare here for the parameters types too if len(ins.called.constructor.parameters) != ins.nbr_arguments: return Nop() internalcall = InternalCall(ins.called.constructor, ins.nbr_arguments, ins.lvalue, ins.type_call) internalcall.call_id = ins.call_id internalcall.set_expression(ins.expression) return internalcall raise Exception('Not extracted {} {}'.format(type(ins.called), ins))
def extract_tmp_call(ins, contract): 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 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.call_id = ins.call_id return internalcall if str(ins.ori.variable_right) in [f.name for f in contract.events]: eventcall = EventCall(ins.ori.variable_right) eventcall.call_id = ins.call_id return eventcall 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, contract) 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) if isinstance(ins.called, Contract): # Called a base constructor, where there is no constructor if ins.called.constructor is None: return Nop() internalcall = InternalCall(ins.called.constructor, ins.nbr_arguments, ins.lvalue, ins.type_call) internalcall.call_id = ins.call_id return internalcall raise Exception('Not extracted {} {}'.format(type(ins.called), ins))
def _post_call_expression(self, expression): # pylint: disable=too-many-branches,too-many-statements called = get(expression.called) args = [get(a) for a in expression.arguments if a] for arg in args: arg_ = Argument(arg) arg_.set_expression(expression) self._result.append(arg_) if isinstance(called, Function): # internal call # If tuple if expression.type_call.startswith( "tuple(") and expression.type_call != "tuple()": val = TupleVariable(self._node) else: val = TemporaryVariable(self._node) internal_call = InternalCall(called, len(args), val, expression.type_call) internal_call.set_expression(expression) self._result.append(internal_call) set_val(expression, val) else: # yul things if called.name == "caller()": val = TemporaryVariable(self._node) var = Assignment(val, SolidityVariableComposed("msg.sender"), "uint256") self._result.append(var) set_val(expression, val) elif called.name == "origin()": val = TemporaryVariable(self._node) var = Assignment(val, SolidityVariableComposed("tx.origin"), "uint256") self._result.append(var) set_val(expression, val) elif called.name == "extcodesize(uint256)": val = ReferenceVariable(self._node) var = Member(args[0], Constant("codesize"), val) self._result.append(var) set_val(expression, val) elif called.name == "selfbalance()": val = TemporaryVariable(self._node) var = TypeConversion(val, SolidityVariable("this"), ElementaryType("address")) self._result.append(var) val1 = ReferenceVariable(self._node) var1 = Member(val, Constant("balance"), val1) self._result.append(var1) set_val(expression, val1) elif called.name == "address()": val = TemporaryVariable(self._node) var = TypeConversion(val, SolidityVariable("this"), ElementaryType("address")) self._result.append(var) set_val(expression, val) elif called.name == "callvalue()": val = TemporaryVariable(self._node) var = Assignment(val, SolidityVariableComposed("msg.value"), "uint256") self._result.append(var) set_val(expression, val) else: # If tuple if expression.type_call.startswith( "tuple(") and expression.type_call != "tuple()": val = TupleVariable(self._node) else: val = TemporaryVariable(self._node) message_call = TmpCall(called, len(args), val, expression.type_call) message_call.set_expression(expression) # Gas/value are only accessible here if the syntax {gas: , value: } # Is used over .gas().value() if expression.call_gas: call_gas = get(expression.call_gas) message_call.call_gas = call_gas if expression.call_value: call_value = get(expression.call_value) message_call.call_value = call_value if expression.call_salt: call_salt = get(expression.call_salt) message_call.call_salt = call_salt self._result.append(message_call) set_val(expression, val)
def _post_member_access(self, expression): expr = get(expression.expression) # Look for type(X).max / min # Because we looked at the AST structure, we need to look into the nested expression # Hopefully this is always on a direct sub field, and there is no weird construction if isinstance(expression.expression, CallExpression) and expression.member_name in [ "min", "max", ]: if isinstance(expression.expression.called, Identifier): if expression.expression.called.value == SolidityFunction( "type()"): assert len(expression.expression.arguments) == 1 val = TemporaryVariable(self._node) type_expression_found = expression.expression.arguments[0] assert isinstance(type_expression_found, ElementaryTypeNameExpression) type_found = type_expression_found.type if expression.member_name == "min:": op = Assignment( val, Constant(str(type_found.min), type_found), type_found, ) else: op = Assignment( val, Constant(str(type_found.max), type_found), type_found, ) self._result.append(op) set_val(expression, val) return # This does not support solidity 0.4 contract_name.balance if (isinstance(expr, Variable) and expr.type == ElementaryType("address") and expression.member_name in ["balance", "code", "codehash"]): val = TemporaryVariable(self._node) name = expression.member_name + "(address)" sol_func = SolidityFunction(name) s = SolidityCall( sol_func, 1, val, sol_func.return_type, ) s.set_expression(expression) s.arguments.append(expr) self._result.append(s) set_val(expression, val) return if isinstance(expr, TypeAlias) and expression.member_name in [ "wrap", "unwrap" ]: # The logic is be handled by _post_call_expression set_val(expression, expr) return # Early lookup to detect user defined types from other contracts definitions # contract A { type MyInt is int} # contract B { function f() public{ A.MyInt test = A.MyInt.wrap(1);}} # The logic is handled by _post_call_expression if isinstance(expr, Contract): if expression.member_name in expr.file_scope.user_defined_types: set_val( expression, expr.file_scope.user_defined_types[expression.member_name]) return val = ReferenceVariable(self._node) member = Member(expr, Constant(expression.member_name), val) member.set_expression(expression) self._result.append(member) set_val(expression, val)
def _post_literal(self, expression): cst = Constant(expression.value, expression.type) set_val(expression, cst)
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 if isinstance(t, ElementaryType) and t.name == 'address': if can_be_solidity_func(ir): return convert_to_solidity_func(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 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_and_internal_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_and_internal_level_call(ir, node.function.contract) if can_be_low_level(ir): 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) or (isinstance(t, ElementaryType) and t.type == 'bytes'): if ir.function_name == 'push' and len(ir.arguments) == 1: return convert_to_push(ir, node) if ir.function_name == 'pop' and len(ir.arguments) == 0: return convert_to_pop(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 if ir.function is None: convert_type_of_high_and_internal_level_call(ir, node.function.contract) 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.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): b = Balance(ir.variable_left, ir.lvalue) b.set_expression(ir.expression) b.set_node(ir.node) return b if ir.variable_right == 'selector' and isinstance(ir.variable_left.type, Function): assignment = Assignment(ir.lvalue, Constant(str(get_function_id(ir.variable_left.type.full_name))), ElementaryType('bytes4')) assignment.set_expression(ir.expression) 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): return _convert_type_contract(ir, node.function.slither) left = ir.variable_left t = None 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) # Allow type propagtion as a Function # Only for reference variables # This allows to track the selector keyword # We dont need to check for function collision, as solc prevents the use of selector # if there are multiple functions with the same name f = next((f for f in type_t.functions if f.name == ir.variable_right), None) if f: ir.lvalue.set_type(f) else: # Allow propgation for variable access through contract's nale # like Base_contract.my_variable v = next((v for v in type_t.state_variables if v.name == ir.variable_right), None) if v: ir.lvalue.set_type(v.type) 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): if ir.function.name == 'type(address)': ir.function.return_type = [TypeInformation(ir.arguments[0])] 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: raise SlithIRError('Not handling {} during type propgation'.format(type(ir)))