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