slither.slithir.convert
1import logging 2from pathlib import Path 3from typing import Any, List, TYPE_CHECKING, Union, Optional 4 5# pylint: disable= too-many-lines,import-outside-toplevel,too-many-branches,too-many-statements,too-many-nested-blocks 6from slither.core.declarations import ( 7 Contract, 8 Enum, 9 Event, 10 Function, 11 SolidityFunction, 12 SolidityVariable, 13 SolidityVariableComposed, 14 Structure, 15) 16from slither.core.declarations.custom_error import CustomError 17from slither.core.declarations.function_contract import FunctionContract 18from slither.core.declarations.function_top_level import FunctionTopLevel 19from slither.core.declarations.solidity_import_placeholder import SolidityImportPlaceHolder 20from slither.core.declarations.solidity_variables import SolidityCustomRevert 21from slither.core.expressions import Identifier, Literal 22from slither.core.expressions.expression import Expression 23from slither.core.solidity_types import ( 24 ArrayType, 25 ElementaryType, 26 FunctionType, 27 MappingType, 28 UserDefinedType, 29 TypeInformation, 30) 31from slither.core.solidity_types.elementary_type import ( 32 Int as ElementaryTypeInt, 33 Uint, 34 Byte, 35 MaxValues, 36) 37from slither.core.solidity_types.type import Type 38from slither.core.solidity_types.type_alias import TypeAliasTopLevel, TypeAlias 39from slither.core.variables.state_variable import StateVariable 40from slither.core.variables.variable import Variable 41from slither.slithir.exceptions import SlithIRError 42from slither.slithir.operations import ( 43 Assignment, 44 Binary, 45 BinaryType, 46 Call, 47 Condition, 48 Delete, 49 EventCall, 50 HighLevelCall, 51 Index, 52 InitArray, 53 InternalCall, 54 InternalDynamicCall, 55 Length, 56 LibraryCall, 57 LowLevelCall, 58 Member, 59 NewArray, 60 NewContract, 61 NewElementaryType, 62 NewStructure, 63 OperationWithLValue, 64 Return, 65 Send, 66 SolidityCall, 67 Transfer, 68 TypeConversion, 69 Unary, 70 Unpack, 71 Nop, 72 Operation, 73) 74from slither.slithir.operations.codesize import CodeSize 75from slither.slithir.tmp_operations.argument import Argument, ArgumentType 76from slither.slithir.tmp_operations.tmp_call import TmpCall 77from slither.slithir.tmp_operations.tmp_new_array import TmpNewArray 78from slither.slithir.tmp_operations.tmp_new_contract import TmpNewContract 79from slither.slithir.tmp_operations.tmp_new_elementary_type import TmpNewElementaryType 80from slither.slithir.tmp_operations.tmp_new_structure import TmpNewStructure 81from slither.slithir.variables import Constant, ReferenceVariable, TemporaryVariable 82from slither.slithir.variables import TupleVariable 83from slither.utils.function import get_function_id, get_event_id 84from slither.utils.type import export_nested_types_from_variable 85from slither.utils.using_for import USING_FOR 86from slither.visitors.slithir.expression_to_slithir import ExpressionToSlithIR 87 88if TYPE_CHECKING: 89 from slither.core.cfg.node import Node 90 91logger = logging.getLogger("ConvertToIR") 92 93 94def convert_expression(expression: Expression, node: "Node") -> List[Operation]: 95 # handle standlone expression 96 # such as return true; 97 from slither.core.cfg.node import NodeType 98 99 if isinstance(expression, Literal) and node.type in [NodeType.IF, NodeType.IFLOOP]: 100 cst = Constant(expression.value, expression.type) 101 cond = Condition(cst) 102 cond.set_expression(expression) 103 cond.set_node(node) 104 return [cond] 105 if isinstance(expression, Identifier) and node.type in [ 106 NodeType.IF, 107 NodeType.IFLOOP, 108 ]: 109 cond = Condition(expression.value) 110 cond.set_expression(expression) 111 cond.set_node(node) 112 return [cond] 113 114 visitor = ExpressionToSlithIR(expression, node) 115 result = visitor.result() 116 is_solidity = node.compilation_unit.is_solidity 117 result = apply_ir_heuristics(result, node, is_solidity) 118 119 if result: 120 if node.type in [NodeType.IF, NodeType.IFLOOP]: 121 prev = result[-1] 122 assert isinstance(prev, (OperationWithLValue)) and prev.lvalue 123 cond = Condition(prev.lvalue) 124 cond.set_expression(expression) 125 cond.set_node(node) 126 result.append(cond) 127 elif node.type == NodeType.RETURN: 128 # May return None 129 prev = result[-1] 130 if isinstance(prev, (OperationWithLValue)): 131 r = Return(prev.lvalue) 132 r.set_expression(expression) 133 r.set_node(node) 134 result.append(r) 135 136 return result 137 138 139################################################################################### 140################################################################################### 141# region Helpers 142################################################################################### 143################################################################################### 144 145 146def is_value(ins: Operation) -> bool: 147 if isinstance(ins, TmpCall): 148 if isinstance(ins.ori, Member): 149 if ins.ori.variable_right == "value": 150 return True 151 return False 152 153 154def is_gas(ins: Operation) -> bool: 155 if isinstance(ins, TmpCall): 156 if isinstance(ins.ori, Member): 157 if ins.ori.variable_right == "gas": 158 return True 159 return False 160 161 162def _fits_under_integer(val: int, can_be_int: bool, can_be_uint: bool) -> List[str]: 163 """ 164 Return the list of uint/int that can contain val 165 166 :param val: 167 :return: 168 """ 169 ret: List[str] = [] 170 n = 8 171 assert can_be_int | can_be_uint 172 while n <= 256: 173 if can_be_uint: 174 if val <= 2**n - 1: 175 ret.append(f"uint{n}") 176 if can_be_int: 177 if val <= (2**n) / 2 - 1: 178 ret.append(f"int{n}") 179 n = n + 8 180 return ret 181 182 183def _fits_under_byte(val: Union[int, str]) -> List[str]: 184 """ 185 Return the list of byte that can contain val 186 187 :param val: 188 :return: 189 """ 190 191 # If the value is written as an int, it can only be saved in one byte size 192 # If its a string, it can be fitted under multiple values 193 194 if isinstance(val, int): 195 hex_val = hex(val)[2:] 196 size = len(hex_val) // 2 197 return [f"bytes{size}"] 198 # val is a str 199 length = len(val.encode("utf8")) 200 return [f"bytes{f}" for f in range(length, 33)] + ["bytes"] 201 202 203def _find_function_from_parameter( 204 arguments: List[Variable], candidates: List[Function], full_comparison: bool 205) -> Optional[Function]: 206 """ 207 Look for a function in candidates that can be the target based on the ir's call arguments 208 209 Try the implicit type conversion for uint/int/bytes. Constant values can be both uint/int 210 While variables stick to their base type, but can changed the size. 211 If full_comparison is True it will do a comparison of all the arguments regardless if 212 the candidate remained is one. 213 214 :param arguments: 215 :param candidates: 216 :param full_comparison: 217 :return: 218 """ 219 type_args: List[str] 220 for idx, arg in enumerate(arguments): 221 if isinstance(arg, (list,)): 222 type_args = [f"{get_type(arg[0].type)}[{len(arg)}]"] 223 elif isinstance(arg, Function): 224 type_args = [arg.signature_str] 225 else: 226 type_args = [get_type(arg.type)] 227 228 arg_type = arg.type 229 if isinstance( 230 arg_type, ElementaryType 231 ) and arg_type.type in ElementaryTypeInt + Uint + Byte + ["string"]: 232 if isinstance(arg, Constant): 233 value = arg.value 234 can_be_uint = True 235 can_be_int = True 236 else: 237 value = MaxValues[arg_type.type] 238 can_be_uint = False 239 can_be_int = False 240 if arg_type.type in ElementaryTypeInt: 241 can_be_int = True 242 elif arg_type.type in Uint: 243 can_be_uint = True 244 245 if arg_type.type in ElementaryTypeInt + Uint: 246 type_args = _fits_under_integer(value, can_be_int, can_be_uint) 247 elif value is None and arg_type.type in ["bytes", "string"]: 248 type_args = ["bytes", "string"] 249 else: 250 type_args = _fits_under_byte(value) 251 if arg_type.type == "string": 252 type_args += ["string"] 253 254 not_found = True 255 candidates_kept: List[Function] = [] 256 for type_arg in type_args: 257 if not not_found: 258 break 259 candidates_kept = [] 260 for candidate in candidates: 261 param = get_type(candidate.parameters[idx].type) 262 if param == type_arg: 263 not_found = False 264 candidates_kept.append(candidate) 265 266 if len(candidates_kept) == 1 and not full_comparison: 267 return candidates_kept[0] 268 candidates = candidates_kept 269 if len(candidates) == 1: 270 return candidates[0] 271 return None 272 273 274def is_temporary(ins: Operation) -> bool: 275 return isinstance( 276 ins, 277 (Argument, TmpNewElementaryType, TmpNewContract, TmpNewArray, TmpNewStructure), 278 ) 279 280 281# endregion 282################################################################################### 283################################################################################### 284# region Calls modification 285################################################################################### 286################################################################################### 287 288 289def integrate_value_gas(result: List[Operation]) -> List[Operation]: 290 """ 291 Integrate value and gas temporary arguments to call instruction 292 """ 293 was_changed = True 294 295 calls = [] 296 297 while was_changed: 298 # We loop until we do not find any call to value or gas 299 was_changed = False 300 301 # Find all the assignments 302 assigments = {} 303 for i in result: 304 if isinstance(i, OperationWithLValue) and i.lvalue: 305 assigments[i.lvalue.name] = i 306 if isinstance(i, TmpCall): 307 if isinstance(i.called, Variable) and i.called.name in assigments: 308 ins_ori = assigments[i.called.name] 309 i.set_ori(ins_ori) 310 311 to_remove = [] 312 variable_to_replace = {} 313 314 # Replace call to value, gas to an argument of the real call 315 for idx, ins in enumerate(result): 316 # value can be shadowed, so we check that the prev ins 317 # is an Argument 318 if idx == 0: 319 continue 320 prev_ins = result[idx - 1] 321 if is_value(ins) and isinstance(prev_ins, Argument): 322 was_changed = True 323 prev_ins.set_type(ArgumentType.VALUE) 324 # Types checked by is_value 325 prev_ins.call_id = ins.ori.variable_left.name # type: ignore 326 calls.append(ins.ori.variable_left) # type: ignore 327 to_remove.append(ins) 328 variable_to_replace[ins.lvalue.name] = ins.ori.variable_left # type: ignore 329 elif is_gas(ins) and isinstance(prev_ins, Argument): 330 was_changed = True 331 prev_ins.set_type(ArgumentType.GAS) 332 # Types checked by is_gas 333 prev_ins.call_id = ins.ori.variable_left.name # type: ignore 334 calls.append(ins.ori.variable_left) # type: ignore 335 to_remove.append(ins) 336 variable_to_replace[ins.lvalue.name] = ins.ori.variable_left # type: ignore 337 338 # Remove the call to value/gas instruction 339 result = [i for i in result if not i in to_remove] 340 341 # update the real call 342 for ins in result: 343 if isinstance(ins, TmpCall): 344 # use of while if there redirections 345 while ins.called.name in variable_to_replace: 346 was_changed = True 347 ins.call_id = variable_to_replace[ins.called.name].name 348 calls.append(ins.called) 349 ins.called = variable_to_replace[ins.called.name] 350 if isinstance(ins, Argument): 351 while ins.call_id in variable_to_replace: 352 was_changed = True 353 ins.call_id = variable_to_replace[ins.call_id].name 354 355 calls = list({str(c) for c in calls}) 356 idx = 0 357 calls_d = {} 358 for call in calls: 359 calls_d[str(call)] = idx 360 idx = idx + 1 361 362 return result 363 364 365# endregion 366################################################################################### 367################################################################################### 368# region Calls modification and Type propagation 369################################################################################### 370################################################################################### 371 372 373def get_declared_param_names( 374 ins: Union[ 375 NewStructure, 376 NewContract, 377 InternalCall, 378 LibraryCall, 379 HighLevelCall, 380 InternalDynamicCall, 381 EventCall, 382 ] 383) -> Optional[List[List[str]]]: 384 """ 385 Given a call operation, return the list of parameter names, in the order 386 listed in the function declaration. 387 #### Parameters 388 ins - 389 The call instruction 390 #### Possible Returns 391 List[List[str]] - 392 A list of list of parameters in declaration order. Note only if it's a Function there can be multiple list 393 None - 394 Workaround: Unable to obtain list of parameters in declaration order 395 """ 396 if isinstance(ins, NewStructure): 397 return [ 398 [x.name for x in ins.structure.elems_ordered if not isinstance(x.type, MappingType)] 399 ] 400 if isinstance(ins, (InternalCall, LibraryCall, HighLevelCall)): 401 if isinstance(ins.function, Function): 402 res = [[p.name for p in ins.function.parameters]] 403 for f in ins.function.overrides: 404 res.append([p.name for p in f.parameters]) 405 return res 406 return None 407 if isinstance(ins, InternalDynamicCall): 408 return [[p.name for p in ins.function_type.params]] 409 410 assert isinstance(ins, (EventCall, NewContract)) 411 return None 412 413 414def reorder_arguments( 415 args: List[Variable], call_names: List[str], decl_names: List[List[str]] 416) -> List[Variable]: 417 """ 418 Reorder named struct constructor arguments so that they match struct declaration ordering rather 419 than call ordering 420 E.g. for `struct S { int x; int y; }` we reorder `S({y : 2, x : 3})` to `S(3, 2)` 421 #### Parameters 422 args - 423 Arguments to constructor call, in call order 424 names - 425 Parameter names in call order 426 decl_names - 427 List of list of parameter names in declaration order 428 #### Returns 429 Reordered arguments to constructor call, now in declaration order 430 """ 431 assert len(args) == len(call_names) 432 for names in decl_names: 433 assert len(call_names) == len(names) 434 435 args_ret = [] 436 index_seen = [] # Will have the correct index order 437 438 # We search for declaration of parameters that is fully compatible with the call arguments 439 # that is why if a arg name is not present in the call name we break and clear index_seen 440 # Known issue if the overridden function reuse the same parameters' name but in different positions 441 for names in decl_names: 442 if len(index_seen) == len(args): 443 break 444 445 for n in names: 446 try: 447 ind = call_names.index(n) 448 if ind in index_seen: 449 continue 450 except ValueError: 451 index_seen.clear() 452 break 453 index_seen.append(ind) 454 455 for ind in index_seen: 456 args_ret.append(args[ind]) 457 458 return args_ret 459 460 461def propagate_type_and_convert_call(result: List[Operation], node: "Node") -> List[Operation]: 462 """ 463 Propagate the types variables and convert tmp call to real call operation 464 """ 465 calls_value = {} 466 calls_gas = {} 467 468 call_data = [] 469 470 idx = 0 471 # use of while len() as result can be modified during the iteration 472 while idx < len(result): 473 ins = result[idx] 474 475 if isinstance(ins, TmpCall): 476 # If the node.function is a FunctionTopLevel, then it does not have a contract 477 contract = ( 478 node.function.contract if isinstance(node.function, FunctionContract) else None 479 ) 480 new_ins = extract_tmp_call(ins, contract) 481 if new_ins: 482 new_ins.set_node(ins.node) 483 ins = new_ins 484 result[idx] = ins 485 486 # If there's two consecutive type conversions, keep only the last 487 # ARG_CALL x call_data = [x] 488 # TMP_4 = CONVERT x to Fix call_data = [] 489 # TMP_5 = CONVERT TMP_4 to uint192 call_data = [TMP_5] 490 if isinstance(ins, TypeConversion) and ins.variable in call_data: 491 call_data.remove(ins.variable) 492 493 if isinstance(ins, Argument): 494 # In case of duplicate arguments we overwrite the value 495 # This can happen because of addr.call.value(1).value(2) 496 if ins.get_type() in [ArgumentType.GAS]: 497 calls_gas[ins.call_id] = ins.argument 498 elif ins.get_type() in [ArgumentType.VALUE]: 499 calls_value[ins.call_id] = ins.argument 500 else: 501 assert ins.get_type() == ArgumentType.CALL 502 call_data.append(ins.argument) 503 504 if isinstance(ins, (HighLevelCall, NewContract, InternalDynamicCall)): 505 if ins.call_id in calls_value: 506 ins.call_value = calls_value[ins.call_id] 507 if ins.call_id in calls_gas and isinstance(ins, (HighLevelCall, InternalDynamicCall)): 508 ins.call_gas = calls_gas[ins.call_id] 509 510 if isinstance(ins, Call) and (ins.names is not None): 511 assert isinstance( 512 ins, 513 ( 514 NewStructure, 515 NewContract, 516 InternalCall, 517 LibraryCall, 518 HighLevelCall, 519 InternalDynamicCall, 520 EventCall, 521 ), 522 ) 523 decl_param_names = get_declared_param_names(ins) 524 if decl_param_names is not None: 525 call_data = reorder_arguments(call_data, ins.names, decl_param_names) 526 527 if isinstance(ins, (Call, NewContract, NewStructure)): 528 # We might have stored some arguments for libraries 529 if ins.arguments: 530 call_data = ins.arguments + call_data 531 ins.arguments = call_data 532 call_data = [] 533 534 if is_temporary(ins): 535 del result[idx] 536 continue 537 538 new_ins = propagate_types(ins, node) 539 if new_ins: 540 if isinstance(new_ins, (list,)): 541 for new_ins_ in new_ins: 542 new_ins_.set_node(ins.node) 543 del result[idx] 544 for i, ins in enumerate(new_ins): 545 result.insert(idx + i, ins) 546 idx = idx + len(new_ins) - 1 547 else: 548 new_ins.set_node(ins.node) 549 result[idx] = new_ins 550 idx = idx + 1 551 return result 552 553 554def _convert_type_contract(ir: Member) -> Assignment: 555 assert isinstance(ir.variable_left.type, TypeInformation) 556 contract = ir.variable_left.type.type 557 558 scope = ir.node.file_scope 559 560 if ir.variable_right == "creationCode": 561 bytecode = scope.bytecode_init( 562 ir.node.compilation_unit.crytic_compile_compilation_unit, contract.name 563 ) 564 assignment = Assignment(ir.lvalue, Constant(str(bytecode)), ElementaryType("bytes")) 565 assignment.set_expression(ir.expression) 566 assignment.set_node(ir.node) 567 assignment.lvalue.set_type(ElementaryType("bytes")) 568 return assignment 569 if ir.variable_right == "runtimeCode": 570 bytecode = scope.bytecode_runtime( 571 ir.node.compilation_unit.crytic_compile_compilation_unit, contract.name 572 ) 573 assignment = Assignment(ir.lvalue, Constant(str(bytecode)), ElementaryType("bytes")) 574 assignment.set_expression(ir.expression) 575 assignment.set_node(ir.node) 576 assignment.lvalue.set_type(ElementaryType("bytes")) 577 return assignment 578 if ir.variable_right == "interfaceId": 579 entry_points = contract.functions_entry_points 580 interfaceId = 0 581 for entry_point in entry_points: 582 interfaceId = interfaceId ^ get_function_id(entry_point.full_name) 583 assignment = Assignment( 584 ir.lvalue, 585 Constant(str(interfaceId), constant_type=ElementaryType("bytes4")), 586 ElementaryType("bytes4"), 587 ) 588 assignment.set_expression(ir.expression) 589 assignment.set_node(ir.node) 590 assignment.lvalue.set_type(ElementaryType("bytes4")) 591 return assignment 592 593 if ir.variable_right == "name": 594 assignment = Assignment(ir.lvalue, Constant(contract.name), ElementaryType("string")) 595 assignment.set_expression(ir.expression) 596 assignment.set_node(ir.node) 597 assignment.lvalue.set_type(ElementaryType("string")) 598 return assignment 599 600 raise SlithIRError(f"type({contract.name}).{ir.variable_right} is unknown") 601 602 603def propagate_types(ir: Operation, node: "Node"): # pylint: disable=too-many-locals 604 # propagate the type 605 node_function = node.function 606 607 using_for: USING_FOR = {} 608 if isinstance(node_function, FunctionContract): 609 using_for = node_function.contract.using_for_complete 610 elif isinstance(node_function, FunctionTopLevel): 611 using_for = node_function.using_for_complete 612 613 if isinstance(ir, OperationWithLValue) and ir.lvalue: 614 # Force assignment in case of missing previous correct type 615 if not ir.lvalue.type: 616 if isinstance(ir, Assignment): 617 ir.lvalue.set_type(ir.rvalue.type) 618 elif isinstance(ir, Binary): 619 if BinaryType.return_bool(ir.type): 620 ir.lvalue.set_type(ElementaryType("bool")) 621 else: 622 ir.lvalue.set_type(ir.variable_left.type) 623 elif isinstance(ir, Delete): 624 # nothing to propagate 625 pass 626 elif isinstance(ir, LibraryCall): 627 return convert_type_library_call(ir, ir.destination) 628 elif isinstance(ir, HighLevelCall): 629 t = ir.destination.type 630 # Temporary operation (they are removed later) 631 if t is None: 632 return None 633 634 if isinstance(t, ElementaryType) and t.name == "address": 635 if can_be_solidity_func(ir): 636 return convert_to_solidity_func(ir) 637 638 # convert library or top level function 639 if t in using_for or "*" in using_for: 640 new_ir = convert_to_library_or_top_level(ir, node, using_for) 641 if new_ir: 642 return new_ir 643 644 # convert library function when used with "this" 645 if ( 646 isinstance(t, ElementaryType) 647 and t.name == "address" 648 and ir.destination.name == "this" 649 and UserDefinedType(node_function.contract) in using_for 650 ): 651 new_ir = convert_to_library_or_top_level(ir, node, using_for) 652 if new_ir: 653 return new_ir 654 655 if isinstance(t, UserDefinedType): 656 # UserdefinedType 657 t_type = t.type 658 if isinstance(t_type, Contract): 659 # the target contract of the IR is the t_type (the destination of the call) 660 return convert_type_of_high_and_internal_level_call(ir, t_type) 661 662 # Convert HighLevelCall to LowLevelCall 663 if (isinstance(t, ElementaryType) and t.name == "address") or ( 664 isinstance(t, TypeAlias) and t.underlying_type.name == "address" 665 ): 666 if ir.destination.name == "this": 667 # Cannot be a top level function with this. 668 assert isinstance(node_function, FunctionContract) 669 # the target contract is the contract itself 670 return convert_type_of_high_and_internal_level_call( 671 ir, node_function.contract 672 ) 673 if can_be_low_level(ir): 674 return convert_to_low_level(ir) 675 676 # Convert push operations 677 # May need to insert a new operation 678 # Which leads to return a list of operation 679 if isinstance(t, ArrayType) or ( 680 isinstance(t, ElementaryType) and t.type == "bytes" 681 ): 682 # Solidity uses push 683 # Vyper uses append 684 if ir.function_name in ["push", "append"] and len(ir.arguments) <= 1: 685 return convert_to_push(ir, node) 686 if ir.function_name == "pop" and len(ir.arguments) == 0: 687 return convert_to_pop(ir, node) 688 689 elif isinstance(ir, Index): 690 if isinstance(ir.variable_left.type, MappingType): 691 ir.lvalue.set_type(ir.variable_left.type.type_to) 692 elif isinstance(ir.variable_left.type, ArrayType): 693 ir.lvalue.set_type(ir.variable_left.type.type) 694 695 elif isinstance(ir, InitArray): 696 length = len(ir.init_values) 697 t = ir.init_values[0].type 698 ir.lvalue.set_type(ArrayType(t, length)) 699 elif isinstance(ir, InternalCall): 700 # if its not a tuple, return a singleton 701 if ir.function is None: 702 func = node.function 703 function_contract = ( 704 func.contract if isinstance(func, FunctionContract) else None 705 ) 706 # the target contract might be None if its a top level function 707 convert_type_of_high_and_internal_level_call(ir, function_contract) 708 return_type = ir.function.return_type 709 if return_type: 710 if len(return_type) == 1: 711 ir.lvalue.set_type(return_type[0]) 712 elif len(return_type) > 1: 713 ir.lvalue.set_type(return_type) 714 else: 715 ir.lvalue = None 716 elif isinstance(ir, InternalDynamicCall): 717 # if its not a tuple, return a singleton 718 return_type = ir.function_type.return_type 719 if return_type: 720 if len(return_type) == 1: 721 ir.lvalue.set_type(return_type[0]) 722 else: 723 ir.lvalue.set_type(return_type) 724 else: 725 ir.lvalue = None 726 elif isinstance(ir, LowLevelCall): 727 # Call are not yet converted 728 # This should not happen 729 assert False 730 elif isinstance(ir, Member): 731 # TODO we should convert the reference to a temporary if the member is a length or a balance 732 if ( 733 ir.variable_right == "length" 734 and not isinstance(ir.variable_left, Contract) 735 and isinstance(ir.variable_left.type, (ElementaryType, ArrayType)) 736 ): 737 new_length = Length(ir.variable_left, ir.lvalue) 738 assert ir.expression 739 new_length.set_expression(ir.expression) 740 new_length.lvalue.points_to = ir.variable_left 741 new_length.set_node(ir.node) 742 return new_length 743 # This only happen for .balance/code/codehash access on a variable for which we dont know at 744 # early parsing time the type 745 # Like 746 # function return_addr() internal returns(addresss) 747 # 748 # return_addr().balance 749 # Here slithIR will incorrectly create a REF variable instead of a Temp variable 750 # However this pattern does not appear so often 751 if ( 752 ir.variable_right.name in ["balance", "code", "codehash"] 753 and not isinstance(ir.variable_left, Contract) 754 and isinstance(ir.variable_left.type, ElementaryType) 755 ): 756 name = ir.variable_right.name + "(address)" 757 sol_func = SolidityFunction(name) 758 s = SolidityCall( 759 sol_func, 760 1, 761 ir.lvalue, 762 sol_func.return_type, 763 ) 764 s.arguments.append(ir.variable_left) 765 s.set_expression(ir.expression) 766 s.lvalue.set_type(sol_func.return_type) 767 s.set_node(ir.node) 768 return s 769 if ( 770 ir.variable_right == "codesize" 771 and not isinstance(ir.variable_left, Contract) 772 and isinstance(ir.variable_left.type, ElementaryType) 773 ): 774 b = CodeSize(ir.variable_left, ir.lvalue) 775 b.set_expression(ir.expression) 776 b.set_node(ir.node) 777 return b 778 if ir.variable_right == "selector" and isinstance(ir.variable_left, (CustomError)): 779 assignment = Assignment( 780 ir.lvalue, 781 Constant( 782 str(get_function_id(ir.variable_left.solidity_signature)), 783 ElementaryType("bytes4"), 784 ), 785 ElementaryType("bytes4"), 786 ) 787 assignment.set_expression(ir.expression) 788 assignment.set_node(ir.node) 789 assignment.lvalue.set_type(ElementaryType("bytes4")) 790 return assignment 791 792 if isinstance(ir.variable_right, (CustomError)): 793 assignment = Assignment( 794 ir.lvalue, 795 Constant( 796 str(get_function_id(ir.variable_left.solidity_signature)), 797 ElementaryType("bytes4"), 798 ), 799 ElementaryType("bytes4"), 800 ) 801 assignment.set_expression(ir.expression) 802 assignment.set_node(ir.node) 803 assignment.lvalue.set_type(ElementaryType("bytes4")) 804 return assignment 805 if ir.variable_right == "selector" and isinstance(ir.variable_left, (Event)): 806 # the event selector returns a bytes32, which is different from the error/function selector 807 # which returns a bytes4 808 assignment = Assignment( 809 ir.lvalue, 810 Constant( 811 str(get_event_id(ir.variable_left.full_name)), ElementaryType("bytes32") 812 ), 813 ElementaryType("bytes32"), 814 ) 815 assignment.set_expression(ir.expression) 816 assignment.set_node(ir.node) 817 assignment.lvalue.set_type(ElementaryType("bytes32")) 818 return assignment 819 if ir.variable_right == "selector" and ( 820 isinstance(ir.variable_left.type, (Function)) 821 ): 822 assignment = Assignment( 823 ir.lvalue, 824 Constant( 825 str(get_function_id(ir.variable_left.type.full_name)), 826 ElementaryType("bytes4"), 827 ), 828 ElementaryType("bytes4"), 829 ) 830 assignment.set_expression(ir.expression) 831 assignment.set_node(ir.node) 832 assignment.lvalue.set_type(ElementaryType("bytes4")) 833 return assignment 834 if isinstance(ir.variable_left, TemporaryVariable) and isinstance( 835 ir.variable_left.type, TypeInformation 836 ): 837 return _convert_type_contract(ir) 838 left = ir.variable_left 839 t = None 840 ir_func = ir.node.function 841 # Handling of this.function_name usage 842 if ( 843 left == SolidityVariable("this") 844 and isinstance(ir.variable_right, Constant) 845 and isinstance(ir_func, FunctionContract) 846 and str(ir.variable_right) in [x.name for x in ir_func.contract.functions] 847 ): 848 # Assumption that this.function_name can only compile if 849 # And the contract does not have two functions starting with function_name 850 # Otherwise solc raises: 851 # Error: Member "f" not unique after argument-dependent lookup in contract 852 targeted_function = next( 853 (x for x in ir_func.contract.functions if x.name == str(ir.variable_right)) 854 ) 855 ir.lvalue.set_type(targeted_function) 856 elif isinstance(left, (Variable, SolidityVariable)): 857 t = left.type 858 elif isinstance(left, (Contract, Enum, Structure)): 859 t = UserDefinedType(left) 860 # can be None due to temporary operation 861 if t: 862 if isinstance(t, UserDefinedType): 863 # UserdefinedType 864 type_t = t.type 865 if isinstance(type_t, Enum): 866 ir.lvalue.set_type(t) 867 elif isinstance(type_t, Structure): 868 elems = type_t.elems 869 for elem in elems: 870 if elem == ir.variable_right: 871 ir.lvalue.set_type(elems[elem].type) 872 else: 873 assert isinstance(type_t, Contract) 874 # Allow type propagation as a Function 875 # Only for reference variables 876 # This allows to track the selector keyword 877 # We don't need to check for function collision, as solc prevents the use of selector 878 # if there are multiple functions with the same name 879 f = next( 880 (f for f in type_t.functions if f.name == ir.variable_right), 881 None, 882 ) 883 if f: 884 ir.lvalue.set_type(f) 885 else: 886 # Allow propagation for variable access through contract's name 887 # like Base_contract.my_variable 888 v = next( 889 ( 890 v 891 for v in type_t.state_variables 892 if v.name == ir.variable_right 893 ), 894 None, 895 ) 896 if v: 897 ir.lvalue.set_type(v.type) 898 elif isinstance(ir, NewArray): 899 ir.lvalue.set_type(ir.array_type) 900 elif isinstance(ir, NewContract): 901 ir.lvalue.set_type(ir.contract_name) 902 elif isinstance(ir, NewElementaryType): 903 ir.lvalue.set_type(ir.type) 904 elif isinstance(ir, NewStructure): 905 ir.lvalue.set_type(UserDefinedType(ir.structure)) 906 elif isinstance(ir, Send): 907 ir.lvalue.set_type(ElementaryType("bool")) 908 elif isinstance(ir, SolidityCall): 909 if ir.function.name in ["type(address)", "type()"]: 910 ir.function.return_type = [TypeInformation(ir.arguments[0])] 911 return_type = ir.function.return_type 912 if len(return_type) == 1: 913 ir.lvalue.set_type(return_type[0]) 914 elif len(return_type) > 1: 915 ir.lvalue.set_type(return_type) 916 elif isinstance(ir, TypeConversion): 917 ir.lvalue.set_type(ir.type) 918 elif isinstance(ir, Unary): 919 ir.lvalue.set_type(ir.rvalue.type) 920 elif isinstance(ir, Unpack): 921 types = ir.tuple.type.type 922 idx = ir.index 923 t = types[idx] 924 ir.lvalue.set_type(t) 925 elif isinstance( 926 ir, 927 ( 928 Argument, 929 TmpCall, 930 TmpNewArray, 931 TmpNewContract, 932 TmpNewStructure, 933 TmpNewElementaryType, 934 ), 935 ): 936 # temporary operation; they will be removed 937 pass 938 else: 939 raise SlithIRError(f"Not handling {type(ir)} during type propagation") 940 return None 941 942 943# pylint: disable=too-many-locals 944def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call, Nop]: 945 assert isinstance(ins, TmpCall) 946 if isinstance(ins.called, Variable) and isinstance(ins.called.type, FunctionType): 947 # If the call is made to a variable member, where the member is this 948 # We need to convert it to a HighLelelCall and not an internal dynamic call 949 if isinstance(ins.ori, Member) and ins.ori.variable_left == SolidityVariable("this"): 950 pass 951 else: 952 call = InternalDynamicCall(ins.lvalue, ins.called, ins.called.type) 953 call.set_expression(ins.expression) 954 call.call_id = ins.call_id 955 return call 956 if isinstance(ins.ori, Member): 957 # If there is a call on an inherited contract, it is an internal call or an event 958 if contract and ins.ori.variable_left in contract.inheritance + [contract]: 959 if str(ins.ori.variable_right) in [f.name for f in contract.functions]: 960 internalcall = InternalCall( 961 (ins.ori.variable_right, ins.ori.variable_left.name), 962 ins.nbr_arguments, 963 ins.lvalue, 964 ins.type_call, 965 ) 966 internalcall.set_expression(ins.expression) 967 internalcall.call_id = ins.call_id 968 return internalcall 969 if str(ins.ori.variable_right) in [f.name for f in contract.events]: 970 eventcall = EventCall(ins.ori.variable_right) 971 eventcall.set_expression(ins.expression) 972 eventcall.call_id = ins.call_id 973 return eventcall 974 if isinstance(ins.ori.variable_left, Contract): 975 st = ins.ori.variable_left.get_structure_from_name(ins.ori.variable_right) 976 if st: 977 op = NewStructure(st, ins.lvalue, names=ins.names) 978 op.set_expression(ins.expression) 979 op.call_id = ins.call_id 980 return op 981 982 # For event emit through library 983 # lib L { event E()} 984 # ... 985 # emit L.E(); 986 if str(ins.ori.variable_right) in [f.name for f in ins.ori.variable_left.events]: 987 eventcall = EventCall(ins.ori.variable_right) 988 eventcall.set_expression(ins.expression) 989 eventcall.call_id = ins.call_id 990 return eventcall 991 992 # lib Lib { error Error()} ... revert Lib.Error() 993 if str(ins.ori.variable_right) in ins.ori.variable_left.custom_errors_as_dict: 994 custom_error = ins.ori.variable_left.custom_errors_as_dict[ 995 str(ins.ori.variable_right) 996 ] 997 assert isinstance( 998 custom_error, 999 CustomError, 1000 ) 1001 sol_function = SolidityCustomRevert(custom_error) 1002 solidity_call = SolidityCall( 1003 sol_function, ins.nbr_arguments, ins.lvalue, ins.type_call 1004 ) 1005 solidity_call.set_expression(ins.expression) 1006 return solidity_call 1007 1008 libcall = LibraryCall( 1009 ins.ori.variable_left, 1010 ins.ori.variable_right, 1011 ins.nbr_arguments, 1012 ins.lvalue, 1013 ins.type_call, 1014 names=ins.names, 1015 ) 1016 libcall.set_expression(ins.expression) 1017 libcall.call_id = ins.call_id 1018 return libcall 1019 if isinstance(ins.ori.variable_left, Function): 1020 # Support for library call where the parameter is a function 1021 # We could merge this with the standard library handling 1022 # Except that we will have some troubles with using_for 1023 # As the type of the function will not match function() 1024 # Additionally we do not have a correct view on the parameters of the tmpcall 1025 # At this level 1026 # 1027 # library FunctionExtensions { 1028 # function h(function() internal _t, uint8) internal { } 1029 # } 1030 # contract FunctionMembers { 1031 # using FunctionExtensions for function(); 1032 # 1033 # function f() public { 1034 # f.h(1); 1035 # } 1036 # } 1037 node_func = ins.node.function 1038 using_for = ( 1039 node_func.contract.using_for_complete 1040 if isinstance(node_func, FunctionContract) 1041 else {} 1042 ) 1043 1044 targeted_libraries = ( 1045 [] + using_for.get("*", []) + using_for.get(FunctionType([], []), []) 1046 ) 1047 lib_contract: Contract 1048 candidates = [] 1049 for lib_contract_type in targeted_libraries: 1050 if not isinstance(lib_contract_type, UserDefinedType) and isinstance( 1051 lib_contract_type.type, Contract 1052 ): 1053 continue 1054 if isinstance(lib_contract_type, FunctionContract): 1055 # Using for with list of functions, this is the function called 1056 candidates.append(lib_contract_type) 1057 else: 1058 lib_contract = lib_contract_type.type 1059 for lib_func in lib_contract.functions: 1060 if lib_func.name == ins.ori.variable_right: 1061 candidates.append(lib_func) 1062 1063 if len(candidates) == 1: 1064 lib_func = candidates[0] 1065 # Library must be from a contract 1066 assert isinstance(lib_func, FunctionContract) 1067 lib_call = LibraryCall( 1068 lib_func.contract, 1069 Constant(lib_func.name), 1070 len(lib_func.parameters), 1071 ins.lvalue, 1072 "d", 1073 names=ins.names, 1074 ) 1075 lib_call.set_expression(ins.expression) 1076 lib_call.set_node(ins.node) 1077 lib_call.call_gas = ins.call_gas 1078 lib_call.call_id = ins.call_id 1079 lib_call.set_node(ins.node) 1080 lib_call.function = lib_func 1081 lib_call.arguments.append(ins.ori.variable_left) 1082 return lib_call 1083 # We do not support something lik 1084 # library FunctionExtensions { 1085 # function h(function() internal _t, uint8) internal { } 1086 # function h(function() internal _t, bool) internal { } 1087 # } 1088 # contract FunctionMembers { 1089 # using FunctionExtensions for function(); 1090 # 1091 # function f() public { 1092 # f.h(1); 1093 # } 1094 # } 1095 to_log = "Slither does not support dynamic functions to libraries if functions have the same name" 1096 to_log += f"{[candidate.full_name for candidate in candidates]}" 1097 raise SlithIRError(to_log) 1098 if isinstance(ins.ori.variable_left, SolidityImportPlaceHolder): 1099 # For top level import, where the import statement renames the filename 1100 # See https://blog.soliditylang.org/2020/09/02/solidity-0.7.1-release-announcement/ 1101 1102 current_path = Path(ins.ori.variable_left.source_mapping.filename.absolute).parent 1103 target = str( 1104 Path(current_path, ins.ori.variable_left.import_directive.filename).absolute() 1105 ) 1106 top_level_function_targets = [ 1107 f 1108 for f in ins.compilation_unit.functions_top_level 1109 if f.source_mapping.filename.absolute == target 1110 and f.name == ins.ori.variable_right 1111 and len(f.parameters) == ins.nbr_arguments 1112 ] 1113 1114 internalcall = InternalCall( 1115 (ins.ori.variable_right, ins.ori.variable_left.name), 1116 ins.nbr_arguments, 1117 ins.lvalue, 1118 ins.type_call, 1119 ) 1120 internalcall.set_expression(ins.expression) 1121 internalcall.call_id = ins.call_id 1122 internalcall.function_candidates = top_level_function_targets 1123 return internalcall 1124 1125 if ins.ori.variable_left == ElementaryType("bytes") and ins.ori.variable_right == Constant( 1126 "concat" 1127 ): 1128 s = SolidityCall( 1129 SolidityFunction("bytes.concat()"), 1130 ins.nbr_arguments, 1131 ins.lvalue, 1132 ins.type_call, 1133 ) 1134 s.set_expression(ins.expression) 1135 return s 1136 1137 if ins.ori.variable_left == ElementaryType("string") and ins.ori.variable_right == Constant( 1138 "concat" 1139 ): 1140 s = SolidityCall( 1141 SolidityFunction("string.concat()"), 1142 ins.nbr_arguments, 1143 ins.lvalue, 1144 ins.type_call, 1145 ) 1146 s.set_expression(ins.expression) 1147 return s 1148 1149 msgcall = HighLevelCall( 1150 ins.ori.variable_left, 1151 ins.ori.variable_right, 1152 ins.nbr_arguments, 1153 ins.lvalue, 1154 ins.type_call, 1155 names=ins.names, 1156 ) 1157 msgcall.call_id = ins.call_id 1158 1159 if ins.call_gas: 1160 msgcall.call_gas = ins.call_gas 1161 if ins.call_value: 1162 msgcall.call_value = ins.call_value 1163 msgcall.set_expression(ins.expression) 1164 1165 return msgcall 1166 1167 if isinstance(ins.ori, TmpCall): 1168 r = extract_tmp_call(ins.ori, contract) 1169 r.set_node(ins.node) 1170 return r 1171 if isinstance(ins.called, SolidityVariableComposed): 1172 if str(ins.called) == "block.blockhash": 1173 ins.called = SolidityFunction("blockhash(uint256)") 1174 1175 if isinstance(ins.called, SolidityFunction): 1176 s = SolidityCall(ins.called, ins.nbr_arguments, ins.lvalue, ins.type_call) 1177 s.set_expression(ins.expression) 1178 return s 1179 1180 if isinstance(ins.called, CustomError): 1181 sol_function = SolidityCustomRevert(ins.called) 1182 s = SolidityCall(sol_function, ins.nbr_arguments, ins.lvalue, ins.type_call) 1183 s.set_expression(ins.expression) 1184 return s 1185 1186 if isinstance(ins.ori, TmpNewElementaryType): 1187 n = NewElementaryType(ins.ori.type, ins.lvalue) 1188 n.set_expression(ins.expression) 1189 return n 1190 1191 if isinstance(ins.ori, TmpNewContract): 1192 op = NewContract(ins.ori.contract_name, ins.lvalue) 1193 op.set_expression(ins.expression) 1194 op.call_id = ins.call_id 1195 if ins.call_value: 1196 op.call_value = ins.call_value 1197 if ins.call_salt: 1198 op.call_salt = ins.call_salt 1199 return op 1200 1201 if isinstance(ins.ori, TmpNewArray): 1202 n = NewArray(ins.ori.array_type, ins.lvalue) 1203 n.set_expression(ins.expression) 1204 return n 1205 1206 if isinstance(ins.called, Structure): 1207 op = NewStructure(ins.called, ins.lvalue, names=ins.names) 1208 op.set_expression(ins.expression) 1209 op.call_id = ins.call_id 1210 op.set_expression(ins.expression) 1211 return op 1212 1213 if isinstance(ins.called, Event): 1214 e = EventCall(ins.called.name) 1215 e.set_expression(ins.expression) 1216 return e 1217 1218 if isinstance(ins.called, Contract): 1219 # Called a base constructor, where there is no constructor 1220 if ins.called.constructor is None: 1221 return Nop() 1222 # Case where: 1223 # contract A{ constructor(uint) } 1224 # contract B is A {} 1225 # contract C is B{ constructor() A(10) B() {} 1226 # C calls B(), which does not exist 1227 # Ideally we should compare here for the parameters types too 1228 if len(ins.called.constructor.parameters) != ins.nbr_arguments: 1229 return Nop() 1230 internalcall = InternalCall( 1231 ins.called.constructor, ins.nbr_arguments, ins.lvalue, ins.type_call, ins.names 1232 ) 1233 internalcall.call_id = ins.call_id 1234 internalcall.set_expression(ins.expression) 1235 return internalcall 1236 1237 raise SlithIRError(f"Not extracted {type(ins.called)}Â {ins}") 1238 1239 1240# endregion 1241################################################################################### 1242################################################################################### 1243# region Conversion operations 1244################################################################################### 1245################################################################################### 1246 1247 1248def can_be_low_level(ir: HighLevelCall) -> bool: 1249 return ir.function_name in [ 1250 "transfer", 1251 "send", 1252 "call", 1253 "delegatecall", 1254 "callcode", 1255 "staticcall", 1256 "raw_call", 1257 ] 1258 1259 1260def convert_to_low_level( 1261 ir: HighLevelCall, 1262) -> Union[Send, LowLevelCall, Transfer,]: 1263 """ 1264 Convert to a transfer/send/or low level call 1265 The function assume to receive a correct IR 1266 The checks must be done by the caller 1267 1268 Must be called after can_be_low_level 1269 """ 1270 if ir.function_name == "transfer": 1271 assert len(ir.arguments) == 1 1272 prev_ir = ir 1273 ir = Transfer(ir.destination, ir.arguments[0]) 1274 ir.set_expression(prev_ir.expression) 1275 ir.set_node(prev_ir.node) 1276 return ir 1277 if ir.function_name == "send": 1278 assert len(ir.arguments) == 1 1279 prev_ir = ir 1280 ir = Send(ir.destination, ir.arguments[0], ir.lvalue) 1281 ir.set_expression(prev_ir.expression) 1282 ir.set_node(prev_ir.node) 1283 ir.lvalue.set_type(ElementaryType("bool")) 1284 return ir 1285 if ir.function_name in ["call", "delegatecall", "callcode", "staticcall", "raw_call"]: 1286 new_ir = LowLevelCall( 1287 ir.destination, ir.function_name, ir.nbr_arguments, ir.lvalue, ir.type_call 1288 ) 1289 new_ir.call_gas = ir.call_gas 1290 new_ir.call_value = ir.call_value 1291 new_ir.arguments = ir.arguments 1292 # TODO fix this for Vyper 1293 if ir.node.compilation_unit.solc_version >= "0.5": 1294 new_ir.lvalue.set_type([ElementaryType("bool"), ElementaryType("bytes")]) 1295 else: 1296 new_ir.lvalue.set_type(ElementaryType("bool")) 1297 new_ir.set_expression(ir.expression) 1298 new_ir.set_node(ir.node) 1299 return new_ir 1300 raise SlithIRError(f"Incorrect conversion to low level {ir}") 1301 1302 1303def can_be_solidity_func(ir: HighLevelCall) -> bool: 1304 if not isinstance(ir, HighLevelCall): 1305 return False 1306 return ir.destination.name == "abi" and ir.function_name in [ 1307 "encode", 1308 "encodePacked", 1309 "encodeWithSelector", 1310 "encodeWithSignature", 1311 "encodeCall", 1312 "decode", 1313 ] 1314 1315 1316def convert_to_solidity_func( 1317 ir: HighLevelCall, 1318) -> SolidityCall: 1319 """ 1320 Must be called after can_be_solidity_func 1321 :param ir: 1322 :return: 1323 """ 1324 call = SolidityFunction(f"abi.{ir.function_name}()") 1325 new_ir = SolidityCall(call, ir.nbr_arguments, ir.lvalue, ir.type_call) 1326 new_ir.arguments = ir.arguments 1327 new_ir.set_expression(ir.expression) 1328 new_ir.set_node(ir.node) 1329 if isinstance(call.return_type, list) and len(call.return_type) == 1: 1330 new_ir.lvalue.set_type(call.return_type[0]) 1331 elif ( 1332 isinstance(new_ir.lvalue, TupleVariable) 1333 and call == SolidityFunction("abi.decode()") 1334 and len(new_ir.arguments) == 2 1335 and isinstance(new_ir.arguments[1], list) 1336 ): 1337 types = [] 1338 for arg_type in new_ir.arguments[1]: 1339 decode_type = arg_type 1340 if isinstance(decode_type, (Structure, Enum, Contract)): 1341 decode_type = UserDefinedType(decode_type) 1342 types.append(decode_type) 1343 new_ir.lvalue.set_type(types) 1344 # abi.decode where the type to decode is a singleton 1345 # abi.decode(a, (uint)) 1346 elif call == SolidityFunction("abi.decode()") and len(new_ir.arguments) == 2: 1347 # If the variable is a referenceVariable, we are lost 1348 # See https://github.com/crytic/slither/issues/566 for potential solutions 1349 if not isinstance(new_ir.arguments[1], ReferenceVariable): 1350 decode_type = new_ir.arguments[1] 1351 if isinstance(decode_type, (Structure, Enum, Contract)): 1352 decode_type = UserDefinedType(decode_type) 1353 new_ir.lvalue.set_type(decode_type) 1354 else: 1355 new_ir.lvalue.set_type(call.return_type) 1356 return new_ir 1357 1358 1359def convert_to_push_expand_arr( 1360 ir: HighLevelCall, node: "Node", ret: List[Any] 1361) -> TemporaryVariable: 1362 arr = ir.destination 1363 1364 length = ReferenceVariable(node) 1365 length.set_type(ElementaryType("uint256")) 1366 1367 ir_length = Length(arr, length) 1368 ir_length.set_expression(ir.expression) 1369 ir_length.set_node(ir.node) 1370 ir_length.lvalue.points_to = arr 1371 ret.append(ir_length) 1372 1373 length_val = TemporaryVariable(node) 1374 length_val.set_type(ElementaryType("uint256")) 1375 ir_get_length = Assignment(length_val, length, ElementaryType("uint256")) 1376 ir_get_length.set_expression(ir.expression) 1377 ir_get_length.set_node(ir.node) 1378 ret.append(ir_get_length) 1379 1380 new_length_val = TemporaryVariable(node) 1381 ir_add_1 = Binary( 1382 new_length_val, length_val, Constant("1", ElementaryType("uint256")), BinaryType.ADDITION 1383 ) 1384 ir_add_1.set_expression(ir.expression) 1385 ir_add_1.set_node(ir.node) 1386 ret.append(ir_add_1) 1387 1388 ir_assign_length = Assignment(length, new_length_val, ElementaryType("uint256")) 1389 ir_assign_length.set_expression(ir.expression) 1390 ir_assign_length.set_node(ir.node) 1391 ret.append(ir_assign_length) 1392 1393 return length_val 1394 1395 1396def convert_to_push_set_val( 1397 ir: HighLevelCall, 1398 node: "Node", 1399 length_val: TemporaryVariable, 1400 ret: List[ 1401 Union[ 1402 Length, 1403 Assignment, 1404 Binary, 1405 ] 1406 ], 1407) -> None: 1408 arr = ir.destination 1409 1410 new_type = ir.destination.type.type 1411 1412 element_to_add = ReferenceVariable(node) 1413 element_to_add.set_type(new_type) 1414 ir_assign_element_to_add = Index(element_to_add, arr, length_val) 1415 ir_assign_element_to_add.set_expression(ir.expression) 1416 ir_assign_element_to_add.set_node(ir.node) 1417 ret.append(ir_assign_element_to_add) 1418 1419 if len(ir.arguments) > 0: 1420 assign_value = ir.arguments[0] 1421 if isinstance(assign_value, list): 1422 assign_value = TemporaryVariable(node) 1423 assign_value.set_type(element_to_add.type) 1424 ir_assign_value = InitArray(ir.arguments[0], assign_value) 1425 ir_assign_value.set_expression(ir.expression) 1426 ir_assign_value.set_node(ir.node) 1427 ret.append(ir_assign_value) 1428 1429 ir_assign_value = Assignment(element_to_add, assign_value, assign_value.type) 1430 ir_assign_value.set_expression(ir.expression) 1431 ir_assign_value.set_node(ir.node) 1432 ret.append(ir_assign_value) 1433 else: 1434 new_element = ir.lvalue 1435 new_element.set_type(new_type) 1436 ir_assign_value = Assignment(new_element, element_to_add, new_type) 1437 ir_assign_value.set_expression(ir.expression) 1438 ir_assign_value.set_node(ir.node) 1439 ret.append(ir_assign_value) 1440 1441 1442def convert_to_push( 1443 ir: HighLevelCall, node: "Node" 1444) -> List[Union[Length, Assignment, Binary, Index, InitArray,]]: 1445 """ 1446 Convert a call to a series of operations to push a new value onto the array 1447 1448 The function assume to receive a correct IR 1449 The checks must be done by the caller 1450 1451 May necessitate to create an intermediate operation (InitArray) 1452 Necessitate to return the length (see push documentation) 1453 As a result, the function return may return a list 1454 """ 1455 1456 ret = [] 1457 1458 length_val = convert_to_push_expand_arr(ir, node, ret) 1459 convert_to_push_set_val(ir, node, length_val, ret) 1460 1461 return ret 1462 1463 1464def convert_to_pop(ir: HighLevelCall, node: "Node") -> List[Operation]: 1465 """ 1466 Convert pop operators 1467 Return a list of 6 operations 1468 """ 1469 1470 ret: List[Operation] = [] 1471 1472 arr = ir.destination 1473 length = ReferenceVariable(node) 1474 length.set_type(ElementaryType("uint256")) 1475 1476 ir_length = Length(arr, length) 1477 assert ir.expression 1478 ir_length.set_expression(ir.expression) 1479 ir_length.set_node(ir.node) 1480 length.points_to = arr 1481 ret.append(ir_length) 1482 1483 val = TemporaryVariable(node) 1484 1485 ir_sub_1 = Binary(val, length, Constant("1", ElementaryType("uint256")), BinaryType.SUBTRACTION) 1486 ir_sub_1.set_expression(ir.expression) 1487 ir_sub_1.set_node(ir.node) 1488 ret.append(ir_sub_1) 1489 1490 element_to_delete = ReferenceVariable(node) 1491 ir_assign_element_to_delete = Index(element_to_delete, arr, val) 1492 # TODO the following is equivalent to length.points_to = arr 1493 # Should it be removed? 1494 ir_length.lvalue.points_to = arr 1495 # Note bytes is an ElementaryType not ArrayType and bytes1 should be returned 1496 # since bytes is bytes1[] without padding between the elements 1497 # while in other cases such as uint256[] (ArrayType) we use ir.destination.type.type 1498 # in this way we will have the type always set to the corresponding ElementaryType 1499 element_to_delete.set_type( 1500 ElementaryType("bytes1") 1501 if isinstance(ir.destination.type, ElementaryType) 1502 else ir.destination.type.type 1503 ) 1504 ir_assign_element_to_delete.set_expression(ir.expression) 1505 ir_assign_element_to_delete.set_node(ir.node) 1506 ret.append(ir_assign_element_to_delete) 1507 1508 ir_delete = Delete(element_to_delete, element_to_delete) 1509 ir_delete.set_expression(ir.expression) 1510 ir_delete.set_node(ir.node) 1511 ret.append(ir_delete) 1512 1513 length_to_assign = ReferenceVariable(node) 1514 length_to_assign.set_type(ElementaryType("uint256")) 1515 ir_length = Length(arr, length_to_assign) 1516 ir_length.set_expression(ir.expression) 1517 length_to_assign.points_to = arr 1518 ir_length.set_node(ir.node) 1519 ret.append(ir_length) 1520 1521 ir_assign_length = Assignment(length_to_assign, val, ElementaryType("uint256")) 1522 ir_assign_length.set_expression(ir.expression) 1523 ir_assign_length.set_node(ir.node) 1524 ret.append(ir_assign_length) 1525 1526 return ret 1527 1528 1529def look_for_library_or_top_level( 1530 ir: HighLevelCall, 1531 using_for, 1532 t: Union[ 1533 UserDefinedType, 1534 ElementaryType, 1535 str, 1536 TypeAliasTopLevel, 1537 ], 1538) -> Optional[Union[LibraryCall, InternalCall,]]: 1539 for destination in using_for[t]: 1540 if isinstance(destination, FunctionTopLevel) and destination.name == ir.function_name: 1541 arguments = [ir.destination] + ir.arguments 1542 if ( 1543 len(destination.parameters) == len(arguments) 1544 and _find_function_from_parameter(arguments, [destination], True) is not None 1545 ): 1546 internalcall = InternalCall(destination, ir.nbr_arguments, ir.lvalue, ir.type_call) 1547 internalcall.set_expression(ir.expression) 1548 internalcall.set_node(ir.node) 1549 internalcall.arguments = [ir.destination] + ir.arguments 1550 return_type = internalcall.function.return_type 1551 if return_type: 1552 if len(return_type) == 1: 1553 internalcall.lvalue.set_type(return_type[0]) 1554 elif len(return_type) > 1: 1555 internalcall.lvalue.set_type(return_type) 1556 else: 1557 internalcall.lvalue = None 1558 return internalcall 1559 1560 lib_contract = None 1561 if isinstance(destination, FunctionContract) and destination.contract.is_library: 1562 lib_contract = destination.contract 1563 elif isinstance(destination, UserDefinedType) and isinstance(destination.type, Contract): 1564 lib_contract = destination.type 1565 1566 if lib_contract: 1567 lib_call = LibraryCall( 1568 lib_contract, 1569 ir.function_name, 1570 ir.nbr_arguments, 1571 ir.lvalue, 1572 ir.type_call, 1573 names=ir.names, 1574 ) 1575 lib_call.set_expression(ir.expression) 1576 lib_call.set_node(ir.node) 1577 lib_call.call_gas = ir.call_gas 1578 lib_call.arguments = [ir.destination] + ir.arguments 1579 new_ir = convert_type_library_call(lib_call, lib_contract) 1580 if new_ir: 1581 new_ir.set_node(ir.node) 1582 return new_ir 1583 return None 1584 1585 1586def convert_to_library_or_top_level( 1587 ir: HighLevelCall, node: "Node", using_for 1588) -> Optional[Union[LibraryCall, InternalCall,]]: 1589 t = ir.destination.type 1590 if t in using_for: 1591 new_ir = look_for_library_or_top_level(ir, using_for, t) 1592 if new_ir: 1593 return new_ir 1594 1595 if "*" in using_for: 1596 new_ir = look_for_library_or_top_level(ir, using_for, "*") 1597 if new_ir: 1598 return new_ir 1599 1600 if ( 1601 isinstance(t, ElementaryType) 1602 and t.name == "address" 1603 and ir.destination.name == "this" 1604 and UserDefinedType(node.function.contract) in using_for 1605 ): 1606 new_ir = look_for_library_or_top_level( 1607 ir, using_for, UserDefinedType(node.function.contract) 1608 ) 1609 if new_ir: 1610 return new_ir 1611 1612 return None 1613 1614 1615def get_type( 1616 t: Union[ 1617 UserDefinedType, 1618 ElementaryType, 1619 ] 1620) -> str: 1621 """ 1622 Convert a type to a str 1623 If the instance is a Contract, return 'address' instead 1624 """ 1625 if isinstance(t, UserDefinedType): 1626 if isinstance(t.type, Contract): 1627 return "address" 1628 return str(t) 1629 1630 1631def _can_be_implicitly_converted(source: str, target: str) -> bool: 1632 if source in ElementaryTypeInt and target in ElementaryTypeInt: 1633 return int(source[3:]) <= int(target[3:]) 1634 if source in Uint and target in Uint: 1635 return int(source[4:]) <= int(target[4:]) 1636 return source == target 1637 1638 1639def convert_type_library_call(ir: HighLevelCall, lib_contract: Contract) -> Optional[LibraryCall]: 1640 func = None 1641 candidates = [ 1642 f 1643 for f in lib_contract.functions 1644 if f.name == ir.function_name 1645 and not f.is_shadowed 1646 and len(f.parameters) == len(ir.arguments) 1647 ] 1648 1649 if len(candidates) == 1: 1650 func = candidates[0] 1651 # We can discard if there are arguments here because libraries only support constant variables 1652 # And constant variables cannot have non-value type 1653 # i.e. "uint[2] constant arr = [1,2];" is not possible in Solidity 1654 # If this were to change, the following condition might be broken 1655 if func is None and not ir.arguments: 1656 # TODO: handle collision with multiple state variables/functions 1657 func = lib_contract.get_state_variable_from_name(ir.function_name) 1658 if func is None and candidates: 1659 func = _find_function_from_parameter(ir.arguments, candidates, False) 1660 1661 # In case of multiple binding to the same type 1662 # TODO: this part might not be needed with _find_function_from_parameter 1663 if not func: 1664 # specific lookup when the compiler does implicit conversion 1665 # for example 1666 # myFunc(uint) 1667 # can be called with an uint8 1668 for function in lib_contract.functions: 1669 if function.name == ir.function_name and len(function.parameters) == len(ir.arguments): 1670 func = function 1671 break 1672 if not func: 1673 return None 1674 ir.function = func 1675 if isinstance(func, Function): 1676 t = func.return_type 1677 # if its not a tuple, return a singleton 1678 if t and len(t) == 1: 1679 t = t[0] 1680 else: 1681 # otherwise its a variable (getter) 1682 t = func.type 1683 if t: 1684 ir.lvalue.set_type(t) 1685 else: 1686 ir.lvalue = None 1687 return ir 1688 1689 1690def _convert_to_structure_to_list(return_type: Type) -> List[Type]: 1691 """ 1692 Convert structure elements types to a list of types 1693 Recursive function 1694 1695 :param return_type: 1696 :return: 1697 """ 1698 if isinstance(return_type, UserDefinedType) and isinstance(return_type.type, Structure): 1699 ret = [] 1700 for v in return_type.type.elems_ordered: 1701 ret += _convert_to_structure_to_list(v.type) 1702 return ret 1703 # Mapping and arrays are not included in external call 1704 # 1705 # contract A{ 1706 # 1707 # struct St{ 1708 # uint a; 1709 # uint b; 1710 # mapping(uint => uint) map; 1711 # uint[] array; 1712 # } 1713 # 1714 # mapping (uint => St) public st; 1715 # 1716 # } 1717 # 1718 # contract B{ 1719 # 1720 # function f(A a) public{ 1721 # (uint a, uint b) = a.st(0); 1722 # } 1723 # } 1724 if isinstance(return_type, (MappingType, ArrayType)): 1725 return [] 1726 1727 assert isinstance(return_type, (ElementaryType, UserDefinedType, TypeAlias)) 1728 return [return_type] 1729 1730 1731def convert_type_of_high_and_internal_level_call( 1732 ir: Operation, contract: Optional[Contract] 1733) -> Optional[Operation]: 1734 """ 1735 Convert the IR type based on heuristic 1736 1737 Args: 1738 ir: target 1739 contract: optional contract. This should be the target of the IR. It will be used to look up potential functions 1740 1741 Returns: 1742 Potential new IR 1743 """ 1744 1745 func = None 1746 if isinstance(ir, InternalCall): 1747 candidates: List[Function] 1748 if ir.function_candidates: 1749 # This path is taken only for SolidityImportPlaceHolder 1750 # Here we have already done a filtering on the potential targets 1751 candidates = ir.function_candidates 1752 else: 1753 candidates = [ 1754 f 1755 for f in contract.functions 1756 if f.name == ir.function_name 1757 and f.contract_declarer.name == ir.contract_name 1758 and len(f.parameters) == len(ir.arguments) 1759 ] 1760 1761 for import_statement in contract.file_scope.imports: 1762 if ( 1763 import_statement.alias is not None 1764 and import_statement.alias == ir.contract_name 1765 ): 1766 imported_scope = contract.compilation_unit.get_scope(import_statement.filename) 1767 candidates += [ 1768 f 1769 for f in list(imported_scope.functions) 1770 if f.name == ir.function_name and len(f.parameters) == len(ir.arguments) 1771 ] 1772 1773 func = _find_function_from_parameter(ir.arguments, candidates, False) 1774 1775 if not func: 1776 assert contract 1777 func = contract.get_state_variable_from_name(ir.function_name) 1778 else: 1779 assert isinstance(ir, HighLevelCall) 1780 assert contract 1781 candidates = [ 1782 f 1783 for f in contract.functions 1784 if f.name == ir.function_name 1785 and not f.is_shadowed 1786 and len(f.parameters) == len(ir.arguments) 1787 ] 1788 if len(candidates) == 1: 1789 func = candidates[0] 1790 if func is None: 1791 # TODO: handle collision with multiple state variables/functions 1792 func = contract.get_state_variable_from_name(ir.function_name) 1793 if func is None and candidates: 1794 func = _find_function_from_parameter(ir.arguments, candidates, False) 1795 1796 # low level lookup needs to be done as last step 1797 if not func: 1798 if can_be_low_level(ir): 1799 return convert_to_low_level(ir) 1800 if can_be_solidity_func(ir): 1801 return convert_to_solidity_func(ir) 1802 if not func: 1803 to_log = f"Function not found {ir.function_name}" 1804 logger.error(to_log) 1805 ir.function = func 1806 if isinstance(func, Function): 1807 return_type = func.return_type 1808 # if its not a tuple; return a singleton 1809 if return_type and len(return_type) == 1: 1810 return_type = return_type[0] 1811 else: 1812 # otherwise its a variable (getter) 1813 # If its a mapping or a array 1814 # we iterate until we find the final type 1815 # mapping and array can be mixed together 1816 # ex: 1817 # mapping ( uint => mapping ( uint => uint)) my_var 1818 # mapping(uint => uint)[] test;p 1819 if isinstance(func.type, (MappingType, ArrayType)): 1820 tmp = func.type 1821 while isinstance(tmp, (MappingType, ArrayType)): 1822 if isinstance(tmp, MappingType): 1823 tmp = tmp.type_to 1824 else: 1825 tmp = tmp.type 1826 return_type = tmp 1827 else: 1828 return_type = func.type 1829 if return_type: 1830 1831 # If the return type is a structure, but the lvalue is a tuple 1832 # We convert the type of the structure to a list of element 1833 # TODO: explore to replace all tuple variables by structures 1834 if ( 1835 isinstance(ir.lvalue, TupleVariable) 1836 and isinstance(return_type, UserDefinedType) 1837 and isinstance(return_type.type, Structure) 1838 ): 1839 return_type = _convert_to_structure_to_list(return_type) 1840 1841 ir.lvalue.set_type(return_type) 1842 else: 1843 ir.lvalue = None 1844 1845 return None 1846 1847 1848# endregion 1849################################################################################### 1850################################################################################### 1851# region Points to operation 1852################################################################################### 1853################################################################################### 1854 1855 1856def find_references_origin(irs: List[Operation]) -> None: 1857 """ 1858 Make lvalue of each Index, Member operation 1859 points to the left variable 1860 """ 1861 for ir in irs: 1862 if isinstance(ir, (Index, Member)): 1863 ir.lvalue.points_to = ir.variable_left 1864 1865 1866# endregion 1867################################################################################### 1868################################################################################### 1869# region Operation filtering 1870################################################################################### 1871################################################################################### 1872 1873 1874def remove_temporary(result): 1875 result = [ 1876 ins 1877 for ins in result 1878 if not isinstance( 1879 ins, 1880 ( 1881 Argument, 1882 TmpNewElementaryType, 1883 TmpNewContract, 1884 TmpNewArray, 1885 TmpNewStructure, 1886 ), 1887 ) 1888 ] 1889 1890 return result 1891 1892 1893def remove_unused(result: List[Operation]) -> List[Operation]: 1894 removed = True 1895 1896 if not result: 1897 return result 1898 1899 # dont remove the last elem, as it may be used by RETURN 1900 last_elem = result[-1] 1901 1902 while removed: 1903 removed = False 1904 1905 to_keep = [] 1906 to_remove = [] 1907 1908 # keep variables that are read 1909 # and reference that are written 1910 for ins in result: 1911 to_keep += [str(x) for x in ins.read] 1912 if isinstance(ins, OperationWithLValue) and not isinstance(ins, (Index, Member)): 1913 if isinstance(ins.lvalue, ReferenceVariable): 1914 to_keep += [str(ins.lvalue)] 1915 1916 for ins in result: 1917 if isinstance(ins, Member): 1918 if not ins.lvalue.name in to_keep and ins != last_elem: 1919 to_remove.append(ins) 1920 removed = True 1921 # Remove type(X) if X is an elementary type 1922 # This assume that type(X) is only used with min/max 1923 # If Solidity introduces other operation, we might remove this removal 1924 if isinstance(ins, SolidityCall) and ins.function == SolidityFunction("type()"): 1925 if isinstance(ins.arguments[0], ElementaryType): 1926 to_remove.append(ins) 1927 1928 result = [i for i in result if not i in to_remove] 1929 return result 1930 1931 1932# endregion 1933################################################################################### 1934################################################################################### 1935# region Constant type conversion 1936################################################################################### 1937################################################################################### 1938 1939 1940def convert_constant_types(irs: List[Operation]) -> None: 1941 """ 1942 late conversion of uint -> type for constant (Literal) 1943 :param irs: 1944 :return: 1945 """ 1946 # TODO: implement instances lookup for events, NewContract 1947 was_changed = True 1948 while was_changed: 1949 was_changed = False 1950 for ir in irs: 1951 if isinstance(ir, (Assignment, Binary)): 1952 if ( 1953 isinstance(ir.lvalue.type, ElementaryType) 1954 and ir.lvalue.type.type in ElementaryTypeInt 1955 ): 1956 for r in ir.read: 1957 if isinstance(r, Constant) and r.type.type not in ElementaryTypeInt: 1958 r.set_type(ElementaryType(ir.lvalue.type.type)) 1959 was_changed = True 1960 1961 if isinstance(ir, (HighLevelCall, InternalCall)): 1962 func = ir.function 1963 if isinstance(func, StateVariable): 1964 types = export_nested_types_from_variable(func) 1965 else: 1966 types = [p.type for p in func.parameters] 1967 assert len(types) == len(ir.arguments) 1968 for idx, arg in enumerate(ir.arguments): 1969 t = types[idx] 1970 if isinstance(t, ElementaryType): 1971 if t.type in ElementaryTypeInt: 1972 if arg.type.type not in ElementaryTypeInt: 1973 arg.set_type(ElementaryType(t.type)) 1974 was_changed = True 1975 1976 if isinstance(ir, NewStructure): 1977 st = ir.structure 1978 for idx, arg in enumerate(ir.arguments): 1979 e = st.elems_ordered[idx] 1980 if isinstance(e.type, ElementaryType): 1981 if e.type.type in ElementaryTypeInt: 1982 if arg.type.type not in ElementaryTypeInt: 1983 arg.set_type(ElementaryType(e.type.type)) 1984 was_changed = True 1985 1986 def is_elementary_array(t): 1987 return isinstance(t, ArrayType) and isinstance(t.type, ElementaryType) 1988 1989 if isinstance(ir, InitArray): 1990 if is_elementary_array(ir.lvalue.type): 1991 if ir.lvalue.type.type.type in ElementaryTypeInt: 1992 for r in ir.read: 1993 if isinstance(r, Constant) and is_elementary_array(r.type): 1994 if r.type.type.type not in ElementaryTypeInt: 1995 r.set_type(ElementaryType(ir.lvalue.type.type.type)) 1996 was_changed = True 1997 1998 1999# endregion 2000################################################################################### 2001################################################################################### 2002# region Delete handling 2003################################################################################### 2004################################################################################### 2005 2006 2007def convert_delete(irs: List[Operation]) -> None: 2008 """ 2009 Convert the lvalue of the Delete to point to the variable removed 2010 This can only be done after find_references_origin is called 2011 :param irs: 2012 :return: 2013 """ 2014 for ir in irs: 2015 if isinstance(ir, Delete): 2016 if isinstance(ir.lvalue, ReferenceVariable): 2017 ir.lvalue = ir.lvalue.points_to 2018 2019 2020# endregion 2021################################################################################### 2022################################################################################### 2023# region Source Mapping 2024################################################################################### 2025################################################################################### 2026 2027 2028def _find_source_mapping_references(irs: List[Operation]) -> None: 2029 for ir in irs: 2030 2031 if isinstance(ir, NewContract): 2032 ir.contract_created.references.append(ir.expression.source_mapping) 2033 2034 if isinstance(ir, HighLevelCall): 2035 ir.function.references.append(ir.expression.source_mapping) 2036 2037 2038# endregion 2039################################################################################### 2040################################################################################### 2041# region Heuristics selection 2042################################################################################### 2043################################################################################### 2044 2045 2046def apply_ir_heuristics(irs: List[Operation], node: "Node", is_solidity: bool) -> List[Operation]: 2047 """ 2048 Apply a set of heuristic to improve slithIR 2049 """ 2050 2051 irs = integrate_value_gas(irs) 2052 2053 irs = propagate_type_and_convert_call(irs, node) 2054 irs = remove_unused(irs) 2055 find_references_origin(irs) 2056 2057 # These are heuristics that are only applied to Solidity 2058 if is_solidity: 2059 convert_constant_types(irs) 2060 convert_delete(irs) 2061 2062 _find_source_mapping_references(irs) 2063 2064 return irs
95def convert_expression(expression: Expression, node: "Node") -> List[Operation]: 96 # handle standlone expression 97 # such as return true; 98 from slither.core.cfg.node import NodeType 99 100 if isinstance(expression, Literal) and node.type in [NodeType.IF, NodeType.IFLOOP]: 101 cst = Constant(expression.value, expression.type) 102 cond = Condition(cst) 103 cond.set_expression(expression) 104 cond.set_node(node) 105 return [cond] 106 if isinstance(expression, Identifier) and node.type in [ 107 NodeType.IF, 108 NodeType.IFLOOP, 109 ]: 110 cond = Condition(expression.value) 111 cond.set_expression(expression) 112 cond.set_node(node) 113 return [cond] 114 115 visitor = ExpressionToSlithIR(expression, node) 116 result = visitor.result() 117 is_solidity = node.compilation_unit.is_solidity 118 result = apply_ir_heuristics(result, node, is_solidity) 119 120 if result: 121 if node.type in [NodeType.IF, NodeType.IFLOOP]: 122 prev = result[-1] 123 assert isinstance(prev, (OperationWithLValue)) and prev.lvalue 124 cond = Condition(prev.lvalue) 125 cond.set_expression(expression) 126 cond.set_node(node) 127 result.append(cond) 128 elif node.type == NodeType.RETURN: 129 # May return None 130 prev = result[-1] 131 if isinstance(prev, (OperationWithLValue)): 132 r = Return(prev.lvalue) 133 r.set_expression(expression) 134 r.set_node(node) 135 result.append(r) 136 137 return result
290def integrate_value_gas(result: List[Operation]) -> List[Operation]: 291 """ 292 Integrate value and gas temporary arguments to call instruction 293 """ 294 was_changed = True 295 296 calls = [] 297 298 while was_changed: 299 # We loop until we do not find any call to value or gas 300 was_changed = False 301 302 # Find all the assignments 303 assigments = {} 304 for i in result: 305 if isinstance(i, OperationWithLValue) and i.lvalue: 306 assigments[i.lvalue.name] = i 307 if isinstance(i, TmpCall): 308 if isinstance(i.called, Variable) and i.called.name in assigments: 309 ins_ori = assigments[i.called.name] 310 i.set_ori(ins_ori) 311 312 to_remove = [] 313 variable_to_replace = {} 314 315 # Replace call to value, gas to an argument of the real call 316 for idx, ins in enumerate(result): 317 # value can be shadowed, so we check that the prev ins 318 # is an Argument 319 if idx == 0: 320 continue 321 prev_ins = result[idx - 1] 322 if is_value(ins) and isinstance(prev_ins, Argument): 323 was_changed = True 324 prev_ins.set_type(ArgumentType.VALUE) 325 # Types checked by is_value 326 prev_ins.call_id = ins.ori.variable_left.name # type: ignore 327 calls.append(ins.ori.variable_left) # type: ignore 328 to_remove.append(ins) 329 variable_to_replace[ins.lvalue.name] = ins.ori.variable_left # type: ignore 330 elif is_gas(ins) and isinstance(prev_ins, Argument): 331 was_changed = True 332 prev_ins.set_type(ArgumentType.GAS) 333 # Types checked by is_gas 334 prev_ins.call_id = ins.ori.variable_left.name # type: ignore 335 calls.append(ins.ori.variable_left) # type: ignore 336 to_remove.append(ins) 337 variable_to_replace[ins.lvalue.name] = ins.ori.variable_left # type: ignore 338 339 # Remove the call to value/gas instruction 340 result = [i for i in result if not i in to_remove] 341 342 # update the real call 343 for ins in result: 344 if isinstance(ins, TmpCall): 345 # use of while if there redirections 346 while ins.called.name in variable_to_replace: 347 was_changed = True 348 ins.call_id = variable_to_replace[ins.called.name].name 349 calls.append(ins.called) 350 ins.called = variable_to_replace[ins.called.name] 351 if isinstance(ins, Argument): 352 while ins.call_id in variable_to_replace: 353 was_changed = True 354 ins.call_id = variable_to_replace[ins.call_id].name 355 356 calls = list({str(c) for c in calls}) 357 idx = 0 358 calls_d = {} 359 for call in calls: 360 calls_d[str(call)] = idx 361 idx = idx + 1 362 363 return result
Integrate value and gas temporary arguments to call instruction
374def get_declared_param_names( 375 ins: Union[ 376 NewStructure, 377 NewContract, 378 InternalCall, 379 LibraryCall, 380 HighLevelCall, 381 InternalDynamicCall, 382 EventCall, 383 ] 384) -> Optional[List[List[str]]]: 385 """ 386 Given a call operation, return the list of parameter names, in the order 387 listed in the function declaration. 388 #### Parameters 389 ins - 390 The call instruction 391 #### Possible Returns 392 List[List[str]] - 393 A list of list of parameters in declaration order. Note only if it's a Function there can be multiple list 394 None - 395 Workaround: Unable to obtain list of parameters in declaration order 396 """ 397 if isinstance(ins, NewStructure): 398 return [ 399 [x.name for x in ins.structure.elems_ordered if not isinstance(x.type, MappingType)] 400 ] 401 if isinstance(ins, (InternalCall, LibraryCall, HighLevelCall)): 402 if isinstance(ins.function, Function): 403 res = [[p.name for p in ins.function.parameters]] 404 for f in ins.function.overrides: 405 res.append([p.name for p in f.parameters]) 406 return res 407 return None 408 if isinstance(ins, InternalDynamicCall): 409 return [[p.name for p in ins.function_type.params]] 410 411 assert isinstance(ins, (EventCall, NewContract)) 412 return None
Given a call operation, return the list of parameter names, in the order listed in the function declaration.
Parameters
ins - The call instruction
Possible Returns
List[List[str]] - A list of list of parameters in declaration order. Note only if it's a Function there can be multiple list None - Workaround: Unable to obtain list of parameters in declaration order
415def reorder_arguments( 416 args: List[Variable], call_names: List[str], decl_names: List[List[str]] 417) -> List[Variable]: 418 """ 419 Reorder named struct constructor arguments so that they match struct declaration ordering rather 420 than call ordering 421 E.g. for `struct S { int x; int y; }` we reorder `S({y : 2, x : 3})` to `S(3, 2)` 422 #### Parameters 423 args - 424 Arguments to constructor call, in call order 425 names - 426 Parameter names in call order 427 decl_names - 428 List of list of parameter names in declaration order 429 #### Returns 430 Reordered arguments to constructor call, now in declaration order 431 """ 432 assert len(args) == len(call_names) 433 for names in decl_names: 434 assert len(call_names) == len(names) 435 436 args_ret = [] 437 index_seen = [] # Will have the correct index order 438 439 # We search for declaration of parameters that is fully compatible with the call arguments 440 # that is why if a arg name is not present in the call name we break and clear index_seen 441 # Known issue if the overridden function reuse the same parameters' name but in different positions 442 for names in decl_names: 443 if len(index_seen) == len(args): 444 break 445 446 for n in names: 447 try: 448 ind = call_names.index(n) 449 if ind in index_seen: 450 continue 451 except ValueError: 452 index_seen.clear() 453 break 454 index_seen.append(ind) 455 456 for ind in index_seen: 457 args_ret.append(args[ind]) 458 459 return args_ret
Reorder named struct constructor arguments so that they match struct declaration ordering rather
than call ordering
E.g. for struct S { int x; int y; }
we reorder S({y : 2, x : 3})
to S(3, 2)
Parameters
args - Arguments to constructor call, in call order names - Parameter names in call order decl_names - List of list of parameter names in declaration order
Returns
Reordered arguments to constructor call, now in declaration order
462def propagate_type_and_convert_call(result: List[Operation], node: "Node") -> List[Operation]: 463 """ 464 Propagate the types variables and convert tmp call to real call operation 465 """ 466 calls_value = {} 467 calls_gas = {} 468 469 call_data = [] 470 471 idx = 0 472 # use of while len() as result can be modified during the iteration 473 while idx < len(result): 474 ins = result[idx] 475 476 if isinstance(ins, TmpCall): 477 # If the node.function is a FunctionTopLevel, then it does not have a contract 478 contract = ( 479 node.function.contract if isinstance(node.function, FunctionContract) else None 480 ) 481 new_ins = extract_tmp_call(ins, contract) 482 if new_ins: 483 new_ins.set_node(ins.node) 484 ins = new_ins 485 result[idx] = ins 486 487 # If there's two consecutive type conversions, keep only the last 488 # ARG_CALL x call_data = [x] 489 # TMP_4 = CONVERT x to Fix call_data = [] 490 # TMP_5 = CONVERT TMP_4 to uint192 call_data = [TMP_5] 491 if isinstance(ins, TypeConversion) and ins.variable in call_data: 492 call_data.remove(ins.variable) 493 494 if isinstance(ins, Argument): 495 # In case of duplicate arguments we overwrite the value 496 # This can happen because of addr.call.value(1).value(2) 497 if ins.get_type() in [ArgumentType.GAS]: 498 calls_gas[ins.call_id] = ins.argument 499 elif ins.get_type() in [ArgumentType.VALUE]: 500 calls_value[ins.call_id] = ins.argument 501 else: 502 assert ins.get_type() == ArgumentType.CALL 503 call_data.append(ins.argument) 504 505 if isinstance(ins, (HighLevelCall, NewContract, InternalDynamicCall)): 506 if ins.call_id in calls_value: 507 ins.call_value = calls_value[ins.call_id] 508 if ins.call_id in calls_gas and isinstance(ins, (HighLevelCall, InternalDynamicCall)): 509 ins.call_gas = calls_gas[ins.call_id] 510 511 if isinstance(ins, Call) and (ins.names is not None): 512 assert isinstance( 513 ins, 514 ( 515 NewStructure, 516 NewContract, 517 InternalCall, 518 LibraryCall, 519 HighLevelCall, 520 InternalDynamicCall, 521 EventCall, 522 ), 523 ) 524 decl_param_names = get_declared_param_names(ins) 525 if decl_param_names is not None: 526 call_data = reorder_arguments(call_data, ins.names, decl_param_names) 527 528 if isinstance(ins, (Call, NewContract, NewStructure)): 529 # We might have stored some arguments for libraries 530 if ins.arguments: 531 call_data = ins.arguments + call_data 532 ins.arguments = call_data 533 call_data = [] 534 535 if is_temporary(ins): 536 del result[idx] 537 continue 538 539 new_ins = propagate_types(ins, node) 540 if new_ins: 541 if isinstance(new_ins, (list,)): 542 for new_ins_ in new_ins: 543 new_ins_.set_node(ins.node) 544 del result[idx] 545 for i, ins in enumerate(new_ins): 546 result.insert(idx + i, ins) 547 idx = idx + len(new_ins) - 1 548 else: 549 new_ins.set_node(ins.node) 550 result[idx] = new_ins 551 idx = idx + 1 552 return result
Propagate the types variables and convert tmp call to real call operation
604def propagate_types(ir: Operation, node: "Node"): # pylint: disable=too-many-locals 605 # propagate the type 606 node_function = node.function 607 608 using_for: USING_FOR = {} 609 if isinstance(node_function, FunctionContract): 610 using_for = node_function.contract.using_for_complete 611 elif isinstance(node_function, FunctionTopLevel): 612 using_for = node_function.using_for_complete 613 614 if isinstance(ir, OperationWithLValue) and ir.lvalue: 615 # Force assignment in case of missing previous correct type 616 if not ir.lvalue.type: 617 if isinstance(ir, Assignment): 618 ir.lvalue.set_type(ir.rvalue.type) 619 elif isinstance(ir, Binary): 620 if BinaryType.return_bool(ir.type): 621 ir.lvalue.set_type(ElementaryType("bool")) 622 else: 623 ir.lvalue.set_type(ir.variable_left.type) 624 elif isinstance(ir, Delete): 625 # nothing to propagate 626 pass 627 elif isinstance(ir, LibraryCall): 628 return convert_type_library_call(ir, ir.destination) 629 elif isinstance(ir, HighLevelCall): 630 t = ir.destination.type 631 # Temporary operation (they are removed later) 632 if t is None: 633 return None 634 635 if isinstance(t, ElementaryType) and t.name == "address": 636 if can_be_solidity_func(ir): 637 return convert_to_solidity_func(ir) 638 639 # convert library or top level function 640 if t in using_for or "*" in using_for: 641 new_ir = convert_to_library_or_top_level(ir, node, using_for) 642 if new_ir: 643 return new_ir 644 645 # convert library function when used with "this" 646 if ( 647 isinstance(t, ElementaryType) 648 and t.name == "address" 649 and ir.destination.name == "this" 650 and UserDefinedType(node_function.contract) in using_for 651 ): 652 new_ir = convert_to_library_or_top_level(ir, node, using_for) 653 if new_ir: 654 return new_ir 655 656 if isinstance(t, UserDefinedType): 657 # UserdefinedType 658 t_type = t.type 659 if isinstance(t_type, Contract): 660 # the target contract of the IR is the t_type (the destination of the call) 661 return convert_type_of_high_and_internal_level_call(ir, t_type) 662 663 # Convert HighLevelCall to LowLevelCall 664 if (isinstance(t, ElementaryType) and t.name == "address") or ( 665 isinstance(t, TypeAlias) and t.underlying_type.name == "address" 666 ): 667 if ir.destination.name == "this": 668 # Cannot be a top level function with this. 669 assert isinstance(node_function, FunctionContract) 670 # the target contract is the contract itself 671 return convert_type_of_high_and_internal_level_call( 672 ir, node_function.contract 673 ) 674 if can_be_low_level(ir): 675 return convert_to_low_level(ir) 676 677 # Convert push operations 678 # May need to insert a new operation 679 # Which leads to return a list of operation 680 if isinstance(t, ArrayType) or ( 681 isinstance(t, ElementaryType) and t.type == "bytes" 682 ): 683 # Solidity uses push 684 # Vyper uses append 685 if ir.function_name in ["push", "append"] and len(ir.arguments) <= 1: 686 return convert_to_push(ir, node) 687 if ir.function_name == "pop" and len(ir.arguments) == 0: 688 return convert_to_pop(ir, node) 689 690 elif isinstance(ir, Index): 691 if isinstance(ir.variable_left.type, MappingType): 692 ir.lvalue.set_type(ir.variable_left.type.type_to) 693 elif isinstance(ir.variable_left.type, ArrayType): 694 ir.lvalue.set_type(ir.variable_left.type.type) 695 696 elif isinstance(ir, InitArray): 697 length = len(ir.init_values) 698 t = ir.init_values[0].type 699 ir.lvalue.set_type(ArrayType(t, length)) 700 elif isinstance(ir, InternalCall): 701 # if its not a tuple, return a singleton 702 if ir.function is None: 703 func = node.function 704 function_contract = ( 705 func.contract if isinstance(func, FunctionContract) else None 706 ) 707 # the target contract might be None if its a top level function 708 convert_type_of_high_and_internal_level_call(ir, function_contract) 709 return_type = ir.function.return_type 710 if return_type: 711 if len(return_type) == 1: 712 ir.lvalue.set_type(return_type[0]) 713 elif len(return_type) > 1: 714 ir.lvalue.set_type(return_type) 715 else: 716 ir.lvalue = None 717 elif isinstance(ir, InternalDynamicCall): 718 # if its not a tuple, return a singleton 719 return_type = ir.function_type.return_type 720 if return_type: 721 if len(return_type) == 1: 722 ir.lvalue.set_type(return_type[0]) 723 else: 724 ir.lvalue.set_type(return_type) 725 else: 726 ir.lvalue = None 727 elif isinstance(ir, LowLevelCall): 728 # Call are not yet converted 729 # This should not happen 730 assert False 731 elif isinstance(ir, Member): 732 # TODO we should convert the reference to a temporary if the member is a length or a balance 733 if ( 734 ir.variable_right == "length" 735 and not isinstance(ir.variable_left, Contract) 736 and isinstance(ir.variable_left.type, (ElementaryType, ArrayType)) 737 ): 738 new_length = Length(ir.variable_left, ir.lvalue) 739 assert ir.expression 740 new_length.set_expression(ir.expression) 741 new_length.lvalue.points_to = ir.variable_left 742 new_length.set_node(ir.node) 743 return new_length 744 # This only happen for .balance/code/codehash access on a variable for which we dont know at 745 # early parsing time the type 746 # Like 747 # function return_addr() internal returns(addresss) 748 # 749 # return_addr().balance 750 # Here slithIR will incorrectly create a REF variable instead of a Temp variable 751 # However this pattern does not appear so often 752 if ( 753 ir.variable_right.name in ["balance", "code", "codehash"] 754 and not isinstance(ir.variable_left, Contract) 755 and isinstance(ir.variable_left.type, ElementaryType) 756 ): 757 name = ir.variable_right.name + "(address)" 758 sol_func = SolidityFunction(name) 759 s = SolidityCall( 760 sol_func, 761 1, 762 ir.lvalue, 763 sol_func.return_type, 764 ) 765 s.arguments.append(ir.variable_left) 766 s.set_expression(ir.expression) 767 s.lvalue.set_type(sol_func.return_type) 768 s.set_node(ir.node) 769 return s 770 if ( 771 ir.variable_right == "codesize" 772 and not isinstance(ir.variable_left, Contract) 773 and isinstance(ir.variable_left.type, ElementaryType) 774 ): 775 b = CodeSize(ir.variable_left, ir.lvalue) 776 b.set_expression(ir.expression) 777 b.set_node(ir.node) 778 return b 779 if ir.variable_right == "selector" and isinstance(ir.variable_left, (CustomError)): 780 assignment = Assignment( 781 ir.lvalue, 782 Constant( 783 str(get_function_id(ir.variable_left.solidity_signature)), 784 ElementaryType("bytes4"), 785 ), 786 ElementaryType("bytes4"), 787 ) 788 assignment.set_expression(ir.expression) 789 assignment.set_node(ir.node) 790 assignment.lvalue.set_type(ElementaryType("bytes4")) 791 return assignment 792 793 if isinstance(ir.variable_right, (CustomError)): 794 assignment = Assignment( 795 ir.lvalue, 796 Constant( 797 str(get_function_id(ir.variable_left.solidity_signature)), 798 ElementaryType("bytes4"), 799 ), 800 ElementaryType("bytes4"), 801 ) 802 assignment.set_expression(ir.expression) 803 assignment.set_node(ir.node) 804 assignment.lvalue.set_type(ElementaryType("bytes4")) 805 return assignment 806 if ir.variable_right == "selector" and isinstance(ir.variable_left, (Event)): 807 # the event selector returns a bytes32, which is different from the error/function selector 808 # which returns a bytes4 809 assignment = Assignment( 810 ir.lvalue, 811 Constant( 812 str(get_event_id(ir.variable_left.full_name)), ElementaryType("bytes32") 813 ), 814 ElementaryType("bytes32"), 815 ) 816 assignment.set_expression(ir.expression) 817 assignment.set_node(ir.node) 818 assignment.lvalue.set_type(ElementaryType("bytes32")) 819 return assignment 820 if ir.variable_right == "selector" and ( 821 isinstance(ir.variable_left.type, (Function)) 822 ): 823 assignment = Assignment( 824 ir.lvalue, 825 Constant( 826 str(get_function_id(ir.variable_left.type.full_name)), 827 ElementaryType("bytes4"), 828 ), 829 ElementaryType("bytes4"), 830 ) 831 assignment.set_expression(ir.expression) 832 assignment.set_node(ir.node) 833 assignment.lvalue.set_type(ElementaryType("bytes4")) 834 return assignment 835 if isinstance(ir.variable_left, TemporaryVariable) and isinstance( 836 ir.variable_left.type, TypeInformation 837 ): 838 return _convert_type_contract(ir) 839 left = ir.variable_left 840 t = None 841 ir_func = ir.node.function 842 # Handling of this.function_name usage 843 if ( 844 left == SolidityVariable("this") 845 and isinstance(ir.variable_right, Constant) 846 and isinstance(ir_func, FunctionContract) 847 and str(ir.variable_right) in [x.name for x in ir_func.contract.functions] 848 ): 849 # Assumption that this.function_name can only compile if 850 # And the contract does not have two functions starting with function_name 851 # Otherwise solc raises: 852 # Error: Member "f" not unique after argument-dependent lookup in contract 853 targeted_function = next( 854 (x for x in ir_func.contract.functions if x.name == str(ir.variable_right)) 855 ) 856 ir.lvalue.set_type(targeted_function) 857 elif isinstance(left, (Variable, SolidityVariable)): 858 t = left.type 859 elif isinstance(left, (Contract, Enum, Structure)): 860 t = UserDefinedType(left) 861 # can be None due to temporary operation 862 if t: 863 if isinstance(t, UserDefinedType): 864 # UserdefinedType 865 type_t = t.type 866 if isinstance(type_t, Enum): 867 ir.lvalue.set_type(t) 868 elif isinstance(type_t, Structure): 869 elems = type_t.elems 870 for elem in elems: 871 if elem == ir.variable_right: 872 ir.lvalue.set_type(elems[elem].type) 873 else: 874 assert isinstance(type_t, Contract) 875 # Allow type propagation as a Function 876 # Only for reference variables 877 # This allows to track the selector keyword 878 # We don't need to check for function collision, as solc prevents the use of selector 879 # if there are multiple functions with the same name 880 f = next( 881 (f for f in type_t.functions if f.name == ir.variable_right), 882 None, 883 ) 884 if f: 885 ir.lvalue.set_type(f) 886 else: 887 # Allow propagation for variable access through contract's name 888 # like Base_contract.my_variable 889 v = next( 890 ( 891 v 892 for v in type_t.state_variables 893 if v.name == ir.variable_right 894 ), 895 None, 896 ) 897 if v: 898 ir.lvalue.set_type(v.type) 899 elif isinstance(ir, NewArray): 900 ir.lvalue.set_type(ir.array_type) 901 elif isinstance(ir, NewContract): 902 ir.lvalue.set_type(ir.contract_name) 903 elif isinstance(ir, NewElementaryType): 904 ir.lvalue.set_type(ir.type) 905 elif isinstance(ir, NewStructure): 906 ir.lvalue.set_type(UserDefinedType(ir.structure)) 907 elif isinstance(ir, Send): 908 ir.lvalue.set_type(ElementaryType("bool")) 909 elif isinstance(ir, SolidityCall): 910 if ir.function.name in ["type(address)", "type()"]: 911 ir.function.return_type = [TypeInformation(ir.arguments[0])] 912 return_type = ir.function.return_type 913 if len(return_type) == 1: 914 ir.lvalue.set_type(return_type[0]) 915 elif len(return_type) > 1: 916 ir.lvalue.set_type(return_type) 917 elif isinstance(ir, TypeConversion): 918 ir.lvalue.set_type(ir.type) 919 elif isinstance(ir, Unary): 920 ir.lvalue.set_type(ir.rvalue.type) 921 elif isinstance(ir, Unpack): 922 types = ir.tuple.type.type 923 idx = ir.index 924 t = types[idx] 925 ir.lvalue.set_type(t) 926 elif isinstance( 927 ir, 928 ( 929 Argument, 930 TmpCall, 931 TmpNewArray, 932 TmpNewContract, 933 TmpNewStructure, 934 TmpNewElementaryType, 935 ), 936 ): 937 # temporary operation; they will be removed 938 pass 939 else: 940 raise SlithIRError(f"Not handling {type(ir)} during type propagation") 941 return None
945def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call, Nop]: 946 assert isinstance(ins, TmpCall) 947 if isinstance(ins.called, Variable) and isinstance(ins.called.type, FunctionType): 948 # If the call is made to a variable member, where the member is this 949 # We need to convert it to a HighLelelCall and not an internal dynamic call 950 if isinstance(ins.ori, Member) and ins.ori.variable_left == SolidityVariable("this"): 951 pass 952 else: 953 call = InternalDynamicCall(ins.lvalue, ins.called, ins.called.type) 954 call.set_expression(ins.expression) 955 call.call_id = ins.call_id 956 return call 957 if isinstance(ins.ori, Member): 958 # If there is a call on an inherited contract, it is an internal call or an event 959 if contract and ins.ori.variable_left in contract.inheritance + [contract]: 960 if str(ins.ori.variable_right) in [f.name for f in contract.functions]: 961 internalcall = InternalCall( 962 (ins.ori.variable_right, ins.ori.variable_left.name), 963 ins.nbr_arguments, 964 ins.lvalue, 965 ins.type_call, 966 ) 967 internalcall.set_expression(ins.expression) 968 internalcall.call_id = ins.call_id 969 return internalcall 970 if str(ins.ori.variable_right) in [f.name for f in contract.events]: 971 eventcall = EventCall(ins.ori.variable_right) 972 eventcall.set_expression(ins.expression) 973 eventcall.call_id = ins.call_id 974 return eventcall 975 if isinstance(ins.ori.variable_left, Contract): 976 st = ins.ori.variable_left.get_structure_from_name(ins.ori.variable_right) 977 if st: 978 op = NewStructure(st, ins.lvalue, names=ins.names) 979 op.set_expression(ins.expression) 980 op.call_id = ins.call_id 981 return op 982 983 # For event emit through library 984 # lib L { event E()} 985 # ... 986 # emit L.E(); 987 if str(ins.ori.variable_right) in [f.name for f in ins.ori.variable_left.events]: 988 eventcall = EventCall(ins.ori.variable_right) 989 eventcall.set_expression(ins.expression) 990 eventcall.call_id = ins.call_id 991 return eventcall 992 993 # lib Lib { error Error()} ... revert Lib.Error() 994 if str(ins.ori.variable_right) in ins.ori.variable_left.custom_errors_as_dict: 995 custom_error = ins.ori.variable_left.custom_errors_as_dict[ 996 str(ins.ori.variable_right) 997 ] 998 assert isinstance( 999 custom_error, 1000 CustomError, 1001 ) 1002 sol_function = SolidityCustomRevert(custom_error) 1003 solidity_call = SolidityCall( 1004 sol_function, ins.nbr_arguments, ins.lvalue, ins.type_call 1005 ) 1006 solidity_call.set_expression(ins.expression) 1007 return solidity_call 1008 1009 libcall = LibraryCall( 1010 ins.ori.variable_left, 1011 ins.ori.variable_right, 1012 ins.nbr_arguments, 1013 ins.lvalue, 1014 ins.type_call, 1015 names=ins.names, 1016 ) 1017 libcall.set_expression(ins.expression) 1018 libcall.call_id = ins.call_id 1019 return libcall 1020 if isinstance(ins.ori.variable_left, Function): 1021 # Support for library call where the parameter is a function 1022 # We could merge this with the standard library handling 1023 # Except that we will have some troubles with using_for 1024 # As the type of the function will not match function() 1025 # Additionally we do not have a correct view on the parameters of the tmpcall 1026 # At this level 1027 # 1028 # library FunctionExtensions { 1029 # function h(function() internal _t, uint8) internal { } 1030 # } 1031 # contract FunctionMembers { 1032 # using FunctionExtensions for function(); 1033 # 1034 # function f() public { 1035 # f.h(1); 1036 # } 1037 # } 1038 node_func = ins.node.function 1039 using_for = ( 1040 node_func.contract.using_for_complete 1041 if isinstance(node_func, FunctionContract) 1042 else {} 1043 ) 1044 1045 targeted_libraries = ( 1046 [] + using_for.get("*", []) + using_for.get(FunctionType([], []), []) 1047 ) 1048 lib_contract: Contract 1049 candidates = [] 1050 for lib_contract_type in targeted_libraries: 1051 if not isinstance(lib_contract_type, UserDefinedType) and isinstance( 1052 lib_contract_type.type, Contract 1053 ): 1054 continue 1055 if isinstance(lib_contract_type, FunctionContract): 1056 # Using for with list of functions, this is the function called 1057 candidates.append(lib_contract_type) 1058 else: 1059 lib_contract = lib_contract_type.type 1060 for lib_func in lib_contract.functions: 1061 if lib_func.name == ins.ori.variable_right: 1062 candidates.append(lib_func) 1063 1064 if len(candidates) == 1: 1065 lib_func = candidates[0] 1066 # Library must be from a contract 1067 assert isinstance(lib_func, FunctionContract) 1068 lib_call = LibraryCall( 1069 lib_func.contract, 1070 Constant(lib_func.name), 1071 len(lib_func.parameters), 1072 ins.lvalue, 1073 "d", 1074 names=ins.names, 1075 ) 1076 lib_call.set_expression(ins.expression) 1077 lib_call.set_node(ins.node) 1078 lib_call.call_gas = ins.call_gas 1079 lib_call.call_id = ins.call_id 1080 lib_call.set_node(ins.node) 1081 lib_call.function = lib_func 1082 lib_call.arguments.append(ins.ori.variable_left) 1083 return lib_call 1084 # We do not support something lik 1085 # library FunctionExtensions { 1086 # function h(function() internal _t, uint8) internal { } 1087 # function h(function() internal _t, bool) internal { } 1088 # } 1089 # contract FunctionMembers { 1090 # using FunctionExtensions for function(); 1091 # 1092 # function f() public { 1093 # f.h(1); 1094 # } 1095 # } 1096 to_log = "Slither does not support dynamic functions to libraries if functions have the same name" 1097 to_log += f"{[candidate.full_name for candidate in candidates]}" 1098 raise SlithIRError(to_log) 1099 if isinstance(ins.ori.variable_left, SolidityImportPlaceHolder): 1100 # For top level import, where the import statement renames the filename 1101 # See https://blog.soliditylang.org/2020/09/02/solidity-0.7.1-release-announcement/ 1102 1103 current_path = Path(ins.ori.variable_left.source_mapping.filename.absolute).parent 1104 target = str( 1105 Path(current_path, ins.ori.variable_left.import_directive.filename).absolute() 1106 ) 1107 top_level_function_targets = [ 1108 f 1109 for f in ins.compilation_unit.functions_top_level 1110 if f.source_mapping.filename.absolute == target 1111 and f.name == ins.ori.variable_right 1112 and len(f.parameters) == ins.nbr_arguments 1113 ] 1114 1115 internalcall = InternalCall( 1116 (ins.ori.variable_right, ins.ori.variable_left.name), 1117 ins.nbr_arguments, 1118 ins.lvalue, 1119 ins.type_call, 1120 ) 1121 internalcall.set_expression(ins.expression) 1122 internalcall.call_id = ins.call_id 1123 internalcall.function_candidates = top_level_function_targets 1124 return internalcall 1125 1126 if ins.ori.variable_left == ElementaryType("bytes") and ins.ori.variable_right == Constant( 1127 "concat" 1128 ): 1129 s = SolidityCall( 1130 SolidityFunction("bytes.concat()"), 1131 ins.nbr_arguments, 1132 ins.lvalue, 1133 ins.type_call, 1134 ) 1135 s.set_expression(ins.expression) 1136 return s 1137 1138 if ins.ori.variable_left == ElementaryType("string") and ins.ori.variable_right == Constant( 1139 "concat" 1140 ): 1141 s = SolidityCall( 1142 SolidityFunction("string.concat()"), 1143 ins.nbr_arguments, 1144 ins.lvalue, 1145 ins.type_call, 1146 ) 1147 s.set_expression(ins.expression) 1148 return s 1149 1150 msgcall = HighLevelCall( 1151 ins.ori.variable_left, 1152 ins.ori.variable_right, 1153 ins.nbr_arguments, 1154 ins.lvalue, 1155 ins.type_call, 1156 names=ins.names, 1157 ) 1158 msgcall.call_id = ins.call_id 1159 1160 if ins.call_gas: 1161 msgcall.call_gas = ins.call_gas 1162 if ins.call_value: 1163 msgcall.call_value = ins.call_value 1164 msgcall.set_expression(ins.expression) 1165 1166 return msgcall 1167 1168 if isinstance(ins.ori, TmpCall): 1169 r = extract_tmp_call(ins.ori, contract) 1170 r.set_node(ins.node) 1171 return r 1172 if isinstance(ins.called, SolidityVariableComposed): 1173 if str(ins.called) == "block.blockhash": 1174 ins.called = SolidityFunction("blockhash(uint256)") 1175 1176 if isinstance(ins.called, SolidityFunction): 1177 s = SolidityCall(ins.called, ins.nbr_arguments, ins.lvalue, ins.type_call) 1178 s.set_expression(ins.expression) 1179 return s 1180 1181 if isinstance(ins.called, CustomError): 1182 sol_function = SolidityCustomRevert(ins.called) 1183 s = SolidityCall(sol_function, ins.nbr_arguments, ins.lvalue, ins.type_call) 1184 s.set_expression(ins.expression) 1185 return s 1186 1187 if isinstance(ins.ori, TmpNewElementaryType): 1188 n = NewElementaryType(ins.ori.type, ins.lvalue) 1189 n.set_expression(ins.expression) 1190 return n 1191 1192 if isinstance(ins.ori, TmpNewContract): 1193 op = NewContract(ins.ori.contract_name, ins.lvalue) 1194 op.set_expression(ins.expression) 1195 op.call_id = ins.call_id 1196 if ins.call_value: 1197 op.call_value = ins.call_value 1198 if ins.call_salt: 1199 op.call_salt = ins.call_salt 1200 return op 1201 1202 if isinstance(ins.ori, TmpNewArray): 1203 n = NewArray(ins.ori.array_type, ins.lvalue) 1204 n.set_expression(ins.expression) 1205 return n 1206 1207 if isinstance(ins.called, Structure): 1208 op = NewStructure(ins.called, ins.lvalue, names=ins.names) 1209 op.set_expression(ins.expression) 1210 op.call_id = ins.call_id 1211 op.set_expression(ins.expression) 1212 return op 1213 1214 if isinstance(ins.called, Event): 1215 e = EventCall(ins.called.name) 1216 e.set_expression(ins.expression) 1217 return e 1218 1219 if isinstance(ins.called, Contract): 1220 # Called a base constructor, where there is no constructor 1221 if ins.called.constructor is None: 1222 return Nop() 1223 # Case where: 1224 # contract A{ constructor(uint) } 1225 # contract B is A {} 1226 # contract C is B{ constructor() A(10) B() {} 1227 # C calls B(), which does not exist 1228 # Ideally we should compare here for the parameters types too 1229 if len(ins.called.constructor.parameters) != ins.nbr_arguments: 1230 return Nop() 1231 internalcall = InternalCall( 1232 ins.called.constructor, ins.nbr_arguments, ins.lvalue, ins.type_call, ins.names 1233 ) 1234 internalcall.call_id = ins.call_id 1235 internalcall.set_expression(ins.expression) 1236 return internalcall 1237 1238 raise SlithIRError(f"Not extracted {type(ins.called)}Â {ins}")
1261def convert_to_low_level( 1262 ir: HighLevelCall, 1263) -> Union[Send, LowLevelCall, Transfer,]: 1264 """ 1265 Convert to a transfer/send/or low level call 1266 The function assume to receive a correct IR 1267 The checks must be done by the caller 1268 1269 Must be called after can_be_low_level 1270 """ 1271 if ir.function_name == "transfer": 1272 assert len(ir.arguments) == 1 1273 prev_ir = ir 1274 ir = Transfer(ir.destination, ir.arguments[0]) 1275 ir.set_expression(prev_ir.expression) 1276 ir.set_node(prev_ir.node) 1277 return ir 1278 if ir.function_name == "send": 1279 assert len(ir.arguments) == 1 1280 prev_ir = ir 1281 ir = Send(ir.destination, ir.arguments[0], ir.lvalue) 1282 ir.set_expression(prev_ir.expression) 1283 ir.set_node(prev_ir.node) 1284 ir.lvalue.set_type(ElementaryType("bool")) 1285 return ir 1286 if ir.function_name in ["call", "delegatecall", "callcode", "staticcall", "raw_call"]: 1287 new_ir = LowLevelCall( 1288 ir.destination, ir.function_name, ir.nbr_arguments, ir.lvalue, ir.type_call 1289 ) 1290 new_ir.call_gas = ir.call_gas 1291 new_ir.call_value = ir.call_value 1292 new_ir.arguments = ir.arguments 1293 # TODO fix this for Vyper 1294 if ir.node.compilation_unit.solc_version >= "0.5": 1295 new_ir.lvalue.set_type([ElementaryType("bool"), ElementaryType("bytes")]) 1296 else: 1297 new_ir.lvalue.set_type(ElementaryType("bool")) 1298 new_ir.set_expression(ir.expression) 1299 new_ir.set_node(ir.node) 1300 return new_ir 1301 raise SlithIRError(f"Incorrect conversion to low level {ir}")
Convert to a transfer/send/or low level call The function assume to receive a correct IR The checks must be done by the caller
Must be called after can_be_low_level
1304def can_be_solidity_func(ir: HighLevelCall) -> bool: 1305 if not isinstance(ir, HighLevelCall): 1306 return False 1307 return ir.destination.name == "abi" and ir.function_name in [ 1308 "encode", 1309 "encodePacked", 1310 "encodeWithSelector", 1311 "encodeWithSignature", 1312 "encodeCall", 1313 "decode", 1314 ]
1317def convert_to_solidity_func( 1318 ir: HighLevelCall, 1319) -> SolidityCall: 1320 """ 1321 Must be called after can_be_solidity_func 1322 :param ir: 1323 :return: 1324 """ 1325 call = SolidityFunction(f"abi.{ir.function_name}()") 1326 new_ir = SolidityCall(call, ir.nbr_arguments, ir.lvalue, ir.type_call) 1327 new_ir.arguments = ir.arguments 1328 new_ir.set_expression(ir.expression) 1329 new_ir.set_node(ir.node) 1330 if isinstance(call.return_type, list) and len(call.return_type) == 1: 1331 new_ir.lvalue.set_type(call.return_type[0]) 1332 elif ( 1333 isinstance(new_ir.lvalue, TupleVariable) 1334 and call == SolidityFunction("abi.decode()") 1335 and len(new_ir.arguments) == 2 1336 and isinstance(new_ir.arguments[1], list) 1337 ): 1338 types = [] 1339 for arg_type in new_ir.arguments[1]: 1340 decode_type = arg_type 1341 if isinstance(decode_type, (Structure, Enum, Contract)): 1342 decode_type = UserDefinedType(decode_type) 1343 types.append(decode_type) 1344 new_ir.lvalue.set_type(types) 1345 # abi.decode where the type to decode is a singleton 1346 # abi.decode(a, (uint)) 1347 elif call == SolidityFunction("abi.decode()") and len(new_ir.arguments) == 2: 1348 # If the variable is a referenceVariable, we are lost 1349 # See https://github.com/crytic/slither/issues/566 for potential solutions 1350 if not isinstance(new_ir.arguments[1], ReferenceVariable): 1351 decode_type = new_ir.arguments[1] 1352 if isinstance(decode_type, (Structure, Enum, Contract)): 1353 decode_type = UserDefinedType(decode_type) 1354 new_ir.lvalue.set_type(decode_type) 1355 else: 1356 new_ir.lvalue.set_type(call.return_type) 1357 return new_ir
Must be called after can_be_solidity_func
Parameters
- ir:
Returns
1360def convert_to_push_expand_arr( 1361 ir: HighLevelCall, node: "Node", ret: List[Any] 1362) -> TemporaryVariable: 1363 arr = ir.destination 1364 1365 length = ReferenceVariable(node) 1366 length.set_type(ElementaryType("uint256")) 1367 1368 ir_length = Length(arr, length) 1369 ir_length.set_expression(ir.expression) 1370 ir_length.set_node(ir.node) 1371 ir_length.lvalue.points_to = arr 1372 ret.append(ir_length) 1373 1374 length_val = TemporaryVariable(node) 1375 length_val.set_type(ElementaryType("uint256")) 1376 ir_get_length = Assignment(length_val, length, ElementaryType("uint256")) 1377 ir_get_length.set_expression(ir.expression) 1378 ir_get_length.set_node(ir.node) 1379 ret.append(ir_get_length) 1380 1381 new_length_val = TemporaryVariable(node) 1382 ir_add_1 = Binary( 1383 new_length_val, length_val, Constant("1", ElementaryType("uint256")), BinaryType.ADDITION 1384 ) 1385 ir_add_1.set_expression(ir.expression) 1386 ir_add_1.set_node(ir.node) 1387 ret.append(ir_add_1) 1388 1389 ir_assign_length = Assignment(length, new_length_val, ElementaryType("uint256")) 1390 ir_assign_length.set_expression(ir.expression) 1391 ir_assign_length.set_node(ir.node) 1392 ret.append(ir_assign_length) 1393 1394 return length_val
1397def convert_to_push_set_val( 1398 ir: HighLevelCall, 1399 node: "Node", 1400 length_val: TemporaryVariable, 1401 ret: List[ 1402 Union[ 1403 Length, 1404 Assignment, 1405 Binary, 1406 ] 1407 ], 1408) -> None: 1409 arr = ir.destination 1410 1411 new_type = ir.destination.type.type 1412 1413 element_to_add = ReferenceVariable(node) 1414 element_to_add.set_type(new_type) 1415 ir_assign_element_to_add = Index(element_to_add, arr, length_val) 1416 ir_assign_element_to_add.set_expression(ir.expression) 1417 ir_assign_element_to_add.set_node(ir.node) 1418 ret.append(ir_assign_element_to_add) 1419 1420 if len(ir.arguments) > 0: 1421 assign_value = ir.arguments[0] 1422 if isinstance(assign_value, list): 1423 assign_value = TemporaryVariable(node) 1424 assign_value.set_type(element_to_add.type) 1425 ir_assign_value = InitArray(ir.arguments[0], assign_value) 1426 ir_assign_value.set_expression(ir.expression) 1427 ir_assign_value.set_node(ir.node) 1428 ret.append(ir_assign_value) 1429 1430 ir_assign_value = Assignment(element_to_add, assign_value, assign_value.type) 1431 ir_assign_value.set_expression(ir.expression) 1432 ir_assign_value.set_node(ir.node) 1433 ret.append(ir_assign_value) 1434 else: 1435 new_element = ir.lvalue 1436 new_element.set_type(new_type) 1437 ir_assign_value = Assignment(new_element, element_to_add, new_type) 1438 ir_assign_value.set_expression(ir.expression) 1439 ir_assign_value.set_node(ir.node) 1440 ret.append(ir_assign_value)
1443def convert_to_push( 1444 ir: HighLevelCall, node: "Node" 1445) -> List[Union[Length, Assignment, Binary, Index, InitArray,]]: 1446 """ 1447 Convert a call to a series of operations to push a new value onto the array 1448 1449 The function assume to receive a correct IR 1450 The checks must be done by the caller 1451 1452 May necessitate to create an intermediate operation (InitArray) 1453 Necessitate to return the length (see push documentation) 1454 As a result, the function return may return a list 1455 """ 1456 1457 ret = [] 1458 1459 length_val = convert_to_push_expand_arr(ir, node, ret) 1460 convert_to_push_set_val(ir, node, length_val, ret) 1461 1462 return ret
Convert a call to a series of operations to push a new value onto the array
The function assume to receive a correct IR The checks must be done by the caller
May necessitate to create an intermediate operation (InitArray) Necessitate to return the length (see push documentation) As a result, the function return may return a list
1465def convert_to_pop(ir: HighLevelCall, node: "Node") -> List[Operation]: 1466 """ 1467 Convert pop operators 1468 Return a list of 6 operations 1469 """ 1470 1471 ret: List[Operation] = [] 1472 1473 arr = ir.destination 1474 length = ReferenceVariable(node) 1475 length.set_type(ElementaryType("uint256")) 1476 1477 ir_length = Length(arr, length) 1478 assert ir.expression 1479 ir_length.set_expression(ir.expression) 1480 ir_length.set_node(ir.node) 1481 length.points_to = arr 1482 ret.append(ir_length) 1483 1484 val = TemporaryVariable(node) 1485 1486 ir_sub_1 = Binary(val, length, Constant("1", ElementaryType("uint256")), BinaryType.SUBTRACTION) 1487 ir_sub_1.set_expression(ir.expression) 1488 ir_sub_1.set_node(ir.node) 1489 ret.append(ir_sub_1) 1490 1491 element_to_delete = ReferenceVariable(node) 1492 ir_assign_element_to_delete = Index(element_to_delete, arr, val) 1493 # TODO the following is equivalent to length.points_to = arr 1494 # Should it be removed? 1495 ir_length.lvalue.points_to = arr 1496 # Note bytes is an ElementaryType not ArrayType and bytes1 should be returned 1497 # since bytes is bytes1[] without padding between the elements 1498 # while in other cases such as uint256[] (ArrayType) we use ir.destination.type.type 1499 # in this way we will have the type always set to the corresponding ElementaryType 1500 element_to_delete.set_type( 1501 ElementaryType("bytes1") 1502 if isinstance(ir.destination.type, ElementaryType) 1503 else ir.destination.type.type 1504 ) 1505 ir_assign_element_to_delete.set_expression(ir.expression) 1506 ir_assign_element_to_delete.set_node(ir.node) 1507 ret.append(ir_assign_element_to_delete) 1508 1509 ir_delete = Delete(element_to_delete, element_to_delete) 1510 ir_delete.set_expression(ir.expression) 1511 ir_delete.set_node(ir.node) 1512 ret.append(ir_delete) 1513 1514 length_to_assign = ReferenceVariable(node) 1515 length_to_assign.set_type(ElementaryType("uint256")) 1516 ir_length = Length(arr, length_to_assign) 1517 ir_length.set_expression(ir.expression) 1518 length_to_assign.points_to = arr 1519 ir_length.set_node(ir.node) 1520 ret.append(ir_length) 1521 1522 ir_assign_length = Assignment(length_to_assign, val, ElementaryType("uint256")) 1523 ir_assign_length.set_expression(ir.expression) 1524 ir_assign_length.set_node(ir.node) 1525 ret.append(ir_assign_length) 1526 1527 return ret
Convert pop operators Return a list of 6 operations
1530def look_for_library_or_top_level( 1531 ir: HighLevelCall, 1532 using_for, 1533 t: Union[ 1534 UserDefinedType, 1535 ElementaryType, 1536 str, 1537 TypeAliasTopLevel, 1538 ], 1539) -> Optional[Union[LibraryCall, InternalCall,]]: 1540 for destination in using_for[t]: 1541 if isinstance(destination, FunctionTopLevel) and destination.name == ir.function_name: 1542 arguments = [ir.destination] + ir.arguments 1543 if ( 1544 len(destination.parameters) == len(arguments) 1545 and _find_function_from_parameter(arguments, [destination], True) is not None 1546 ): 1547 internalcall = InternalCall(destination, ir.nbr_arguments, ir.lvalue, ir.type_call) 1548 internalcall.set_expression(ir.expression) 1549 internalcall.set_node(ir.node) 1550 internalcall.arguments = [ir.destination] + ir.arguments 1551 return_type = internalcall.function.return_type 1552 if return_type: 1553 if len(return_type) == 1: 1554 internalcall.lvalue.set_type(return_type[0]) 1555 elif len(return_type) > 1: 1556 internalcall.lvalue.set_type(return_type) 1557 else: 1558 internalcall.lvalue = None 1559 return internalcall 1560 1561 lib_contract = None 1562 if isinstance(destination, FunctionContract) and destination.contract.is_library: 1563 lib_contract = destination.contract 1564 elif isinstance(destination, UserDefinedType) and isinstance(destination.type, Contract): 1565 lib_contract = destination.type 1566 1567 if lib_contract: 1568 lib_call = LibraryCall( 1569 lib_contract, 1570 ir.function_name, 1571 ir.nbr_arguments, 1572 ir.lvalue, 1573 ir.type_call, 1574 names=ir.names, 1575 ) 1576 lib_call.set_expression(ir.expression) 1577 lib_call.set_node(ir.node) 1578 lib_call.call_gas = ir.call_gas 1579 lib_call.arguments = [ir.destination] + ir.arguments 1580 new_ir = convert_type_library_call(lib_call, lib_contract) 1581 if new_ir: 1582 new_ir.set_node(ir.node) 1583 return new_ir 1584 return None
1587def convert_to_library_or_top_level( 1588 ir: HighLevelCall, node: "Node", using_for 1589) -> Optional[Union[LibraryCall, InternalCall,]]: 1590 t = ir.destination.type 1591 if t in using_for: 1592 new_ir = look_for_library_or_top_level(ir, using_for, t) 1593 if new_ir: 1594 return new_ir 1595 1596 if "*" in using_for: 1597 new_ir = look_for_library_or_top_level(ir, using_for, "*") 1598 if new_ir: 1599 return new_ir 1600 1601 if ( 1602 isinstance(t, ElementaryType) 1603 and t.name == "address" 1604 and ir.destination.name == "this" 1605 and UserDefinedType(node.function.contract) in using_for 1606 ): 1607 new_ir = look_for_library_or_top_level( 1608 ir, using_for, UserDefinedType(node.function.contract) 1609 ) 1610 if new_ir: 1611 return new_ir 1612 1613 return None
1616def get_type( 1617 t: Union[ 1618 UserDefinedType, 1619 ElementaryType, 1620 ] 1621) -> str: 1622 """ 1623 Convert a type to a str 1624 If the instance is a Contract, return 'address' instead 1625 """ 1626 if isinstance(t, UserDefinedType): 1627 if isinstance(t.type, Contract): 1628 return "address" 1629 return str(t)
Convert a type to a str If the instance is a Contract, return 'address' instead
1640def convert_type_library_call(ir: HighLevelCall, lib_contract: Contract) -> Optional[LibraryCall]: 1641 func = None 1642 candidates = [ 1643 f 1644 for f in lib_contract.functions 1645 if f.name == ir.function_name 1646 and not f.is_shadowed 1647 and len(f.parameters) == len(ir.arguments) 1648 ] 1649 1650 if len(candidates) == 1: 1651 func = candidates[0] 1652 # We can discard if there are arguments here because libraries only support constant variables 1653 # And constant variables cannot have non-value type 1654 # i.e. "uint[2] constant arr = [1,2];" is not possible in Solidity 1655 # If this were to change, the following condition might be broken 1656 if func is None and not ir.arguments: 1657 # TODO: handle collision with multiple state variables/functions 1658 func = lib_contract.get_state_variable_from_name(ir.function_name) 1659 if func is None and candidates: 1660 func = _find_function_from_parameter(ir.arguments, candidates, False) 1661 1662 # In case of multiple binding to the same type 1663 # TODO: this part might not be needed with _find_function_from_parameter 1664 if not func: 1665 # specific lookup when the compiler does implicit conversion 1666 # for example 1667 # myFunc(uint) 1668 # can be called with an uint8 1669 for function in lib_contract.functions: 1670 if function.name == ir.function_name and len(function.parameters) == len(ir.arguments): 1671 func = function 1672 break 1673 if not func: 1674 return None 1675 ir.function = func 1676 if isinstance(func, Function): 1677 t = func.return_type 1678 # if its not a tuple, return a singleton 1679 if t and len(t) == 1: 1680 t = t[0] 1681 else: 1682 # otherwise its a variable (getter) 1683 t = func.type 1684 if t: 1685 ir.lvalue.set_type(t) 1686 else: 1687 ir.lvalue = None 1688 return ir
1732def convert_type_of_high_and_internal_level_call( 1733 ir: Operation, contract: Optional[Contract] 1734) -> Optional[Operation]: 1735 """ 1736 Convert the IR type based on heuristic 1737 1738 Args: 1739 ir: target 1740 contract: optional contract. This should be the target of the IR. It will be used to look up potential functions 1741 1742 Returns: 1743 Potential new IR 1744 """ 1745 1746 func = None 1747 if isinstance(ir, InternalCall): 1748 candidates: List[Function] 1749 if ir.function_candidates: 1750 # This path is taken only for SolidityImportPlaceHolder 1751 # Here we have already done a filtering on the potential targets 1752 candidates = ir.function_candidates 1753 else: 1754 candidates = [ 1755 f 1756 for f in contract.functions 1757 if f.name == ir.function_name 1758 and f.contract_declarer.name == ir.contract_name 1759 and len(f.parameters) == len(ir.arguments) 1760 ] 1761 1762 for import_statement in contract.file_scope.imports: 1763 if ( 1764 import_statement.alias is not None 1765 and import_statement.alias == ir.contract_name 1766 ): 1767 imported_scope = contract.compilation_unit.get_scope(import_statement.filename) 1768 candidates += [ 1769 f 1770 for f in list(imported_scope.functions) 1771 if f.name == ir.function_name and len(f.parameters) == len(ir.arguments) 1772 ] 1773 1774 func = _find_function_from_parameter(ir.arguments, candidates, False) 1775 1776 if not func: 1777 assert contract 1778 func = contract.get_state_variable_from_name(ir.function_name) 1779 else: 1780 assert isinstance(ir, HighLevelCall) 1781 assert contract 1782 candidates = [ 1783 f 1784 for f in contract.functions 1785 if f.name == ir.function_name 1786 and not f.is_shadowed 1787 and len(f.parameters) == len(ir.arguments) 1788 ] 1789 if len(candidates) == 1: 1790 func = candidates[0] 1791 if func is None: 1792 # TODO: handle collision with multiple state variables/functions 1793 func = contract.get_state_variable_from_name(ir.function_name) 1794 if func is None and candidates: 1795 func = _find_function_from_parameter(ir.arguments, candidates, False) 1796 1797 # low level lookup needs to be done as last step 1798 if not func: 1799 if can_be_low_level(ir): 1800 return convert_to_low_level(ir) 1801 if can_be_solidity_func(ir): 1802 return convert_to_solidity_func(ir) 1803 if not func: 1804 to_log = f"Function not found {ir.function_name}" 1805 logger.error(to_log) 1806 ir.function = func 1807 if isinstance(func, Function): 1808 return_type = func.return_type 1809 # if its not a tuple; return a singleton 1810 if return_type and len(return_type) == 1: 1811 return_type = return_type[0] 1812 else: 1813 # otherwise its a variable (getter) 1814 # If its a mapping or a array 1815 # we iterate until we find the final type 1816 # mapping and array can be mixed together 1817 # ex: 1818 # mapping ( uint => mapping ( uint => uint)) my_var 1819 # mapping(uint => uint)[] test;p 1820 if isinstance(func.type, (MappingType, ArrayType)): 1821 tmp = func.type 1822 while isinstance(tmp, (MappingType, ArrayType)): 1823 if isinstance(tmp, MappingType): 1824 tmp = tmp.type_to 1825 else: 1826 tmp = tmp.type 1827 return_type = tmp 1828 else: 1829 return_type = func.type 1830 if return_type: 1831 1832 # If the return type is a structure, but the lvalue is a tuple 1833 # We convert the type of the structure to a list of element 1834 # TODO: explore to replace all tuple variables by structures 1835 if ( 1836 isinstance(ir.lvalue, TupleVariable) 1837 and isinstance(return_type, UserDefinedType) 1838 and isinstance(return_type.type, Structure) 1839 ): 1840 return_type = _convert_to_structure_to_list(return_type) 1841 1842 ir.lvalue.set_type(return_type) 1843 else: 1844 ir.lvalue = None 1845 1846 return None
Convert the IR type based on heuristic
Args: ir: target contract: optional contract. This should be the target of the IR. It will be used to look up potential functions
Returns: Potential new IR
1857def find_references_origin(irs: List[Operation]) -> None: 1858 """ 1859 Make lvalue of each Index, Member operation 1860 points to the left variable 1861 """ 1862 for ir in irs: 1863 if isinstance(ir, (Index, Member)): 1864 ir.lvalue.points_to = ir.variable_left
Make lvalue of each Index, Member operation points to the left variable
1894def remove_unused(result: List[Operation]) -> List[Operation]: 1895 removed = True 1896 1897 if not result: 1898 return result 1899 1900 # dont remove the last elem, as it may be used by RETURN 1901 last_elem = result[-1] 1902 1903 while removed: 1904 removed = False 1905 1906 to_keep = [] 1907 to_remove = [] 1908 1909 # keep variables that are read 1910 # and reference that are written 1911 for ins in result: 1912 to_keep += [str(x) for x in ins.read] 1913 if isinstance(ins, OperationWithLValue) and not isinstance(ins, (Index, Member)): 1914 if isinstance(ins.lvalue, ReferenceVariable): 1915 to_keep += [str(ins.lvalue)] 1916 1917 for ins in result: 1918 if isinstance(ins, Member): 1919 if not ins.lvalue.name in to_keep and ins != last_elem: 1920 to_remove.append(ins) 1921 removed = True 1922 # Remove type(X) if X is an elementary type 1923 # This assume that type(X) is only used with min/max 1924 # If Solidity introduces other operation, we might remove this removal 1925 if isinstance(ins, SolidityCall) and ins.function == SolidityFunction("type()"): 1926 if isinstance(ins.arguments[0], ElementaryType): 1927 to_remove.append(ins) 1928 1929 result = [i for i in result if not i in to_remove] 1930 return result
1941def convert_constant_types(irs: List[Operation]) -> None: 1942 """ 1943 late conversion of uint -> type for constant (Literal) 1944 :param irs: 1945 :return: 1946 """ 1947 # TODO: implement instances lookup for events, NewContract 1948 was_changed = True 1949 while was_changed: 1950 was_changed = False 1951 for ir in irs: 1952 if isinstance(ir, (Assignment, Binary)): 1953 if ( 1954 isinstance(ir.lvalue.type, ElementaryType) 1955 and ir.lvalue.type.type in ElementaryTypeInt 1956 ): 1957 for r in ir.read: 1958 if isinstance(r, Constant) and r.type.type not in ElementaryTypeInt: 1959 r.set_type(ElementaryType(ir.lvalue.type.type)) 1960 was_changed = True 1961 1962 if isinstance(ir, (HighLevelCall, InternalCall)): 1963 func = ir.function 1964 if isinstance(func, StateVariable): 1965 types = export_nested_types_from_variable(func) 1966 else: 1967 types = [p.type for p in func.parameters] 1968 assert len(types) == len(ir.arguments) 1969 for idx, arg in enumerate(ir.arguments): 1970 t = types[idx] 1971 if isinstance(t, ElementaryType): 1972 if t.type in ElementaryTypeInt: 1973 if arg.type.type not in ElementaryTypeInt: 1974 arg.set_type(ElementaryType(t.type)) 1975 was_changed = True 1976 1977 if isinstance(ir, NewStructure): 1978 st = ir.structure 1979 for idx, arg in enumerate(ir.arguments): 1980 e = st.elems_ordered[idx] 1981 if isinstance(e.type, ElementaryType): 1982 if e.type.type in ElementaryTypeInt: 1983 if arg.type.type not in ElementaryTypeInt: 1984 arg.set_type(ElementaryType(e.type.type)) 1985 was_changed = True 1986 1987 def is_elementary_array(t): 1988 return isinstance(t, ArrayType) and isinstance(t.type, ElementaryType) 1989 1990 if isinstance(ir, InitArray): 1991 if is_elementary_array(ir.lvalue.type): 1992 if ir.lvalue.type.type.type in ElementaryTypeInt: 1993 for r in ir.read: 1994 if isinstance(r, Constant) and is_elementary_array(r.type): 1995 if r.type.type.type not in ElementaryTypeInt: 1996 r.set_type(ElementaryType(ir.lvalue.type.type.type)) 1997 was_changed = True
late conversion of uint -> type for constant (Literal)
Parameters
- irs:
Returns
2008def convert_delete(irs: List[Operation]) -> None: 2009 """ 2010 Convert the lvalue of the Delete to point to the variable removed 2011 This can only be done after find_references_origin is called 2012 :param irs: 2013 :return: 2014 """ 2015 for ir in irs: 2016 if isinstance(ir, Delete): 2017 if isinstance(ir.lvalue, ReferenceVariable): 2018 ir.lvalue = ir.lvalue.points_to
Convert the lvalue of the Delete to point to the variable removed This can only be done after find_references_origin is called
Parameters
- irs:
Returns
2047def apply_ir_heuristics(irs: List[Operation], node: "Node", is_solidity: bool) -> List[Operation]: 2048 """ 2049 Apply a set of heuristic to improve slithIR 2050 """ 2051 2052 irs = integrate_value_gas(irs) 2053 2054 irs = propagate_type_and_convert_call(irs, node) 2055 irs = remove_unused(irs) 2056 find_references_origin(irs) 2057 2058 # These are heuristics that are only applied to Solidity 2059 if is_solidity: 2060 convert_constant_types(irs) 2061 convert_delete(irs) 2062 2063 _find_source_mapping_references(irs) 2064 2065 return irs
Apply a set of heuristic to improve slithIR