def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[Expression]: name = ast["name"] if name in builtins: return Identifier(YulBuiltin(name)) # check function-scoped variables parent_func = root.parent_func if parent_func: variable = parent_func.get_local_variable_from_name(name) if variable: return Identifier(variable) if isinstance(parent_func, FunctionContract): variable = parent_func.contract.get_state_variable_from_name(name) if variable: return Identifier(variable) # check yul-scoped variable variable = root.get_yul_local_variable_from_name(name) if variable: return Identifier(variable.underlying) # check yul-scoped function func = root.get_yul_local_function_from_name(name) if func: return Identifier(func.underlying) magic_suffix = _parse_yul_magic_suffixes(name, root) if magic_suffix: return magic_suffix raise SlitherException(f"unresolved reference to identifier {name}")
def parse_yul_typed_name(root: YulScope, _node: YulNode, ast: Dict) -> Optional[Expression]: var = root.get_yul_local_variable_from_name(ast["name"]) i = Identifier(var.underlying) i.type = var.underlying.type return i
def _parse_yul_magic_suffixes(name: str, root: YulScope) -> Optional[Expression]: # check for magic suffixes # TODO: the following leads to wrong IR # Currently SlithIR doesnt support raw access to memory # So things like .offset/.slot will return the variable # Instaed of the actual offset/slot if name.endswith(("_slot", ".slot")): potential_name = name[:-5] variable_found = _check_for_state_variable_name(root, potential_name) if variable_found: return variable_found var = root.function.get_local_variable_from_name(potential_name) if var and var.is_storage: return Identifier(var) if name.endswith(("_offset", ".offset")): potential_name = name[:-7] variable_found = _check_for_state_variable_name(root, potential_name) if variable_found: return variable_found var = root.function.get_local_variable_from_name(potential_name) if var and var.location == "calldata": return Identifier(var) if name.endswith(".length"): # TODO: length should create a new IP operation LENGTH var # The code below is an hotfix to allow slither to process length in yul # Until we have a better support potential_name = name[:-7] var = root.function.get_local_variable_from_name(potential_name) if var and var.location == "calldata": return Identifier(var) return None
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]) 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 _create_node( self, func: Function, counter: int, variable: "Variable", scope: Union[Scope, Function] ): from slither.core.cfg.node import Node, NodeType from slither.core.expressions import ( AssignmentOperationType, AssignmentOperation, Identifier, ) # Function uses to create node for state variable declaration statements node = Node(NodeType.OTHER_ENTRYPOINT, counter, scope) node.set_offset(variable.source_mapping, self.compilation_unit) node.set_function(func) func.add_node(node) assert variable.expression expression = AssignmentOperation( Identifier(variable), variable.expression, AssignmentOperationType.ASSIGN, variable.type, ) expression.set_offset(variable.source_mapping, self.compilation_unit) node.add_expression(expression) return node
def _check_for_state_variable_name(root: YulScope, potential_name: str) -> Optional[Identifier]: root_function = root.function if isinstance(root_function, FunctionContract): var = root_function.contract.get_state_variable_from_name(potential_name) if var: return Identifier(var) return None
def _stat_vars_info_in_contract(self): # 全局变量定义 for v in self.contract.state_variables: if v.expression is None: exp = str(v.type) + " " + str(Identifier(v)) self.state_var_declare_function_map[str(Identifier(v))] = { "type": str(v.type), "exp": exp } for function in self.contract.functions: # print("[列表] 函数名:{} fid:{}".format(function.name, function.id)) self.fid_2_function[function.id] = function # 全局变量定义 if function.is_constructor or function.is_constructor_variables: for node in function.nodes: for v in node.state_variables_written: full_exp = "{} {}".format(str(v.type), node.expression) self.state_var_declare_function_map[str(v)] = { "fun": function, "expr": node.expression, "full_expr": full_exp } else: # 全局变量读 for v in function.state_variables_read: if str(v) not in self.state_var_read_function_map: self.state_var_read_function_map[str(v)] = [function] else: self.state_var_read_function_map[str(v)].append( function) # 全局变量写 for v in function.state_variables_written: if not function.can_send_eth(): continue # NOTE:对于参与交易的函数,下面会进行重点分析 if str(v) not in self.state_var_write_function_map: self.state_var_write_function_map[str(v)] = [function] else: self.state_var_write_function_map[str(v)].append( function)
def _create_node(self, func, counter, variable): # Function uses to create node for state variable declaration statements node = Node(NodeType.OTHER_ENTRYPOINT, counter) node.set_offset(variable.source_mapping, self.slither) node.set_function(func) func.add_node(node) expression = AssignmentOperation(Identifier(variable), variable.expression, AssignmentOperationType.ASSIGN, variable.type) node.add_expression(expression) return node
def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[Expression]: name = ast["name"] if name in builtins: return Identifier(YulBuiltin(name)) # check function-scoped variables if root.parent_func: variable = root.parent_func.get_local_variable_from_name(name) if variable: return Identifier(variable) variable = root.parent_func.contract.get_state_variable_from_name(name) if variable: return Identifier(variable) # check yul-scoped variable variable = root.get_yul_local_variable_from_name(name) if variable: return Identifier(variable.underlying) # check yul-scoped function func = root.get_yul_local_function_from_name(name) if func: return Identifier(func.underlying) # check for magic suffixes if name.endswith("_slot"): potential_name = name[:-5] var = root.function.contract.get_state_variable_from_name( potential_name) if var: return Identifier(var) var = root.function.get_local_variable_from_name(potential_name) if var and var.is_storage: return Identifier(var) if name.endswith("_offset"): potential_name = name[:-7] var = root.function.contract.get_state_variable_from_name( potential_name) if var: return Identifier(var) raise SlitherException(f"unresolved reference to identifier {name}")
def analyze_expressions(self): if self._node.type == NodeType.VARIABLE and not self._node.expression: self._node.add_expression( self._node.variable_declaration.expression) if self._unparsed_expression: expression = parse_yul(self._scope, self, self._unparsed_expression) self._node.add_expression(expression) if self._node.expression: if self._node.type == NodeType.VARIABLE: # Update the expression to be an assignement to the variable _expression = AssignmentOperation( Identifier(self._node.variable_declaration), self._node.expression, AssignmentOperationType.ASSIGN, self._node.variable_declaration.type, ) _expression.set_offset(self._node.expression.source_mapping, self._node.slither) self._node.add_expression(_expression, bypass_verif_empty=True) expression = self._node.expression read_var = ReadVar(expression) self._node.variables_read_as_expression = read_var.result() write_var = WriteVar(expression) self._node.variables_written_as_expression = write_var.result() find_call = FindCalls(expression) self._node.calls_as_expression = find_call.result() self._node.external_calls_as_expressions = [ c for c in self._node.calls_as_expression if not isinstance(c.called, Identifier) ] self._node.internal_calls_as_expressions = [ c for c in self._node.calls_as_expression if isinstance(c.called, Identifier) ]
for contract in slither.contracts: state_var_declare_function_map = {} # 全局变量定义函数 state_var_write_function_map = {} # 全局变量写函数列表 state_var_read_function_map = {} # 全局变量读函数列表 transaction_state_vars = [] # 与交易行为相关的全局变量 transaction_state_vars_stmts = {} eth_send_functions = {} functions_slice_criterias = {} # 每个函数对应的切片准则:阶段1、4都会生成切片准则 # NOTE: stat_var <==> function 对应表 print("\n=====阶段1:stat_var <==> function 对应表=====\n") for v in contract.state_variables: if v.expression is None: exp = str(v.type) + " " + str(Identifier(v)) print("expression:", exp) state_var_declare_function_map[str(Identifier(v))] = { "exp": exp } for function in contract.functions: get_function_cfg(function) # 全局变量定义 if function.is_constructor or function.is_constructor_variables: for v in function.state_variables_written: if str(v) not in state_var_declare_function_map: state_var_declare_function_map[str(v)] = { "fun": function