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