def _check(self): signatures_implem = get_signatures(self.contract) signatures_proxy = get_signatures(self.proxy) signatures_ids_implem = {get_function_id(s): s for s in signatures_implem} signatures_ids_proxy = {get_function_id(s): s for s in signatures_proxy} results = [] for (k, _) in signatures_ids_implem.items(): if k in signatures_ids_proxy: if signatures_ids_implem[k] != signatures_ids_proxy[k]: implem_function = _get_function_or_variable( self.contract, signatures_ids_implem[k] ) proxy_function = _get_function_or_variable(self.proxy, signatures_ids_proxy[k]) info = [ "Function id collision found: ", implem_function, " ", proxy_function, "\n", ] json = self.generate_result(info) results.append(json) return results
def compare_function_ids(implem, implem_name, proxy, proxy_name): implem_contract = implem.get_contract_from_name(implem_name) if implem_contract is None: logger.info(red(f'{implem_name} not found in {implem.filename}')) return proxy_contract = proxy.get_contract_from_name(proxy_name) if proxy_contract is None: logger.info(red(f'{proxy_name} not found in {proxy.filename}')) return signatures_implem = get_signatures(implem_contract) signatures_proxy = get_signatures(proxy_contract) signatures_ids_implem = {get_function_id(s): s for s in signatures_implem} signatures_ids_proxy = {get_function_id(s): s for s in signatures_proxy} found = False for (k, _) in signatures_ids_implem.items(): if k in signatures_ids_proxy: found = True logger.info( red('Function id collision found {} {}'.format( signatures_ids_implem[k], signatures_ids_proxy[k]))) if not found: logger.info(green('No function id collision found'))
def compare_function_ids(implem, implem_name, proxy, proxy_name): logger.info(green('Run function ids checks... (see https://github.com/crytic/slither/wiki/Upgradeability-Checks#functions-ids-checks)')) implem_contract = implem.get_contract_from_name(implem_name) if implem_contract is None: logger.info(red(f'{implem_name} not found in {implem.filename}')) return proxy_contract = proxy.get_contract_from_name(proxy_name) if proxy_contract is None: logger.info(red(f'{proxy_name} not found in {proxy.filename}')) return signatures_implem = get_signatures(implem_contract) signatures_proxy = get_signatures(proxy_contract) signatures_ids_implem = {get_function_id(s): s for s in signatures_implem} signatures_ids_proxy = {get_function_id(s): s for s in signatures_proxy} found = False for (k, _) in signatures_ids_implem.items(): if k in signatures_ids_proxy: found = True if signatures_ids_implem[k] != signatures_ids_proxy[k]: logger.info(red('Function id collision found {} {}'.format(signatures_ids_implem[k], signatures_ids_proxy[k]))) else: logger.info(red('Shadowing between proxy and implementation found {}'.format(signatures_ids_implem[k]))) if not found: logger.info(green('No function ids collision found'))
def output(self, _filename): """ _filename is not used Args: _filename(string) """ txt = '' all_tables = [] for contract in self.slither.contracts_derived: txt += '\n{}:\n'.format(contract.name) table = MyPrettyTable(['Name', 'ID']) for function in contract.functions: if function.visibility in ['public', 'external']: table.add_row([ function.solidity_signature, hex(get_function_id(function.solidity_signature)) ]) for variable in contract.state_variables: if variable.visibility in ['public']: sig = variable.function_name table.add_row([sig, hex(get_function_id(sig))]) txt += str(table) + '\n' all_tables.append((contract.name, table)) self.info(txt) res = self.generate_output(txt) for name, table in all_tables: res.add_pretty_table(table, name) return res
def output(self, _filename): """ _filename is not used Args: _filename(string) """ txt = "" all_tables = [] for contract in self.slither.contracts_derived: txt += f"\n{contract.name}:\n" table = MyPrettyTable(["Name", "ID"]) for function in contract.functions: if function.visibility in ["public", "external"]: function_id = get_function_id(function.solidity_signature) table.add_row([ function.solidity_signature, f"{function_id:#0{10}x}" ]) for variable in contract.state_variables: if variable.visibility in ["public"]: sig = variable.function_name function_id = get_function_id(sig) table.add_row([sig, f"{function_id:#0{10}x}"]) txt += str(table) + "\n" all_tables.append((contract.name, table)) self.info(txt) res = self.generate_output(txt) for name, table in all_tables: res.add_pretty_table(table, name) return res
def output(self, _filename): """ _filename is not used Args: _filename(string) """ txt = '' for contract in self.slither.contracts_derived: txt += '\n{}:\n'.format(contract.name) table = PrettyTable(['Name', 'ID']) for function in contract.functions: if function.visibility in ['public', 'external']: table.add_row([ function.full_name, hex(get_function_id(function.full_name)) ]) for variable in contract.state_variables: if variable.visibility in ['public']: table.add_row([ variable.name + '()', hex(get_function_id(variable.name + '()')) ]) txt += str(table) + '\n' self.info(txt)
def compare_function_ids(implem, proxy): results = { 'function-id-collision':[], 'shadowing':[], } logger.info(green('\n## Run function ids checks... (see https://github.com/crytic/slither/wiki/Upgradeability-Checks#functions-ids-checks)')) signatures_implem = get_signatures(implem) signatures_proxy = get_signatures(proxy) signatures_ids_implem = {get_function_id(s): s for s in signatures_implem} signatures_ids_proxy = {get_function_id(s): s for s in signatures_proxy} error_found = False for (k, _) in signatures_ids_implem.items(): if k in signatures_ids_proxy: error_found = True if signatures_ids_implem[k] != signatures_ids_proxy[k]: implem_function = _get_function_or_variable(implem, signatures_ids_implem[k]) proxy_function = _get_function_or_variable(proxy, signatures_ids_proxy[k]) info = f'Function id collision found: {implem_function.canonical_name} ({implem_function.source_mapping_str}) {proxy_function.canonical_name} ({proxy_function.source_mapping_str})' logger.info(red(info)) res = Output(info) res.add(implem_function) res.add(proxy_function) results['function-id-collision'].append(res.data) else: implem_function = _get_function_or_variable(implem, signatures_ids_implem[k]) proxy_function = _get_function_or_variable(proxy, signatures_ids_proxy[k]) info = f'Shadowing between {implem_function.canonical_name} ({implem_function.source_mapping_str}) and {proxy_function.canonical_name} ({proxy_function.source_mapping_str})' logger.info(red(info)) res = Output(info) res.add(implem_function) res.add(proxy_function) results['shadowing'].append(res.data) if not error_found: logger.info(green('No error found')) return results
def _get_evm_instructions_function(function_info): function = function_info["function"] # CFG depends on function being constructor or not if function.is_constructor: cfg = function_info["contract_info"]["cfg_init"] # _dispatcher is the only function recognised by evm-cfg-builder in bytecode_init. # _dispatcher serves the role of the constructor in init code, # given that there are no other functions. # Todo: Could rename it appropriately in evm-cfg-builder # by detecting that init bytecode is being parsed. name = "_dispatcher" func_hash = "" else: cfg = function_info["contract_info"]["cfg"] name = function.name # Get first four bytes of function singature's keccak-256 hash used as function selector func_hash = str(hex(get_function_id(function.full_name))) function_evm = _get_function_evm(cfg, name, func_hash) if function_evm is None: to_log = "Function " + function.name + " not found in the EVM code" logger.error(to_log) raise SlitherError("Function " + function.name + " not found in the EVM code") function_ins = [] for basic_block in sorted(function_evm.basic_blocks, key=lambda x: x.start.pc): for ins in basic_block.instructions: function_ins.append(ins) return function_ins
def compare_function_ids(implem, proxy): signatures_implem = get_signatures(implem) signatures_proxy = get_signatures(proxy) signatures_ids_implem = {get_function_id(s): s for s in signatures_implem} signatures_ids_proxy = {get_function_id(s): s for s in signatures_proxy} found = False for (k, _) in signatures_ids_implem.items(): if k in signatures_ids_proxy: found = True logger.info( red('Function id collision found {} {}'.format( signatures_ids_implem[k], signatures_ids_proxy[k]))) if not found: logger.info(green('No function id collision found'))
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 _check(self): signatures_implem = get_signatures(self.contract) signatures_proxy = get_signatures(self.proxy) signatures_ids_implem = {get_function_id(s): s for s in signatures_implem} signatures_ids_proxy = {get_function_id(s): s for s in signatures_proxy} results = [] for (k, _) in signatures_ids_implem.items(): if k in signatures_ids_proxy: if signatures_ids_implem[k] == signatures_ids_proxy[k]: implem_function = _get_function_or_variable(self.contract, signatures_ids_implem[k]) proxy_function = _get_function_or_variable(self.proxy, signatures_ids_proxy[k]) info = ['Function shadowing found: ', implem_function, ' ', proxy_function, '\n'] json = self.generate_result(info) results.append(json) return results
def output(self, _filename): """ _filename is not used Args: _filename(string) """ txt = '' for contract in self.slither.contracts_derived: txt += '\n{}:\n'.format(contract.name) table = PrettyTable(['Name', 'ID']) for function in contract.functions: if function.visibility in ['public', 'external']: table.add_row([ function.full_name, hex(get_function_id(function.full_name)) ]) for variable in contract.state_variables: if variable.visibility in ['public']: variable_getter_args = "" if type(variable.type) is ArrayType: length = 0 v = variable while type(v.type) is ArrayType: length += 1 v = v.type variable_getter_args = ','.join(["uint256"] * length) elif type(variable.type) is MappingType: variable_getter_args = variable.type.type_from table.add_row([ f"{variable.name}({variable_getter_args})", hex( get_function_id( f"{variable.name}({variable_getter_args})")) ]) txt += str(table) + '\n' self.info(txt)
def _setter(self, function: Slither_Function, parent_contract): """ Setting values when initializing Finished. """ self._function_call_setter(function, parent_contract) self._add_constant_values_to_parameters() self._visibility = function.visibility self._view = True if function.view else False self._pure = True if function.pure else False self._payable = True if function.payable else False self._is_constructor = True if function.is_constructor else False self._sig_hash = get_function_id(function.solidity_signature) self._is_suicidal = self._check_is_suicidal() # load modifier objects. # requires within modifiers will be loaded into self.requires as well. self._load_modifiers(function) self._check_sat_by_default()
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)))