Improve support for unit and global

- Remove Balance IR (BREAKING CHANGE), and use solidity function instead
- Add support for addr.code / addr/codehash
- Add block.basefee support
- Improve bytes.concat support
- Add type early on for convert operator (useful for
.balance/.code/.codehash detection)

Replacing Balance by a solidity function allows to ease the analysis,
and prevent adding an IR opcode per address.function (code/codehash)

Additionally this PR add thorough tests based on
https://docs.soliditylang.org/en/latest/units-and-global-variables.html
for all the different solidity versions.
pull/985/head
Josselin 3 years ago
parent 5de54f7089
commit b85d9e8cbf
  1. 3
      slither/analyses/write/are_variables_written.py
  2. 5
      slither/core/cfg/node.py
  3. 6
      slither/core/declarations/solidity_variables.py
  4. 7
      slither/detectors/statements/incorrect_strict_equality.py
  5. 5
      slither/printers/guidance/echidna.py
  6. 49
      slither/slithir/convert.py
  7. 1
      slither/slithir/operations/__init__.py
  8. 24
      slither/slithir/operations/balance.py
  9. 5
      slither/slithir/operations/member.py
  10. 5
      slither/slithir/utils/ssa.py
  11. 3
      slither/tools/similarity/encode.py
  12. 29
      slither/visitors/slithir/expression_to_slithir.py
  13. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.0-legacy.zip
  14. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.1-legacy.zip
  15. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.10-legacy.zip
  16. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.11-legacy.zip
  17. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.12-compact.zip
  18. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.12-legacy.zip
  19. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.13-compact.zip
  20. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.13-legacy.zip
  21. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.14-compact.zip
  22. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.14-legacy.zip
  23. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.15-compact.zip
  24. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.15-legacy.zip
  25. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.16-compact.zip
  26. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.16-legacy.zip
  27. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.17-compact.zip
  28. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.17-legacy.zip
  29. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.18-compact.zip
  30. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.18-legacy.zip
  31. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.19-compact.zip
  32. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.19-legacy.zip
  33. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.2-legacy.zip
  34. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.20-compact.zip
  35. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.20-legacy.zip
  36. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.21-compact.zip
  37. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.21-legacy.zip
  38. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.22-compact.zip
  39. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.22-legacy.zip
  40. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.23-compact.zip
  41. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.23-legacy.zip
  42. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.24-compact.zip
  43. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.24-legacy.zip
  44. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.25-compact.zip
  45. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.25-legacy.zip
  46. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.26-compact.zip
  47. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.26-legacy.zip
  48. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.3-legacy.zip
  49. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.4-legacy.zip
  50. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.5-legacy.zip
  51. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.6-legacy.zip
  52. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.7-legacy.zip
  53. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.8-legacy.zip
  54. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.4.9-legacy.zip
  55. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.0-compact.zip
  56. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.0-legacy.zip
  57. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.1-compact.zip
  58. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.1-legacy.zip
  59. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.10-compact.zip
  60. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.10-legacy.zip
  61. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.11-compact.zip
  62. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.11-legacy.zip
  63. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.12-compact.zip
  64. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.12-legacy.zip
  65. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.13-compact.zip
  66. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.13-legacy.zip
  67. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.14-compact.zip
  68. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.14-legacy.zip
  69. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.15-compact.zip
  70. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.15-legacy.zip
  71. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.16-compact.zip
  72. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.16-legacy.zip
  73. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.17-compact.zip
  74. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.17-legacy.zip
  75. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.2-compact.zip
  76. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.2-legacy.zip
  77. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.3-compact.zip
  78. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.3-legacy.zip
  79. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.4-compact.zip
  80. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.4-legacy.zip
  81. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.5-compact.zip
  82. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.5-legacy.zip
  83. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.6-compact.zip
  84. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.6-legacy.zip
  85. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.7-compact.zip
  86. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.7-legacy.zip
  87. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.8-compact.zip
  88. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.8-legacy.zip
  89. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.9-compact.zip
  90. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.5.9-legacy.zip
  91. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.6.0-compact.zip
  92. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.6.0-legacy.zip
  93. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.6.1-compact.zip
  94. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.6.1-legacy.zip
  95. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.6.10-compact.zip
  96. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.6.10-legacy.zip
  97. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.6.11-compact.zip
  98. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.6.11-legacy.zip
  99. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.6.12-compact.zip
  100. BIN
      tests/ast-parsing/compile/units_and_global_variables-0.6.12-legacy.zip
  101. Some files were not shown because too many files have changed in this diff Show More

