slither.core.declarations.function
Function module
1""" 2 Function module 3""" 4import logging 5from abc import abstractmethod, ABCMeta 6from collections import namedtuple 7from enum import Enum 8from itertools import groupby 9from typing import Any, Dict, TYPE_CHECKING, List, Optional, Set, Union, Callable, Tuple 10 11from slither.core.cfg.scope import Scope 12from slither.core.declarations.solidity_variables import ( 13 SolidityFunction, 14 SolidityVariable, 15 SolidityVariableComposed, 16) 17from slither.core.expressions import ( 18 Identifier, 19 IndexAccess, 20 MemberAccess, 21 UnaryOperation, 22) 23from slither.core.solidity_types.type import Type 24from slither.core.source_mapping.source_mapping import SourceMapping 25from slither.core.variables.local_variable import LocalVariable 26from slither.core.variables.state_variable import StateVariable 27from slither.utils.type import convert_type_for_solidity_signature_to_string 28from slither.utils.utils import unroll 29 30 31# pylint: disable=import-outside-toplevel,too-many-instance-attributes,too-many-statements,too-many-lines 32 33if TYPE_CHECKING: 34 from slither.utils.type_helpers import ( 35 InternalCallType, 36 LowLevelCallType, 37 HighLevelCallType, 38 LibraryCallType, 39 ) 40 from slither.core.declarations import Contract, FunctionContract 41 from slither.core.cfg.node import Node, NodeType 42 from slither.core.variables.variable import Variable 43 from slither.slithir.variables.variable import SlithIRVariable 44 from slither.slithir.variables import LocalIRVariable 45 from slither.core.expressions.expression import Expression 46 from slither.slithir.operations import Operation 47 from slither.core.compilation_unit import SlitherCompilationUnit 48 from slither.core.scope.scope import FileScope 49 50LOGGER = logging.getLogger("Function") 51ReacheableNode = namedtuple("ReacheableNode", ["node", "ir"]) 52 53 54class ModifierStatements: 55 def __init__( 56 self, 57 modifier: Union["Contract", "Function"], 58 entry_point: "Node", 59 nodes: List["Node"], 60 ) -> None: 61 self._modifier = modifier 62 self._entry_point = entry_point 63 self._nodes = nodes 64 65 @property 66 def modifier(self) -> Union["Contract", "Function"]: 67 return self._modifier 68 69 @property 70 def entry_point(self) -> "Node": 71 return self._entry_point 72 73 @entry_point.setter 74 def entry_point(self, entry_point: "Node"): 75 self._entry_point = entry_point 76 77 @property 78 def nodes(self) -> List["Node"]: 79 return self._nodes 80 81 @nodes.setter 82 def nodes(self, nodes: List["Node"]): 83 self._nodes = nodes 84 85 86class FunctionType(Enum): 87 NORMAL = 0 88 CONSTRUCTOR = 1 89 FALLBACK = 2 90 RECEIVE = 3 91 CONSTRUCTOR_VARIABLES = 10 # Fake function to hold variable declaration statements 92 CONSTRUCTOR_CONSTANT_VARIABLES = 11 # Fake function to hold variable declaration statements 93 94 95def _filter_state_variables_written(expressions: List["Expression"]): 96 ret = [] 97 98 for expression in expressions: 99 if isinstance(expression, (Identifier, UnaryOperation, MemberAccess)): 100 ret.append(expression.expression) 101 elif isinstance(expression, IndexAccess): 102 ret.append(expression.expression_left) 103 return ret 104 105 106class FunctionLanguage(Enum): 107 Solidity = 0 108 Yul = 1 109 Vyper = 2 110 111 112class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-public-methods 113 """ 114 Function class 115 """ 116 117 def __init__(self, compilation_unit: "SlitherCompilationUnit") -> None: 118 super().__init__() 119 self._internal_scope: List[str] = [] 120 self._name: Optional[str] = None 121 self._view: bool = False 122 self._pure: bool = False 123 self._payable: bool = False 124 self._visibility: Optional[str] = None 125 self._virtual: bool = False 126 self._overrides: List["FunctionContract"] = [] 127 self._overridden_by: List["FunctionContract"] = [] 128 129 self._is_implemented: Optional[bool] = None 130 self._is_empty: Optional[bool] = None 131 self._entry_point: Optional["Node"] = None 132 self._nodes: List["Node"] = [] 133 self._variables: Dict[str, "LocalVariable"] = {} 134 # slithir Temporary and references variables (but not SSA) 135 self._slithir_variables: Set["SlithIRVariable"] = set() 136 self._parameters: List["LocalVariable"] = [] 137 self._parameters_ssa: List["LocalIRVariable"] = [] 138 self._parameters_src: SourceMapping = SourceMapping() 139 # This is used for vyper calls with default arguments 140 self._default_args_as_expressions: List["Expression"] = [] 141 self._returns: List["LocalVariable"] = [] 142 self._returns_ssa: List["LocalIRVariable"] = [] 143 self._returns_src: SourceMapping = SourceMapping() 144 self._return_values: Optional[List["SlithIRVariable"]] = None 145 self._return_values_ssa: Optional[List["SlithIRVariable"]] = None 146 self._vars_read: List["Variable"] = [] 147 self._vars_written: List["Variable"] = [] 148 self._state_vars_read: List["StateVariable"] = [] 149 self._vars_read_or_written: List["Variable"] = [] 150 self._solidity_vars_read: List["SolidityVariable"] = [] 151 self._state_vars_written: List["StateVariable"] = [] 152 self._internal_calls: List["InternalCallType"] = [] 153 self._solidity_calls: List["SolidityFunction"] = [] 154 self._low_level_calls: List["LowLevelCallType"] = [] 155 self._high_level_calls: List["HighLevelCallType"] = [] 156 self._library_calls: List["LibraryCallType"] = [] 157 self._external_calls_as_expressions: List["Expression"] = [] 158 self._expression_vars_read: List["Expression"] = [] 159 self._expression_vars_written: List["Expression"] = [] 160 self._expression_calls: List["Expression"] = [] 161 # self._expression_modifiers: List["Expression"] = [] 162 self._modifiers: List[ModifierStatements] = [] 163 self._explicit_base_constructor_calls: List[ModifierStatements] = [] 164 self._contains_assembly: bool = False 165 166 self._expressions: Optional[List["Expression"]] = None 167 self._slithir_operations: Optional[List["Operation"]] = None 168 self._slithir_ssa_operations: Optional[List["Operation"]] = None 169 170 self._all_expressions: Optional[List["Expression"]] = None 171 self._all_slithir_operations: Optional[List["Operation"]] = None 172 self._all_internals_calls: Optional[List["InternalCallType"]] = None 173 self._all_high_level_calls: Optional[List["HighLevelCallType"]] = None 174 self._all_library_calls: Optional[List["LibraryCallType"]] = None 175 self._all_low_level_calls: Optional[List["LowLevelCallType"]] = None 176 self._all_solidity_calls: Optional[List["SolidityFunction"]] = None 177 self._all_variables_read: Optional[List["Variable"]] = None 178 self._all_variables_written: Optional[List["Variable"]] = None 179 self._all_state_variables_read: Optional[List["StateVariable"]] = None 180 self._all_solidity_variables_read: Optional[List["SolidityVariable"]] = None 181 self._all_state_variables_written: Optional[List["StateVariable"]] = None 182 self._all_slithir_variables: Optional[List["SlithIRVariable"]] = None 183 self._all_nodes: Optional[List["Node"]] = None 184 self._all_conditional_state_variables_read: Optional[List["StateVariable"]] = None 185 self._all_conditional_state_variables_read_with_loop: Optional[List["StateVariable"]] = None 186 self._all_conditional_solidity_variables_read: Optional[List["SolidityVariable"]] = None 187 self._all_conditional_solidity_variables_read_with_loop: Optional[ 188 List["SolidityVariable"] 189 ] = None 190 self._all_solidity_variables_used_as_args: Optional[List["SolidityVariable"]] = None 191 192 self._is_shadowed: bool = False 193 self._shadows: bool = False 194 195 # set(ReacheableNode) 196 self._reachable_from_nodes: Set[ReacheableNode] = set() 197 self._reachable_from_functions: Set[Function] = set() 198 self._all_reachable_from_functions: Optional[Set[Function]] = None 199 200 # Constructor, fallback, State variable constructor 201 self._function_type: Optional[FunctionType] = None 202 self._is_constructor: Optional[bool] = None 203 204 # Computed on the fly, can be True of False 205 self._can_reenter: Optional[bool] = None 206 self._can_send_eth: Optional[bool] = None 207 208 self._nodes_ordered_dominators: Optional[List["Node"]] = None 209 210 self._counter_nodes = 0 211 212 # Memoize parameters: 213 # TODO: identify all the memoize parameters and add a way to undo the memoization 214 self._full_name: Optional[str] = None 215 self._signature: Optional[Tuple[str, List[str], List[str]]] = None 216 self._solidity_signature: Optional[str] = None 217 self._signature_str: Optional[str] = None 218 self._canonical_name: Optional[str] = None 219 self._is_protected: Optional[bool] = None 220 221 self.compilation_unit: "SlitherCompilationUnit" = compilation_unit 222 223 self.function_language: FunctionLanguage = ( 224 FunctionLanguage.Solidity if compilation_unit.is_solidity else FunctionLanguage.Vyper 225 ) 226 227 self._id: Optional[str] = None 228 229 # To be improved with a parsing of the documentation 230 self.has_documentation: bool = False 231 232 ################################################################################### 233 ################################################################################### 234 # region General properties 235 ################################################################################### 236 ################################################################################### 237 238 @property 239 def name(self) -> str: 240 """ 241 str: function name 242 """ 243 if self._name == "" and self._function_type == FunctionType.CONSTRUCTOR: 244 return "constructor" 245 if self._name == "" and self._function_type == FunctionType.FALLBACK: 246 return "fallback" 247 if self._function_type == FunctionType.RECEIVE: 248 return "receive" 249 if self._function_type == FunctionType.CONSTRUCTOR_VARIABLES: 250 return "slitherConstructorVariables" 251 if self._function_type == FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES: 252 return "slitherConstructorConstantVariables" 253 return self._name 254 255 @name.setter 256 def name(self, new_name: str): 257 self._name = new_name 258 259 @property 260 def internal_scope(self) -> List[str]: 261 """ 262 Return a list of name representing the scope of the function 263 This is used to model nested functions declared in YUL 264 265 :return: 266 """ 267 return self._internal_scope 268 269 @internal_scope.setter 270 def internal_scope(self, new_scope: List[str]): 271 self._internal_scope = new_scope 272 273 @property 274 def full_name(self) -> str: 275 """ 276 str: func_name(type1,type2) 277 Return the function signature without the return values 278 The difference between this function and solidity_function is that full_name does not translate the underlying 279 type (ex: structure, contract to address, ...) 280 """ 281 if self._full_name is None: 282 name, parameters, _ = self.signature 283 full_name = ".".join(self._internal_scope + [name]) + "(" + ",".join(parameters) + ")" 284 self._full_name = full_name 285 return self._full_name 286 287 @property 288 @abstractmethod 289 def canonical_name(self) -> str: 290 """ 291 str: contract.func_name(type1,type2) 292 Return the function signature without the return values 293 """ 294 return "" 295 296 @property 297 def contains_assembly(self) -> bool: 298 return self._contains_assembly 299 300 @contains_assembly.setter 301 def contains_assembly(self, c: bool): 302 self._contains_assembly = c 303 304 def can_reenter(self, callstack: Optional[List[Union["Function", "Variable"]]] = None) -> bool: 305 """ 306 Check if the function can re-enter 307 Follow internal calls. 308 Do not consider CREATE as potential re-enter, but check if the 309 destination's constructor can contain a call (recurs. follow nested CREATE) 310 For Solidity > 0.5, filter access to public variables and constant/pure/view 311 For call to this. check if the destination can re-enter 312 Do not consider Send/Transfer as there is not enough gas 313 :param callstack: used internally to check for recursion 314 :return bool: 315 """ 316 from slither.slithir.operations import Call 317 318 if self._can_reenter is None: 319 self._can_reenter = False 320 for ir in self.all_slithir_operations(): 321 if isinstance(ir, Call) and ir.can_reenter(callstack): 322 self._can_reenter = True 323 return True 324 return self._can_reenter 325 326 def can_send_eth(self) -> bool: 327 """ 328 Check if the function or any internal (not external) functions called by it can send eth 329 :return bool: 330 """ 331 from slither.slithir.operations import Call 332 333 if self._can_send_eth is None: 334 self._can_send_eth = False 335 for ir in self.all_slithir_operations(): 336 if isinstance(ir, Call) and ir.can_send_eth(): 337 self._can_send_eth = True 338 return True 339 return self._can_send_eth 340 341 @property 342 def is_checked(self) -> bool: 343 """ 344 Return true if the overflow are enabled by default 345 346 347 :return: 348 """ 349 350 return self.compilation_unit.solc_version >= "0.8.0" 351 352 @property 353 def id(self) -> Optional[str]: 354 """ 355 Return the reference ID of the function, if available. 356 357 :return: 358 :rtype: 359 """ 360 return self._id 361 362 @id.setter 363 def id(self, new_id: str): 364 self._id = new_id 365 366 @property 367 @abstractmethod 368 def file_scope(self) -> "FileScope": 369 pass 370 371 # endregion 372 ################################################################################### 373 ################################################################################### 374 # region Type (FunctionType) 375 ################################################################################### 376 ################################################################################### 377 378 def set_function_type(self, t: FunctionType) -> None: 379 assert isinstance(t, FunctionType) 380 self._function_type = t 381 382 @property 383 def function_type(self) -> Optional[FunctionType]: 384 return self._function_type 385 386 @function_type.setter 387 def function_type(self, t: FunctionType): 388 self._function_type = t 389 390 @property 391 def is_constructor(self) -> bool: 392 """ 393 bool: True if the function is the constructor 394 """ 395 return self._function_type == FunctionType.CONSTRUCTOR 396 397 @property 398 def is_constructor_variables(self) -> bool: 399 """ 400 bool: True if the function is the constructor of the variables 401 Slither has inbuilt functions to hold the state variables initialization 402 """ 403 return self._function_type in [ 404 FunctionType.CONSTRUCTOR_VARIABLES, 405 FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES, 406 ] 407 408 @property 409 def is_fallback(self) -> bool: 410 """ 411 Determine if the function is the fallback function for the contract 412 Returns 413 (bool) 414 """ 415 return self._function_type == FunctionType.FALLBACK 416 417 @property 418 def is_receive(self) -> bool: 419 """ 420 Determine if the function is the receive function for the contract 421 Returns 422 (bool) 423 """ 424 return self._function_type == FunctionType.RECEIVE 425 426 # endregion 427 ################################################################################### 428 ################################################################################### 429 # region Payable 430 ################################################################################### 431 ################################################################################### 432 433 @property 434 def payable(self) -> bool: 435 """ 436 bool: True if the function is payable 437 """ 438 return self._payable 439 440 @payable.setter 441 def payable(self, p: bool): 442 self._payable = p 443 444 # endregion 445 ################################################################################### 446 ################################################################################### 447 # region Virtual 448 ################################################################################### 449 ################################################################################### 450 451 @property 452 def is_virtual(self) -> bool: 453 """ 454 Note for Solidity < 0.6.0 it will always be false 455 bool: True if the function is virtual 456 """ 457 return self._virtual 458 459 @is_virtual.setter 460 def is_virtual(self, v: bool): 461 self._virtual = v 462 463 @property 464 def is_override(self) -> bool: 465 """ 466 Note for Solidity < 0.6.0 it will always be false 467 bool: True if the function overrides a base function 468 """ 469 return len(self._overrides) > 0 470 471 @property 472 def overridden_by(self) -> List["FunctionContract"]: 473 """ 474 List["FunctionContract"]: List of functions in child contracts that override this function 475 This may include distinct instances of the same function due to inheritance 476 """ 477 return self._overridden_by 478 479 @property 480 def overrides(self) -> List["FunctionContract"]: 481 """ 482 List["FunctionContract"]: List of functions in parent contracts that this function overrides 483 This may include distinct instances of the same function due to inheritance 484 """ 485 return self._overrides 486 487 # endregion 488 ################################################################################### 489 ################################################################################### 490 # region Visibility 491 ################################################################################### 492 ################################################################################### 493 494 @property 495 def visibility(self) -> str: 496 """ 497 str: Function visibility 498 """ 499 assert self._visibility is not None 500 return self._visibility 501 502 @visibility.setter 503 def visibility(self, v: str): 504 self._visibility = v 505 506 def set_visibility(self, v: str) -> None: 507 self._visibility = v 508 509 @property 510 def view(self) -> bool: 511 """ 512 bool: True if the function is declared as view 513 """ 514 return self._view 515 516 @view.setter 517 def view(self, v: bool): 518 self._view = v 519 520 @property 521 def pure(self) -> bool: 522 """ 523 bool: True if the function is declared as pure 524 """ 525 return self._pure 526 527 @pure.setter 528 def pure(self, p: bool): 529 self._pure = p 530 531 @property 532 def is_shadowed(self) -> bool: 533 return self._is_shadowed 534 535 @is_shadowed.setter 536 def is_shadowed(self, is_shadowed): 537 self._is_shadowed = is_shadowed 538 539 @property 540 def shadows(self) -> bool: 541 return self._shadows 542 543 @shadows.setter 544 def shadows(self, _shadows: bool): 545 self._shadows = _shadows 546 547 # endregion 548 ################################################################################### 549 ################################################################################### 550 # region Function's body 551 ################################################################################### 552 ################################################################################### 553 554 @property 555 def is_implemented(self) -> bool: 556 """ 557 bool: True if the function is implemented 558 """ 559 return self._is_implemented 560 561 @is_implemented.setter 562 def is_implemented(self, is_impl: bool): 563 self._is_implemented = is_impl 564 565 @property 566 def is_empty(self) -> bool: 567 """ 568 bool: True if the function is empty, None if the function is an interface 569 """ 570 return self._is_empty 571 572 @is_empty.setter 573 def is_empty(self, empty: bool): 574 self._is_empty = empty 575 576 # endregion 577 ################################################################################### 578 ################################################################################### 579 # region Nodes 580 ################################################################################### 581 ################################################################################### 582 583 @property 584 def nodes(self) -> List["Node"]: 585 """ 586 list(Node): List of the nodes 587 """ 588 return list(self._nodes) 589 590 @nodes.setter 591 def nodes(self, nodes: List["Node"]): 592 self._nodes = nodes 593 594 @property 595 def entry_point(self) -> Optional["Node"]: 596 """ 597 Node: Entry point of the function 598 """ 599 return self._entry_point 600 601 @entry_point.setter 602 def entry_point(self, node: "Node"): 603 self._entry_point = node 604 605 def add_node(self, node: "Node") -> None: 606 if not self._entry_point: 607 self._entry_point = node 608 self._nodes.append(node) 609 610 @property 611 def nodes_ordered_dominators(self) -> List["Node"]: 612 # TODO: does not work properly; most likely due to modifier call 613 # This will not work for modifier call that lead to multiple nodes 614 # from slither.core.cfg.node import NodeType 615 if self._nodes_ordered_dominators is None: 616 self._nodes_ordered_dominators = [] 617 if self.entry_point: 618 self._compute_nodes_ordered_dominators(self.entry_point) 619 620 for node in self.nodes: 621 # if node.type == NodeType.OTHER_ENTRYPOINT: 622 if not node in self._nodes_ordered_dominators: 623 self._compute_nodes_ordered_dominators(node) 624 625 return self._nodes_ordered_dominators 626 627 def _compute_nodes_ordered_dominators(self, node: "Node"): 628 assert self._nodes_ordered_dominators is not None 629 if node in self._nodes_ordered_dominators: 630 return 631 self._nodes_ordered_dominators.append(node) 632 for dom in node.dominance_exploration_ordered: 633 self._compute_nodes_ordered_dominators(dom) 634 635 # endregion 636 ################################################################################### 637 ################################################################################### 638 # region Parameters 639 ################################################################################### 640 ################################################################################### 641 642 @property 643 def parameters(self) -> List["LocalVariable"]: 644 """ 645 list(LocalVariable): List of the parameters 646 """ 647 return list(self._parameters) 648 649 def add_parameters(self, p: "LocalVariable") -> None: 650 self._parameters.append(p) 651 652 @property 653 def parameters_ssa(self) -> List["LocalIRVariable"]: 654 """ 655 list(LocalIRVariable): List of the parameters (SSA form) 656 """ 657 return list(self._parameters_ssa) 658 659 def add_parameter_ssa(self, var: "LocalIRVariable") -> None: 660 self._parameters_ssa.append(var) 661 662 def parameters_src(self) -> SourceMapping: 663 return self._parameters_src 664 665 # endregion 666 ################################################################################### 667 ################################################################################### 668 # region Return values 669 ################################################################################### 670 ################################################################################### 671 672 @property 673 def return_type(self) -> Optional[List[Type]]: 674 """ 675 Return the list of return type 676 If no return, return None 677 """ 678 returns = self.returns 679 if returns: 680 return [r.type for r in returns] 681 return None 682 683 def returns_src(self) -> SourceMapping: 684 return self._returns_src 685 686 @property 687 def type(self) -> Optional[List[Type]]: 688 """ 689 Return the list of return type 690 If no return, return None 691 Alias of return_type 692 """ 693 return self.return_type 694 695 @property 696 def returns(self) -> List["LocalVariable"]: 697 """ 698 list(LocalVariable): List of the return variables 699 """ 700 return list(self._returns) 701 702 def add_return(self, r: "LocalVariable") -> None: 703 self._returns.append(r) 704 705 @property 706 def returns_ssa(self) -> List["LocalIRVariable"]: 707 """ 708 list(LocalIRVariable): List of the return variables (SSA form) 709 """ 710 return list(self._returns_ssa) 711 712 def add_return_ssa(self, var: "LocalIRVariable") -> None: 713 self._returns_ssa.append(var) 714 715 # endregion 716 ################################################################################### 717 ################################################################################### 718 # region Modifiers 719 ################################################################################### 720 ################################################################################### 721 722 @property 723 def modifiers(self) -> List[Union["Contract", "Function"]]: 724 """ 725 list(Modifier): List of the modifiers 726 Can be contract for constructor's calls 727 728 """ 729 return [c.modifier for c in self._modifiers] 730 731 def add_modifier(self, modif: "ModifierStatements") -> None: 732 self._modifiers.append(modif) 733 734 @property 735 def modifiers_statements(self) -> List[ModifierStatements]: 736 """ 737 list(ModifierCall): List of the modifiers call (include expression and irs) 738 """ 739 return list(self._modifiers) 740 741 @property 742 def explicit_base_constructor_calls(self) -> List["Function"]: 743 """ 744 list(Function): List of the base constructors called explicitly by this presumed constructor definition. 745 746 Base constructors implicitly or explicitly called by the contract definition will not be 747 included. 748 """ 749 # This is a list of contracts internally, so we convert it to a list of constructor functions. 750 return [ 751 c.modifier.constructors_declared 752 for c in self._explicit_base_constructor_calls 753 if c.modifier.constructors_declared 754 ] 755 756 @property 757 def explicit_base_constructor_calls_statements(self) -> List[ModifierStatements]: 758 """ 759 list(ModifierCall): List of the base constructors called explicitly by this presumed constructor definition. 760 761 """ 762 # This is a list of contracts internally, so we convert it to a list of constructor functions. 763 return list(self._explicit_base_constructor_calls) 764 765 def add_explicit_base_constructor_calls_statements(self, modif: ModifierStatements) -> None: 766 self._explicit_base_constructor_calls.append(modif) 767 768 # endregion 769 ################################################################################### 770 ################################################################################### 771 # region Variables 772 ################################################################################### 773 ################################################################################### 774 775 @property 776 def variables(self) -> List[LocalVariable]: 777 """ 778 Return all local variables 779 Include paramters and return values 780 """ 781 return list(self._variables.values()) 782 783 @property 784 def local_variables(self) -> List[LocalVariable]: 785 """ 786 Return all local variables (dont include paramters and return values) 787 """ 788 return list(set(self.variables) - set(self.returns) - set(self.parameters)) 789 790 @property 791 def variables_as_dict(self) -> Dict[str, LocalVariable]: 792 return self._variables 793 794 @property 795 def variables_read(self) -> List["Variable"]: 796 """ 797 list(Variable): Variables read (local/state/solidity) 798 """ 799 return list(self._vars_read) 800 801 @property 802 def variables_written(self) -> List["Variable"]: 803 """ 804 list(Variable): Variables written (local/state/solidity) 805 """ 806 return list(self._vars_written) 807 808 @property 809 def state_variables_read(self) -> List["StateVariable"]: 810 """ 811 list(StateVariable): State variables read 812 """ 813 return list(self._state_vars_read) 814 815 @property 816 def solidity_variables_read(self) -> List["SolidityVariable"]: 817 """ 818 list(SolidityVariable): Solidity variables read 819 """ 820 return list(self._solidity_vars_read) 821 822 @property 823 def state_variables_written(self) -> List["StateVariable"]: 824 """ 825 list(StateVariable): State variables written 826 """ 827 return list(self._state_vars_written) 828 829 @property 830 def variables_read_or_written(self) -> List["Variable"]: 831 """ 832 list(Variable): Variables read or written (local/state/solidity) 833 """ 834 return list(self._vars_read_or_written) 835 836 @property 837 def variables_read_as_expression(self) -> List["Expression"]: 838 return self._expression_vars_read 839 840 @property 841 def variables_written_as_expression(self) -> List["Expression"]: 842 return self._expression_vars_written 843 844 @property 845 def slithir_variables(self) -> List["SlithIRVariable"]: 846 """ 847 Temporary and Reference Variables (not SSA form) 848 """ 849 850 return list(self._slithir_variables) 851 852 # endregion 853 ################################################################################### 854 ################################################################################### 855 # region Calls 856 ################################################################################### 857 ################################################################################### 858 859 @property 860 def internal_calls(self) -> List["InternalCallType"]: 861 """ 862 list(Function or SolidityFunction): List of function calls (that does not create a transaction) 863 """ 864 return list(self._internal_calls) 865 866 @property 867 def solidity_calls(self) -> List[SolidityFunction]: 868 """ 869 list(SolidityFunction): List of Soldity calls 870 """ 871 return list(self._solidity_calls) 872 873 @property 874 def high_level_calls(self) -> List["HighLevelCallType"]: 875 """ 876 list((Contract, Function|Variable)): 877 List of high level calls (external calls). 878 A variable is called in case of call to a public state variable 879 Include library calls 880 """ 881 return list(self._high_level_calls) 882 883 @property 884 def library_calls(self) -> List["LibraryCallType"]: 885 """ 886 list((Contract, Function)): 887 """ 888 return list(self._library_calls) 889 890 @property 891 def low_level_calls(self) -> List["LowLevelCallType"]: 892 """ 893 list((Variable|SolidityVariable, str)): List of low_level call 894 A low level call is defined by 895 - the variable called 896 - the name of the function (call/delegatecall/codecall) 897 """ 898 return list(self._low_level_calls) 899 900 @property 901 def external_calls_as_expressions(self) -> List["Expression"]: 902 """ 903 list(ExpressionCall): List of message calls (that creates a transaction) 904 """ 905 return list(self._external_calls_as_expressions) 906 907 # endregion 908 ################################################################################### 909 ################################################################################### 910 # region Expressions 911 ################################################################################### 912 ################################################################################### 913 914 @property 915 def calls_as_expressions(self) -> List["Expression"]: 916 return self._expression_calls 917 918 @property 919 def expressions(self) -> List["Expression"]: 920 """ 921 list(Expression): List of the expressions 922 """ 923 if self._expressions is None: 924 expressionss = [n.expression for n in self.nodes] 925 expressions = [e for e in expressionss if e] 926 self._expressions = expressions 927 return self._expressions 928 929 @property 930 def return_values(self) -> List["SlithIRVariable"]: 931 """ 932 list(Return Values): List of the return values 933 """ 934 from slither.core.cfg.node import NodeType 935 from slither.slithir.operations import Return 936 from slither.slithir.variables import Constant 937 938 if self._return_values is None: 939 return_values = [] 940 returns = [n for n in self.nodes if n.type == NodeType.RETURN] 941 [ # pylint: disable=expression-not-assigned 942 return_values.extend(ir.values) 943 for node in returns 944 for ir in node.irs 945 if isinstance(ir, Return) 946 ] 947 self._return_values = list({x for x in return_values if not isinstance(x, Constant)}) 948 return self._return_values 949 950 @property 951 def return_values_ssa(self) -> List["SlithIRVariable"]: 952 """ 953 list(Return Values in SSA form): List of the return values in ssa form 954 """ 955 from slither.core.cfg.node import NodeType 956 from slither.slithir.operations import Return 957 from slither.slithir.variables import Constant 958 959 if self._return_values_ssa is None: 960 return_values_ssa = [] 961 returns = [n for n in self.nodes if n.type == NodeType.RETURN] 962 [ # pylint: disable=expression-not-assigned 963 return_values_ssa.extend(ir.values) 964 for node in returns 965 for ir in node.irs_ssa 966 if isinstance(ir, Return) 967 ] 968 self._return_values_ssa = list( 969 {x for x in return_values_ssa if not isinstance(x, Constant)} 970 ) 971 return self._return_values_ssa 972 973 # endregion 974 ################################################################################### 975 ################################################################################### 976 # region SlithIR 977 ################################################################################### 978 ################################################################################### 979 980 @property 981 def slithir_operations(self) -> List["Operation"]: 982 """ 983 list(Operation): List of the slithir operations 984 """ 985 if self._slithir_operations is None: 986 operationss = [n.irs for n in self.nodes] 987 operations = [item for sublist in operationss for item in sublist if item] 988 self._slithir_operations = operations 989 return self._slithir_operations 990 991 @property 992 def slithir_ssa_operations(self) -> List["Operation"]: 993 """ 994 list(Operation): List of the slithir operations (SSA) 995 """ 996 if self._slithir_ssa_operations is None: 997 operationss = [n.irs_ssa for n in self.nodes] 998 operations = [item for sublist in operationss for item in sublist if item] 999 self._slithir_ssa_operations = operations 1000 return self._slithir_ssa_operations 1001 1002 # endregion 1003 ################################################################################### 1004 ################################################################################### 1005 # region Signature 1006 ################################################################################### 1007 ################################################################################### 1008 1009 @property 1010 def solidity_signature(self) -> str: 1011 """ 1012 Return a signature following the Solidity Standard 1013 Contract and converted into address 1014 1015 It might still keep internal types (ex: structure name) for internal functions. 1016 The reason is that internal functions allows recursive structure definition, which 1017 can't be converted following the Solidity stand ard 1018 1019 :return: the solidity signature 1020 """ 1021 if self._solidity_signature is None: 1022 parameters = [ 1023 convert_type_for_solidity_signature_to_string(x.type) for x in self.parameters 1024 ] 1025 self._solidity_signature = self.name + "(" + ",".join(parameters) + ")" 1026 return self._solidity_signature 1027 1028 @property 1029 def signature(self) -> Tuple[str, List[str], List[str]]: 1030 """ 1031 (str, list(str), list(str)): Function signature as 1032 (name, list parameters type, list return values type) 1033 """ 1034 # FIXME memoizing this function is not working properly for vyper 1035 # if self._signature is None: 1036 return ( 1037 self.name, 1038 [str(x.type) for x in self.parameters], 1039 [str(x.type) for x in self.returns], 1040 ) 1041 # self._signature = signature 1042 # return self._signature 1043 1044 @property 1045 def signature_str(self) -> str: 1046 """ 1047 str: func_name(type1,type2) returns (type3) 1048 Return the function signature as a str (contains the return values) 1049 """ 1050 if self._signature_str is None: 1051 name, parameters, returnVars = self.signature 1052 self._signature_str = ( 1053 name + "(" + ",".join(parameters) + ") returns(" + ",".join(returnVars) + ")" 1054 ) 1055 return self._signature_str 1056 1057 # endregion 1058 ################################################################################### 1059 ################################################################################### 1060 # region Functions 1061 ################################################################################### 1062 ################################################################################### 1063 1064 @property 1065 @abstractmethod 1066 def functions_shadowed(self) -> List["Function"]: 1067 pass 1068 1069 # endregion 1070 ################################################################################### 1071 ################################################################################### 1072 # region Reachable 1073 ################################################################################### 1074 ################################################################################### 1075 1076 @property 1077 def reachable_from_nodes(self) -> Set[ReacheableNode]: 1078 """ 1079 Return 1080 ReacheableNode 1081 """ 1082 return self._reachable_from_nodes 1083 1084 @property 1085 def reachable_from_functions(self) -> Set["Function"]: 1086 return self._reachable_from_functions 1087 1088 @property 1089 def all_reachable_from_functions(self) -> Set["Function"]: 1090 """ 1091 Give the recursive version of reachable_from_functions (all the functions that lead to call self in the CFG) 1092 """ 1093 if self._all_reachable_from_functions is None: 1094 functions: Set["Function"] = set() 1095 1096 new_functions = self.reachable_from_functions 1097 # iterate until we have are finding new functions 1098 while new_functions and not new_functions.issubset(functions): 1099 functions = functions.union(new_functions) 1100 # Use a temporary set, because we iterate over new_functions 1101 new_functionss: Set["Function"] = set() 1102 for f in new_functions: 1103 new_functionss = new_functionss.union(f.reachable_from_functions) 1104 new_functions = new_functionss - functions 1105 1106 self._all_reachable_from_functions = functions 1107 return self._all_reachable_from_functions 1108 1109 def add_reachable_from_node(self, n: "Node", ir: "Operation") -> None: 1110 self._reachable_from_nodes.add(ReacheableNode(n, ir)) 1111 self._reachable_from_functions.add(n.function) 1112 1113 # endregion 1114 ################################################################################### 1115 ################################################################################### 1116 # region Recursive getters 1117 ################################################################################### 1118 ################################################################################### 1119 1120 def _explore_functions(self, f_new_values: Callable[["Function"], List]) -> List[Any]: 1121 values = f_new_values(self) 1122 explored = [self] 1123 to_explore = [ 1124 c for c in self.internal_calls if isinstance(c, Function) and c not in explored 1125 ] 1126 to_explore += [ 1127 c for (_, c) in self.library_calls if isinstance(c, Function) and c not in explored 1128 ] 1129 to_explore += [m for m in self.modifiers if m not in explored] 1130 1131 while to_explore: 1132 f = to_explore[0] 1133 to_explore = to_explore[1:] 1134 if f in explored: 1135 continue 1136 explored.append(f) 1137 1138 values += f_new_values(f) 1139 1140 to_explore += [ 1141 c 1142 for c in f.internal_calls 1143 if isinstance(c, Function) and c not in explored and c not in to_explore 1144 ] 1145 to_explore += [ 1146 c 1147 for (_, c) in f.library_calls 1148 if isinstance(c, Function) and c not in explored and c not in to_explore 1149 ] 1150 to_explore += [m for m in f.modifiers if m not in explored and m not in to_explore] 1151 1152 return list(set(values)) 1153 1154 def all_variables_read(self) -> List["Variable"]: 1155 """recursive version of variables_read""" 1156 if self._all_variables_read is None: 1157 self._all_variables_read = self._explore_functions(lambda x: x.variables_read) 1158 return self._all_variables_read 1159 1160 def all_variables_written(self) -> List["Variable"]: 1161 """recursive version of variables_written""" 1162 if self._all_variables_written is None: 1163 self._all_variables_written = self._explore_functions(lambda x: x.variables_written) 1164 return self._all_variables_written 1165 1166 def all_state_variables_read(self) -> List["StateVariable"]: 1167 """recursive version of variables_read""" 1168 if self._all_state_variables_read is None: 1169 self._all_state_variables_read = self._explore_functions( 1170 lambda x: x.state_variables_read 1171 ) 1172 return self._all_state_variables_read 1173 1174 def all_solidity_variables_read(self) -> List[SolidityVariable]: 1175 """recursive version of solidity_read""" 1176 if self._all_solidity_variables_read is None: 1177 self._all_solidity_variables_read = self._explore_functions( 1178 lambda x: x.solidity_variables_read 1179 ) 1180 return self._all_solidity_variables_read 1181 1182 def all_slithir_variables(self) -> List["SlithIRVariable"]: 1183 """recursive version of slithir_variables""" 1184 if self._all_slithir_variables is None: 1185 self._all_slithir_variables = self._explore_functions(lambda x: x.slithir_variables) 1186 return self._all_slithir_variables 1187 1188 def all_nodes(self) -> List["Node"]: 1189 """recursive version of nodes""" 1190 if self._all_nodes is None: 1191 self._all_nodes = self._explore_functions(lambda x: x.nodes) 1192 return self._all_nodes 1193 1194 def all_expressions(self) -> List["Expression"]: 1195 """recursive version of variables_read""" 1196 if self._all_expressions is None: 1197 self._all_expressions = self._explore_functions(lambda x: x.expressions) 1198 return self._all_expressions 1199 1200 def all_slithir_operations(self) -> List["Operation"]: 1201 if self._all_slithir_operations is None: 1202 self._all_slithir_operations = self._explore_functions(lambda x: x.slithir_operations) 1203 return self._all_slithir_operations 1204 1205 def all_state_variables_written(self) -> List[StateVariable]: 1206 """recursive version of variables_written""" 1207 if self._all_state_variables_written is None: 1208 self._all_state_variables_written = self._explore_functions( 1209 lambda x: x.state_variables_written 1210 ) 1211 return self._all_state_variables_written 1212 1213 def all_internal_calls(self) -> List["InternalCallType"]: 1214 """recursive version of internal_calls""" 1215 if self._all_internals_calls is None: 1216 self._all_internals_calls = self._explore_functions(lambda x: x.internal_calls) 1217 return self._all_internals_calls 1218 1219 def all_low_level_calls(self) -> List["LowLevelCallType"]: 1220 """recursive version of low_level calls""" 1221 if self._all_low_level_calls is None: 1222 self._all_low_level_calls = self._explore_functions(lambda x: x.low_level_calls) 1223 return self._all_low_level_calls 1224 1225 def all_high_level_calls(self) -> List["HighLevelCallType"]: 1226 """recursive version of high_level calls""" 1227 if self._all_high_level_calls is None: 1228 self._all_high_level_calls = self._explore_functions(lambda x: x.high_level_calls) 1229 return self._all_high_level_calls 1230 1231 def all_library_calls(self) -> List["LibraryCallType"]: 1232 """recursive version of library calls""" 1233 if self._all_library_calls is None: 1234 self._all_library_calls = self._explore_functions(lambda x: x.library_calls) 1235 return self._all_library_calls 1236 1237 def all_solidity_calls(self) -> List[SolidityFunction]: 1238 """recursive version of solidity calls""" 1239 if self._all_solidity_calls is None: 1240 self._all_solidity_calls = self._explore_functions(lambda x: x.solidity_calls) 1241 return self._all_solidity_calls 1242 1243 @staticmethod 1244 def _explore_func_cond_read(func: "Function", include_loop: bool) -> List["StateVariable"]: 1245 ret = [n.state_variables_read for n in func.nodes if n.is_conditional(include_loop)] 1246 return [item for sublist in ret for item in sublist] 1247 1248 def all_conditional_state_variables_read(self, include_loop=True) -> List["StateVariable"]: 1249 """ 1250 Return the state variable used in a condition 1251 1252 Over approximate and also return index access 1253 It won't work if the variable is assigned to a temp variable 1254 """ 1255 if include_loop: 1256 if self._all_conditional_state_variables_read_with_loop is None: 1257 self._all_conditional_state_variables_read_with_loop = self._explore_functions( 1258 lambda x: self._explore_func_cond_read(x, include_loop) 1259 ) 1260 return self._all_conditional_state_variables_read_with_loop 1261 if self._all_conditional_state_variables_read is None: 1262 self._all_conditional_state_variables_read = self._explore_functions( 1263 lambda x: self._explore_func_cond_read(x, include_loop) 1264 ) 1265 return self._all_conditional_state_variables_read 1266 1267 @staticmethod 1268 def _solidity_variable_in_binary(node: "Node") -> List[SolidityVariable]: 1269 from slither.slithir.operations.binary import Binary 1270 1271 ret = [] 1272 for ir in node.irs: 1273 if isinstance(ir, Binary): 1274 ret += ir.read 1275 return [var for var in ret if isinstance(var, SolidityVariable)] 1276 1277 @staticmethod 1278 def _explore_func_conditional( 1279 func: "Function", 1280 f: Callable[["Node"], List[SolidityVariable]], 1281 include_loop: bool, 1282 ) -> List[Any]: 1283 ret = [f(n) for n in func.nodes if n.is_conditional(include_loop)] 1284 return [item for sublist in ret for item in sublist] 1285 1286 def all_conditional_solidity_variables_read( 1287 self, include_loop: bool = True 1288 ) -> List[SolidityVariable]: 1289 """ 1290 Return the Soldiity variables directly used in a condtion 1291 1292 Use of the IR to filter index access 1293 Assumption: the solidity vars are used directly in the conditional node 1294 It won't work if the variable is assigned to a temp variable 1295 """ 1296 if include_loop: 1297 if self._all_conditional_solidity_variables_read_with_loop is None: 1298 self._all_conditional_solidity_variables_read_with_loop = self._explore_functions( 1299 lambda x: self._explore_func_conditional( 1300 x, self._solidity_variable_in_binary, include_loop 1301 ) 1302 ) 1303 return self._all_conditional_solidity_variables_read_with_loop 1304 1305 if self._all_conditional_solidity_variables_read is None: 1306 self._all_conditional_solidity_variables_read = self._explore_functions( 1307 lambda x: self._explore_func_conditional( 1308 x, self._solidity_variable_in_binary, include_loop 1309 ) 1310 ) 1311 return self._all_conditional_solidity_variables_read 1312 1313 @staticmethod 1314 def _solidity_variable_in_internal_calls(node: "Node") -> List[SolidityVariable]: 1315 from slither.slithir.operations.internal_call import InternalCall 1316 1317 ret = [] 1318 for ir in node.irs: 1319 if isinstance(ir, InternalCall): 1320 ret += ir.read 1321 return [var for var in ret if isinstance(var, SolidityVariable)] 1322 1323 @staticmethod 1324 def _explore_func_nodes( 1325 func: "Function", f: Callable[["Node"], List[SolidityVariable]] 1326 ) -> List[Union[Any, SolidityVariableComposed]]: 1327 ret = [f(n) for n in func.nodes] 1328 return [item for sublist in ret for item in sublist] 1329 1330 def all_solidity_variables_used_as_args(self) -> List[SolidityVariable]: 1331 """ 1332 Return the Soldiity variables directly used in a call 1333 1334 Use of the IR to filter index access 1335 Used to catch check(msg.sender) 1336 """ 1337 if self._all_solidity_variables_used_as_args is None: 1338 self._all_solidity_variables_used_as_args = self._explore_functions( 1339 lambda x: self._explore_func_nodes(x, self._solidity_variable_in_internal_calls) 1340 ) 1341 return self._all_solidity_variables_used_as_args 1342 1343 # endregion 1344 ################################################################################### 1345 ################################################################################### 1346 # region Visitor 1347 ################################################################################### 1348 ################################################################################### 1349 1350 def apply_visitor(self, Visitor: Callable) -> List: 1351 """ 1352 Apply a visitor to all the function expressions 1353 Args: 1354 Visitor: slither.visitors 1355 Returns 1356 list(): results of the visit 1357 """ 1358 expressions = self.expressions 1359 v = [Visitor(e).result() for e in expressions] 1360 return [item for sublist in v for item in sublist] 1361 1362 # endregion 1363 ################################################################################### 1364 ################################################################################### 1365 # region Getters from/to object 1366 ################################################################################### 1367 ################################################################################### 1368 1369 def get_local_variable_from_name(self, variable_name: str) -> Optional[LocalVariable]: 1370 """ 1371 Return a local variable from a name 1372 1373 Args: 1374 variable_name (str): name of the variable 1375 Returns: 1376 LocalVariable 1377 """ 1378 return next((v for v in self.variables if v.name == variable_name), None) 1379 1380 # endregion 1381 ################################################################################### 1382 ################################################################################### 1383 # region Export 1384 ################################################################################### 1385 ################################################################################### 1386 1387 def cfg_to_dot(self, filename: str): 1388 """ 1389 Export the function to a dot file 1390 Args: 1391 filename (str) 1392 """ 1393 with open(filename, "w", encoding="utf8") as f: 1394 f.write("digraph{\n") 1395 for node in self.nodes: 1396 f.write(f'{node.node_id}[label="{str(node)}"];\n') 1397 for son in node.sons: 1398 f.write(f"{node.node_id}->{son.node_id};\n") 1399 1400 f.write("}\n") 1401 1402 def dominator_tree_to_dot(self, filename: str): 1403 """ 1404 Export the dominator tree of the function to a dot file 1405 Args: 1406 filename (str) 1407 """ 1408 1409 def description(node): 1410 desc = f"{node}\n" 1411 desc += f"id: {node.node_id}" 1412 if node.dominance_frontier: 1413 desc += f"\ndominance frontier: {[n.node_id for n in node.dominance_frontier]}" 1414 return desc 1415 1416 with open(filename, "w", encoding="utf8") as f: 1417 f.write("digraph{\n") 1418 for node in self.nodes: 1419 f.write(f'{node.node_id}[label="{description(node)}"];\n') 1420 if node.immediate_dominator: 1421 f.write(f"{node.immediate_dominator.node_id}->{node.node_id};\n") 1422 1423 f.write("}\n") 1424 1425 def slithir_cfg_to_dot(self, filename: str): 1426 """ 1427 Export the CFG to a DOT file. The nodes includes the Solidity expressions and the IRs 1428 :param filename: 1429 :return: 1430 """ 1431 content = self.slithir_cfg_to_dot_str() 1432 with open(filename, "w", encoding="utf8") as f: 1433 f.write(content) 1434 1435 def slithir_cfg_to_dot_str(self, skip_expressions: bool = False) -> str: 1436 """ 1437 Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs 1438 :return: the DOT content 1439 :rtype: str 1440 """ 1441 from slither.core.cfg.node import NodeType 1442 1443 content = "" 1444 content += "digraph{\n" 1445 for node in self.nodes: 1446 label = f"Node Type: {node.type.value} {node.node_id}\n" 1447 if node.expression and not skip_expressions: 1448 label += f"\nEXPRESSION:\n{node.expression}\n" 1449 if node.irs and not skip_expressions: 1450 label += "\nIRs:\n" + "\n".join([str(ir) for ir in node.irs]) 1451 content += f'{node.node_id}[label="{label}"];\n' 1452 if node.type in [NodeType.IF, NodeType.IFLOOP]: 1453 true_node = node.son_true 1454 if true_node: 1455 content += f'{node.node_id}->{true_node.node_id}[label="True"];\n' 1456 false_node = node.son_false 1457 if false_node: 1458 content += f'{node.node_id}->{false_node.node_id}[label="False"];\n' 1459 else: 1460 for son in node.sons: 1461 content += f"{node.node_id}->{son.node_id};\n" 1462 1463 content += "}\n" 1464 return content 1465 1466 # endregion 1467 ################################################################################### 1468 ################################################################################### 1469 # region Summary information 1470 ################################################################################### 1471 ################################################################################### 1472 1473 def is_reading(self, variable: "Variable") -> bool: 1474 """ 1475 Check if the function reads the variable 1476 Args: 1477 variable (Variable): 1478 Returns: 1479 bool: True if the variable is read 1480 """ 1481 return variable in self.variables_read 1482 1483 def is_reading_in_conditional_node(self, variable: "Variable") -> bool: 1484 """ 1485 Check if the function reads the variable in a IF node 1486 Args: 1487 variable (Variable): 1488 Returns: 1489 bool: True if the variable is read 1490 """ 1491 variables_reads = [n.variables_read for n in self.nodes if n.contains_if()] 1492 variables_read = [item for sublist in variables_reads for item in sublist] 1493 return variable in variables_read 1494 1495 def is_reading_in_require_or_assert(self, variable: "Variable") -> bool: 1496 """ 1497 Check if the function reads the variable in an require or assert 1498 Args: 1499 variable (Variable): 1500 Returns: 1501 bool: True if the variable is read 1502 """ 1503 variables_reads = [n.variables_read for n in self.nodes if n.contains_require_or_assert()] 1504 variables_read = [item for sublist in variables_reads for item in sublist] 1505 return variable in variables_read 1506 1507 def is_writing(self, variable: "Variable") -> bool: 1508 """ 1509 Check if the function writes the variable 1510 Args: 1511 variable (Variable): 1512 Returns: 1513 bool: True if the variable is written 1514 """ 1515 return variable in self.variables_written 1516 1517 @abstractmethod 1518 def get_summary( 1519 self, 1520 ) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]: 1521 pass 1522 1523 def is_protected(self) -> bool: 1524 """ 1525 Determine if the function is protected using a check on msg.sender 1526 1527 Consider onlyOwner as a safe modifier. 1528 If the owner functionality is incorrectly implemented, this will lead to incorrectly 1529 classify the function as protected 1530 1531 Otherwise only detects if msg.sender is directly used in a condition 1532 For example, it wont work for: 1533 address a = msg.sender 1534 require(a == owner) 1535 Returns 1536 (bool) 1537 """ 1538 1539 if self._is_protected is None: 1540 if self.is_constructor: 1541 self._is_protected = True 1542 return True 1543 if "onlyOwner" in [m.name for m in self.modifiers]: 1544 self._is_protected = True 1545 return True 1546 conditional_vars = self.all_conditional_solidity_variables_read(include_loop=False) 1547 args_vars = self.all_solidity_variables_used_as_args() 1548 self._is_protected = ( 1549 SolidityVariableComposed("msg.sender") in conditional_vars + args_vars 1550 ) 1551 return self._is_protected 1552 1553 @property 1554 def is_reentrant(self) -> bool: 1555 """ 1556 Determine if the function can be re-entered 1557 """ 1558 reentrancy_modifier = "nonReentrant" 1559 1560 if self.function_language == FunctionLanguage.Vyper: 1561 reentrancy_modifier = "nonreentrant(lock)" 1562 1563 # TODO: compare with hash of known nonReentrant modifier instead of the name 1564 if reentrancy_modifier in [m.name for m in self.modifiers]: 1565 return False 1566 1567 if self.visibility in ["public", "external"]: 1568 return True 1569 1570 # If it's an internal function, check if all its entry points have the nonReentrant modifier 1571 all_entry_points = [ 1572 f for f in self.all_reachable_from_functions if f.visibility in ["public", "external"] 1573 ] 1574 if not all_entry_points: 1575 return True 1576 return not all( 1577 (reentrancy_modifier in [m.name for m in f.modifiers] for f in all_entry_points) 1578 ) 1579 1580 # endregion 1581 ################################################################################### 1582 ################################################################################### 1583 # region Analyses 1584 ################################################################################### 1585 ################################################################################### 1586 1587 def _analyze_read_write(self) -> None: 1588 """Compute variables read/written/...""" 1589 write_var = [x.variables_written_as_expression for x in self.nodes] 1590 write_var = [x for x in write_var if x] 1591 write_var = [item for sublist in write_var for item in sublist] 1592 write_var = list(set(write_var)) 1593 # Remove duplicate if they share the same string representation 1594 write_var = [ 1595 next(obj) 1596 for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x)) 1597 ] 1598 self._expression_vars_written = write_var 1599 1600 write_var = [x.variables_written for x in self.nodes] 1601 write_var = [x for x in write_var if x] 1602 write_var = [item for sublist in write_var for item in sublist] 1603 write_var = list(set(write_var)) 1604 # Remove duplicate if they share the same string representation 1605 write_var = [ 1606 next(obj) 1607 for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x)) 1608 ] 1609 self._vars_written = write_var 1610 1611 read_var = [x.variables_read_as_expression for x in self.nodes] 1612 read_var = [x for x in read_var if x] 1613 read_var = [item for sublist in read_var for item in sublist] 1614 # Remove duplicate if they share the same string representation 1615 read_var = [ 1616 next(obj) 1617 for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x)) 1618 ] 1619 self._expression_vars_read = read_var 1620 1621 read_var = [x.variables_read for x in self.nodes] 1622 read_var = [x for x in read_var if x] 1623 read_var = [item for sublist in read_var for item in sublist] 1624 # Remove duplicate if they share the same string representation 1625 read_var = [ 1626 next(obj) 1627 for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x)) 1628 ] 1629 self._vars_read = read_var 1630 1631 self._state_vars_written = [ 1632 x for x in self.variables_written if isinstance(x, StateVariable) 1633 ] 1634 self._state_vars_read = [x for x in self.variables_read if isinstance(x, StateVariable)] 1635 self._solidity_vars_read = [ 1636 x for x in self.variables_read if isinstance(x, SolidityVariable) 1637 ] 1638 1639 self._vars_read_or_written = self._vars_written + self._vars_read 1640 1641 slithir_variables = [x.slithir_variables for x in self.nodes] 1642 slithir_variables = [x for x in slithir_variables if x] 1643 self._slithir_variables = [item for sublist in slithir_variables for item in sublist] 1644 1645 def _analyze_calls(self) -> None: 1646 calls = [x.calls_as_expression for x in self.nodes] 1647 calls = [x for x in calls if x] 1648 calls = [item for sublist in calls for item in sublist] 1649 self._expression_calls = list(set(calls)) 1650 1651 internal_calls = [x.internal_calls for x in self.nodes] 1652 internal_calls = [x for x in internal_calls if x] 1653 internal_calls = [item for sublist in internal_calls for item in sublist] 1654 self._internal_calls = list(set(internal_calls)) 1655 1656 self._solidity_calls = [c for c in internal_calls if isinstance(c, SolidityFunction)] 1657 1658 low_level_calls = [x.low_level_calls for x in self.nodes] 1659 low_level_calls = [x for x in low_level_calls if x] 1660 low_level_calls = [item for sublist in low_level_calls for item in sublist] 1661 self._low_level_calls = list(set(low_level_calls)) 1662 1663 high_level_calls = [x.high_level_calls for x in self.nodes] 1664 high_level_calls = [x for x in high_level_calls if x] 1665 high_level_calls = [item for sublist in high_level_calls for item in sublist] 1666 self._high_level_calls = list(set(high_level_calls)) 1667 1668 library_calls = [x.library_calls for x in self.nodes] 1669 library_calls = [x for x in library_calls if x] 1670 library_calls = [item for sublist in library_calls for item in sublist] 1671 self._library_calls = list(set(library_calls)) 1672 1673 external_calls_as_expressions = [x.external_calls_as_expressions for x in self.nodes] 1674 external_calls_as_expressions = [x for x in external_calls_as_expressions if x] 1675 external_calls_as_expressions = [ 1676 item for sublist in external_calls_as_expressions for item in sublist 1677 ] 1678 self._external_calls_as_expressions = list(set(external_calls_as_expressions)) 1679 1680 # endregion 1681 ################################################################################### 1682 ################################################################################### 1683 # region Nodes 1684 ################################################################################### 1685 ################################################################################### 1686 1687 def new_node( 1688 self, node_type: "NodeType", src: Union[str, Dict], scope: Union[Scope, "Function"] 1689 ) -> "Node": 1690 from slither.core.cfg.node import Node 1691 1692 node = Node(node_type, self._counter_nodes, scope, self.file_scope) 1693 node.set_offset(src, self.compilation_unit) 1694 self._counter_nodes += 1 1695 node.set_function(self) 1696 self._nodes.append(node) 1697 1698 return node 1699 1700 # endregion 1701 ################################################################################### 1702 ################################################################################### 1703 # region SlithIr and SSA 1704 ################################################################################### 1705 ################################################################################### 1706 1707 def _get_last_ssa_variable_instances( 1708 self, target_state: bool, target_local: bool 1709 ) -> Dict[str, Set["SlithIRVariable"]]: 1710 # pylint: disable=too-many-locals,too-many-branches 1711 from slither.slithir.variables import ReferenceVariable 1712 from slither.slithir.operations import OperationWithLValue 1713 from slither.core.cfg.node import NodeType 1714 1715 if not self.is_implemented: 1716 return {} 1717 1718 if self._entry_point is None: 1719 return {} 1720 # node, values 1721 to_explore: List[Tuple["Node", Dict]] = [(self._entry_point, {})] 1722 # node -> values 1723 explored: Dict = {} 1724 # name -> instances 1725 ret: Dict = {} 1726 1727 while to_explore: 1728 node, values = to_explore[0] 1729 to_explore = to_explore[1::] 1730 1731 if node.type != NodeType.ENTRYPOINT: 1732 for ir_ssa in node.irs_ssa: 1733 if isinstance(ir_ssa, OperationWithLValue): 1734 lvalue = ir_ssa.lvalue 1735 if isinstance(lvalue, ReferenceVariable): 1736 lvalue = lvalue.points_to_origin 1737 if isinstance(lvalue, StateVariable) and target_state: 1738 values[lvalue.canonical_name] = {lvalue} 1739 if isinstance(lvalue, LocalVariable) and target_local: 1740 values[lvalue.canonical_name] = {lvalue} 1741 1742 # Check for fixpoint 1743 if node in explored: 1744 if values == explored[node]: 1745 continue 1746 for k, instances in values.items(): 1747 if k not in explored[node]: 1748 explored[node][k] = set() 1749 explored[node][k] |= instances 1750 values = explored[node] 1751 else: 1752 explored[node] = values 1753 1754 # Return condition 1755 if node.will_return: 1756 for name, instances in values.items(): 1757 if name not in ret: 1758 ret[name] = set() 1759 ret[name] |= instances 1760 1761 for son in node.sons: 1762 to_explore.append((son, dict(values))) 1763 1764 return ret 1765 1766 def get_last_ssa_state_variables_instances( 1767 self, 1768 ) -> Dict[str, Set["SlithIRVariable"]]: 1769 return self._get_last_ssa_variable_instances(target_state=True, target_local=False) 1770 1771 def get_last_ssa_local_variables_instances( 1772 self, 1773 ) -> Dict[str, Set["SlithIRVariable"]]: 1774 return self._get_last_ssa_variable_instances(target_state=False, target_local=True) 1775 1776 @staticmethod 1777 def _unchange_phi(ir: "Operation") -> bool: 1778 from slither.slithir.operations import Phi, PhiCallback 1779 1780 if not isinstance(ir, (Phi, PhiCallback)) or len(ir.rvalues) > 1: 1781 return False 1782 if not ir.rvalues: 1783 return True 1784 return ir.rvalues[0] == ir.lvalue 1785 1786 def fix_phi( 1787 self, 1788 last_state_variables_instances: Dict[str, List["StateVariable"]], 1789 initial_state_variables_instances: Dict[str, "StateVariable"], 1790 ) -> None: 1791 from slither.slithir.operations import InternalCall, PhiCallback 1792 from slither.slithir.variables import Constant, StateIRVariable 1793 1794 for node in self.nodes: 1795 for ir in node.irs_ssa: 1796 if node == self.entry_point: 1797 if isinstance(ir.lvalue, StateIRVariable): 1798 additional = [initial_state_variables_instances[ir.lvalue.canonical_name]] 1799 additional += last_state_variables_instances[ir.lvalue.canonical_name] 1800 ir.rvalues = list(set(additional + ir.rvalues)) 1801 # function parameter 1802 else: 1803 # find index of the parameter 1804 idx = self.parameters.index(ir.lvalue.non_ssa_version) 1805 # find non ssa version of that index 1806 additional = [n.ir.arguments[idx] for n in self.reachable_from_nodes] 1807 additional = unroll(additional) 1808 additional = [a for a in additional if not isinstance(a, Constant)] 1809 ir.rvalues = list(set(additional + ir.rvalues)) 1810 if isinstance(ir, PhiCallback): 1811 callee_ir = ir.callee_ir 1812 if isinstance(callee_ir, InternalCall): 1813 last_ssa = callee_ir.function.get_last_ssa_state_variables_instances() 1814 if ir.lvalue.canonical_name in last_ssa: 1815 ir.rvalues = list(last_ssa[ir.lvalue.canonical_name]) 1816 else: 1817 ir.rvalues = [ir.lvalue] 1818 else: 1819 additional = last_state_variables_instances[ir.lvalue.canonical_name] 1820 ir.rvalues = list(set(additional + ir.rvalues)) 1821 1822 node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)] 1823 1824 def generate_slithir_and_analyze(self) -> None: 1825 1826 for node in self.nodes: 1827 node.slithir_generation() 1828 1829 self._analyze_read_write() 1830 self._analyze_calls() 1831 1832 @abstractmethod 1833 def generate_slithir_ssa(self, all_ssa_state_variables_instances): 1834 pass 1835 1836 def update_read_write_using_ssa(self) -> None: 1837 for node in self.nodes: 1838 node.update_read_write_using_ssa() 1839 self._analyze_read_write() 1840 1841 ################################################################################### 1842 ################################################################################### 1843 # region Built in definitions 1844 ################################################################################### 1845 ################################################################################### 1846 1847 def __str__(self) -> str: 1848 return self.name 1849 1850 # endregion
ReacheableNode(node, ir)
Inherited Members
- builtins.tuple
- index
- count
55class ModifierStatements: 56 def __init__( 57 self, 58 modifier: Union["Contract", "Function"], 59 entry_point: "Node", 60 nodes: List["Node"], 61 ) -> None: 62 self._modifier = modifier 63 self._entry_point = entry_point 64 self._nodes = nodes 65 66 @property 67 def modifier(self) -> Union["Contract", "Function"]: 68 return self._modifier 69 70 @property 71 def entry_point(self) -> "Node": 72 return self._entry_point 73 74 @entry_point.setter 75 def entry_point(self, entry_point: "Node"): 76 self._entry_point = entry_point 77 78 @property 79 def nodes(self) -> List["Node"]: 80 return self._nodes 81 82 @nodes.setter 83 def nodes(self, nodes: List["Node"]): 84 self._nodes = nodes
87class FunctionType(Enum): 88 NORMAL = 0 89 CONSTRUCTOR = 1 90 FALLBACK = 2 91 RECEIVE = 3 92 CONSTRUCTOR_VARIABLES = 10 # Fake function to hold variable declaration statements 93 CONSTRUCTOR_CONSTANT_VARIABLES = 11 # Fake function to hold variable declaration statements
An enumeration.
Inherited Members
- enum.Enum
- name
- value
An enumeration.
Inherited Members
- enum.Enum
- name
- value
113class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-public-methods 114 """ 115 Function class 116 """ 117 118 def __init__(self, compilation_unit: "SlitherCompilationUnit") -> None: 119 super().__init__() 120 self._internal_scope: List[str] = [] 121 self._name: Optional[str] = None 122 self._view: bool = False 123 self._pure: bool = False 124 self._payable: bool = False 125 self._visibility: Optional[str] = None 126 self._virtual: bool = False 127 self._overrides: List["FunctionContract"] = [] 128 self._overridden_by: List["FunctionContract"] = [] 129 130 self._is_implemented: Optional[bool] = None 131 self._is_empty: Optional[bool] = None 132 self._entry_point: Optional["Node"] = None 133 self._nodes: List["Node"] = [] 134 self._variables: Dict[str, "LocalVariable"] = {} 135 # slithir Temporary and references variables (but not SSA) 136 self._slithir_variables: Set["SlithIRVariable"] = set() 137 self._parameters: List["LocalVariable"] = [] 138 self._parameters_ssa: List["LocalIRVariable"] = [] 139 self._parameters_src: SourceMapping = SourceMapping() 140 # This is used for vyper calls with default arguments 141 self._default_args_as_expressions: List["Expression"] = [] 142 self._returns: List["LocalVariable"] = [] 143 self._returns_ssa: List["LocalIRVariable"] = [] 144 self._returns_src: SourceMapping = SourceMapping() 145 self._return_values: Optional[List["SlithIRVariable"]] = None 146 self._return_values_ssa: Optional[List["SlithIRVariable"]] = None 147 self._vars_read: List["Variable"] = [] 148 self._vars_written: List["Variable"] = [] 149 self._state_vars_read: List["StateVariable"] = [] 150 self._vars_read_or_written: List["Variable"] = [] 151 self._solidity_vars_read: List["SolidityVariable"] = [] 152 self._state_vars_written: List["StateVariable"] = [] 153 self._internal_calls: List["InternalCallType"] = [] 154 self._solidity_calls: List["SolidityFunction"] = [] 155 self._low_level_calls: List["LowLevelCallType"] = [] 156 self._high_level_calls: List["HighLevelCallType"] = [] 157 self._library_calls: List["LibraryCallType"] = [] 158 self._external_calls_as_expressions: List["Expression"] = [] 159 self._expression_vars_read: List["Expression"] = [] 160 self._expression_vars_written: List["Expression"] = [] 161 self._expression_calls: List["Expression"] = [] 162 # self._expression_modifiers: List["Expression"] = [] 163 self._modifiers: List[ModifierStatements] = [] 164 self._explicit_base_constructor_calls: List[ModifierStatements] = [] 165 self._contains_assembly: bool = False 166 167 self._expressions: Optional[List["Expression"]] = None 168 self._slithir_operations: Optional[List["Operation"]] = None 169 self._slithir_ssa_operations: Optional[List["Operation"]] = None 170 171 self._all_expressions: Optional[List["Expression"]] = None 172 self._all_slithir_operations: Optional[List["Operation"]] = None 173 self._all_internals_calls: Optional[List["InternalCallType"]] = None 174 self._all_high_level_calls: Optional[List["HighLevelCallType"]] = None 175 self._all_library_calls: Optional[List["LibraryCallType"]] = None 176 self._all_low_level_calls: Optional[List["LowLevelCallType"]] = None 177 self._all_solidity_calls: Optional[List["SolidityFunction"]] = None 178 self._all_variables_read: Optional[List["Variable"]] = None 179 self._all_variables_written: Optional[List["Variable"]] = None 180 self._all_state_variables_read: Optional[List["StateVariable"]] = None 181 self._all_solidity_variables_read: Optional[List["SolidityVariable"]] = None 182 self._all_state_variables_written: Optional[List["StateVariable"]] = None 183 self._all_slithir_variables: Optional[List["SlithIRVariable"]] = None 184 self._all_nodes: Optional[List["Node"]] = None 185 self._all_conditional_state_variables_read: Optional[List["StateVariable"]] = None 186 self._all_conditional_state_variables_read_with_loop: Optional[List["StateVariable"]] = None 187 self._all_conditional_solidity_variables_read: Optional[List["SolidityVariable"]] = None 188 self._all_conditional_solidity_variables_read_with_loop: Optional[ 189 List["SolidityVariable"] 190 ] = None 191 self._all_solidity_variables_used_as_args: Optional[List["SolidityVariable"]] = None 192 193 self._is_shadowed: bool = False 194 self._shadows: bool = False 195 196 # set(ReacheableNode) 197 self._reachable_from_nodes: Set[ReacheableNode] = set() 198 self._reachable_from_functions: Set[Function] = set() 199 self._all_reachable_from_functions: Optional[Set[Function]] = None 200 201 # Constructor, fallback, State variable constructor 202 self._function_type: Optional[FunctionType] = None 203 self._is_constructor: Optional[bool] = None 204 205 # Computed on the fly, can be True of False 206 self._can_reenter: Optional[bool] = None 207 self._can_send_eth: Optional[bool] = None 208 209 self._nodes_ordered_dominators: Optional[List["Node"]] = None 210 211 self._counter_nodes = 0 212 213 # Memoize parameters: 214 # TODO: identify all the memoize parameters and add a way to undo the memoization 215 self._full_name: Optional[str] = None 216 self._signature: Optional[Tuple[str, List[str], List[str]]] = None 217 self._solidity_signature: Optional[str] = None 218 self._signature_str: Optional[str] = None 219 self._canonical_name: Optional[str] = None 220 self._is_protected: Optional[bool] = None 221 222 self.compilation_unit: "SlitherCompilationUnit" = compilation_unit 223 224 self.function_language: FunctionLanguage = ( 225 FunctionLanguage.Solidity if compilation_unit.is_solidity else FunctionLanguage.Vyper 226 ) 227 228 self._id: Optional[str] = None 229 230 # To be improved with a parsing of the documentation 231 self.has_documentation: bool = False 232 233 ################################################################################### 234 ################################################################################### 235 # region General properties 236 ################################################################################### 237 ################################################################################### 238 239 @property 240 def name(self) -> str: 241 """ 242 str: function name 243 """ 244 if self._name == "" and self._function_type == FunctionType.CONSTRUCTOR: 245 return "constructor" 246 if self._name == "" and self._function_type == FunctionType.FALLBACK: 247 return "fallback" 248 if self._function_type == FunctionType.RECEIVE: 249 return "receive" 250 if self._function_type == FunctionType.CONSTRUCTOR_VARIABLES: 251 return "slitherConstructorVariables" 252 if self._function_type == FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES: 253 return "slitherConstructorConstantVariables" 254 return self._name 255 256 @name.setter 257 def name(self, new_name: str): 258 self._name = new_name 259 260 @property 261 def internal_scope(self) -> List[str]: 262 """ 263 Return a list of name representing the scope of the function 264 This is used to model nested functions declared in YUL 265 266 :return: 267 """ 268 return self._internal_scope 269 270 @internal_scope.setter 271 def internal_scope(self, new_scope: List[str]): 272 self._internal_scope = new_scope 273 274 @property 275 def full_name(self) -> str: 276 """ 277 str: func_name(type1,type2) 278 Return the function signature without the return values 279 The difference between this function and solidity_function is that full_name does not translate the underlying 280 type (ex: structure, contract to address, ...) 281 """ 282 if self._full_name is None: 283 name, parameters, _ = self.signature 284 full_name = ".".join(self._internal_scope + [name]) + "(" + ",".join(parameters) + ")" 285 self._full_name = full_name 286 return self._full_name 287 288 @property 289 @abstractmethod 290 def canonical_name(self) -> str: 291 """ 292 str: contract.func_name(type1,type2) 293 Return the function signature without the return values 294 """ 295 return "" 296 297 @property 298 def contains_assembly(self) -> bool: 299 return self._contains_assembly 300 301 @contains_assembly.setter 302 def contains_assembly(self, c: bool): 303 self._contains_assembly = c 304 305 def can_reenter(self, callstack: Optional[List[Union["Function", "Variable"]]] = None) -> bool: 306 """ 307 Check if the function can re-enter 308 Follow internal calls. 309 Do not consider CREATE as potential re-enter, but check if the 310 destination's constructor can contain a call (recurs. follow nested CREATE) 311 For Solidity > 0.5, filter access to public variables and constant/pure/view 312 For call to this. check if the destination can re-enter 313 Do not consider Send/Transfer as there is not enough gas 314 :param callstack: used internally to check for recursion 315 :return bool: 316 """ 317 from slither.slithir.operations import Call 318 319 if self._can_reenter is None: 320 self._can_reenter = False 321 for ir in self.all_slithir_operations(): 322 if isinstance(ir, Call) and ir.can_reenter(callstack): 323 self._can_reenter = True 324 return True 325 return self._can_reenter 326 327 def can_send_eth(self) -> bool: 328 """ 329 Check if the function or any internal (not external) functions called by it can send eth 330 :return bool: 331 """ 332 from slither.slithir.operations import Call 333 334 if self._can_send_eth is None: 335 self._can_send_eth = False 336 for ir in self.all_slithir_operations(): 337 if isinstance(ir, Call) and ir.can_send_eth(): 338 self._can_send_eth = True 339 return True 340 return self._can_send_eth 341 342 @property 343 def is_checked(self) -> bool: 344 """ 345 Return true if the overflow are enabled by default 346 347 348 :return: 349 """ 350 351 return self.compilation_unit.solc_version >= "0.8.0" 352 353 @property 354 def id(self) -> Optional[str]: 355 """ 356 Return the reference ID of the function, if available. 357 358 :return: 359 :rtype: 360 """ 361 return self._id 362 363 @id.setter 364 def id(self, new_id: str): 365 self._id = new_id 366 367 @property 368 @abstractmethod 369 def file_scope(self) -> "FileScope": 370 pass 371 372 # endregion 373 ################################################################################### 374 ################################################################################### 375 # region Type (FunctionType) 376 ################################################################################### 377 ################################################################################### 378 379 def set_function_type(self, t: FunctionType) -> None: 380 assert isinstance(t, FunctionType) 381 self._function_type = t 382 383 @property 384 def function_type(self) -> Optional[FunctionType]: 385 return self._function_type 386 387 @function_type.setter 388 def function_type(self, t: FunctionType): 389 self._function_type = t 390 391 @property 392 def is_constructor(self) -> bool: 393 """ 394 bool: True if the function is the constructor 395 """ 396 return self._function_type == FunctionType.CONSTRUCTOR 397 398 @property 399 def is_constructor_variables(self) -> bool: 400 """ 401 bool: True if the function is the constructor of the variables 402 Slither has inbuilt functions to hold the state variables initialization 403 """ 404 return self._function_type in [ 405 FunctionType.CONSTRUCTOR_VARIABLES, 406 FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES, 407 ] 408 409 @property 410 def is_fallback(self) -> bool: 411 """ 412 Determine if the function is the fallback function for the contract 413 Returns 414 (bool) 415 """ 416 return self._function_type == FunctionType.FALLBACK 417 418 @property 419 def is_receive(self) -> bool: 420 """ 421 Determine if the function is the receive function for the contract 422 Returns 423 (bool) 424 """ 425 return self._function_type == FunctionType.RECEIVE 426 427 # endregion 428 ################################################################################### 429 ################################################################################### 430 # region Payable 431 ################################################################################### 432 ################################################################################### 433 434 @property 435 def payable(self) -> bool: 436 """ 437 bool: True if the function is payable 438 """ 439 return self._payable 440 441 @payable.setter 442 def payable(self, p: bool): 443 self._payable = p 444 445 # endregion 446 ################################################################################### 447 ################################################################################### 448 # region Virtual 449 ################################################################################### 450 ################################################################################### 451 452 @property 453 def is_virtual(self) -> bool: 454 """ 455 Note for Solidity < 0.6.0 it will always be false 456 bool: True if the function is virtual 457 """ 458 return self._virtual 459 460 @is_virtual.setter 461 def is_virtual(self, v: bool): 462 self._virtual = v 463 464 @property 465 def is_override(self) -> bool: 466 """ 467 Note for Solidity < 0.6.0 it will always be false 468 bool: True if the function overrides a base function 469 """ 470 return len(self._overrides) > 0 471 472 @property 473 def overridden_by(self) -> List["FunctionContract"]: 474 """ 475 List["FunctionContract"]: List of functions in child contracts that override this function 476 This may include distinct instances of the same function due to inheritance 477 """ 478 return self._overridden_by 479 480 @property 481 def overrides(self) -> List["FunctionContract"]: 482 """ 483 List["FunctionContract"]: List of functions in parent contracts that this function overrides 484 This may include distinct instances of the same function due to inheritance 485 """ 486 return self._overrides 487 488 # endregion 489 ################################################################################### 490 ################################################################################### 491 # region Visibility 492 ################################################################################### 493 ################################################################################### 494 495 @property 496 def visibility(self) -> str: 497 """ 498 str: Function visibility 499 """ 500 assert self._visibility is not None 501 return self._visibility 502 503 @visibility.setter 504 def visibility(self, v: str): 505 self._visibility = v 506 507 def set_visibility(self, v: str) -> None: 508 self._visibility = v 509 510 @property 511 def view(self) -> bool: 512 """ 513 bool: True if the function is declared as view 514 """ 515 return self._view 516 517 @view.setter 518 def view(self, v: bool): 519 self._view = v 520 521 @property 522 def pure(self) -> bool: 523 """ 524 bool: True if the function is declared as pure 525 """ 526 return self._pure 527 528 @pure.setter 529 def pure(self, p: bool): 530 self._pure = p 531 532 @property 533 def is_shadowed(self) -> bool: 534 return self._is_shadowed 535 536 @is_shadowed.setter 537 def is_shadowed(self, is_shadowed): 538 self._is_shadowed = is_shadowed 539 540 @property 541 def shadows(self) -> bool: 542 return self._shadows 543 544 @shadows.setter 545 def shadows(self, _shadows: bool): 546 self._shadows = _shadows 547 548 # endregion 549 ################################################################################### 550 ################################################################################### 551 # region Function's body 552 ################################################################################### 553 ################################################################################### 554 555 @property 556 def is_implemented(self) -> bool: 557 """ 558 bool: True if the function is implemented 559 """ 560 return self._is_implemented 561 562 @is_implemented.setter 563 def is_implemented(self, is_impl: bool): 564 self._is_implemented = is_impl 565 566 @property 567 def is_empty(self) -> bool: 568 """ 569 bool: True if the function is empty, None if the function is an interface 570 """ 571 return self._is_empty 572 573 @is_empty.setter 574 def is_empty(self, empty: bool): 575 self._is_empty = empty 576 577 # endregion 578 ################################################################################### 579 ################################################################################### 580 # region Nodes 581 ################################################################################### 582 ################################################################################### 583 584 @property 585 def nodes(self) -> List["Node"]: 586 """ 587 list(Node): List of the nodes 588 """ 589 return list(self._nodes) 590 591 @nodes.setter 592 def nodes(self, nodes: List["Node"]): 593 self._nodes = nodes 594 595 @property 596 def entry_point(self) -> Optional["Node"]: 597 """ 598 Node: Entry point of the function 599 """ 600 return self._entry_point 601 602 @entry_point.setter 603 def entry_point(self, node: "Node"): 604 self._entry_point = node 605 606 def add_node(self, node: "Node") -> None: 607 if not self._entry_point: 608 self._entry_point = node 609 self._nodes.append(node) 610 611 @property 612 def nodes_ordered_dominators(self) -> List["Node"]: 613 # TODO: does not work properly; most likely due to modifier call 614 # This will not work for modifier call that lead to multiple nodes 615 # from slither.core.cfg.node import NodeType 616 if self._nodes_ordered_dominators is None: 617 self._nodes_ordered_dominators = [] 618 if self.entry_point: 619 self._compute_nodes_ordered_dominators(self.entry_point) 620 621 for node in self.nodes: 622 # if node.type == NodeType.OTHER_ENTRYPOINT: 623 if not node in self._nodes_ordered_dominators: 624 self._compute_nodes_ordered_dominators(node) 625 626 return self._nodes_ordered_dominators 627 628 def _compute_nodes_ordered_dominators(self, node: "Node"): 629 assert self._nodes_ordered_dominators is not None 630 if node in self._nodes_ordered_dominators: 631 return 632 self._nodes_ordered_dominators.append(node) 633 for dom in node.dominance_exploration_ordered: 634 self._compute_nodes_ordered_dominators(dom) 635 636 # endregion 637 ################################################################################### 638 ################################################################################### 639 # region Parameters 640 ################################################################################### 641 ################################################################################### 642 643 @property 644 def parameters(self) -> List["LocalVariable"]: 645 """ 646 list(LocalVariable): List of the parameters 647 """ 648 return list(self._parameters) 649 650 def add_parameters(self, p: "LocalVariable") -> None: 651 self._parameters.append(p) 652 653 @property 654 def parameters_ssa(self) -> List["LocalIRVariable"]: 655 """ 656 list(LocalIRVariable): List of the parameters (SSA form) 657 """ 658 return list(self._parameters_ssa) 659 660 def add_parameter_ssa(self, var: "LocalIRVariable") -> None: 661 self._parameters_ssa.append(var) 662 663 def parameters_src(self) -> SourceMapping: 664 return self._parameters_src 665 666 # endregion 667 ################################################################################### 668 ################################################################################### 669 # region Return values 670 ################################################################################### 671 ################################################################################### 672 673 @property 674 def return_type(self) -> Optional[List[Type]]: 675 """ 676 Return the list of return type 677 If no return, return None 678 """ 679 returns = self.returns 680 if returns: 681 return [r.type for r in returns] 682 return None 683 684 def returns_src(self) -> SourceMapping: 685 return self._returns_src 686 687 @property 688 def type(self) -> Optional[List[Type]]: 689 """ 690 Return the list of return type 691 If no return, return None 692 Alias of return_type 693 """ 694 return self.return_type 695 696 @property 697 def returns(self) -> List["LocalVariable"]: 698 """ 699 list(LocalVariable): List of the return variables 700 """ 701 return list(self._returns) 702 703 def add_return(self, r: "LocalVariable") -> None: 704 self._returns.append(r) 705 706 @property 707 def returns_ssa(self) -> List["LocalIRVariable"]: 708 """ 709 list(LocalIRVariable): List of the return variables (SSA form) 710 """ 711 return list(self._returns_ssa) 712 713 def add_return_ssa(self, var: "LocalIRVariable") -> None: 714 self._returns_ssa.append(var) 715 716 # endregion 717 ################################################################################### 718 ################################################################################### 719 # region Modifiers 720 ################################################################################### 721 ################################################################################### 722 723 @property 724 def modifiers(self) -> List[Union["Contract", "Function"]]: 725 """ 726 list(Modifier): List of the modifiers 727 Can be contract for constructor's calls 728 729 """ 730 return [c.modifier for c in self._modifiers] 731 732 def add_modifier(self, modif: "ModifierStatements") -> None: 733 self._modifiers.append(modif) 734 735 @property 736 def modifiers_statements(self) -> List[ModifierStatements]: 737 """ 738 list(ModifierCall): List of the modifiers call (include expression and irs) 739 """ 740 return list(self._modifiers) 741 742 @property 743 def explicit_base_constructor_calls(self) -> List["Function"]: 744 """ 745 list(Function): List of the base constructors called explicitly by this presumed constructor definition. 746 747 Base constructors implicitly or explicitly called by the contract definition will not be 748 included. 749 """ 750 # This is a list of contracts internally, so we convert it to a list of constructor functions. 751 return [ 752 c.modifier.constructors_declared 753 for c in self._explicit_base_constructor_calls 754 if c.modifier.constructors_declared 755 ] 756 757 @property 758 def explicit_base_constructor_calls_statements(self) -> List[ModifierStatements]: 759 """ 760 list(ModifierCall): List of the base constructors called explicitly by this presumed constructor definition. 761 762 """ 763 # This is a list of contracts internally, so we convert it to a list of constructor functions. 764 return list(self._explicit_base_constructor_calls) 765 766 def add_explicit_base_constructor_calls_statements(self, modif: ModifierStatements) -> None: 767 self._explicit_base_constructor_calls.append(modif) 768 769 # endregion 770 ################################################################################### 771 ################################################################################### 772 # region Variables 773 ################################################################################### 774 ################################################################################### 775 776 @property 777 def variables(self) -> List[LocalVariable]: 778 """ 779 Return all local variables 780 Include paramters and return values 781 """ 782 return list(self._variables.values()) 783 784 @property 785 def local_variables(self) -> List[LocalVariable]: 786 """ 787 Return all local variables (dont include paramters and return values) 788 """ 789 return list(set(self.variables) - set(self.returns) - set(self.parameters)) 790 791 @property 792 def variables_as_dict(self) -> Dict[str, LocalVariable]: 793 return self._variables 794 795 @property 796 def variables_read(self) -> List["Variable"]: 797 """ 798 list(Variable): Variables read (local/state/solidity) 799 """ 800 return list(self._vars_read) 801 802 @property 803 def variables_written(self) -> List["Variable"]: 804 """ 805 list(Variable): Variables written (local/state/solidity) 806 """ 807 return list(self._vars_written) 808 809 @property 810 def state_variables_read(self) -> List["StateVariable"]: 811 """ 812 list(StateVariable): State variables read 813 """ 814 return list(self._state_vars_read) 815 816 @property 817 def solidity_variables_read(self) -> List["SolidityVariable"]: 818 """ 819 list(SolidityVariable): Solidity variables read 820 """ 821 return list(self._solidity_vars_read) 822 823 @property 824 def state_variables_written(self) -> List["StateVariable"]: 825 """ 826 list(StateVariable): State variables written 827 """ 828 return list(self._state_vars_written) 829 830 @property 831 def variables_read_or_written(self) -> List["Variable"]: 832 """ 833 list(Variable): Variables read or written (local/state/solidity) 834 """ 835 return list(self._vars_read_or_written) 836 837 @property 838 def variables_read_as_expression(self) -> List["Expression"]: 839 return self._expression_vars_read 840 841 @property 842 def variables_written_as_expression(self) -> List["Expression"]: 843 return self._expression_vars_written 844 845 @property 846 def slithir_variables(self) -> List["SlithIRVariable"]: 847 """ 848 Temporary and Reference Variables (not SSA form) 849 """ 850 851 return list(self._slithir_variables) 852 853 # endregion 854 ################################################################################### 855 ################################################################################### 856 # region Calls 857 ################################################################################### 858 ################################################################################### 859 860 @property 861 def internal_calls(self) -> List["InternalCallType"]: 862 """ 863 list(Function or SolidityFunction): List of function calls (that does not create a transaction) 864 """ 865 return list(self._internal_calls) 866 867 @property 868 def solidity_calls(self) -> List[SolidityFunction]: 869 """ 870 list(SolidityFunction): List of Soldity calls 871 """ 872 return list(self._solidity_calls) 873 874 @property 875 def high_level_calls(self) -> List["HighLevelCallType"]: 876 """ 877 list((Contract, Function|Variable)): 878 List of high level calls (external calls). 879 A variable is called in case of call to a public state variable 880 Include library calls 881 """ 882 return list(self._high_level_calls) 883 884 @property 885 def library_calls(self) -> List["LibraryCallType"]: 886 """ 887 list((Contract, Function)): 888 """ 889 return list(self._library_calls) 890 891 @property 892 def low_level_calls(self) -> List["LowLevelCallType"]: 893 """ 894 list((Variable|SolidityVariable, str)): List of low_level call 895 A low level call is defined by 896 - the variable called 897 - the name of the function (call/delegatecall/codecall) 898 """ 899 return list(self._low_level_calls) 900 901 @property 902 def external_calls_as_expressions(self) -> List["Expression"]: 903 """ 904 list(ExpressionCall): List of message calls (that creates a transaction) 905 """ 906 return list(self._external_calls_as_expressions) 907 908 # endregion 909 ################################################################################### 910 ################################################################################### 911 # region Expressions 912 ################################################################################### 913 ################################################################################### 914 915 @property 916 def calls_as_expressions(self) -> List["Expression"]: 917 return self._expression_calls 918 919 @property 920 def expressions(self) -> List["Expression"]: 921 """ 922 list(Expression): List of the expressions 923 """ 924 if self._expressions is None: 925 expressionss = [n.expression for n in self.nodes] 926 expressions = [e for e in expressionss if e] 927 self._expressions = expressions 928 return self._expressions 929 930 @property 931 def return_values(self) -> List["SlithIRVariable"]: 932 """ 933 list(Return Values): List of the return values 934 """ 935 from slither.core.cfg.node import NodeType 936 from slither.slithir.operations import Return 937 from slither.slithir.variables import Constant 938 939 if self._return_values is None: 940 return_values = [] 941 returns = [n for n in self.nodes if n.type == NodeType.RETURN] 942 [ # pylint: disable=expression-not-assigned 943 return_values.extend(ir.values) 944 for node in returns 945 for ir in node.irs 946 if isinstance(ir, Return) 947 ] 948 self._return_values = list({x for x in return_values if not isinstance(x, Constant)}) 949 return self._return_values 950 951 @property 952 def return_values_ssa(self) -> List["SlithIRVariable"]: 953 """ 954 list(Return Values in SSA form): List of the return values in ssa form 955 """ 956 from slither.core.cfg.node import NodeType 957 from slither.slithir.operations import Return 958 from slither.slithir.variables import Constant 959 960 if self._return_values_ssa is None: 961 return_values_ssa = [] 962 returns = [n for n in self.nodes if n.type == NodeType.RETURN] 963 [ # pylint: disable=expression-not-assigned 964 return_values_ssa.extend(ir.values) 965 for node in returns 966 for ir in node.irs_ssa 967 if isinstance(ir, Return) 968 ] 969 self._return_values_ssa = list( 970 {x for x in return_values_ssa if not isinstance(x, Constant)} 971 ) 972 return self._return_values_ssa 973 974 # endregion 975 ################################################################################### 976 ################################################################################### 977 # region SlithIR 978 ################################################################################### 979 ################################################################################### 980 981 @property 982 def slithir_operations(self) -> List["Operation"]: 983 """ 984 list(Operation): List of the slithir operations 985 """ 986 if self._slithir_operations is None: 987 operationss = [n.irs for n in self.nodes] 988 operations = [item for sublist in operationss for item in sublist if item] 989 self._slithir_operations = operations 990 return self._slithir_operations 991 992 @property 993 def slithir_ssa_operations(self) -> List["Operation"]: 994 """ 995 list(Operation): List of the slithir operations (SSA) 996 """ 997 if self._slithir_ssa_operations is None: 998 operationss = [n.irs_ssa for n in self.nodes] 999 operations = [item for sublist in operationss for item in sublist if item] 1000 self._slithir_ssa_operations = operations 1001 return self._slithir_ssa_operations 1002 1003 # endregion 1004 ################################################################################### 1005 ################################################################################### 1006 # region Signature 1007 ################################################################################### 1008 ################################################################################### 1009 1010 @property 1011 def solidity_signature(self) -> str: 1012 """ 1013 Return a signature following the Solidity Standard 1014 Contract and converted into address 1015 1016 It might still keep internal types (ex: structure name) for internal functions. 1017 The reason is that internal functions allows recursive structure definition, which 1018 can't be converted following the Solidity stand ard 1019 1020 :return: the solidity signature 1021 """ 1022 if self._solidity_signature is None: 1023 parameters = [ 1024 convert_type_for_solidity_signature_to_string(x.type) for x in self.parameters 1025 ] 1026 self._solidity_signature = self.name + "(" + ",".join(parameters) + ")" 1027 return self._solidity_signature 1028 1029 @property 1030 def signature(self) -> Tuple[str, List[str], List[str]]: 1031 """ 1032 (str, list(str), list(str)): Function signature as 1033 (name, list parameters type, list return values type) 1034 """ 1035 # FIXME memoizing this function is not working properly for vyper 1036 # if self._signature is None: 1037 return ( 1038 self.name, 1039 [str(x.type) for x in self.parameters], 1040 [str(x.type) for x in self.returns], 1041 ) 1042 # self._signature = signature 1043 # return self._signature 1044 1045 @property 1046 def signature_str(self) -> str: 1047 """ 1048 str: func_name(type1,type2) returns (type3) 1049 Return the function signature as a str (contains the return values) 1050 """ 1051 if self._signature_str is None: 1052 name, parameters, returnVars = self.signature 1053 self._signature_str = ( 1054 name + "(" + ",".join(parameters) + ") returns(" + ",".join(returnVars) + ")" 1055 ) 1056 return self._signature_str 1057 1058 # endregion 1059 ################################################################################### 1060 ################################################################################### 1061 # region Functions 1062 ################################################################################### 1063 ################################################################################### 1064 1065 @property 1066 @abstractmethod 1067 def functions_shadowed(self) -> List["Function"]: 1068 pass 1069 1070 # endregion 1071 ################################################################################### 1072 ################################################################################### 1073 # region Reachable 1074 ################################################################################### 1075 ################################################################################### 1076 1077 @property 1078 def reachable_from_nodes(self) -> Set[ReacheableNode]: 1079 """ 1080 Return 1081 ReacheableNode 1082 """ 1083 return self._reachable_from_nodes 1084 1085 @property 1086 def reachable_from_functions(self) -> Set["Function"]: 1087 return self._reachable_from_functions 1088 1089 @property 1090 def all_reachable_from_functions(self) -> Set["Function"]: 1091 """ 1092 Give the recursive version of reachable_from_functions (all the functions that lead to call self in the CFG) 1093 """ 1094 if self._all_reachable_from_functions is None: 1095 functions: Set["Function"] = set() 1096 1097 new_functions = self.reachable_from_functions 1098 # iterate until we have are finding new functions 1099 while new_functions and not new_functions.issubset(functions): 1100 functions = functions.union(new_functions) 1101 # Use a temporary set, because we iterate over new_functions 1102 new_functionss: Set["Function"] = set() 1103 for f in new_functions: 1104 new_functionss = new_functionss.union(f.reachable_from_functions) 1105 new_functions = new_functionss - functions 1106 1107 self._all_reachable_from_functions = functions 1108 return self._all_reachable_from_functions 1109 1110 def add_reachable_from_node(self, n: "Node", ir: "Operation") -> None: 1111 self._reachable_from_nodes.add(ReacheableNode(n, ir)) 1112 self._reachable_from_functions.add(n.function) 1113 1114 # endregion 1115 ################################################################################### 1116 ################################################################################### 1117 # region Recursive getters 1118 ################################################################################### 1119 ################################################################################### 1120 1121 def _explore_functions(self, f_new_values: Callable[["Function"], List]) -> List[Any]: 1122 values = f_new_values(self) 1123 explored = [self] 1124 to_explore = [ 1125 c for c in self.internal_calls if isinstance(c, Function) and c not in explored 1126 ] 1127 to_explore += [ 1128 c for (_, c) in self.library_calls if isinstance(c, Function) and c not in explored 1129 ] 1130 to_explore += [m for m in self.modifiers if m not in explored] 1131 1132 while to_explore: 1133 f = to_explore[0] 1134 to_explore = to_explore[1:] 1135 if f in explored: 1136 continue 1137 explored.append(f) 1138 1139 values += f_new_values(f) 1140 1141 to_explore += [ 1142 c 1143 for c in f.internal_calls 1144 if isinstance(c, Function) and c not in explored and c not in to_explore 1145 ] 1146 to_explore += [ 1147 c 1148 for (_, c) in f.library_calls 1149 if isinstance(c, Function) and c not in explored and c not in to_explore 1150 ] 1151 to_explore += [m for m in f.modifiers if m not in explored and m not in to_explore] 1152 1153 return list(set(values)) 1154 1155 def all_variables_read(self) -> List["Variable"]: 1156 """recursive version of variables_read""" 1157 if self._all_variables_read is None: 1158 self._all_variables_read = self._explore_functions(lambda x: x.variables_read) 1159 return self._all_variables_read 1160 1161 def all_variables_written(self) -> List["Variable"]: 1162 """recursive version of variables_written""" 1163 if self._all_variables_written is None: 1164 self._all_variables_written = self._explore_functions(lambda x: x.variables_written) 1165 return self._all_variables_written 1166 1167 def all_state_variables_read(self) -> List["StateVariable"]: 1168 """recursive version of variables_read""" 1169 if self._all_state_variables_read is None: 1170 self._all_state_variables_read = self._explore_functions( 1171 lambda x: x.state_variables_read 1172 ) 1173 return self._all_state_variables_read 1174 1175 def all_solidity_variables_read(self) -> List[SolidityVariable]: 1176 """recursive version of solidity_read""" 1177 if self._all_solidity_variables_read is None: 1178 self._all_solidity_variables_read = self._explore_functions( 1179 lambda x: x.solidity_variables_read 1180 ) 1181 return self._all_solidity_variables_read 1182 1183 def all_slithir_variables(self) -> List["SlithIRVariable"]: 1184 """recursive version of slithir_variables""" 1185 if self._all_slithir_variables is None: 1186 self._all_slithir_variables = self._explore_functions(lambda x: x.slithir_variables) 1187 return self._all_slithir_variables 1188 1189 def all_nodes(self) -> List["Node"]: 1190 """recursive version of nodes""" 1191 if self._all_nodes is None: 1192 self._all_nodes = self._explore_functions(lambda x: x.nodes) 1193 return self._all_nodes 1194 1195 def all_expressions(self) -> List["Expression"]: 1196 """recursive version of variables_read""" 1197 if self._all_expressions is None: 1198 self._all_expressions = self._explore_functions(lambda x: x.expressions) 1199 return self._all_expressions 1200 1201 def all_slithir_operations(self) -> List["Operation"]: 1202 if self._all_slithir_operations is None: 1203 self._all_slithir_operations = self._explore_functions(lambda x: x.slithir_operations) 1204 return self._all_slithir_operations 1205 1206 def all_state_variables_written(self) -> List[StateVariable]: 1207 """recursive version of variables_written""" 1208 if self._all_state_variables_written is None: 1209 self._all_state_variables_written = self._explore_functions( 1210 lambda x: x.state_variables_written 1211 ) 1212 return self._all_state_variables_written 1213 1214 def all_internal_calls(self) -> List["InternalCallType"]: 1215 """recursive version of internal_calls""" 1216 if self._all_internals_calls is None: 1217 self._all_internals_calls = self._explore_functions(lambda x: x.internal_calls) 1218 return self._all_internals_calls 1219 1220 def all_low_level_calls(self) -> List["LowLevelCallType"]: 1221 """recursive version of low_level calls""" 1222 if self._all_low_level_calls is None: 1223 self._all_low_level_calls = self._explore_functions(lambda x: x.low_level_calls) 1224 return self._all_low_level_calls 1225 1226 def all_high_level_calls(self) -> List["HighLevelCallType"]: 1227 """recursive version of high_level calls""" 1228 if self._all_high_level_calls is None: 1229 self._all_high_level_calls = self._explore_functions(lambda x: x.high_level_calls) 1230 return self._all_high_level_calls 1231 1232 def all_library_calls(self) -> List["LibraryCallType"]: 1233 """recursive version of library calls""" 1234 if self._all_library_calls is None: 1235 self._all_library_calls = self._explore_functions(lambda x: x.library_calls) 1236 return self._all_library_calls 1237 1238 def all_solidity_calls(self) -> List[SolidityFunction]: 1239 """recursive version of solidity calls""" 1240 if self._all_solidity_calls is None: 1241 self._all_solidity_calls = self._explore_functions(lambda x: x.solidity_calls) 1242 return self._all_solidity_calls 1243 1244 @staticmethod 1245 def _explore_func_cond_read(func: "Function", include_loop: bool) -> List["StateVariable"]: 1246 ret = [n.state_variables_read for n in func.nodes if n.is_conditional(include_loop)] 1247 return [item for sublist in ret for item in sublist] 1248 1249 def all_conditional_state_variables_read(self, include_loop=True) -> List["StateVariable"]: 1250 """ 1251 Return the state variable used in a condition 1252 1253 Over approximate and also return index access 1254 It won't work if the variable is assigned to a temp variable 1255 """ 1256 if include_loop: 1257 if self._all_conditional_state_variables_read_with_loop is None: 1258 self._all_conditional_state_variables_read_with_loop = self._explore_functions( 1259 lambda x: self._explore_func_cond_read(x, include_loop) 1260 ) 1261 return self._all_conditional_state_variables_read_with_loop 1262 if self._all_conditional_state_variables_read is None: 1263 self._all_conditional_state_variables_read = self._explore_functions( 1264 lambda x: self._explore_func_cond_read(x, include_loop) 1265 ) 1266 return self._all_conditional_state_variables_read 1267 1268 @staticmethod 1269 def _solidity_variable_in_binary(node: "Node") -> List[SolidityVariable]: 1270 from slither.slithir.operations.binary import Binary 1271 1272 ret = [] 1273 for ir in node.irs: 1274 if isinstance(ir, Binary): 1275 ret += ir.read 1276 return [var for var in ret if isinstance(var, SolidityVariable)] 1277 1278 @staticmethod 1279 def _explore_func_conditional( 1280 func: "Function", 1281 f: Callable[["Node"], List[SolidityVariable]], 1282 include_loop: bool, 1283 ) -> List[Any]: 1284 ret = [f(n) for n in func.nodes if n.is_conditional(include_loop)] 1285 return [item for sublist in ret for item in sublist] 1286 1287 def all_conditional_solidity_variables_read( 1288 self, include_loop: bool = True 1289 ) -> List[SolidityVariable]: 1290 """ 1291 Return the Soldiity variables directly used in a condtion 1292 1293 Use of the IR to filter index access 1294 Assumption: the solidity vars are used directly in the conditional node 1295 It won't work if the variable is assigned to a temp variable 1296 """ 1297 if include_loop: 1298 if self._all_conditional_solidity_variables_read_with_loop is None: 1299 self._all_conditional_solidity_variables_read_with_loop = self._explore_functions( 1300 lambda x: self._explore_func_conditional( 1301 x, self._solidity_variable_in_binary, include_loop 1302 ) 1303 ) 1304 return self._all_conditional_solidity_variables_read_with_loop 1305 1306 if self._all_conditional_solidity_variables_read is None: 1307 self._all_conditional_solidity_variables_read = self._explore_functions( 1308 lambda x: self._explore_func_conditional( 1309 x, self._solidity_variable_in_binary, include_loop 1310 ) 1311 ) 1312 return self._all_conditional_solidity_variables_read 1313 1314 @staticmethod 1315 def _solidity_variable_in_internal_calls(node: "Node") -> List[SolidityVariable]: 1316 from slither.slithir.operations.internal_call import InternalCall 1317 1318 ret = [] 1319 for ir in node.irs: 1320 if isinstance(ir, InternalCall): 1321 ret += ir.read 1322 return [var for var in ret if isinstance(var, SolidityVariable)] 1323 1324 @staticmethod 1325 def _explore_func_nodes( 1326 func: "Function", f: Callable[["Node"], List[SolidityVariable]] 1327 ) -> List[Union[Any, SolidityVariableComposed]]: 1328 ret = [f(n) for n in func.nodes] 1329 return [item for sublist in ret for item in sublist] 1330 1331 def all_solidity_variables_used_as_args(self) -> List[SolidityVariable]: 1332 """ 1333 Return the Soldiity variables directly used in a call 1334 1335 Use of the IR to filter index access 1336 Used to catch check(msg.sender) 1337 """ 1338 if self._all_solidity_variables_used_as_args is None: 1339 self._all_solidity_variables_used_as_args = self._explore_functions( 1340 lambda x: self._explore_func_nodes(x, self._solidity_variable_in_internal_calls) 1341 ) 1342 return self._all_solidity_variables_used_as_args 1343 1344 # endregion 1345 ################################################################################### 1346 ################################################################################### 1347 # region Visitor 1348 ################################################################################### 1349 ################################################################################### 1350 1351 def apply_visitor(self, Visitor: Callable) -> List: 1352 """ 1353 Apply a visitor to all the function expressions 1354 Args: 1355 Visitor: slither.visitors 1356 Returns 1357 list(): results of the visit 1358 """ 1359 expressions = self.expressions 1360 v = [Visitor(e).result() for e in expressions] 1361 return [item for sublist in v for item in sublist] 1362 1363 # endregion 1364 ################################################################################### 1365 ################################################################################### 1366 # region Getters from/to object 1367 ################################################################################### 1368 ################################################################################### 1369 1370 def get_local_variable_from_name(self, variable_name: str) -> Optional[LocalVariable]: 1371 """ 1372 Return a local variable from a name 1373 1374 Args: 1375 variable_name (str): name of the variable 1376 Returns: 1377 LocalVariable 1378 """ 1379 return next((v for v in self.variables if v.name == variable_name), None) 1380 1381 # endregion 1382 ################################################################################### 1383 ################################################################################### 1384 # region Export 1385 ################################################################################### 1386 ################################################################################### 1387 1388 def cfg_to_dot(self, filename: str): 1389 """ 1390 Export the function to a dot file 1391 Args: 1392 filename (str) 1393 """ 1394 with open(filename, "w", encoding="utf8") as f: 1395 f.write("digraph{\n") 1396 for node in self.nodes: 1397 f.write(f'{node.node_id}[label="{str(node)}"];\n') 1398 for son in node.sons: 1399 f.write(f"{node.node_id}->{son.node_id};\n") 1400 1401 f.write("}\n") 1402 1403 def dominator_tree_to_dot(self, filename: str): 1404 """ 1405 Export the dominator tree of the function to a dot file 1406 Args: 1407 filename (str) 1408 """ 1409 1410 def description(node): 1411 desc = f"{node}\n" 1412 desc += f"id: {node.node_id}" 1413 if node.dominance_frontier: 1414 desc += f"\ndominance frontier: {[n.node_id for n in node.dominance_frontier]}" 1415 return desc 1416 1417 with open(filename, "w", encoding="utf8") as f: 1418 f.write("digraph{\n") 1419 for node in self.nodes: 1420 f.write(f'{node.node_id}[label="{description(node)}"];\n') 1421 if node.immediate_dominator: 1422 f.write(f"{node.immediate_dominator.node_id}->{node.node_id};\n") 1423 1424 f.write("}\n") 1425 1426 def slithir_cfg_to_dot(self, filename: str): 1427 """ 1428 Export the CFG to a DOT file. The nodes includes the Solidity expressions and the IRs 1429 :param filename: 1430 :return: 1431 """ 1432 content = self.slithir_cfg_to_dot_str() 1433 with open(filename, "w", encoding="utf8") as f: 1434 f.write(content) 1435 1436 def slithir_cfg_to_dot_str(self, skip_expressions: bool = False) -> str: 1437 """ 1438 Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs 1439 :return: the DOT content 1440 :rtype: str 1441 """ 1442 from slither.core.cfg.node import NodeType 1443 1444 content = "" 1445 content += "digraph{\n" 1446 for node in self.nodes: 1447 label = f"Node Type: {node.type.value} {node.node_id}\n" 1448 if node.expression and not skip_expressions: 1449 label += f"\nEXPRESSION:\n{node.expression}\n" 1450 if node.irs and not skip_expressions: 1451 label += "\nIRs:\n" + "\n".join([str(ir) for ir in node.irs]) 1452 content += f'{node.node_id}[label="{label}"];\n' 1453 if node.type in [NodeType.IF, NodeType.IFLOOP]: 1454 true_node = node.son_true 1455 if true_node: 1456 content += f'{node.node_id}->{true_node.node_id}[label="True"];\n' 1457 false_node = node.son_false 1458 if false_node: 1459 content += f'{node.node_id}->{false_node.node_id}[label="False"];\n' 1460 else: 1461 for son in node.sons: 1462 content += f"{node.node_id}->{son.node_id};\n" 1463 1464 content += "}\n" 1465 return content 1466 1467 # endregion 1468 ################################################################################### 1469 ################################################################################### 1470 # region Summary information 1471 ################################################################################### 1472 ################################################################################### 1473 1474 def is_reading(self, variable: "Variable") -> bool: 1475 """ 1476 Check if the function reads the variable 1477 Args: 1478 variable (Variable): 1479 Returns: 1480 bool: True if the variable is read 1481 """ 1482 return variable in self.variables_read 1483 1484 def is_reading_in_conditional_node(self, variable: "Variable") -> bool: 1485 """ 1486 Check if the function reads the variable in a IF node 1487 Args: 1488 variable (Variable): 1489 Returns: 1490 bool: True if the variable is read 1491 """ 1492 variables_reads = [n.variables_read for n in self.nodes if n.contains_if()] 1493 variables_read = [item for sublist in variables_reads for item in sublist] 1494 return variable in variables_read 1495 1496 def is_reading_in_require_or_assert(self, variable: "Variable") -> bool: 1497 """ 1498 Check if the function reads the variable in an require or assert 1499 Args: 1500 variable (Variable): 1501 Returns: 1502 bool: True if the variable is read 1503 """ 1504 variables_reads = [n.variables_read for n in self.nodes if n.contains_require_or_assert()] 1505 variables_read = [item for sublist in variables_reads for item in sublist] 1506 return variable in variables_read 1507 1508 def is_writing(self, variable: "Variable") -> bool: 1509 """ 1510 Check if the function writes the variable 1511 Args: 1512 variable (Variable): 1513 Returns: 1514 bool: True if the variable is written 1515 """ 1516 return variable in self.variables_written 1517 1518 @abstractmethod 1519 def get_summary( 1520 self, 1521 ) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]: 1522 pass 1523 1524 def is_protected(self) -> bool: 1525 """ 1526 Determine if the function is protected using a check on msg.sender 1527 1528 Consider onlyOwner as a safe modifier. 1529 If the owner functionality is incorrectly implemented, this will lead to incorrectly 1530 classify the function as protected 1531 1532 Otherwise only detects if msg.sender is directly used in a condition 1533 For example, it wont work for: 1534 address a = msg.sender 1535 require(a == owner) 1536 Returns 1537 (bool) 1538 """ 1539 1540 if self._is_protected is None: 1541 if self.is_constructor: 1542 self._is_protected = True 1543 return True 1544 if "onlyOwner" in [m.name for m in self.modifiers]: 1545 self._is_protected = True 1546 return True 1547 conditional_vars = self.all_conditional_solidity_variables_read(include_loop=False) 1548 args_vars = self.all_solidity_variables_used_as_args() 1549 self._is_protected = ( 1550 SolidityVariableComposed("msg.sender") in conditional_vars + args_vars 1551 ) 1552 return self._is_protected 1553 1554 @property 1555 def is_reentrant(self) -> bool: 1556 """ 1557 Determine if the function can be re-entered 1558 """ 1559 reentrancy_modifier = "nonReentrant" 1560 1561 if self.function_language == FunctionLanguage.Vyper: 1562 reentrancy_modifier = "nonreentrant(lock)" 1563 1564 # TODO: compare with hash of known nonReentrant modifier instead of the name 1565 if reentrancy_modifier in [m.name for m in self.modifiers]: 1566 return False 1567 1568 if self.visibility in ["public", "external"]: 1569 return True 1570 1571 # If it's an internal function, check if all its entry points have the nonReentrant modifier 1572 all_entry_points = [ 1573 f for f in self.all_reachable_from_functions if f.visibility in ["public", "external"] 1574 ] 1575 if not all_entry_points: 1576 return True 1577 return not all( 1578 (reentrancy_modifier in [m.name for m in f.modifiers] for f in all_entry_points) 1579 ) 1580 1581 # endregion 1582 ################################################################################### 1583 ################################################################################### 1584 # region Analyses 1585 ################################################################################### 1586 ################################################################################### 1587 1588 def _analyze_read_write(self) -> None: 1589 """Compute variables read/written/...""" 1590 write_var = [x.variables_written_as_expression for x in self.nodes] 1591 write_var = [x for x in write_var if x] 1592 write_var = [item for sublist in write_var for item in sublist] 1593 write_var = list(set(write_var)) 1594 # Remove duplicate if they share the same string representation 1595 write_var = [ 1596 next(obj) 1597 for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x)) 1598 ] 1599 self._expression_vars_written = write_var 1600 1601 write_var = [x.variables_written for x in self.nodes] 1602 write_var = [x for x in write_var if x] 1603 write_var = [item for sublist in write_var for item in sublist] 1604 write_var = list(set(write_var)) 1605 # Remove duplicate if they share the same string representation 1606 write_var = [ 1607 next(obj) 1608 for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x)) 1609 ] 1610 self._vars_written = write_var 1611 1612 read_var = [x.variables_read_as_expression for x in self.nodes] 1613 read_var = [x for x in read_var if x] 1614 read_var = [item for sublist in read_var for item in sublist] 1615 # Remove duplicate if they share the same string representation 1616 read_var = [ 1617 next(obj) 1618 for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x)) 1619 ] 1620 self._expression_vars_read = read_var 1621 1622 read_var = [x.variables_read for x in self.nodes] 1623 read_var = [x for x in read_var if x] 1624 read_var = [item for sublist in read_var for item in sublist] 1625 # Remove duplicate if they share the same string representation 1626 read_var = [ 1627 next(obj) 1628 for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x)) 1629 ] 1630 self._vars_read = read_var 1631 1632 self._state_vars_written = [ 1633 x for x in self.variables_written if isinstance(x, StateVariable) 1634 ] 1635 self._state_vars_read = [x for x in self.variables_read if isinstance(x, StateVariable)] 1636 self._solidity_vars_read = [ 1637 x for x in self.variables_read if isinstance(x, SolidityVariable) 1638 ] 1639 1640 self._vars_read_or_written = self._vars_written + self._vars_read 1641 1642 slithir_variables = [x.slithir_variables for x in self.nodes] 1643 slithir_variables = [x for x in slithir_variables if x] 1644 self._slithir_variables = [item for sublist in slithir_variables for item in sublist] 1645 1646 def _analyze_calls(self) -> None: 1647 calls = [x.calls_as_expression for x in self.nodes] 1648 calls = [x for x in calls if x] 1649 calls = [item for sublist in calls for item in sublist] 1650 self._expression_calls = list(set(calls)) 1651 1652 internal_calls = [x.internal_calls for x in self.nodes] 1653 internal_calls = [x for x in internal_calls if x] 1654 internal_calls = [item for sublist in internal_calls for item in sublist] 1655 self._internal_calls = list(set(internal_calls)) 1656 1657 self._solidity_calls = [c for c in internal_calls if isinstance(c, SolidityFunction)] 1658 1659 low_level_calls = [x.low_level_calls for x in self.nodes] 1660 low_level_calls = [x for x in low_level_calls if x] 1661 low_level_calls = [item for sublist in low_level_calls for item in sublist] 1662 self._low_level_calls = list(set(low_level_calls)) 1663 1664 high_level_calls = [x.high_level_calls for x in self.nodes] 1665 high_level_calls = [x for x in high_level_calls if x] 1666 high_level_calls = [item for sublist in high_level_calls for item in sublist] 1667 self._high_level_calls = list(set(high_level_calls)) 1668 1669 library_calls = [x.library_calls for x in self.nodes] 1670 library_calls = [x for x in library_calls if x] 1671 library_calls = [item for sublist in library_calls for item in sublist] 1672 self._library_calls = list(set(library_calls)) 1673 1674 external_calls_as_expressions = [x.external_calls_as_expressions for x in self.nodes] 1675 external_calls_as_expressions = [x for x in external_calls_as_expressions if x] 1676 external_calls_as_expressions = [ 1677 item for sublist in external_calls_as_expressions for item in sublist 1678 ] 1679 self._external_calls_as_expressions = list(set(external_calls_as_expressions)) 1680 1681 # endregion 1682 ################################################################################### 1683 ################################################################################### 1684 # region Nodes 1685 ################################################################################### 1686 ################################################################################### 1687 1688 def new_node( 1689 self, node_type: "NodeType", src: Union[str, Dict], scope: Union[Scope, "Function"] 1690 ) -> "Node": 1691 from slither.core.cfg.node import Node 1692 1693 node = Node(node_type, self._counter_nodes, scope, self.file_scope) 1694 node.set_offset(src, self.compilation_unit) 1695 self._counter_nodes += 1 1696 node.set_function(self) 1697 self._nodes.append(node) 1698 1699 return node 1700 1701 # endregion 1702 ################################################################################### 1703 ################################################################################### 1704 # region SlithIr and SSA 1705 ################################################################################### 1706 ################################################################################### 1707 1708 def _get_last_ssa_variable_instances( 1709 self, target_state: bool, target_local: bool 1710 ) -> Dict[str, Set["SlithIRVariable"]]: 1711 # pylint: disable=too-many-locals,too-many-branches 1712 from slither.slithir.variables import ReferenceVariable 1713 from slither.slithir.operations import OperationWithLValue 1714 from slither.core.cfg.node import NodeType 1715 1716 if not self.is_implemented: 1717 return {} 1718 1719 if self._entry_point is None: 1720 return {} 1721 # node, values 1722 to_explore: List[Tuple["Node", Dict]] = [(self._entry_point, {})] 1723 # node -> values 1724 explored: Dict = {} 1725 # name -> instances 1726 ret: Dict = {} 1727 1728 while to_explore: 1729 node, values = to_explore[0] 1730 to_explore = to_explore[1::] 1731 1732 if node.type != NodeType.ENTRYPOINT: 1733 for ir_ssa in node.irs_ssa: 1734 if isinstance(ir_ssa, OperationWithLValue): 1735 lvalue = ir_ssa.lvalue 1736 if isinstance(lvalue, ReferenceVariable): 1737 lvalue = lvalue.points_to_origin 1738 if isinstance(lvalue, StateVariable) and target_state: 1739 values[lvalue.canonical_name] = {lvalue} 1740 if isinstance(lvalue, LocalVariable) and target_local: 1741 values[lvalue.canonical_name] = {lvalue} 1742 1743 # Check for fixpoint 1744 if node in explored: 1745 if values == explored[node]: 1746 continue 1747 for k, instances in values.items(): 1748 if k not in explored[node]: 1749 explored[node][k] = set() 1750 explored[node][k] |= instances 1751 values = explored[node] 1752 else: 1753 explored[node] = values 1754 1755 # Return condition 1756 if node.will_return: 1757 for name, instances in values.items(): 1758 if name not in ret: 1759 ret[name] = set() 1760 ret[name] |= instances 1761 1762 for son in node.sons: 1763 to_explore.append((son, dict(values))) 1764 1765 return ret 1766 1767 def get_last_ssa_state_variables_instances( 1768 self, 1769 ) -> Dict[str, Set["SlithIRVariable"]]: 1770 return self._get_last_ssa_variable_instances(target_state=True, target_local=False) 1771 1772 def get_last_ssa_local_variables_instances( 1773 self, 1774 ) -> Dict[str, Set["SlithIRVariable"]]: 1775 return self._get_last_ssa_variable_instances(target_state=False, target_local=True) 1776 1777 @staticmethod 1778 def _unchange_phi(ir: "Operation") -> bool: 1779 from slither.slithir.operations import Phi, PhiCallback 1780 1781 if not isinstance(ir, (Phi, PhiCallback)) or len(ir.rvalues) > 1: 1782 return False 1783 if not ir.rvalues: 1784 return True 1785 return ir.rvalues[0] == ir.lvalue 1786 1787 def fix_phi( 1788 self, 1789 last_state_variables_instances: Dict[str, List["StateVariable"]], 1790 initial_state_variables_instances: Dict[str, "StateVariable"], 1791 ) -> None: 1792 from slither.slithir.operations import InternalCall, PhiCallback 1793 from slither.slithir.variables import Constant, StateIRVariable 1794 1795 for node in self.nodes: 1796 for ir in node.irs_ssa: 1797 if node == self.entry_point: 1798 if isinstance(ir.lvalue, StateIRVariable): 1799 additional = [initial_state_variables_instances[ir.lvalue.canonical_name]] 1800 additional += last_state_variables_instances[ir.lvalue.canonical_name] 1801 ir.rvalues = list(set(additional + ir.rvalues)) 1802 # function parameter 1803 else: 1804 # find index of the parameter 1805 idx = self.parameters.index(ir.lvalue.non_ssa_version) 1806 # find non ssa version of that index 1807 additional = [n.ir.arguments[idx] for n in self.reachable_from_nodes] 1808 additional = unroll(additional) 1809 additional = [a for a in additional if not isinstance(a, Constant)] 1810 ir.rvalues = list(set(additional + ir.rvalues)) 1811 if isinstance(ir, PhiCallback): 1812 callee_ir = ir.callee_ir 1813 if isinstance(callee_ir, InternalCall): 1814 last_ssa = callee_ir.function.get_last_ssa_state_variables_instances() 1815 if ir.lvalue.canonical_name in last_ssa: 1816 ir.rvalues = list(last_ssa[ir.lvalue.canonical_name]) 1817 else: 1818 ir.rvalues = [ir.lvalue] 1819 else: 1820 additional = last_state_variables_instances[ir.lvalue.canonical_name] 1821 ir.rvalues = list(set(additional + ir.rvalues)) 1822 1823 node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)] 1824 1825 def generate_slithir_and_analyze(self) -> None: 1826 1827 for node in self.nodes: 1828 node.slithir_generation() 1829 1830 self._analyze_read_write() 1831 self._analyze_calls() 1832 1833 @abstractmethod 1834 def generate_slithir_ssa(self, all_ssa_state_variables_instances): 1835 pass 1836 1837 def update_read_write_using_ssa(self) -> None: 1838 for node in self.nodes: 1839 node.update_read_write_using_ssa() 1840 self._analyze_read_write() 1841 1842 ################################################################################### 1843 ################################################################################### 1844 # region Built in definitions 1845 ################################################################################### 1846 ################################################################################### 1847 1848 def __str__(self) -> str: 1849 return self.name 1850 1851 # endregion
Function class
239 @property 240 def name(self) -> str: 241 """ 242 str: function name 243 """ 244 if self._name == "" and self._function_type == FunctionType.CONSTRUCTOR: 245 return "constructor" 246 if self._name == "" and self._function_type == FunctionType.FALLBACK: 247 return "fallback" 248 if self._function_type == FunctionType.RECEIVE: 249 return "receive" 250 if self._function_type == FunctionType.CONSTRUCTOR_VARIABLES: 251 return "slitherConstructorVariables" 252 if self._function_type == FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES: 253 return "slitherConstructorConstantVariables" 254 return self._name
str: function name
260 @property 261 def internal_scope(self) -> List[str]: 262 """ 263 Return a list of name representing the scope of the function 264 This is used to model nested functions declared in YUL 265 266 :return: 267 """ 268 return self._internal_scope
Return a list of name representing the scope of the function This is used to model nested functions declared in YUL
Returns
274 @property 275 def full_name(self) -> str: 276 """ 277 str: func_name(type1,type2) 278 Return the function signature without the return values 279 The difference between this function and solidity_function is that full_name does not translate the underlying 280 type (ex: structure, contract to address, ...) 281 """ 282 if self._full_name is None: 283 name, parameters, _ = self.signature 284 full_name = ".".join(self._internal_scope + [name]) + "(" + ",".join(parameters) + ")" 285 self._full_name = full_name 286 return self._full_name
str: func_name(type1,type2) Return the function signature without the return values The difference between this function and solidity_function is that full_name does not translate the underlying type (ex: structure, contract to address, ...)
288 @property 289 @abstractmethod 290 def canonical_name(self) -> str: 291 """ 292 str: contract.func_name(type1,type2) 293 Return the function signature without the return values 294 """ 295 return ""
str: contract.func_name(type1,type2) Return the function signature without the return values
305 def can_reenter(self, callstack: Optional[List[Union["Function", "Variable"]]] = None) -> bool: 306 """ 307 Check if the function can re-enter 308 Follow internal calls. 309 Do not consider CREATE as potential re-enter, but check if the 310 destination's constructor can contain a call (recurs. follow nested CREATE) 311 For Solidity > 0.5, filter access to public variables and constant/pure/view 312 For call to this. check if the destination can re-enter 313 Do not consider Send/Transfer as there is not enough gas 314 :param callstack: used internally to check for recursion 315 :return bool: 316 """ 317 from slither.slithir.operations import Call 318 319 if self._can_reenter is None: 320 self._can_reenter = False 321 for ir in self.all_slithir_operations(): 322 if isinstance(ir, Call) and ir.can_reenter(callstack): 323 self._can_reenter = True 324 return True 325 return self._can_reenter
Check if the function can re-enter Follow internal calls. Do not consider CREATE as potential re-enter, but check if the destination's constructor can contain a call (recurs. follow nested CREATE) For Solidity > 0.5, filter access to public variables and constant/pure/view For call to this. check if the destination can re-enter Do not consider Send/Transfer as there is not enough gas
Parameters
- callstack: used internally to check for recursion
Returns
327 def can_send_eth(self) -> bool: 328 """ 329 Check if the function or any internal (not external) functions called by it can send eth 330 :return bool: 331 """ 332 from slither.slithir.operations import Call 333 334 if self._can_send_eth is None: 335 self._can_send_eth = False 336 for ir in self.all_slithir_operations(): 337 if isinstance(ir, Call) and ir.can_send_eth(): 338 self._can_send_eth = True 339 return True 340 return self._can_send_eth
Check if the function or any internal (not external) functions called by it can send eth
Returns
342 @property 343 def is_checked(self) -> bool: 344 """ 345 Return true if the overflow are enabled by default 346 347 348 :return: 349 """ 350 351 return self.compilation_unit.solc_version >= "0.8.0"
Return true if the overflow are enabled by default
Returns
353 @property 354 def id(self) -> Optional[str]: 355 """ 356 Return the reference ID of the function, if available. 357 358 :return: 359 :rtype: 360 """ 361 return self._id
Return the reference ID of the function, if available.
Returns
391 @property 392 def is_constructor(self) -> bool: 393 """ 394 bool: True if the function is the constructor 395 """ 396 return self._function_type == FunctionType.CONSTRUCTOR
bool: True if the function is the constructor
398 @property 399 def is_constructor_variables(self) -> bool: 400 """ 401 bool: True if the function is the constructor of the variables 402 Slither has inbuilt functions to hold the state variables initialization 403 """ 404 return self._function_type in [ 405 FunctionType.CONSTRUCTOR_VARIABLES, 406 FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES, 407 ]
bool: True if the function is the constructor of the variables Slither has inbuilt functions to hold the state variables initialization
409 @property 410 def is_fallback(self) -> bool: 411 """ 412 Determine if the function is the fallback function for the contract 413 Returns 414 (bool) 415 """ 416 return self._function_type == FunctionType.FALLBACK
Determine if the function is the fallback function for the contract Returns (bool)
418 @property 419 def is_receive(self) -> bool: 420 """ 421 Determine if the function is the receive function for the contract 422 Returns 423 (bool) 424 """ 425 return self._function_type == FunctionType.RECEIVE
Determine if the function is the receive function for the contract Returns (bool)
434 @property 435 def payable(self) -> bool: 436 """ 437 bool: True if the function is payable 438 """ 439 return self._payable
bool: True if the function is payable
452 @property 453 def is_virtual(self) -> bool: 454 """ 455 Note for Solidity < 0.6.0 it will always be false 456 bool: True if the function is virtual 457 """ 458 return self._virtual
Note for Solidity < 0.6.0 it will always be false bool: True if the function is virtual
464 @property 465 def is_override(self) -> bool: 466 """ 467 Note for Solidity < 0.6.0 it will always be false 468 bool: True if the function overrides a base function 469 """ 470 return len(self._overrides) > 0
Note for Solidity < 0.6.0 it will always be false bool: True if the function overrides a base function
472 @property 473 def overridden_by(self) -> List["FunctionContract"]: 474 """ 475 List["FunctionContract"]: List of functions in child contracts that override this function 476 This may include distinct instances of the same function due to inheritance 477 """ 478 return self._overridden_by
List["FunctionContract"]: List of functions in child contracts that override this function This may include distinct instances of the same function due to inheritance
480 @property 481 def overrides(self) -> List["FunctionContract"]: 482 """ 483 List["FunctionContract"]: List of functions in parent contracts that this function overrides 484 This may include distinct instances of the same function due to inheritance 485 """ 486 return self._overrides
List["FunctionContract"]: List of functions in parent contracts that this function overrides This may include distinct instances of the same function due to inheritance
495 @property 496 def visibility(self) -> str: 497 """ 498 str: Function visibility 499 """ 500 assert self._visibility is not None 501 return self._visibility
str: Function visibility
510 @property 511 def view(self) -> bool: 512 """ 513 bool: True if the function is declared as view 514 """ 515 return self._view
bool: True if the function is declared as view
521 @property 522 def pure(self) -> bool: 523 """ 524 bool: True if the function is declared as pure 525 """ 526 return self._pure
bool: True if the function is declared as pure
555 @property 556 def is_implemented(self) -> bool: 557 """ 558 bool: True if the function is implemented 559 """ 560 return self._is_implemented
bool: True if the function is implemented
566 @property 567 def is_empty(self) -> bool: 568 """ 569 bool: True if the function is empty, None if the function is an interface 570 """ 571 return self._is_empty
bool: True if the function is empty, None if the function is an interface
584 @property 585 def nodes(self) -> List["Node"]: 586 """ 587 list(Node): List of the nodes 588 """ 589 return list(self._nodes)
list(Node): List of the nodes
595 @property 596 def entry_point(self) -> Optional["Node"]: 597 """ 598 Node: Entry point of the function 599 """ 600 return self._entry_point
Node: Entry point of the function
611 @property 612 def nodes_ordered_dominators(self) -> List["Node"]: 613 # TODO: does not work properly; most likely due to modifier call 614 # This will not work for modifier call that lead to multiple nodes 615 # from slither.core.cfg.node import NodeType 616 if self._nodes_ordered_dominators is None: 617 self._nodes_ordered_dominators = [] 618 if self.entry_point: 619 self._compute_nodes_ordered_dominators(self.entry_point) 620 621 for node in self.nodes: 622 # if node.type == NodeType.OTHER_ENTRYPOINT: 623 if not node in self._nodes_ordered_dominators: 624 self._compute_nodes_ordered_dominators(node) 625 626 return self._nodes_ordered_dominators
643 @property 644 def parameters(self) -> List["LocalVariable"]: 645 """ 646 list(LocalVariable): List of the parameters 647 """ 648 return list(self._parameters)
list(LocalVariable): List of the parameters
653 @property 654 def parameters_ssa(self) -> List["LocalIRVariable"]: 655 """ 656 list(LocalIRVariable): List of the parameters (SSA form) 657 """ 658 return list(self._parameters_ssa)
list(LocalIRVariable): List of the parameters (SSA form)
673 @property 674 def return_type(self) -> Optional[List[Type]]: 675 """ 676 Return the list of return type 677 If no return, return None 678 """ 679 returns = self.returns 680 if returns: 681 return [r.type for r in returns] 682 return None
Return the list of return type If no return, return None
687 @property 688 def type(self) -> Optional[List[Type]]: 689 """ 690 Return the list of return type 691 If no return, return None 692 Alias of return_type 693 """ 694 return self.return_type
Return the list of return type If no return, return None Alias of return_type
696 @property 697 def returns(self) -> List["LocalVariable"]: 698 """ 699 list(LocalVariable): List of the return variables 700 """ 701 return list(self._returns)
list(LocalVariable): List of the return variables
706 @property 707 def returns_ssa(self) -> List["LocalIRVariable"]: 708 """ 709 list(LocalIRVariable): List of the return variables (SSA form) 710 """ 711 return list(self._returns_ssa)
list(LocalIRVariable): List of the return variables (SSA form)
723 @property 724 def modifiers(self) -> List[Union["Contract", "Function"]]: 725 """ 726 list(Modifier): List of the modifiers 727 Can be contract for constructor's calls 728 729 """ 730 return [c.modifier for c in self._modifiers]
list(Modifier): List of the modifiers Can be contract for constructor's calls
735 @property 736 def modifiers_statements(self) -> List[ModifierStatements]: 737 """ 738 list(ModifierCall): List of the modifiers call (include expression and irs) 739 """ 740 return list(self._modifiers)
list(ModifierCall): List of the modifiers call (include expression and irs)
742 @property 743 def explicit_base_constructor_calls(self) -> List["Function"]: 744 """ 745 list(Function): List of the base constructors called explicitly by this presumed constructor definition. 746 747 Base constructors implicitly or explicitly called by the contract definition will not be 748 included. 749 """ 750 # This is a list of contracts internally, so we convert it to a list of constructor functions. 751 return [ 752 c.modifier.constructors_declared 753 for c in self._explicit_base_constructor_calls 754 if c.modifier.constructors_declared 755 ]
list(Function): List of the base constructors called explicitly by this presumed constructor definition.
Base constructors implicitly or explicitly called by the contract definition will not be included.
757 @property 758 def explicit_base_constructor_calls_statements(self) -> List[ModifierStatements]: 759 """ 760 list(ModifierCall): List of the base constructors called explicitly by this presumed constructor definition. 761 762 """ 763 # This is a list of contracts internally, so we convert it to a list of constructor functions. 764 return list(self._explicit_base_constructor_calls)
list(ModifierCall): List of the base constructors called explicitly by this presumed constructor definition.
776 @property 777 def variables(self) -> List[LocalVariable]: 778 """ 779 Return all local variables 780 Include paramters and return values 781 """ 782 return list(self._variables.values())
Return all local variables Include paramters and return values
784 @property 785 def local_variables(self) -> List[LocalVariable]: 786 """ 787 Return all local variables (dont include paramters and return values) 788 """ 789 return list(set(self.variables) - set(self.returns) - set(self.parameters))
Return all local variables (dont include paramters and return values)
795 @property 796 def variables_read(self) -> List["Variable"]: 797 """ 798 list(Variable): Variables read (local/state/solidity) 799 """ 800 return list(self._vars_read)
list(Variable): Variables read (local/state/solidity)
802 @property 803 def variables_written(self) -> List["Variable"]: 804 """ 805 list(Variable): Variables written (local/state/solidity) 806 """ 807 return list(self._vars_written)
list(Variable): Variables written (local/state/solidity)
809 @property 810 def state_variables_read(self) -> List["StateVariable"]: 811 """ 812 list(StateVariable): State variables read 813 """ 814 return list(self._state_vars_read)
list(StateVariable): State variables read
816 @property 817 def solidity_variables_read(self) -> List["SolidityVariable"]: 818 """ 819 list(SolidityVariable): Solidity variables read 820 """ 821 return list(self._solidity_vars_read)
list(SolidityVariable): Solidity variables read
823 @property 824 def state_variables_written(self) -> List["StateVariable"]: 825 """ 826 list(StateVariable): State variables written 827 """ 828 return list(self._state_vars_written)
list(StateVariable): State variables written
830 @property 831 def variables_read_or_written(self) -> List["Variable"]: 832 """ 833 list(Variable): Variables read or written (local/state/solidity) 834 """ 835 return list(self._vars_read_or_written)
list(Variable): Variables read or written (local/state/solidity)
845 @property 846 def slithir_variables(self) -> List["SlithIRVariable"]: 847 """ 848 Temporary and Reference Variables (not SSA form) 849 """ 850 851 return list(self._slithir_variables)
Temporary and Reference Variables (not SSA form)
860 @property 861 def internal_calls(self) -> List["InternalCallType"]: 862 """ 863 list(Function or SolidityFunction): List of function calls (that does not create a transaction) 864 """ 865 return list(self._internal_calls)
list(Function or SolidityFunction): List of function calls (that does not create a transaction)
867 @property 868 def solidity_calls(self) -> List[SolidityFunction]: 869 """ 870 list(SolidityFunction): List of Soldity calls 871 """ 872 return list(self._solidity_calls)
list(SolidityFunction): List of Soldity calls
874 @property 875 def high_level_calls(self) -> List["HighLevelCallType"]: 876 """ 877 list((Contract, Function|Variable)): 878 List of high level calls (external calls). 879 A variable is called in case of call to a public state variable 880 Include library calls 881 """ 882 return list(self._high_level_calls)
list((Contract, Function|Variable)): List of high level calls (external calls). A variable is called in case of call to a public state variable Include library calls
884 @property 885 def library_calls(self) -> List["LibraryCallType"]: 886 """ 887 list((Contract, Function)): 888 """ 889 return list(self._library_calls)
list((Contract, Function)):
891 @property 892 def low_level_calls(self) -> List["LowLevelCallType"]: 893 """ 894 list((Variable|SolidityVariable, str)): List of low_level call 895 A low level call is defined by 896 - the variable called 897 - the name of the function (call/delegatecall/codecall) 898 """ 899 return list(self._low_level_calls)
list((Variable|SolidityVariable, str)): List of low_level call A low level call is defined by
- the variable called
- the name of the function (call/delegatecall/codecall)
901 @property 902 def external_calls_as_expressions(self) -> List["Expression"]: 903 """ 904 list(ExpressionCall): List of message calls (that creates a transaction) 905 """ 906 return list(self._external_calls_as_expressions)
list(ExpressionCall): List of message calls (that creates a transaction)
919 @property 920 def expressions(self) -> List["Expression"]: 921 """ 922 list(Expression): List of the expressions 923 """ 924 if self._expressions is None: 925 expressionss = [n.expression for n in self.nodes] 926 expressions = [e for e in expressionss if e] 927 self._expressions = expressions 928 return self._expressions
list(Expression): List of the expressions
930 @property 931 def return_values(self) -> List["SlithIRVariable"]: 932 """ 933 list(Return Values): List of the return values 934 """ 935 from slither.core.cfg.node import NodeType 936 from slither.slithir.operations import Return 937 from slither.slithir.variables import Constant 938 939 if self._return_values is None: 940 return_values = [] 941 returns = [n for n in self.nodes if n.type == NodeType.RETURN] 942 [ # pylint: disable=expression-not-assigned 943 return_values.extend(ir.values) 944 for node in returns 945 for ir in node.irs 946 if isinstance(ir, Return) 947 ] 948 self._return_values = list({x for x in return_values if not isinstance(x, Constant)}) 949 return self._return_values
list(Return Values): List of the return values
951 @property 952 def return_values_ssa(self) -> List["SlithIRVariable"]: 953 """ 954 list(Return Values in SSA form): List of the return values in ssa form 955 """ 956 from slither.core.cfg.node import NodeType 957 from slither.slithir.operations import Return 958 from slither.slithir.variables import Constant 959 960 if self._return_values_ssa is None: 961 return_values_ssa = [] 962 returns = [n for n in self.nodes if n.type == NodeType.RETURN] 963 [ # pylint: disable=expression-not-assigned 964 return_values_ssa.extend(ir.values) 965 for node in returns 966 for ir in node.irs_ssa 967 if isinstance(ir, Return) 968 ] 969 self._return_values_ssa = list( 970 {x for x in return_values_ssa if not isinstance(x, Constant)} 971 ) 972 return self._return_values_ssa
list(Return Values in SSA form): List of the return values in ssa form
981 @property 982 def slithir_operations(self) -> List["Operation"]: 983 """ 984 list(Operation): List of the slithir operations 985 """ 986 if self._slithir_operations is None: 987 operationss = [n.irs for n in self.nodes] 988 operations = [item for sublist in operationss for item in sublist if item] 989 self._slithir_operations = operations 990 return self._slithir_operations
list(Operation): List of the slithir operations
992 @property 993 def slithir_ssa_operations(self) -> List["Operation"]: 994 """ 995 list(Operation): List of the slithir operations (SSA) 996 """ 997 if self._slithir_ssa_operations is None: 998 operationss = [n.irs_ssa for n in self.nodes] 999 operations = [item for sublist in operationss for item in sublist if item] 1000 self._slithir_ssa_operations = operations 1001 return self._slithir_ssa_operations
list(Operation): List of the slithir operations (SSA)
1010 @property 1011 def solidity_signature(self) -> str: 1012 """ 1013 Return a signature following the Solidity Standard 1014 Contract and converted into address 1015 1016 It might still keep internal types (ex: structure name) for internal functions. 1017 The reason is that internal functions allows recursive structure definition, which 1018 can't be converted following the Solidity stand ard 1019 1020 :return: the solidity signature 1021 """ 1022 if self._solidity_signature is None: 1023 parameters = [ 1024 convert_type_for_solidity_signature_to_string(x.type) for x in self.parameters 1025 ] 1026 self._solidity_signature = self.name + "(" + ",".join(parameters) + ")" 1027 return self._solidity_signature
Return a signature following the Solidity Standard Contract and converted into address
It might still keep internal types (ex: structure name) for internal functions. The reason is that internal functions allows recursive structure definition, which can't be converted following the Solidity stand ard
Returns
the solidity signature
1029 @property 1030 def signature(self) -> Tuple[str, List[str], List[str]]: 1031 """ 1032 (str, list(str), list(str)): Function signature as 1033 (name, list parameters type, list return values type) 1034 """ 1035 # FIXME memoizing this function is not working properly for vyper 1036 # if self._signature is None: 1037 return ( 1038 self.name, 1039 [str(x.type) for x in self.parameters], 1040 [str(x.type) for x in self.returns], 1041 ) 1042 # self._signature = signature 1043 # return self._signature
(str, list(str), list(str)): Function signature as (name, list parameters type, list return values type)
1045 @property 1046 def signature_str(self) -> str: 1047 """ 1048 str: func_name(type1,type2) returns (type3) 1049 Return the function signature as a str (contains the return values) 1050 """ 1051 if self._signature_str is None: 1052 name, parameters, returnVars = self.signature 1053 self._signature_str = ( 1054 name + "(" + ",".join(parameters) + ") returns(" + ",".join(returnVars) + ")" 1055 ) 1056 return self._signature_str
str: func_name(type1,type2) returns (type3) Return the function signature as a str (contains the return values)
1077 @property 1078 def reachable_from_nodes(self) -> Set[ReacheableNode]: 1079 """ 1080 Return 1081 ReacheableNode 1082 """ 1083 return self._reachable_from_nodes
Return ReacheableNode
1089 @property 1090 def all_reachable_from_functions(self) -> Set["Function"]: 1091 """ 1092 Give the recursive version of reachable_from_functions (all the functions that lead to call self in the CFG) 1093 """ 1094 if self._all_reachable_from_functions is None: 1095 functions: Set["Function"] = set() 1096 1097 new_functions = self.reachable_from_functions 1098 # iterate until we have are finding new functions 1099 while new_functions and not new_functions.issubset(functions): 1100 functions = functions.union(new_functions) 1101 # Use a temporary set, because we iterate over new_functions 1102 new_functionss: Set["Function"] = set() 1103 for f in new_functions: 1104 new_functionss = new_functionss.union(f.reachable_from_functions) 1105 new_functions = new_functionss - functions 1106 1107 self._all_reachable_from_functions = functions 1108 return self._all_reachable_from_functions
Give the recursive version of reachable_from_functions (all the functions that lead to call self in the CFG)
1155 def all_variables_read(self) -> List["Variable"]: 1156 """recursive version of variables_read""" 1157 if self._all_variables_read is None: 1158 self._all_variables_read = self._explore_functions(lambda x: x.variables_read) 1159 return self._all_variables_read
recursive version of variables_read
1161 def all_variables_written(self) -> List["Variable"]: 1162 """recursive version of variables_written""" 1163 if self._all_variables_written is None: 1164 self._all_variables_written = self._explore_functions(lambda x: x.variables_written) 1165 return self._all_variables_written
recursive version of variables_written
1167 def all_state_variables_read(self) -> List["StateVariable"]: 1168 """recursive version of variables_read""" 1169 if self._all_state_variables_read is None: 1170 self._all_state_variables_read = self._explore_functions( 1171 lambda x: x.state_variables_read 1172 ) 1173 return self._all_state_variables_read
recursive version of variables_read
1175 def all_solidity_variables_read(self) -> List[SolidityVariable]: 1176 """recursive version of solidity_read""" 1177 if self._all_solidity_variables_read is None: 1178 self._all_solidity_variables_read = self._explore_functions( 1179 lambda x: x.solidity_variables_read 1180 ) 1181 return self._all_solidity_variables_read
recursive version of solidity_read
1183 def all_slithir_variables(self) -> List["SlithIRVariable"]: 1184 """recursive version of slithir_variables""" 1185 if self._all_slithir_variables is None: 1186 self._all_slithir_variables = self._explore_functions(lambda x: x.slithir_variables) 1187 return self._all_slithir_variables
recursive version of slithir_variables
1189 def all_nodes(self) -> List["Node"]: 1190 """recursive version of nodes""" 1191 if self._all_nodes is None: 1192 self._all_nodes = self._explore_functions(lambda x: x.nodes) 1193 return self._all_nodes
recursive version of nodes
1195 def all_expressions(self) -> List["Expression"]: 1196 """recursive version of variables_read""" 1197 if self._all_expressions is None: 1198 self._all_expressions = self._explore_functions(lambda x: x.expressions) 1199 return self._all_expressions
recursive version of variables_read
1206 def all_state_variables_written(self) -> List[StateVariable]: 1207 """recursive version of variables_written""" 1208 if self._all_state_variables_written is None: 1209 self._all_state_variables_written = self._explore_functions( 1210 lambda x: x.state_variables_written 1211 ) 1212 return self._all_state_variables_written
recursive version of variables_written
1214 def all_internal_calls(self) -> List["InternalCallType"]: 1215 """recursive version of internal_calls""" 1216 if self._all_internals_calls is None: 1217 self._all_internals_calls = self._explore_functions(lambda x: x.internal_calls) 1218 return self._all_internals_calls
recursive version of internal_calls
1220 def all_low_level_calls(self) -> List["LowLevelCallType"]: 1221 """recursive version of low_level calls""" 1222 if self._all_low_level_calls is None: 1223 self._all_low_level_calls = self._explore_functions(lambda x: x.low_level_calls) 1224 return self._all_low_level_calls
recursive version of low_level calls
1226 def all_high_level_calls(self) -> List["HighLevelCallType"]: 1227 """recursive version of high_level calls""" 1228 if self._all_high_level_calls is None: 1229 self._all_high_level_calls = self._explore_functions(lambda x: x.high_level_calls) 1230 return self._all_high_level_calls
recursive version of high_level calls
1232 def all_library_calls(self) -> List["LibraryCallType"]: 1233 """recursive version of library calls""" 1234 if self._all_library_calls is None: 1235 self._all_library_calls = self._explore_functions(lambda x: x.library_calls) 1236 return self._all_library_calls
recursive version of library calls
1238 def all_solidity_calls(self) -> List[SolidityFunction]: 1239 """recursive version of solidity calls""" 1240 if self._all_solidity_calls is None: 1241 self._all_solidity_calls = self._explore_functions(lambda x: x.solidity_calls) 1242 return self._all_solidity_calls
recursive version of solidity calls
1249 def all_conditional_state_variables_read(self, include_loop=True) -> List["StateVariable"]: 1250 """ 1251 Return the state variable used in a condition 1252 1253 Over approximate and also return index access 1254 It won't work if the variable is assigned to a temp variable 1255 """ 1256 if include_loop: 1257 if self._all_conditional_state_variables_read_with_loop is None: 1258 self._all_conditional_state_variables_read_with_loop = self._explore_functions( 1259 lambda x: self._explore_func_cond_read(x, include_loop) 1260 ) 1261 return self._all_conditional_state_variables_read_with_loop 1262 if self._all_conditional_state_variables_read is None: 1263 self._all_conditional_state_variables_read = self._explore_functions( 1264 lambda x: self._explore_func_cond_read(x, include_loop) 1265 ) 1266 return self._all_conditional_state_variables_read
Return the state variable used in a condition
Over approximate and also return index access It won't work if the variable is assigned to a temp variable
1287 def all_conditional_solidity_variables_read( 1288 self, include_loop: bool = True 1289 ) -> List[SolidityVariable]: 1290 """ 1291 Return the Soldiity variables directly used in a condtion 1292 1293 Use of the IR to filter index access 1294 Assumption: the solidity vars are used directly in the conditional node 1295 It won't work if the variable is assigned to a temp variable 1296 """ 1297 if include_loop: 1298 if self._all_conditional_solidity_variables_read_with_loop is None: 1299 self._all_conditional_solidity_variables_read_with_loop = self._explore_functions( 1300 lambda x: self._explore_func_conditional( 1301 x, self._solidity_variable_in_binary, include_loop 1302 ) 1303 ) 1304 return self._all_conditional_solidity_variables_read_with_loop 1305 1306 if self._all_conditional_solidity_variables_read is None: 1307 self._all_conditional_solidity_variables_read = self._explore_functions( 1308 lambda x: self._explore_func_conditional( 1309 x, self._solidity_variable_in_binary, include_loop 1310 ) 1311 ) 1312 return self._all_conditional_solidity_variables_read
Return the Soldiity variables directly used in a condtion
Use of the IR to filter index access Assumption: the solidity vars are used directly in the conditional node It won't work if the variable is assigned to a temp variable
1331 def all_solidity_variables_used_as_args(self) -> List[SolidityVariable]: 1332 """ 1333 Return the Soldiity variables directly used in a call 1334 1335 Use of the IR to filter index access 1336 Used to catch check(msg.sender) 1337 """ 1338 if self._all_solidity_variables_used_as_args is None: 1339 self._all_solidity_variables_used_as_args = self._explore_functions( 1340 lambda x: self._explore_func_nodes(x, self._solidity_variable_in_internal_calls) 1341 ) 1342 return self._all_solidity_variables_used_as_args
Return the Soldiity variables directly used in a call
Use of the IR to filter index access Used to catch check(msg.sender)
1351 def apply_visitor(self, Visitor: Callable) -> List: 1352 """ 1353 Apply a visitor to all the function expressions 1354 Args: 1355 Visitor: slither.visitors 1356 Returns 1357 list(): results of the visit 1358 """ 1359 expressions = self.expressions 1360 v = [Visitor(e).result() for e in expressions] 1361 return [item for sublist in v for item in sublist]
Apply a visitor to all the function expressions Args: Visitor: slither.visitors Returns list(): results of the visit
1370 def get_local_variable_from_name(self, variable_name: str) -> Optional[LocalVariable]: 1371 """ 1372 Return a local variable from a name 1373 1374 Args: 1375 variable_name (str): name of the variable 1376 Returns: 1377 LocalVariable 1378 """ 1379 return next((v for v in self.variables if v.name == variable_name), None)
Return a local variable from a name
Args: variable_name (str): name of the variable Returns: LocalVariable
1388 def cfg_to_dot(self, filename: str): 1389 """ 1390 Export the function to a dot file 1391 Args: 1392 filename (str) 1393 """ 1394 with open(filename, "w", encoding="utf8") as f: 1395 f.write("digraph{\n") 1396 for node in self.nodes: 1397 f.write(f'{node.node_id}[label="{str(node)}"];\n') 1398 for son in node.sons: 1399 f.write(f"{node.node_id}->{son.node_id};\n") 1400 1401 f.write("}\n")
Export the function to a dot file Args: filename (str)
1403 def dominator_tree_to_dot(self, filename: str): 1404 """ 1405 Export the dominator tree of the function to a dot file 1406 Args: 1407 filename (str) 1408 """ 1409 1410 def description(node): 1411 desc = f"{node}\n" 1412 desc += f"id: {node.node_id}" 1413 if node.dominance_frontier: 1414 desc += f"\ndominance frontier: {[n.node_id for n in node.dominance_frontier]}" 1415 return desc 1416 1417 with open(filename, "w", encoding="utf8") as f: 1418 f.write("digraph{\n") 1419 for node in self.nodes: 1420 f.write(f'{node.node_id}[label="{description(node)}"];\n') 1421 if node.immediate_dominator: 1422 f.write(f"{node.immediate_dominator.node_id}->{node.node_id};\n") 1423 1424 f.write("}\n")
Export the dominator tree of the function to a dot file Args: filename (str)
1426 def slithir_cfg_to_dot(self, filename: str): 1427 """ 1428 Export the CFG to a DOT file. The nodes includes the Solidity expressions and the IRs 1429 :param filename: 1430 :return: 1431 """ 1432 content = self.slithir_cfg_to_dot_str() 1433 with open(filename, "w", encoding="utf8") as f: 1434 f.write(content)
Export the CFG to a DOT file. The nodes includes the Solidity expressions and the IRs
Parameters
- filename:
Returns
1436 def slithir_cfg_to_dot_str(self, skip_expressions: bool = False) -> str: 1437 """ 1438 Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs 1439 :return: the DOT content 1440 :rtype: str 1441 """ 1442 from slither.core.cfg.node import NodeType 1443 1444 content = "" 1445 content += "digraph{\n" 1446 for node in self.nodes: 1447 label = f"Node Type: {node.type.value} {node.node_id}\n" 1448 if node.expression and not skip_expressions: 1449 label += f"\nEXPRESSION:\n{node.expression}\n" 1450 if node.irs and not skip_expressions: 1451 label += "\nIRs:\n" + "\n".join([str(ir) for ir in node.irs]) 1452 content += f'{node.node_id}[label="{label}"];\n' 1453 if node.type in [NodeType.IF, NodeType.IFLOOP]: 1454 true_node = node.son_true 1455 if true_node: 1456 content += f'{node.node_id}->{true_node.node_id}[label="True"];\n' 1457 false_node = node.son_false 1458 if false_node: 1459 content += f'{node.node_id}->{false_node.node_id}[label="False"];\n' 1460 else: 1461 for son in node.sons: 1462 content += f"{node.node_id}->{son.node_id};\n" 1463 1464 content += "}\n" 1465 return content
Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs
Returns
the DOT content
1474 def is_reading(self, variable: "Variable") -> bool: 1475 """ 1476 Check if the function reads the variable 1477 Args: 1478 variable (Variable): 1479 Returns: 1480 bool: True if the variable is read 1481 """ 1482 return variable in self.variables_read
Check if the function reads the variable Args: variable (Variable): Returns: bool: True if the variable is read
1484 def is_reading_in_conditional_node(self, variable: "Variable") -> bool: 1485 """ 1486 Check if the function reads the variable in a IF node 1487 Args: 1488 variable (Variable): 1489 Returns: 1490 bool: True if the variable is read 1491 """ 1492 variables_reads = [n.variables_read for n in self.nodes if n.contains_if()] 1493 variables_read = [item for sublist in variables_reads for item in sublist] 1494 return variable in variables_read
Check if the function reads the variable in a IF node Args: variable (Variable): Returns: bool: True if the variable is read
1496 def is_reading_in_require_or_assert(self, variable: "Variable") -> bool: 1497 """ 1498 Check if the function reads the variable in an require or assert 1499 Args: 1500 variable (Variable): 1501 Returns: 1502 bool: True if the variable is read 1503 """ 1504 variables_reads = [n.variables_read for n in self.nodes if n.contains_require_or_assert()] 1505 variables_read = [item for sublist in variables_reads for item in sublist] 1506 return variable in variables_read
Check if the function reads the variable in an require or assert Args: variable (Variable): Returns: bool: True if the variable is read
1508 def is_writing(self, variable: "Variable") -> bool: 1509 """ 1510 Check if the function writes the variable 1511 Args: 1512 variable (Variable): 1513 Returns: 1514 bool: True if the variable is written 1515 """ 1516 return variable in self.variables_written
Check if the function writes the variable Args: variable (Variable): Returns: bool: True if the variable is written
1524 def is_protected(self) -> bool: 1525 """ 1526 Determine if the function is protected using a check on msg.sender 1527 1528 Consider onlyOwner as a safe modifier. 1529 If the owner functionality is incorrectly implemented, this will lead to incorrectly 1530 classify the function as protected 1531 1532 Otherwise only detects if msg.sender is directly used in a condition 1533 For example, it wont work for: 1534 address a = msg.sender 1535 require(a == owner) 1536 Returns 1537 (bool) 1538 """ 1539 1540 if self._is_protected is None: 1541 if self.is_constructor: 1542 self._is_protected = True 1543 return True 1544 if "onlyOwner" in [m.name for m in self.modifiers]: 1545 self._is_protected = True 1546 return True 1547 conditional_vars = self.all_conditional_solidity_variables_read(include_loop=False) 1548 args_vars = self.all_solidity_variables_used_as_args() 1549 self._is_protected = ( 1550 SolidityVariableComposed("msg.sender") in conditional_vars + args_vars 1551 ) 1552 return self._is_protected
Determine if the function is protected using a check on msg.sender
Consider onlyOwner as a safe modifier.
If the owner functionality is incorrectly implemented, this will lead to incorrectly
classify the function as protected
Otherwise only detects if msg.sender is directly used in a condition
For example, it wont work for:
address a = msg.sender
require(a == owner)
Returns (bool)
1554 @property 1555 def is_reentrant(self) -> bool: 1556 """ 1557 Determine if the function can be re-entered 1558 """ 1559 reentrancy_modifier = "nonReentrant" 1560 1561 if self.function_language == FunctionLanguage.Vyper: 1562 reentrancy_modifier = "nonreentrant(lock)" 1563 1564 # TODO: compare with hash of known nonReentrant modifier instead of the name 1565 if reentrancy_modifier in [m.name for m in self.modifiers]: 1566 return False 1567 1568 if self.visibility in ["public", "external"]: 1569 return True 1570 1571 # If it's an internal function, check if all its entry points have the nonReentrant modifier 1572 all_entry_points = [ 1573 f for f in self.all_reachable_from_functions if f.visibility in ["public", "external"] 1574 ] 1575 if not all_entry_points: 1576 return True 1577 return not all( 1578 (reentrancy_modifier in [m.name for m in f.modifiers] for f in all_entry_points) 1579 )
Determine if the function can be re-entered
1688 def new_node( 1689 self, node_type: "NodeType", src: Union[str, Dict], scope: Union[Scope, "Function"] 1690 ) -> "Node": 1691 from slither.core.cfg.node import Node 1692 1693 node = Node(node_type, self._counter_nodes, scope, self.file_scope) 1694 node.set_offset(src, self.compilation_unit) 1695 self._counter_nodes += 1 1696 node.set_function(self) 1697 self._nodes.append(node) 1698 1699 return node
1787 def fix_phi( 1788 self, 1789 last_state_variables_instances: Dict[str, List["StateVariable"]], 1790 initial_state_variables_instances: Dict[str, "StateVariable"], 1791 ) -> None: 1792 from slither.slithir.operations import InternalCall, PhiCallback 1793 from slither.slithir.variables import Constant, StateIRVariable 1794 1795 for node in self.nodes: 1796 for ir in node.irs_ssa: 1797 if node == self.entry_point: 1798 if isinstance(ir.lvalue, StateIRVariable): 1799 additional = [initial_state_variables_instances[ir.lvalue.canonical_name]] 1800 additional += last_state_variables_instances[ir.lvalue.canonical_name] 1801 ir.rvalues = list(set(additional + ir.rvalues)) 1802 # function parameter 1803 else: 1804 # find index of the parameter 1805 idx = self.parameters.index(ir.lvalue.non_ssa_version) 1806 # find non ssa version of that index 1807 additional = [n.ir.arguments[idx] for n in self.reachable_from_nodes] 1808 additional = unroll(additional) 1809 additional = [a for a in additional if not isinstance(a, Constant)] 1810 ir.rvalues = list(set(additional + ir.rvalues)) 1811 if isinstance(ir, PhiCallback): 1812 callee_ir = ir.callee_ir 1813 if isinstance(callee_ir, InternalCall): 1814 last_ssa = callee_ir.function.get_last_ssa_state_variables_instances() 1815 if ir.lvalue.canonical_name in last_ssa: 1816 ir.rvalues = list(last_ssa[ir.lvalue.canonical_name]) 1817 else: 1818 ir.rvalues = [ir.lvalue] 1819 else: 1820 additional = last_state_variables_instances[ir.lvalue.canonical_name] 1821 ir.rvalues = list(set(additional + ir.rvalues)) 1822 1823 node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)]