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 _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 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.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) 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) ret.append(ir_assign_element_to_delete) ir_delete = Delete(element_to_delete, element_to_delete) ir_delete.set_expression(ir.expression) 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 ret.append(ir_length) ir_assign_length = Assignment(length_to_assign, val, ElementaryType('uint256')) ir_assign_length.set_expression(ir.expression) ret.append(ir_assign_length) return ret
def convert_assignment(left, right, t, return_type): if t == AssignmentOperationType.ASSIGN: return Assignment(left, right, return_type) elif t == AssignmentOperationType.ASSIGN_OR: return Binary(left, left, right, BinaryType.OR) elif t == AssignmentOperationType.ASSIGN_CARET: return Binary(left, left, right, BinaryType.CARET) elif t == AssignmentOperationType.ASSIGN_AND: return Binary(left, left, right, BinaryType.AND) elif t == AssignmentOperationType.ASSIGN_LEFT_SHIFT: return Binary(left, left, right, BinaryType.LEFT_SHIFT) elif t == AssignmentOperationType.ASSIGN_RIGHT_SHIFT: return Binary(left, left, right, BinaryType.RIGHT_SHIFT) elif t == AssignmentOperationType.ASSIGN_ADDITION: return Binary(left, left, right, BinaryType.ADDITION) elif t == AssignmentOperationType.ASSIGN_SUBTRACTION: return Binary(left, left, right, BinaryType.SUBTRACTION) elif t == AssignmentOperationType.ASSIGN_MULTIPLICATION: return Binary(left, left, right, BinaryType.MULTIPLICATION) elif t == AssignmentOperationType.ASSIGN_DIVISION: return Binary(left, left, right, BinaryType.DIVISION) elif t == AssignmentOperationType.ASSIGN_MODULO: return Binary(left, left, right, BinaryType.MODULO) raise SlithIRError('Missing type during assignment conversion')
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 copy_ir(ir, local_variables_instances, state_variables_instances, temporary_variables_instances, reference_variables_instances, all_local_variables_instances): ''' Args: ir (Operation) local_variables_instances(dict(str -> LocalVariable)) state_variables_instances(dict(str -> StateVariable)) temporary_variables_instances(dict(int -> Variable)) reference_variables_instances(dict(int -> Variable)) Note: temporary and reference can be indexed by int, as they dont need phi functions ''' def get(variable): if isinstance(variable, LocalVariable): if variable.name in local_variables_instances: return local_variables_instances[variable.name] new_var = LocalIRVariable(variable) local_variables_instances[variable.name] = new_var all_local_variables_instances[variable.name] = new_var return new_var if isinstance( variable, StateVariable ) and variable.canonical_name in state_variables_instances: return state_variables_instances[variable.canonical_name] elif isinstance(variable, ReferenceVariable): if not variable.index in reference_variables_instances: new_variable = ReferenceVariable(variable.node, index=variable.index) if variable.points_to: new_variable.points_to = get(variable.points_to) new_variable.set_type(variable.type) reference_variables_instances[variable.index] = new_variable return reference_variables_instances[variable.index] elif isinstance(variable, TemporaryVariable): if not variable.index in temporary_variables_instances: new_variable = TemporaryVariable(variable.node, index=variable.index) new_variable.set_type(variable.type) temporary_variables_instances[variable.index] = new_variable return temporary_variables_instances[variable.index] return variable def get_variable(ir, f): variable = f(ir) variable = get(variable) return variable def get_arguments(ir): arguments = [] for arg in ir.arguments: arg = get(arg) arguments.append(arg) return arguments def get_rec_values(ir, f): # Use by InitArray and NewArray # Potential recursive array(s) ori_init_values = f(ir) def traversal(values): ret = [] for v in values: if isinstance(v, list): v = traversal(v) else: v = get(v) ret.append(v) return ret return traversal(ori_init_values) if isinstance(ir, Assignment): lvalue = get_variable(ir, lambda x: ir.lvalue) rvalue = get_variable(ir, lambda x: ir.rvalue) variable_return_type = ir.variable_return_type return Assignment(lvalue, rvalue, variable_return_type) elif isinstance(ir, Balance): lvalue = get_variable(ir, lambda x: ir.lvalue) value = get_variable(ir, lambda x: ir.value) return Balance(value, lvalue) elif isinstance(ir, Binary): lvalue = get_variable(ir, lambda x: ir.lvalue) variable_left = get_variable(ir, lambda x: ir.variable_left) variable_right = get_variable(ir, lambda x: ir.variable_right) operation_type = ir.type return Binary(lvalue, variable_left, variable_right, operation_type) elif isinstance(ir, Condition): val = get_variable(ir, lambda x: ir.value) return Condition(val) elif isinstance(ir, Delete): lvalue = get_variable(ir, lambda x: ir.lvalue) variable = get_variable(ir, lambda x: ir.variable) return Delete(lvalue, variable) elif isinstance(ir, EventCall): name = ir.name return EventCall(name) elif isinstance(ir, HighLevelCall): # include LibraryCall destination = get_variable(ir, lambda x: ir.destination) function_name = ir.function_name nbr_arguments = ir.nbr_arguments lvalue = get_variable(ir, lambda x: ir.lvalue) type_call = ir.type_call if isinstance(ir, LibraryCall): new_ir = LibraryCall(destination, function_name, nbr_arguments, lvalue, type_call) else: new_ir = HighLevelCall(destination, function_name, nbr_arguments, lvalue, type_call) new_ir.call_id = ir.call_id new_ir.call_value = get_variable(ir, lambda x: ir.call_value) new_ir.call_gas = get_variable(ir, lambda x: ir.call_gas) new_ir.arguments = get_arguments(ir) new_ir.function = ir.function return new_ir elif isinstance(ir, Index): lvalue = get_variable(ir, lambda x: ir.lvalue) variable_left = get_variable(ir, lambda x: ir.variable_left) variable_right = get_variable(ir, lambda x: ir.variable_right) index_type = ir.index_type return Index(lvalue, variable_left, variable_right, index_type) elif isinstance(ir, InitArray): lvalue = get_variable(ir, lambda x: ir.lvalue) init_values = get_rec_values(ir, lambda x: ir.init_values) return InitArray(init_values, lvalue) elif isinstance(ir, InternalCall): function = ir.function nbr_arguments = ir.nbr_arguments lvalue = get_variable(ir, lambda x: ir.lvalue) type_call = ir.type_call new_ir = InternalCall(function, nbr_arguments, lvalue, type_call) new_ir.arguments = get_arguments(ir) return new_ir elif isinstance(ir, InternalDynamicCall): lvalue = get_variable(ir, lambda x: ir.lvalue) function = ir.function function_type = ir.function_type new_ir = InternalDynamicCall(lvalue, function, function_type) new_ir.arguments = get_arguments(ir) return new_ir elif isinstance(ir, LowLevelCall): destination = get_variable(ir, lambda x: x.destination) function_name = ir.function_name nbr_arguments = ir.nbr_arguments lvalue = get_variable(ir, lambda x: ir.lvalue) type_call = ir.type_call new_ir = LowLevelCall(destination, function_name, nbr_arguments, lvalue, type_call) new_ir.call_id = ir.call_id new_ir.call_value = get_variable(ir, lambda x: ir.call_value) new_ir.call_gas = get_variable(ir, lambda x: ir.call_gas) new_ir.arguments = get_arguments(ir) return new_ir elif isinstance(ir, Member): lvalue = get_variable(ir, lambda x: ir.lvalue) variable_left = get_variable(ir, lambda x: ir.variable_left) variable_right = get_variable(ir, lambda x: ir.variable_right) return Member(variable_left, variable_right, lvalue) elif isinstance(ir, NewArray): depth = ir.depth array_type = ir.array_type lvalue = get_variable(ir, lambda x: ir.lvalue) new_ir = NewArray(depth, array_type, lvalue) new_ir.arguments = get_rec_values(ir, lambda x: ir.arguments) return new_ir elif isinstance(ir, NewElementaryType): new_type = ir.type lvalue = get_variable(ir, lambda x: ir.lvalue) new_ir = NewElementaryType(new_type, lvalue) new_ir.arguments = get_arguments(ir) return new_ir elif isinstance(ir, NewContract): contract_name = ir.contract_name lvalue = get_variable(ir, lambda x: ir.lvalue) new_ir = NewContract(contract_name, lvalue) new_ir.arguments = get_arguments(ir) return new_ir elif isinstance(ir, NewStructure): structure = ir.structure lvalue = get_variable(ir, lambda x: ir.lvalue) new_ir = NewStructure(structure, lvalue) new_ir.arguments = get_arguments(ir) return new_ir elif isinstance(ir, Push): array = get_variable(ir, lambda x: ir.array) lvalue = get_variable(ir, lambda x: ir.lvalue) return Push(array, lvalue) elif isinstance(ir, Return): value = [get_variable(x, lambda y: y) for x in ir.values] return Return(value) elif isinstance(ir, Send): destination = get_variable(ir, lambda x: ir.destination) value = get_variable(ir, lambda x: ir.call_value) lvalue = get_variable(ir, lambda x: ir.lvalue) return Send(destination, value, lvalue) elif isinstance(ir, SolidityCall): function = ir.function nbr_arguments = ir.nbr_arguments lvalue = get_variable(ir, lambda x: ir.lvalue) type_call = ir.type_call new_ir = SolidityCall(function, nbr_arguments, lvalue, type_call) new_ir.arguments = get_arguments(ir) return new_ir elif isinstance(ir, Transfer): destination = get_variable(ir, lambda x: ir.destination) value = get_variable(ir, lambda x: ir.call_value) return Transfer(destination, value) elif isinstance(ir, TypeConversion): lvalue = get_variable(ir, lambda x: ir.lvalue) variable = get_variable(ir, lambda x: ir.variable) variable_type = ir.type return TypeConversion(lvalue, variable, variable_type) elif isinstance(ir, Unary): lvalue = get_variable(ir, lambda x: ir.lvalue) rvalue = get_variable(ir, lambda x: ir.rvalue) operation_type = ir.type return Unary(lvalue, rvalue, operation_type) elif isinstance(ir, Unpack): lvalue = get_variable(ir, lambda x: ir.lvalue) tuple_var = ir.tuple idx = ir.index return Unpack(lvalue, tuple_var, idx) elif isinstance(ir, Length): lvalue = get_variable(ir, lambda x: ir.lvalue) value = get_variable(ir, lambda x: ir.value) return Length(value, lvalue) logger.error('Impossible ir copy on {} ({})'.format(ir, type(ir))) exit(-1)
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)))
def copy_ir(ir, *instances): ''' Args: ir (Operation) local_variables_instances(dict(str -> LocalVariable)) state_variables_instances(dict(str -> StateVariable)) temporary_variables_instances(dict(int -> Variable)) reference_variables_instances(dict(int -> Variable)) Note: temporary and reference can be indexed by int, as they dont need phi functions ''' if isinstance(ir, Assignment): lvalue = get_variable(ir, lambda x: x.lvalue, *instances) rvalue = get_variable(ir, lambda x: x.rvalue, *instances) variable_return_type = ir.variable_return_type return Assignment(lvalue, rvalue, variable_return_type) elif isinstance(ir, Balance): lvalue = get_variable(ir, lambda x: x.lvalue, *instances) value = get_variable(ir, lambda x: x.value, *instances) return Balance(value, lvalue) elif isinstance(ir, Binary): lvalue = get_variable(ir, lambda x: x.lvalue, *instances) variable_left = get_variable(ir, lambda x: x.variable_left, *instances) variable_right = get_variable(ir, lambda x: x.variable_right, *instances) operation_type = ir.type return Binary(lvalue, variable_left, variable_right, operation_type) elif isinstance(ir, Condition): val = get_variable(ir, lambda x: x.value, *instances) return Condition(val) elif isinstance(ir, Delete): lvalue = get_variable(ir, lambda x: x.lvalue, *instances) variable = get_variable(ir, lambda x: x.variable, *instances) return Delete(lvalue, variable) elif isinstance(ir, EventCall): name = ir.name return EventCall(name) elif isinstance(ir, HighLevelCall): # include LibraryCall destination = get_variable(ir, lambda x: x.destination, *instances) function_name = ir.function_name nbr_arguments = ir.nbr_arguments lvalue = get_variable(ir, lambda x: x.lvalue, *instances) type_call = ir.type_call if isinstance(ir, LibraryCall): new_ir = LibraryCall(destination, function_name, nbr_arguments, lvalue, type_call) else: new_ir = HighLevelCall(destination, function_name, nbr_arguments, lvalue, type_call) new_ir.call_id = ir.call_id new_ir.call_value = get_variable(ir, lambda x: x.call_value, *instances) new_ir.call_gas = get_variable(ir, lambda x: x.call_gas, *instances) new_ir.arguments = get_arguments(ir, *instances) new_ir.function = ir.function return new_ir elif isinstance(ir, Index): lvalue = get_variable(ir, lambda x: x.lvalue, *instances) variable_left = get_variable(ir, lambda x: x.variable_left, *instances) variable_right = get_variable(ir, lambda x: x.variable_right, *instances) index_type = ir.index_type return Index(lvalue, variable_left, variable_right, index_type) elif isinstance(ir, InitArray): lvalue = get_variable(ir, lambda x: x.lvalue, *instances) init_values = get_rec_values(ir, lambda x: x.init_values, *instances) return InitArray(init_values, lvalue) elif isinstance(ir, InternalCall): function = ir.function nbr_arguments = ir.nbr_arguments lvalue = get_variable(ir, lambda x: x.lvalue, *instances) type_call = ir.type_call new_ir = InternalCall(function, nbr_arguments, lvalue, type_call) new_ir.arguments = get_arguments(ir, *instances) return new_ir elif isinstance(ir, InternalDynamicCall): lvalue = get_variable(ir, lambda x: x.lvalue, *instances) function = get_variable(ir, lambda x: x.function, *instances) function_type = ir.function_type new_ir = InternalDynamicCall(lvalue, function, function_type) new_ir.arguments = get_arguments(ir, *instances) return new_ir elif isinstance(ir, LowLevelCall): destination = get_variable(ir, lambda x: x.destination, *instances) function_name = ir.function_name nbr_arguments = ir.nbr_arguments lvalue = get_variable(ir, lambda x: x.lvalue, *instances) type_call = ir.type_call new_ir = LowLevelCall(destination, function_name, nbr_arguments, lvalue, type_call) new_ir.call_id = ir.call_id new_ir.call_value = get_variable(ir, lambda x: x.call_value, *instances) new_ir.call_gas = get_variable(ir, lambda x: x.call_gas, *instances) new_ir.arguments = get_arguments(ir, *instances) return new_ir elif isinstance(ir, Member): lvalue = get_variable(ir, lambda x: x.lvalue, *instances) variable_left = get_variable(ir, lambda x: x.variable_left, *instances) variable_right = get_variable(ir, lambda x: x.variable_right, *instances) return Member(variable_left, variable_right, lvalue) elif isinstance(ir, NewArray): depth = ir.depth array_type = ir.array_type lvalue = get_variable(ir, lambda x: x.lvalue, *instances) new_ir = NewArray(depth, array_type, lvalue) new_ir.arguments = get_rec_values(ir, lambda x: x.arguments, *instances) return new_ir elif isinstance(ir, NewElementaryType): new_type = ir.type lvalue = get_variable(ir, lambda x: x.lvalue, *instances) new_ir = NewElementaryType(new_type, lvalue) new_ir.arguments = get_arguments(ir, *instances) return new_ir elif isinstance(ir, NewContract): contract_name = ir.contract_name lvalue = get_variable(ir, lambda x: x.lvalue, *instances) new_ir = NewContract(contract_name, lvalue) new_ir.arguments = get_arguments(ir, *instances) return new_ir elif isinstance(ir, NewStructure): structure = ir.structure lvalue = get_variable(ir, lambda x: x.lvalue, *instances) new_ir = NewStructure(structure, lvalue) new_ir.arguments = get_arguments(ir, *instances) return new_ir elif isinstance(ir, Push): array = get_variable(ir, lambda x: x.array, *instances) lvalue = get_variable(ir, lambda x: x.lvalue, *instances) return Push(array, lvalue) elif isinstance(ir, Return): values = get_rec_values(ir, lambda x: x.values, *instances) return Return(values) elif isinstance(ir, Send): destination = get_variable(ir, lambda x: x.destination, *instances) value = get_variable(ir, lambda x: x.call_value, *instances) lvalue = get_variable(ir, lambda x: x.lvalue, *instances) return Send(destination, value, lvalue) elif isinstance(ir, SolidityCall): function = ir.function nbr_arguments = ir.nbr_arguments lvalue = get_variable(ir, lambda x: x.lvalue, *instances) type_call = ir.type_call new_ir = SolidityCall(function, nbr_arguments, lvalue, type_call) new_ir.arguments = get_arguments(ir, *instances) return new_ir elif isinstance(ir, Transfer): destination = get_variable(ir, lambda x: x.destination, *instances) value = get_variable(ir, lambda x: x.call_value, *instances) return Transfer(destination, value) elif isinstance(ir, TypeConversion): lvalue = get_variable(ir, lambda x: x.lvalue, *instances) variable = get_variable(ir, lambda x: x.variable, *instances) variable_type = ir.type return TypeConversion(lvalue, variable, variable_type) elif isinstance(ir, Unary): lvalue = get_variable(ir, lambda x: x.lvalue, *instances) rvalue = get_variable(ir, lambda x: x.rvalue, *instances) operation_type = ir.type return Unary(lvalue, rvalue, operation_type) elif isinstance(ir, Unpack): lvalue = get_variable(ir, lambda x: x.lvalue, *instances) tuple_var = get_variable(ir, lambda x: x.tuple, *instances) idx = ir.index return Unpack(lvalue, tuple_var, idx) elif isinstance(ir, Length): lvalue = get_variable(ir, lambda x: x.lvalue, *instances) value = get_variable(ir, lambda x: x.value, *instances) return Length(value, lvalue) logger.error('Impossible ir copy on {} ({})'.format(ir, type(ir))) exit(-1)
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)