evm: Fix MUL overflow false positive (#767)
* initial * Add test for mul checker * clean * Rename and clean * Rm testing * Better comment, and clean * Split tests, add more tests * Clean up stuff, remove unnecessary masking * fmt * Add back constrain to make it match the originall buggy situation * Clean up surrounding code, make it unit testable * Correct name * Use individual arguments for helpers
This commit is contained in:
parent
3f1c51fa91
commit
b9aa483745
@ -1,7 +1,7 @@
|
|||||||
import string
|
import string
|
||||||
|
|
||||||
from . import Manticore
|
from . import Manticore
|
||||||
from .core.smtlib import ConstraintSet, Operators, solver, issymbolic, Array, Expression, Constant
|
from .core.smtlib import ConstraintSet, Operators, solver, issymbolic, Array, Expression, Constant, operators
|
||||||
from .core.smtlib.visitors import arithmetic_simplifier
|
from .core.smtlib.visitors import arithmetic_simplifier
|
||||||
from .platforms import evm
|
from .platforms import evm
|
||||||
from .core.state import State
|
from .core.state import State
|
||||||
@ -55,13 +55,30 @@ class IntegerOverflow(Detector):
|
|||||||
'''
|
'''
|
||||||
Detects potential overflow and underflow conditions on ADD and SUB instructions.
|
Detects potential overflow and underflow conditions on ADD and SUB instructions.
|
||||||
'''
|
'''
|
||||||
|
@staticmethod
|
||||||
|
def _can_add_overflow(state, result, a, b):
|
||||||
|
# TODO FIXME (mark) this is using a signed LT. need to check if this is correct
|
||||||
|
return state.can_be_true(result < a) or state.can_be_true(result < b)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _can_mul_overflow(state, result, a, b):
|
||||||
|
return state.can_be_true(operators.ULT(result, a) & operators.ULT(result, b))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _can_sub_underflow(state, a, b):
|
||||||
|
return state.can_be_true(b > a)
|
||||||
|
|
||||||
def did_evm_execute_instruction_callback(self, state, instruction, arguments, result):
|
def did_evm_execute_instruction_callback(self, state, instruction, arguments, result):
|
||||||
mnemonic = instruction.semantics
|
mnemonic = instruction.semantics
|
||||||
if mnemonic in ('ADD', 'MUL'):
|
|
||||||
if state.can_be_true(result < arguments[0]) or state.can_be_true(result < arguments[1]):
|
if mnemonic == 'ADD':
|
||||||
|
if self._can_add_overflow(state, result, *arguments):
|
||||||
|
self.add_finding(state, "Integer overflow at {} instruction".format(mnemonic))
|
||||||
|
elif mnemonic == 'MUL':
|
||||||
|
if self._can_mul_overflow(state, result, *arguments):
|
||||||
self.add_finding(state, "Integer overflow at {} instruction".format(mnemonic))
|
self.add_finding(state, "Integer overflow at {} instruction".format(mnemonic))
|
||||||
elif mnemonic == 'SUB':
|
elif mnemonic == 'SUB':
|
||||||
if state.can_be_true(arguments[1] > arguments[0]):
|
if self._can_sub_underflow(state, *arguments):
|
||||||
self.add_finding(state, "Integer underflow at {} instruction".format(mnemonic))
|
self.add_finding(state, "Integer underflow at {} instruction".format(mnemonic))
|
||||||
|
|
||||||
class UninitializedMemory(Detector):
|
class UninitializedMemory(Detector):
|
||||||
|
|||||||
@ -1,15 +1,20 @@
|
|||||||
import unittest
|
import unittest
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from manticore.core.smtlib import ConstraintSet, operators
|
||||||
|
from manticore.core.state import State
|
||||||
from manticore.ethereum import ManticoreEVM, IntegerOverflow, Detector
|
from manticore.ethereum import ManticoreEVM, IntegerOverflow, Detector
|
||||||
|
from manticore.platforms.evm import EVMWorld
|
||||||
|
|
||||||
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
|
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
# FIXME(mark): Remove these two lines when logging works for ManticoreEVM
|
# FIXME(mark): Remove these two lines when logging works for ManticoreEVM
|
||||||
from manticore.utils.log import init_logging
|
from manticore.utils.log import init_logging
|
||||||
|
|
||||||
init_logging()
|
init_logging()
|
||||||
|
|
||||||
class EthDetectors(unittest.TestCase):
|
|
||||||
|
class EthDetectorsIntegrationTest(unittest.TestCase):
|
||||||
def test_int_ovf(self):
|
def test_int_ovf(self):
|
||||||
mevm = ManticoreEVM()
|
mevm = ManticoreEVM()
|
||||||
mevm.register_detector(IntegerOverflow())
|
mevm.register_detector(IntegerOverflow())
|
||||||
@ -21,6 +26,50 @@ class EthDetectors(unittest.TestCase):
|
|||||||
self.assertIn('overflow at ADD', all_findings)
|
self.assertIn('overflow at ADD', all_findings)
|
||||||
self.assertIn('overflow at MUL', all_findings)
|
self.assertIn('overflow at MUL', all_findings)
|
||||||
|
|
||||||
|
|
||||||
|
class EthDetectors(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.io = IntegerOverflow()
|
||||||
|
self.state = self.make_mock_evm_state()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def make_mock_evm_state():
|
||||||
|
cs = ConstraintSet()
|
||||||
|
fakestate = State(cs, EVMWorld(cs))
|
||||||
|
return fakestate
|
||||||
|
|
||||||
|
def test_mul_no_overflow(self):
|
||||||
|
"""
|
||||||
|
Regression test added for issue 714, where we were using the ADD ovf check for MUL
|
||||||
|
"""
|
||||||
|
arguments = [1 << (8 * 31), self.state.new_symbolic_value(256)]
|
||||||
|
self.state.constrain(operators.ULT(arguments[1], 256))
|
||||||
|
|
||||||
|
# TODO(mark) We should actually call into the EVM cpu here, and below, rather than
|
||||||
|
# effectively copy pasting what the MUL does
|
||||||
|
result = arguments[0] * arguments[1]
|
||||||
|
|
||||||
|
check = self.io._can_mul_overflow(self.state, result, *arguments)
|
||||||
|
self.assertFalse(check)
|
||||||
|
|
||||||
|
def test_mul_overflow0(self):
|
||||||
|
arguments = [2 << (8 * 31), self.state.new_symbolic_value(256)]
|
||||||
|
self.state.constrain(operators.ULT(arguments[1], 256))
|
||||||
|
|
||||||
|
result = arguments[0] * arguments[1]
|
||||||
|
|
||||||
|
check = self.io._can_mul_overflow(self.state, result, *arguments)
|
||||||
|
self.assertTrue(check)
|
||||||
|
|
||||||
|
def test_mul_overflow1(self):
|
||||||
|
arguments = [1 << 255, self.state.new_symbolic_value(256)]
|
||||||
|
|
||||||
|
result = arguments[0] * arguments[1]
|
||||||
|
|
||||||
|
check = self.io._can_mul_overflow(self.state, result, *arguments)
|
||||||
|
self.assertTrue(check)
|
||||||
|
|
||||||
|
|
||||||
class EthTests(unittest.TestCase):
|
class EthTests(unittest.TestCase):
|
||||||
def test_emit_did_execute_end_instructions(self):
|
def test_emit_did_execute_end_instructions(self):
|
||||||
class TestDetector(Detector):
|
class TestDetector(Detector):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user