@ -50,37 +50,13 @@ def is_assert(node):
return False
class DivideBeforeMultiply ( AbstractDetector ) :
"""
Divide before multiply
"""
ARGUMENT = " divide-before-multiply "
HELP = " Imprecise arithmetic operations order "
IMPACT = DetectorClassification . MEDIUM
CONFIDENCE = DetectorClassification . MEDIUM
WIKI = " https://github.com/crytic/slither/wiki/Detector-Documentation#divide-before-multiply "
WIKI_TITLE = " Divide before multiply "
WIKI_DESCRIPTION = """ Solidity integer division might truncate. As a result, performing multiplication before division can sometimes avoid loss of precision. """
WIKI_EXPLOIT_SCENARIO = """
` ` ` solidity
contract A {
function f ( uint n ) public {
coins = ( oldSupply / n ) * interest ;
}
}
` ` `
If ` n ` is greater than ` oldSupply ` , ` coins ` will be zero . For example , with ` oldSupply = 5 ; n = 10 , interest = 2 ` , coins will be zero .
If ` ( oldSupply * interest / n ) ` was used , ` coins ` would have been ` 1 ` .
In general , it ' s usually a good idea to re-arrange arithmetic to perform multiplication before division, unless the limit of a smaller type makes this dangerous. " " "
WIKI_RECOMMENDATION = """ Consider ordering multiplication before division. """
def _explore ( to_explore , f_results , divisions ) : # pylint: disable=too-many-branches
explored = set ( )
while to_explore : # pylint: disable=too-many-nested-blocks
node = to_explore . pop ( )
def _explore ( self , node , explored , f_results , divisions ) : # pylint: disable=too-many-branches
if node in explored :
return
continue
explored . add ( node )
equality_found = False
@ -121,15 +97,14 @@ In general, it's usually a good idea to re-arrange arithmetic to perform multipl
# We do not track the case where the multiplication is done in a require() or assert()
# Which also contains a ==, to prevent FP due to the form
# assert(a == b * c + a % b)
if is_assert ( node ) and equality_found :
pass
else :
if not ( is_assert ( node ) and equality_found ) :
f_results . append ( node_results )
for son in node . sons :
self . _explore ( son , explored , f_results , divisions )
to_explore . add ( son )
def detect_divide_before_multiply ( self , contract ) :
def detect_divide_before_multiply ( contract ) :
"""
Detects and returns all nodes with multiplications of division results .
: param contract : Contract to detect assignment within .
@ -155,7 +130,7 @@ In general, it's usually a good idea to re-arrange arithmetic to perform multipl
# track all the division results (and the assignment of the division results)
divisions = defaultdict ( list )
self . _explore ( function . entry_point , set ( ) , f_results , divisions )
_explore ( { function . entry_point } , f_results , divisions )
for f_result in f_results :
results . append ( ( function , f_result ) )
@ -163,13 +138,42 @@ In general, it's usually a good idea to re-arrange arithmetic to perform multipl
# Return the resulting set of nodes with divisions before multiplications
return results
class DivideBeforeMultiply ( AbstractDetector ) :
"""
Divide before multiply
"""
ARGUMENT = " divide-before-multiply "
HELP = " Imprecise arithmetic operations order "
IMPACT = DetectorClassification . MEDIUM
CONFIDENCE = DetectorClassification . MEDIUM
WIKI = " https://github.com/crytic/slither/wiki/Detector-Documentation#divide-before-multiply "
WIKI_TITLE = " Divide before multiply "
WIKI_DESCRIPTION = """ Solidity integer division might truncate. As a result, performing multiplication before division can sometimes avoid loss of precision. """
WIKI_EXPLOIT_SCENARIO = """
` ` ` solidity
contract A {
function f ( uint n ) public {
coins = ( oldSupply / n ) * interest ;
}
}
` ` `
If ` n ` is greater than ` oldSupply ` , ` coins ` will be zero . For example , with ` oldSupply = 5 ; n = 10 , interest = 2 ` , coins will be zero .
If ` ( oldSupply * interest / n ) ` was used , ` coins ` would have been ` 1 ` .
In general , it ' s usually a good idea to re-arrange arithmetic to perform multiplication before division, unless the limit of a smaller type makes this dangerous. " " "
WIKI_RECOMMENDATION = """ Consider ordering multiplication before division. """
def _detect ( self ) :
"""
Detect divisions before multiplications
"""
results = [ ]
for contract in self . contracts :
divisions_before_multiplications = self . detect_divide_before_multiply ( contract )
divisions_before_multiplications = detect_divide_before_multiply ( contract )
if divisions_before_multiplications :
for ( func , nodes ) in divisions_before_multiplications :
@ -178,6 +182,9 @@ In general, it's usually a good idea to re-arrange arithmetic to perform multipl
" performs a multiplication on the result of a division: \n " ,
]
# sort the nodes to get deterministic results
nodes . sort ( key = lambda x : x . node_id )
for node in nodes :
info + = [ " \t - " , node , " \n " ]