Skip to content

Commit

Permalink
Merge branch 'fixes_after_evaluation' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
talfao committed Apr 26, 2024
2 parents b746a01 + 8b8a7bb commit 89b9711
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 35 deletions.
35 changes: 22 additions & 13 deletions slither/detectors/oracles/oracle_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ def check_var_condition_match(var, node) -> bool:
@staticmethod
def map_param_to_var(var, function: FunctionContract):
for param in function.parameters:
origin_vars = get_dependencies(param, function)
try:
origin_vars = get_dependencies(param, function)
# DATA_DEPENDENCY error can be throw out
except KeyError:
continue
for var2 in origin_vars:
if var2 == var:
return param
Expand Down Expand Up @@ -144,7 +148,6 @@ def map_condition_to_var(self, var, function: FunctionContract):

# Check if the vars occurs in require/assert statement or in conditional node
def vars_in_conditions(self, oracle: Oracle):
# vars_in_condition = []
vars_not_in_condition = []
oracle_vars = []
nodes = []
Expand All @@ -157,26 +160,27 @@ def vars_in_conditions(self, oracle: Oracle):
for node in self.nodes_with_var:
for ir in node.irs:
if isinstance(ir, InternalCall):
self.investigate_internal_call(ir.function, var, None)
self.investigate_internal_call(
oracle,
ir.function,
var,
)
oracle.remove_occurances_in_function()

if len(self.nodes_with_var) > 0:
# vars_in_condition.append(VarInCondition(var, self.nodes_with_var))
oracle_vars.append(VarInCondition(var, self.nodes_with_var))
else:
if self.investigate_internal_call(oracle.function, var, None):
# vars_in_condition.append(VarInCondition(var, self.nodes_with_var))
if self.investigate_internal_call(oracle, oracle.function, var):
oracle_vars.append(VarInCondition(var, self.nodes_with_var))
oracle.remove_occurances_in_function()
elif nodes := self.investigate_on_return(oracle, var):
oracle_vars.append(VarInCondition(var, nodes))
oracle.out_of_function_checks.append((var, nodes))
# except RecursionError:
# vars_not_in_condition.append(var)
# oracle_vars.append(var)
oracle.remove_occurances_in_function()
else:
vars_not_in_condition.append(var)
oracle_vars.append(var)

# oracle.vars_in_condition = vars_in_condition
oracle.vars_not_in_condition = vars_not_in_condition
oracle.oracle_vars = oracle_vars
return True
Expand All @@ -201,6 +205,8 @@ def checks_performed_out_of_original_function(self, oracle, returned_var):
nodes = []
for node in nodes_of_call:
oracle.set_function(functions_of_call[i])
if not oracle.add_occurance_in_function(functions_of_call[i]):
break
oracle.set_node(node)
new_vars = self.get_returned_variables_from_oracle(node)
for var in new_vars:
Expand Down Expand Up @@ -239,10 +245,12 @@ def investigate_on_return(self, oracle, var) -> bool:
return False

# This function interates through all internal calls in function and checks if the var is used in condition any of them
def investigate_internal_call(self, function: FunctionContract, var, original_function) -> bool:
def investigate_internal_call(self, oracle, function: FunctionContract, var) -> bool:
if function is None:
return False
if function == original_function:

result = oracle.add_occurance_in_function(function)
if not result:
return False

original_var_as_param = self.map_param_to_var(var, function)
Expand All @@ -268,8 +276,9 @@ def investigate_internal_call(self, function: FunctionContract, var, original_fu
for node in function.nodes:
for ir in node.irs:
if isinstance(ir, InternalCall):
if self.investigate_internal_call(ir.function, original_var_as_param, function):
if self.investigate_internal_call(oracle, ir.function, original_var_as_param):
return True

return False

def _detect(self):
Expand Down
14 changes: 8 additions & 6 deletions slither/detectors/oracles/supported_oracles/chainlink_oracle.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"latestRoundData",
"getRoundData",
]
INTERFACES = ["AggregatorV3Interface", "FeedRegistryInterface"]


class ChainlinkVars(Enum):
Expand All @@ -25,7 +26,7 @@ class ChainlinkVars(Enum):