@ -13,7 +13,6 @@ from slither.slithir.operations import (
OperationWithLValue,
SolidityCall,
Length,
Balance,
)
from slither.slithir.variables import ReferenceVariable, TemporaryVariable
@ -65,7 +64,7 @@ def _visit(
continue
if isinstance(ir, (Index, Member)):
refs[ir.lvalue] = ir.variable_left
if isinstance(ir, (Length, Balance)):
if isinstance(ir, Length):
refs[ir.lvalue] = ir.value
if ir.lvalue and not isinstance(ir.lvalue, (TemporaryVariable, ReferenceVariable)):

@ -16,7 +16,6 @@ from slither.core.variables.variable import Variable
from slither.core.solidity_types import ElementaryType
from slither.slithir.convert import convert_expression
from slither.slithir.operations import (
Balance,
HighLevelCall,
Index,
InternalCall,
@ -875,7 +874,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
self._vars_read.append(origin)
if isinstance(ir, OperationWithLValue):
if isinstance(ir, (Index, Member, Length, Balance)):
if isinstance(ir, (Index, Member, Length)):
continue # Don't consider Member and Index operations -> ReferenceVariable
var = ir.lvalue
if isinstance(var, ReferenceVariable):
@ -959,7 +958,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
self._ssa_vars_read.append(origin)
if isinstance(ir, OperationWithLValue):
if isinstance(ir, (Index, Member, Length, Balance)):
if isinstance(ir, (Index, Member, Length)):
continue # Don't consider Member and Index operations -> ReferenceVariable
var = ir.lvalue
if isinstance(var, ReferenceVariable):

@ -20,6 +20,7 @@ SOLIDITY_VARIABLES = {
}
SOLIDITY_VARIABLES_COMPOSED = {
"block.basefee": "uint",
"block.coinbase": "address",
"block.difficulty": "uint256",
"block.gaslimit": "uint256",
@ -69,10 +70,15 @@ SOLIDITY_FUNCTIONS: Dict[str, List[str]] = {
"abi.encodePacked()": ["bytes"],
"abi.encodeWithSelector()": ["bytes"],
"abi.encodeWithSignature()": ["bytes"],
"bytes.concat()": ["bytes"],
# abi.decode returns an a list arbitrary types
"abi.decode()": [],
"type(address)": [],
"type()": [], # 0.6.8 changed type(address) to type()
# The following are conversion from address.something
"balance(address)": ["uint256"],
"code(address)": ["bytes"],
"codehash(address)": ["bytes32"],
}

@ -9,10 +9,10 @@ from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.slithir.operations import (
Assignment,
Balance,
Binary,
BinaryType,
HighLevelCall,
SolidityCall,
)
from slither.core.solidity_types import MappingType, ElementaryType
@ -21,6 +21,7 @@ from slither.core.variables.state_variable import StateVariable
from slither.core.declarations.solidity_variables import (
SolidityVariable,
SolidityVariableComposed,
SolidityFunction,
)
@ -78,7 +79,9 @@ contract Crowdsale{
for func in functions:
for node in func.nodes:
for ir in node.irs_ssa:
if isinstance(ir, Balance):
if isinstance(ir, SolidityCall) and ir.function == SolidityFunction(
"balance(address)"
):
taints.append(ir.lvalue)
if isinstance(ir, HighLevelCall):
# print(ir.function.full_name)

@ -27,7 +27,6 @@ from slither.slithir.operations import (
InternalDynamicCall,
InternalCall,
TypeConversion,
Balance,
)
from slither.slithir.operations.binary import Binary
from slither.slithir.variables import Constant
@ -288,7 +287,9 @@ def _use_balance(slither: SlitherCore) -> Dict[str, List[str]]:
for contract in slither.contracts:
for function in contract.functions_entry_points:
for ir in function.all_slithir_operations():
if isinstance(ir, Balance):
if isinstance(ir, SolidityCall) and ir.function == SolidityFunction(
"balance(address)"
):
ret[contract.name].append(_get_name(function))
if contract.name in ret:
ret[contract.name] = list(set(ret[contract.name]))

@ -39,7 +39,6 @@ from slither.core.variables.variable import Variable
from slither.slithir.exceptions import SlithIRError
from slither.slithir.operations import (
Assignment,
Balance,
Binary,
BinaryType,
Call,
@ -605,15 +604,32 @@ def propagate_types(ir, node: "Node"): # pylint: disable=too-many-locals
length.lvalue.points_to = ir.variable_left
length.set_node(ir.node)
return length
# This only happen for .balance/code/codehash access on a variable for which we dont know at
# early parsing time the type
# Like
# function return_addr() internal returns(addresss)
#
# return_addr().balance
# Here slithIR will incorrectly create a REF variable instead of a Temp variable
# However this pattern does not appear so often
if (
ir.variable_right == "balance"
ir.variable_right.name in ["balance", "code", "codehash"]
and not isinstance(ir.variable_left, Contract)
and isinstance(ir.variable_left.type, ElementaryType)
):
b = Balance(ir.variable_left, ir.lvalue)
b.set_expression(ir.expression)
b.set_node(ir.node)
return b
name = ir.variable_right.name + "(address)"
sol_func = SolidityFunction(name)
s = SolidityCall(
sol_func,
1,
ir.lvalue,
sol_func.return_type,
)
s.arguments.append(ir.variable_left)
s.set_expression(ir.expression)
s.lvalue.set_type(sol_func.return_type)
s.set_node(ir.node)
return s
if (
ir.variable_right == "codesize"
and not isinstance(ir.variable_left, Contract)
@ -916,6 +932,18 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]): # pylint: dis
internalcall.function_candidates = top_level_function_targets
return internalcall
if ins.ori.variable_left == ElementaryType("bytes") and ins.ori.variable_right == Constant(
"concat"
):
s = SolidityCall(
SolidityFunction("bytes.concat()"),
ins.nbr_arguments,
ins.lvalue,
ins.type_call,
)
s.set_expression(ins.expression)
return s
msgcall = HighLevelCall(
ins.ori.variable_left,
ins.ori.variable_right,
@ -940,15 +968,6 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]): # pylint: dis
if isinstance(ins.called, SolidityVariableComposed):
if str(ins.called) == "block.blockhash":
ins.called = SolidityFunction("blockhash(uint256)")
elif str(ins.called) == "this.balance":
s = SolidityCall(
SolidityFunction("this.balance()"),
ins.nbr_arguments,
ins.lvalue,
ins.type_call,
)
s.set_expression(ins.expression)
return s
if isinstance(ins.called, SolidityFunction):
s = SolidityCall(ins.called, ins.nbr_arguments, ins.lvalue, ins.type_call)

@ -27,7 +27,6 @@ from .type_conversion import TypeConversion
from .unary import Unary, UnaryType
from .unpack import Unpack
from .length import Length
from .balance import Balance
from .phi import Phi
from .phi_callback import PhiCallback
from .nop import Nop

@ -1,24 +0,0 @@
from slither.core.solidity_types import ElementaryType
from slither.slithir.operations.lvalue import OperationWithLValue
from slither.slithir.utils.utils import is_valid_lvalue, is_valid_rvalue
class Balance(OperationWithLValue):
def __init__(self, value, lvalue):
super().__init__()
assert is_valid_rvalue(value)
assert is_valid_lvalue(lvalue)
self._value = value
self._lvalue = lvalue
lvalue.set_type(ElementaryType("uint256"))
@property
def read(self):
return [self._value]
@property
def value(self):
return self._value
def __str__(self):
return "{} -> BALANCE {}".format(self.lvalue, self.value)

@ -2,6 +2,7 @@ from slither.core.declarations import Contract, Function
from slither.core.declarations.custom_error import CustomError
from slither.core.declarations.enum import Enum
from slither.core.declarations.solidity_import_placeholder import SolidityImportPlaceHolder
from slither.core.solidity_types import ElementaryType
from slither.slithir.operations.lvalue import OperationWithLValue
from slither.slithir.utils.utils import is_valid_rvalue
from slither.slithir.variables.constant import Constant
@ -21,8 +22,10 @@ class Member(OperationWithLValue):
# f.h(1);
# }
# }
# Can be an ElementaryType because of bytes.concat
assert is_valid_rvalue(variable_left) or isinstance(
variable_left, (Contract, Enum, Function, CustomError, SolidityImportPlaceHolder)
variable_left,
(Contract, Enum, Function, CustomError, SolidityImportPlaceHolder, ElementaryType),
)
assert isinstance(variable_right, Constant)

@ -15,7 +15,6 @@ from slither.core.variables.local_variable import LocalVariable
from slither.core.variables.state_variable import StateVariable
from slither.slithir.operations import (
Assignment,
Balance,
Binary,
Condition,
Delete,
@ -670,10 +669,6 @@ def copy_ir(ir, *instances):
rvalue = get_variable(ir, lambda x: x.rvalue, *instances)
variable_return_type = ir.variable_return_type
return Assignment(lvalue, rvalue, variable_return_type)
if isinstance(ir, Balance):
lvalue = get_variable(ir, lambda x: x.lvalue, *instances)
value = get_variable(ir, lambda x: x.value, *instances)
return Balance(value, lvalue)
if isinstance(ir, Binary):
lvalue = get_variable(ir, lambda x: x.lvalue, *instances)
variable_left = get_variable(ir, lambda x: x.variable_left, *instances)

@ -23,7 +23,6 @@ from slither.slithir.operations import (
Index,
Member,
Length,
Balance,
Binary,
Unary,
Condition,
@ -150,8 +149,6 @@ def encode_ir(ir): # pylint: disable=too-many-branches
return "member" # .format(ntype(ir._type))
if isinstance(ir, Length):
return "length"
if isinstance(ir, Balance):
return "balance"
if isinstance(ir, Binary):
return "binary({})".format(str(ir.type))
if isinstance(ir, Unary):

@ -17,6 +17,7 @@ from slither.core.expressions import (
from slither.core.solidity_types import ArrayType, ElementaryType
from slither.core.solidity_types.type import Type
from slither.core.variables.local_variable_init_from_tuple import LocalVariableInitFromTuple
from slither.core.variables.variable import Variable
from slither.slithir.exceptions import SlithIRError
from slither.slithir.operations import (
Assignment,
@ -31,6 +32,7 @@ from slither.slithir.operations import (
Unary,
Unpack,
Return,
SolidityCall,
)
from slither.slithir.tmp_operations.argument import Argument
from slither.slithir.tmp_operations.tmp_call import TmpCall
@ -207,12 +209,14 @@ class ExpressionToSlithIR(ExpressionVisitor):
if expression.type in _signed_to_unsigned:
new_left = TemporaryVariable(self._node)
conv_left = TypeConversion(new_left, left, ElementaryType("int256"))
new_left.set_type(ElementaryType("int256"))
conv_left.set_expression(expression)
self._result.append(conv_left)
if expression.type != BinaryOperationType.RIGHT_SHIFT_ARITHMETIC:
new_right = TemporaryVariable(self._node)
conv_right = TypeConversion(new_right, right, ElementaryType("int256"))
new_right.set_type(ElementaryType("int256"))
conv_right.set_expression(expression)
self._result.append(conv_right)
else:
@ -224,6 +228,7 @@ class ExpressionToSlithIR(ExpressionVisitor):
self._result.append(operation)
conv_final = TypeConversion(val, new_final, ElementaryType("uint256"))
val.set_type(ElementaryType("uint256"))
conv_final.set_expression(expression)
self._result.append(conv_final)
else:
@ -274,6 +279,7 @@ class ExpressionToSlithIR(ExpressionVisitor):
elif called.name == "selfbalance()":
val = TemporaryVariable(self._node)
var = TypeConversion(val, SolidityVariable("this"), ElementaryType("address"))
val.set_type(ElementaryType("address"))
self._result.append(var)
val1 = ReferenceVariable(self._node)
@ -283,6 +289,7 @@ class ExpressionToSlithIR(ExpressionVisitor):
elif called.name == "address()":
val = TemporaryVariable(self._node)
var = TypeConversion(val, SolidityVariable("this"), ElementaryType("address"))
val.set_type(ElementaryType("address"))
self._result.append(var)
set_val(expression, val)
elif called.name == "callvalue()":
@ -385,6 +392,27 @@ class ExpressionToSlithIR(ExpressionVisitor):
set_val(expression, val)
return
# This does not support solidity 0.4 contract_name.balance
if (
isinstance(expr, Variable)
and expr.type == ElementaryType("address")
and expression.member_name in ["balance", "code", "codehash"]
):
val = TemporaryVariable(self._node)
name = expression.member_name + "(address)"
sol_func = SolidityFunction(name)
s = SolidityCall(
sol_func,
1,
val,
sol_func.return_type,
)
s.set_expression(expression)
s.arguments.append(expr)
self._result.append(s)
set_val(expression, val)
return
val = ReferenceVariable(self._node)
member = Member(expr, Constant(expression.member_name), val)
member.set_expression(expression)
@ -432,6 +460,7 @@ class ExpressionToSlithIR(ExpressionVisitor):
expr = get(expression.expression)
val = TemporaryVariable(self._node)
operation = TypeConversion(val, expr, expression.type)
val.set_type(expression.type)
operation.set_expression(expression)
self._result.append(operation)
set_val(expression, val)

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save