def taint_balance_equalities(self, functions): taints = [] for func in functions: for node in func.nodes: for ir in node.irs_ssa: if isinstance(ir, Balance): taints.append(ir.lvalue) if isinstance(ir, HighLevelCall): # print(ir.function.full_name) if (isinstance(ir.function, Function) and ir.function.full_name == "balanceOf(address)"): taints.append(ir.lvalue) if (isinstance(ir.function, StateVariable) and isinstance(ir.function.type, MappingType) and ir.function.name == "balanceOf" and ir.function.type.type_from == ElementaryType("address") and ir.function.type.type_to == ElementaryType("uint256")): taints.append(ir.lvalue) if isinstance(ir, Assignment): if ir.rvalue in self.sources_taint: taints.append(ir.lvalue) return taints
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 Must be called after can_be_low_level """ if ir.function_name == 'transfer': assert len(ir.arguments) == 1 prev_ir = ir ir = Transfer(ir.destination, ir.arguments[0]) ir.set_expression(prev_ir.expression) return ir elif ir.function_name == 'send': assert len(ir.arguments) == 1 prev_ir = ir ir = Send(ir.destination, ir.arguments[0], ir.lvalue) ir.set_expression(prev_ir.expression) ir.lvalue.set_type(ElementaryType('bool')) return 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')) new_ir.set_expression(ir.expression) return new_ir raise SlithIRError('Incorrect conversion to low level {}'.format(ir))
def _post_binary_operation(self, expression): left = get(expression.expression_left) right = get(expression.expression_right) val = TemporaryVariable(self._node) if expression.type in _signed_to_unsigned: new_left = TemporaryVariable(self._node) conv_left = TypeConversion(new_left, left, ElementaryType('int256')) conv_left.set_expression(expression) self._result.append(conv_left) if expression.type != BinaryOperationType.RIGHT_SHIFT_ARITHMETIC: new_right = TemporaryVariable(self._node) conv_right = TypeConversion(new_right, right, ElementaryType('int256')) conv_right.set_expression(expression) self._result.append(conv_right) else: new_right = right new_final = TemporaryVariable(self._node) operation = Binary(new_final, new_left, new_right, _signed_to_unsigned[expression.type]) operation.set_expression(expression) self._result.append(operation) conv_final = TypeConversion(val, new_final, ElementaryType('uint256')) conv_final.set_expression(expression) self._result.append(conv_final) else: operation = Binary(val, left, right, _binary_to_binary[expression.type]) operation.set_expression(expression) self._result.append(operation) set_val(expression, val)
def convert_arguments(arguments): argss = [[]] for arg in 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 argss
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.set_expression(ir.expression) assignment.set_node(ir.node) 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.set_expression(ir.expression) assignment.set_node(ir.node) assignment.lvalue.set_type(ElementaryType('bytes')) return assignment if ir.variable_right == 'interfaceId': entry_points = contract.functions_entry_points interfaceId = 0 for entry_point in entry_points: interfaceId = interfaceId ^ get_function_id(entry_point.full_name) assignment = Assignment( ir.lvalue, Constant(str(interfaceId), type=ElementaryType('bytes4')), ElementaryType('bytes4')) assignment.set_expression(ir.expression) assignment.set_node(ir.node) assignment.lvalue.set_type(ElementaryType('bytes4')) return assignment if ir.variable_right == 'name': assignment = Assignment(ir.lvalue, Constant(contract.name), ElementaryType('string')) assignment.set_expression(ir.expression) assignment.set_node(ir.node) assignment.lvalue.set_type(ElementaryType('string')) return assignment raise SlithIRError(f'type({contract.name}).{ir.variable_right} is unknown')
def is_upgradable_gap_variable(contract: Contract, variable: StateVariable) -> bool: """Helper function that returns true if 'variable' is a gap variable used for upgradable contracts. More specifically, the function returns true if: - variable is named "__gap" - it is a uint256 array declared at the end of the contract - it has private visibility""" # Return early on if the variable name is != gap to avoid iterating over all the state variables if variable.name != "__gap": return False declared_variable_ordered = [ v for v in contract.state_variables_ordered if v in contract.state_variables_declared ] if not declared_variable_ordered: return False variable_type = variable.type return (declared_variable_ordered[-1] is variable and isinstance(variable_type, ArrayType) and variable_type.type == ElementaryType("uint256") and variable.visibility == "private")
def __init__(self, value, lvalue): super().__init__() assert is_valid_rvalue(value) assert is_valid_lvalue(lvalue) self._value = value self._lvalue = lvalue lvalue.set_type(ElementaryType("uint256"))
def __init__(self, name: str): assert name in SOLIDITY_FUNCTIONS self._name = name # Can be TypeInformation if type(address) is used self._return_type: List[Union[TypeInformation, ElementaryType]] = [ ElementaryType(x) for x in SOLIDITY_FUNCTIONS[self.name] ]
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 __init__(self, import_directive: Import): super().__init__() assert import_directive.alias is not None self._import_directive = import_directive self._name = import_directive.alias self._type = ElementaryType("string") self._initialized = True self._visibility = "private" self._is_constant = True
def parse_yul_literal(root: YulScope, node: YulNode, ast: Dict) -> Optional[Expression]: type_ = ast['type'] value = ast['value'] if not type_: type_ = 'bool' if value in ['true', 'false'] else 'uint256' return Literal(value, ElementaryType(type_))
def parse_yul_function_call(root: YulScope, node: YulNode, ast: Dict) -> Optional[Expression]: args = [parse_yul(root, node, arg) for arg in ast["arguments"]] ident = parse_yul(root, node, ast["functionName"]) if not isinstance(ident, Identifier): raise SlitherException( "expected identifier from parsing function name") if isinstance(ident.value, YulBuiltin): name = ident.value.name if name in binary_ops: if name in ["shl", "shr", "sar"]: # lmao ok return BinaryOperation(args[1], args[0], binary_ops[name]) return BinaryOperation(args[0], args[1], binary_ops[name]) if name in unary_ops: return UnaryOperation(args[0], unary_ops[name]) if name == "stop": name = "return" ident = Identifier( SolidityFunction(format_function_descriptor(name))) args = [ Literal("0", ElementaryType("uint256")), Literal("0", ElementaryType("uint256")), ] else: ident = Identifier( SolidityFunction(format_function_descriptor(ident.value.name))) if isinstance(ident.value, Function): return CallExpression(ident, args, vars_to_typestr(ident.value.returns)) if isinstance(ident.value, SolidityFunction): return CallExpression(ident, args, vars_to_typestr(ident.value.return_type)) raise SlitherException( f"unexpected function call target type {str(type(ident.value))}")
def parse_yul_literal(_root: YulScope, _node: YulNode, ast: Dict) -> Optional[Expression]: kind = ast["kind"] value = ast["value"] if not kind: kind = "bool" if value in ["true", "false"] else "uint256" if kind == "number": kind = "uint256" return Literal(value, ElementaryType(kind))
def __init__(self, result, left_variable, right_variable, operation_type): assert is_valid_rvalue(left_variable) assert is_valid_rvalue(right_variable) assert is_valid_lvalue(result) super(Binary, self).__init__() self._variables = [left_variable, right_variable] self._type = operation_type self._lvalue = result if BinaryType.return_bool(operation_type): result.set_type(ElementaryType('bool')) else: result.set_type(left_variable.type)
def __init__(self, result, left_variable, right_variable, operation_type: BinaryType): assert is_valid_rvalue(left_variable) or isinstance(left_variable, Function) assert is_valid_rvalue(right_variable) or isinstance(right_variable, Function) assert is_valid_lvalue(result) assert isinstance(operation_type, BinaryType) super().__init__() self._variables = [left_variable, right_variable] self._type = operation_type self._lvalue = result if BinaryType.return_bool(operation_type): result.set_type(ElementaryType("bool")) else: result.set_type(left_variable.type)
def __init__(self, var: LocalVariable, root: YulScope, ast: Dict): assert ast["nodeType"] == "YulTypedName" self._variable = var self._root = root # start initializing the underlying variable var.set_function(root.function) var.set_offset(ast["src"], root.slither) var.name = _name_to_yul_name(ast["name"], root.id) var.set_type(ElementaryType("uint256")) var.set_location("memory")
def __init__(self, var: LocalVariable, root: YulScope, ast: Dict): assert (ast['nodeType'] == 'YulTypedName') self._variable = var self._root = root # start initializing the underlying variable var.set_function(root.function) var.set_offset(ast["src"], root.slither) var.name = ast['name'] var.set_type(ElementaryType('uint256')) var.set_location('memory')
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 _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 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 is_conditional(self, include_loop=True) -> bool: """ Check if the node is a conditional node A conditional node is either a IF or a require/assert or a RETURN bool Returns: bool: True if the node is a conditional node """ if self.contains_if(include_loop) or self.contains_require_or_assert(): return True if self.irs: last_ir = self.irs[-1] if last_ir: if isinstance(last_ir, Return): for r in last_ir.read: if r.type == ElementaryType("bool"): return True return False
def _parse_type_alias(self, item: Dict) -> None: assert "name" in item assert "underlyingType" in item underlying_type = item["underlyingType"] assert "nodeType" in underlying_type and underlying_type["nodeType"] == "ElementaryTypeName" assert "name" in underlying_type original_type = ElementaryType(underlying_type["name"]) # For user defined types defined at the contract level the lookup can be done # Using the name or the canonical name # For example during the type parsing the canonical name # Note that Solidity allows shadowing of user defined types # Between top level and contract definitions alias = item["name"] alias_canonical = self._contract.name + "." + item["name"] user_defined_type = TypeAliasContract(original_type, alias, self.underlying_contract) user_defined_type.set_offset(item["src"], self.compilation_unit) self._contract.file_scope.user_defined_types[alias] = user_defined_type self._contract.file_scope.user_defined_types[alias_canonical] = user_defined_type
def __init__(self, value, lvalue): assert is_valid_rvalue(value) assert is_valid_lvalue(lvalue) self._value = value self._lvalue = lvalue lvalue.set_type(ElementaryType('uint256'))
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 type(self) -> ElementaryType: return ElementaryType(SOLIDITY_VARIABLES_COMPOSED[self.name])
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 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 type(self): return ElementaryType(SOLIDITY_VARIABLES[self.name])
def return_type(self): return [ElementaryType(x) for x in SOLIDITY_FUNCTIONS[self.name]]
def parse_expression(expression, caller_context): """ Returns: str: expression """ # Expression # = Expression ('++' | '--') # | NewExpression # | IndexAccess # | MemberAccess # | FunctionCall # | '(' Expression ')' # | ('!' | '~' | 'delete' | '++' | '--' | '+' | '-') Expression # | Expression '**' Expression # | Expression ('*' | '/' | '%') Expression # | Expression ('+' | '-') Expression # | Expression ('<<' | '>>') Expression # | Expression '&' Expression # | Expression '^' Expression # | Expression '|' Expression # | Expression ('<' | '>' | '<=' | '>=') Expression # | Expression ('==' | '!=') Expression # | Expression '&&' Expression # | Expression '||' Expression # | Expression '?' Expression ':' Expression # | Expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') Expression # | PrimaryExpression # The AST naming does not follow the spec name = expression[caller_context.get_key()] is_compact_ast = caller_context.is_compact_ast if name == 'UnaryOperation': if is_compact_ast: attributes = expression else: attributes = expression['attributes'] assert 'prefix' in attributes operation_type = UnaryOperationType.get_type(attributes['operator'], attributes['prefix']) if is_compact_ast: expression = parse_expression(expression['subExpression'], caller_context) else: assert len(expression['children']) == 1 expression = parse_expression(expression['children'][0], caller_context) unary_op = UnaryOperation(expression, operation_type) return unary_op elif name == 'BinaryOperation': if is_compact_ast: attributes = expression else: attributes = expression['attributes'] operation_type = BinaryOperationType.get_type(attributes['operator']) if is_compact_ast: left_expression = parse_expression(expression['leftExpression'], caller_context) right_expression = parse_expression(expression['rightExpression'], caller_context) else: assert len(expression['children']) == 2 left_expression = parse_expression(expression['children'][0], caller_context) right_expression = parse_expression(expression['children'][1], caller_context) binary_op = BinaryOperation(left_expression, right_expression, operation_type) return binary_op elif name == 'FunctionCall': return parse_call(expression, caller_context) elif name == 'TupleExpression': """ For expression like (a,,c) = (1,2,3) the AST provides only two children in the left side We check the type provided (tuple(uint256,,uint256)) To determine that there is an empty variable Otherwhise we would not be able to determine that a = 1, c = 3, and 2 is lost Note: this is only possible with Solidity >= 0.4.12 """ if is_compact_ast: expressions = [ parse_expression(e, caller_context) if e else None for e in expression['components'] ] else: if 'children' not in expression: attributes = expression['attributes'] components = attributes['components'] expressions = [ parse_expression(c, caller_context) if c else None for c in components ] else: expressions = [ parse_expression(e, caller_context) for e in expression['children'] ] # Add none for empty tuple items if "attributes" in expression: if "type" in expression['attributes']: t = expression['attributes']['type'] if ',,' in t or '(,' in t or ',)' in t: t = t[len('tuple('):-1] elems = t.split(',') for idx in range(len(elems)): if elems[idx] == '': expressions.insert(idx, None) t = TupleExpression(expressions) return t elif name == 'Conditional': if is_compact_ast: if_expression = parse_expression(expression['condition'], caller_context) then_expression = parse_expression(expression['trueExpression'], caller_context) else_expression = parse_expression(expression['falseExpression'], caller_context) else: children = expression['children'] assert len(children) == 3 if_expression = parse_expression(children[0], caller_context) then_expression = parse_expression(children[1], caller_context) else_expression = parse_expression(children[2], caller_context) conditional = ConditionalExpression(if_expression, then_expression, else_expression) return conditional elif name == 'Assignment': if is_compact_ast: left_expression = parse_expression(expression['leftHandSide'], caller_context) right_expression = parse_expression(expression['rightHandSide'], caller_context) operation_type = AssignmentOperationType.get_type( expression['operator']) operation_return_type = expression['typeDescriptions'][ 'typeString'] else: attributes = expression['attributes'] children = expression['children'] assert len(expression['children']) == 2 left_expression = parse_expression(children[0], caller_context) right_expression = parse_expression(children[1], caller_context) operation_type = AssignmentOperationType.get_type( attributes['operator']) operation_return_type = attributes['type'] assignement = AssignmentOperation(left_expression, right_expression, operation_type, operation_return_type) return assignement elif name == 'Literal': assert 'children' not in expression if is_compact_ast: value = expression['value'] if not value: value = '0x' + expression['hexValue'] else: value = expression['attributes']['value'] if value is None: # for literal declared as hex # see https://solidity.readthedocs.io/en/v0.4.25/types.html?highlight=hex#hexadecimal-literals assert 'hexvalue' in expression['attributes'] value = '0x' + expression['attributes']['hexvalue'] literal = Literal(value) return literal elif name == 'Identifier': assert 'children' not in expression t = None if caller_context.is_compact_ast: value = expression['name'] t = expression['typeDescriptions']['typeString'] else: value = expression['attributes']['value'] if 'type' in expression['attributes']: t = expression['attributes']['type'] if t: found = re.findall( '[struct|enum|function|modifier] \(([\[\] ()a-zA-Z0-9\.,_]*)\)', t) assert len(found) <= 1 if found: value = value + '(' + found[0] + ')' value = filter_name(value) var = find_variable(value, caller_context) identifier = Identifier(var) return identifier elif name == 'IndexAccess': if is_compact_ast: index_type = expression['typeDescriptions']['typeString'] left = expression['baseExpression'] right = expression['indexExpression'] else: index_type = expression['attributes']['type'] children = expression['children'] assert len(children) == 2 left = children[0] right = children[1] left_expression = parse_expression(left, caller_context) right_expression = parse_expression(right, caller_context) index = IndexAccess(left_expression, right_expression, index_type) return index elif name == 'MemberAccess': if caller_context.is_compact_ast: member_name = expression['memberName'] member_type = expression['typeDescriptions']['typeString'] member_expression = parse_expression(expression['expression'], caller_context) else: member_name = expression['attributes']['member_name'] member_type = expression['attributes']['type'] children = expression['children'] assert len(children) == 1 member_expression = parse_expression(children[0], caller_context) if str(member_expression) == 'super': super_name = parse_super_name(expression, is_compact_ast) if isinstance(caller_context, Contract): inheritance = caller_context.inheritance else: assert isinstance(caller_context, Function) inheritance = caller_context.contract.inheritance var = None for father in inheritance: try: var = find_variable(super_name, father) break except VariableNotFound: continue if var is None: raise VariableNotFound( 'Variable not found: {}'.format(super_name)) return SuperIdentifier(var) member_access = MemberAccess(member_name, member_type, member_expression) if str(member_access) in SOLIDITY_VARIABLES_COMPOSED: return Identifier(SolidityVariableComposed(str(member_access))) return member_access elif name == 'ElementaryTypeNameExpression': # nop exression # uint; if is_compact_ast: value = expression['typeName'] else: assert 'children' not in expression value = expression['attributes']['value'] t = parse_type(UnknownType(value), caller_context) return ElementaryTypeNameExpression(t) # NewExpression is not a root expression, it's always the child of another expression elif name == 'NewExpression': if is_compact_ast: type_name = expression['typeName'] else: children = expression['children'] assert len(children) == 1 type_name = children[0] if type_name[caller_context.get_key()] == 'ArrayTypeName': depth = 0 while type_name[caller_context.get_key()] == 'ArrayTypeName': # Note: dont conserve the size of the array if provided # We compute it directly if is_compact_ast: type_name = type_name['baseType'] else: type_name = type_name['children'][0] depth += 1 if type_name[caller_context.get_key()] == 'ElementaryTypeName': if is_compact_ast: array_type = ElementaryType(type_name['name']) else: array_type = ElementaryType( type_name['attributes']['name']) elif type_name[caller_context.get_key()] == 'UserDefinedTypeName': if is_compact_ast: array_type = parse_type(UnknownType(type_name['name']), caller_context) else: array_type = parse_type( UnknownType(type_name['attributes']['name']), caller_context) else: logger.error('Incorrect type array {}'.format(type_name)) exit(-1) array = NewArray(depth, array_type) return array if type_name[caller_context.get_key()] == 'ElementaryTypeName': if is_compact_ast: elem_type = ElementaryType(type_name['name']) else: elem_type = ElementaryType(type_name['attributes']['name']) new_elem = NewElementaryType(elem_type) return new_elem assert type_name[caller_context.get_key()] == 'UserDefinedTypeName' if is_compact_ast: contract_name = type_name['name'] else: contract_name = type_name['attributes']['name'] new = NewContract(contract_name) return new elif name == 'ModifierInvocation': if is_compact_ast: called = parse_expression(expression['modifierName'], caller_context) arguments = [] if expression['arguments']: arguments = [ parse_expression(a, caller_context) for a in expression['arguments'] ] else: children = expression['children'] called = parse_expression(children[0], caller_context) arguments = [ parse_expression(a, caller_context) for a in children[1::] ] call = CallExpression(called, arguments, 'Modifier') return call logger.error('Expression not parsed %s' % name) exit(-1)