diff --git a/manticore/native/cpu/abstractcpu.py b/manticore/native/cpu/abstractcpu.py index 5678828ff..7b57bc2d2 100644 --- a/manticore/native/cpu/abstractcpu.py +++ b/manticore/native/cpu/abstractcpu.py @@ -23,11 +23,17 @@ from capstone.x86 import X86_REG_ENDING from capstone.arm import ARM_REG_ENDING -from typing import Any, Dict, List, Optional, Union +from typing import Any, Callable, Dict, Optional, Tuple logger = logging.getLogger(__name__) register_logger = logging.getLogger(f"{__name__}.registers") + +def _sig_is_varargs(sig: inspect.Signature) -> bool: + VAR_POSITIONAL = inspect.Parameter.VAR_POSITIONAL + return any(p.kind == VAR_POSITIONAL for p in sig.parameters.values()) + + ################################################################################### # Exceptions @@ -309,26 +315,21 @@ def values_from(self, base): yield base base += word_bytes - def get_argument_values(self, model, prefix_args): + def get_argument_values(self, model: Callable, prefix_args: Tuple) -> Tuple: """ Extract arguments for model from the environment and return as a tuple that is ready to be passed to the model. - :param callable model: Python model of the function - :param tuple prefix_args: Parameters to pass to model before actual ones + :param model: Python model of the function + :param prefix_args: Parameters to pass to model before actual ones :return: Arguments to be passed to the model - :rtype: tuple """ - spec = inspect.getfullargspec(model) - - if spec.varargs: - logger.warning("ABI: A vararg model must be a unary function.") + sig = inspect.signature(model) + if _sig_is_varargs(sig): + model_name = getattr(model, "__qualname__", "") + logger.warning("ABI: %s: a vararg model must be a unary function.", model_name) - nargs = len(spec.args) - len(prefix_args) - - # If the model is a method, we need to account for `self` - if inspect.ismethod(model): - nargs -= 1 + nargs = len(sig.parameters) - len(prefix_args) def resolve_argument(arg): if isinstance(arg, str): @@ -343,11 +344,9 @@ def resolve_argument(arg): from ..models import isvariadic # prevent circular imports if isvariadic(model): - arguments = prefix_args + (argument_iter,) + return prefix_args + (argument_iter,) else: - arguments = prefix_args + tuple(islice(argument_iter, nargs)) - - return arguments + return prefix_args + tuple(islice(argument_iter, nargs)) def invoke(self, model, prefix_args=None): """ diff --git a/manticore/platforms/linux.py b/manticore/platforms/linux.py index fc9e32bba..028e15bc3 100644 --- a/manticore/platforms/linux.py +++ b/manticore/platforms/linux.py @@ -9,7 +9,6 @@ import time import resource import tempfile -from typing import Deque, Union, List, TypeVar, cast, Optional import io import os @@ -33,10 +32,12 @@ from ..native.state import State from ..platforms.platform import Platform, SyscallNotImplemented, unimplemented -from typing import Any, Dict, IO, List, Optional, Set, Tuple, Union +from typing import Any, cast, Deque, Dict, IO, List, Optional, Set, Tuple, Union + logger = logging.getLogger(__name__) + MixedSymbolicBuffer = Union[List[Union[bytes, Expression]], bytes] diff --git a/manticore/platforms/linux_syscall_stubs.py b/manticore/platforms/linux_syscall_stubs.py index 45a5aaead..30b08d7dd 100644 --- a/manticore/platforms/linux_syscall_stubs.py +++ b/manticore/platforms/linux_syscall_stubs.py @@ -1,6 +1,10 @@ from ..platforms.platform import SyscallNotImplemented, unimplemented from .linux_syscalls import amd64 +import logging + +logger = logging.getLogger(__name__) + class SyscallStubs: """ @@ -20,9 +24,11 @@ def __init__(self, *, default_to_fail=False, parent=None): self.parent = parent def __getattr__(self, item): - print( - "System calls should be copied and pasted into linux.py, not implemented within the stub file.", - "If you're seeing this message, you may have forgotten to do that.", + logger.warning( + f"Getting {item!r} attribute from {self.__class__}: " + "System calls should be copied and pasted into linux.py, " + "not implemented within the stub file. " + "If you're seeing this message, you may have forgotten to do that." ) return getattr(self.parent, item) diff --git a/manticore/platforms/platform.py b/manticore/platforms/platform.py index 859cdd1b6..dc95090a7 100644 --- a/manticore/platforms/platform.py +++ b/manticore/platforms/platform.py @@ -1,8 +1,10 @@ -import wrapt import logging + +from functools import wraps +from typing import Any, Callable, TypeVar + from ..utils.event import Eventful -from typing import Callable, Dict, Tuple logger = logging.getLogger(__name__) @@ -11,23 +13,29 @@ class OSException(Exception): pass -@wrapt.decorator -def unimplemented(wrapped: Callable, _instance, args: Tuple, kwargs: Dict): - cpu = getattr(getattr(_instance, "parent", None), "current", None) - addr_str = "" if cpu is None else f" at {hex(cpu.read_register('PC'))}" - logger.warning( - f"Unimplemented system call: %s: %s(%s)", - addr_str, - wrapped.__name__, - ", ".join(hex(a) if isinstance(a, int) else str(a) for a in args), - ) - return wrapped(*args, **kwargs) +T = TypeVar("T") + + +def unimplemented(wrapped: Callable[..., T]) -> Callable[..., T]: + @wraps(wrapped) + def new_wrapped(self: Any, *args, **kwargs) -> T: + cpu = getattr(getattr(self, "parent", None), "current", None) + pc_str = "" if cpu is None else hex(cpu.read_register("PC")) + logger.warning( + f"Unimplemented system call: %s: %s(%s)", + pc_str, + wrapped.__name__, + ", ".join(hex(a) if isinstance(a, int) else str(a) for a in args), + ) + return wrapped(self, *args, **kwargs) + + return new_wrapped class SyscallNotImplemented(OSException): """ Exception raised when you try to call an unimplemented system call. - Go to linux.py and add it! + Go to linux.py and add an implementation! """ def __init__(self, idx, name): diff --git a/mypy.ini b/mypy.ini index 5ded6ce08..82669511c 100644 --- a/mypy.ini +++ b/mypy.ini @@ -37,9 +37,6 @@ ignore_missing_imports = True [mypy-prettytable.*] ignore_missing_imports = True -[mypy-wrapt.*] -ignore_missing_imports = True - [mypy-wasm.*] ignore_missing_imports = True diff --git a/setup.py b/setup.py index bb2f36b3e..4e593157e 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,6 @@ def rtd_dependent_deps(): python_requires=">=3.6", install_requires=[ "pyyaml", - "wrapt", # evm dependencies "pysha3", "prettytable", diff --git a/tests/native/test_syscalls.py b/tests/native/test_syscalls.py index 604ea550f..2c4963bef 100644 --- a/tests/native/test_syscalls.py +++ b/tests/native/test_syscalls.py @@ -1,16 +1,18 @@ +import logging import random import struct import socket import tempfile +import time import unittest import os import errno +import re -from manticore.core.smtlib import * from manticore.platforms import linux, linux_syscall_stubs from manticore.platforms.linux import SymbolicSocket -from manticore.platforms.platform import SyscallNotImplemented +from manticore.platforms.platform import SyscallNotImplemented, logger as platform_logger class LinuxTest(unittest.TestCase): @@ -374,27 +376,31 @@ def test_llseek_end_broken(self): self.linux.sys_write(fd, 0x1200, len(buf)) # FIXME: currently broken -- raises a Python OSError invalid argument exception! + resultp = 0x1900 res = self.linux.sys_llseek(fd, 0, -2 * len(buf), resultp, os.SEEK_END) self.assertTrue(res < 0) - def test_unimplemented(self): + def test_unimplemented_stubs(self) -> None: stubs = linux_syscall_stubs.SyscallStubs(default_to_fail=False) - if hasattr(stubs, "sys_bpf"): + with self.assertLogs(platform_logger, logging.WARNING) as cm: self.assertRaises(SyscallNotImplemented, stubs.sys_bpf, 0, 0, 0) - - self.linux.stubs.default_to_fail = False - self.linux.current.RAX = 321 # SYS_BPF - self.assertRaises(SyscallNotImplemented, self.linux.syscall) - - self.linux.stubs.default_to_fail = True - self.linux.current.RAX = 321 - self.linux.syscall() - self.assertEqual(0xFFFFFFFFFFFFFFFF, self.linux.current.RAX) - else: - import warnings - - warnings.warn( - "Couldn't find sys_bpf in the stubs file. " - + "If you've implemented it, you need to fix test_syscalls:LinuxTest.test_unimplemented" - ) + # make sure that log message contains expected info + pat = re.compile(r"Unimplemented system call: .+: .+\(.+\)", re.MULTILINE) + self.assertRegex("\n".join(cm.output), pat) + + self.linux.stubs.default_to_fail = False + self.linux.current.RAX = 321 # SYS_BPF + self.assertRaises(SyscallNotImplemented, self.linux.syscall) + + self.linux.stubs.default_to_fail = True + self.linux.current.RAX = 321 + self.linux.syscall() + self.assertEqual(0xFFFFFFFFFFFFFFFF, self.linux.current.RAX) + + def test_unimplemented_linux(self) -> None: + with self.assertLogs(platform_logger, logging.WARNING) as cm: + self.linux.sys_futex(0, 0, 0, 0, 0, 0) + # make sure that log message contains expected info + pat = re.compile(r"Unimplemented system call: .+: .+\(.+\)", re.MULTILINE) + self.assertRegex("\n".join(cm.output), pat)