class ChainlinkOracle(Oracle):
def __init__(self):
super().__init__(CHAINLINK_ORACLE_CALLS)
super().__init__(CHAINLINK_ORACLE_CALLS, INTERFACES)

# This function checks if the RoundId value is validated in connection with answeredInRound value
# But this last variable was deprecated. We left this function for possible future use.
Expand Down Expand Up @@ -83,11 +84,12 @@ def is_sequencer_check(self, answer, startedAt):
answer_checked = False
startedAt_checked = False

for node in answer.nodes_with_var:
for ir in node.irs:
if isinstance(ir, Binary):
if self.price_check_for_liveness(ir):
answer_checked = True
if hasattr(answer, "nodes_with_var"):
for node in answer.nodes_with_var:
for ir in node.irs:
if isinstance(ir, Binary):
if self.price_check_for_liveness(ir):
answer_checked = True
startedAt_checked = self.check_staleness(startedAt)

return answer_checked and startedAt_checked
Expand Down
54 changes: 38 additions & 16 deletions slither/detectors/oracles/supported_oracles/oracle.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ def __init__(self, _var, _nodes):


class Oracle: # pylint: disable=too-few-public-methods, too-many-instance-attributes
def __init__(self, _calls):
def __init__(self, _calls, _interfaces):
self.calls = _calls
self.interfaces = _interfaces
self.contract = None
self.function = None
self.node = None
Expand All @@ -26,6 +27,19 @@ def __init__(self, _calls):
self.returned_vars_indexes = None
self.interface = None
self.oracle_api = None
## Works as protection against recursion loop
self.occured_in_functions = []

# This function adds function to the list of functions where the oracle data were used
# Used to prevent recursion of visiting same functions
def add_occurance_in_function(self, _function):
if _function in self.occured_in_functions:
return False
self.occured_in_functions.append(_function)
return True

def remove_occurances_in_function(self):
self.occured_in_functions = []

def get_calls(self):
return self.calls
Expand All @@ -34,7 +48,8 @@ def is_instance_of(self, ir: Operation) -> bool:
return isinstance(ir, HighLevelCall) and (
isinstance(ir.function, Function)
and self.compare_call(ir.function.name)
# add interface
and hasattr(ir.destination, "type")
and self.compare_interface(str(ir.destination.type))
)

def set_node(self, _node):
Expand All @@ -43,6 +58,11 @@ def set_node(self, _node):
def set_function(self, _function):
self.function = _function

def compare_interface(self, interface) -> bool:
if interface in self.interfaces:
return True
return False

def compare_call(self, function) -> bool:
for call in self.calls:
if call in str(function):
Expand Down Expand Up @@ -87,12 +107,13 @@ def check_staleness(self, var: VarInCondition) -> bool:
if var is None:
return False
different_behavior = False
for node in var.nodes_with_var:
# This is temporarily check which will be improved in the future. Mostly we are looking for block.timestamp and trust the developer that he is using it correctly
if self.timestamp_in_node(node):
return True
if not different_behavior:
different_behavior = check_revert(node) or return_boolean(node)
if hasattr(var, "nodes_with_var"):
for node in var.nodes_with_var:
# This is temporarily check which will be improved in the future. Mostly we are looking for block.timestamp and trust the developer that he is using it correctly
if self.timestamp_in_node(node):
return True
if not different_behavior:
different_behavior = check_revert(node) or return_boolean(node)

return different_behavior

Expand All @@ -101,13 +122,14 @@ def check_price(self, var: VarInCondition) -> bool:
if var is None:
return False
different_behavior = False
for node in var.nodes_with_var:
for ir in node.irs:
if isinstance(ir, Binary):
if self.check_greater_zero(ir):
return True
# If the conditions does not match we are looking for revert or return node
if not different_behavior:
different_behavior = check_revert(node) or return_boolean(node)
if hasattr(var, "nodes_with_var"):
for node in var.nodes_with_var:
for ir in node.irs:
if isinstance(ir, Binary):
if self.check_greater_zero(ir):
return True
# If the conditions does not match we are looking for revert or return node
if not different_behavior:
different_behavior = check_revert(node) or return_boolean(node)

return different_behavior

0 comments on commit 89b9711

Please sign in to comment.