Skip to content

Commit

Permalink
Add methods for getting list of (un)implemented system calls (#2491)
Browse files Browse the repository at this point in the history
This is primarily a helper function that is useful in evaluating the
capabilities of Manticore on Linux

* Add Linux Kernel version constant to syscalls list

* Add method for getting unimplemented syscalls

This is specific per architecture
  • Loading branch information
ekilmer authored Sep 15, 2021
1 parent b73986b commit 522f8ba
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 6 deletions.
38 changes: 38 additions & 0 deletions manticore/platforms/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -3316,6 +3316,44 @@ def _interp_total_size(interp):
last = load_segs[-1]
return last.header.p_vaddr + last.header.p_memsz

@classmethod
def implemented_syscalls(cls) -> Iterable[str]:
"""
Get a listing of all concretely implemented system calls for Linux. This
does not include whether a symbolic version exists. To get that listing,
use the SLinux.implemented_syscalls() method.
"""
import inspect

return (
name
for (name, obj) in inspect.getmembers(cls, predicate=inspect.isfunction)
if name.startswith("sys_") and
# Check that the class defining the method is exactly this one
getattr(inspect.getmodule(obj), obj.__qualname__.rsplit(".", 1)[0], None) == cls
)

@classmethod
def unimplemented_syscalls(cls, syscalls: Union[Set[str], Dict[int, str]]) -> Set[str]:
"""
Get a listing of all unimplemented concrete system calls for a given
collection of Linux system calls. To get a listing of unimplemented
symbolic system calls, use the ``SLinux.unimplemented_syscalls()``
method.
Available system calls can be found at ``linux_syscalls.py`` or you may
pass your own as either a set of system calls or as a mapping of system
call number to system call name.
Note that passed system calls should follow the naming convention
located in ``linux_syscalls.py``.
"""
implemented_syscalls = set(cls.implemented_syscalls())
if isinstance(syscalls, set):
return syscalls.difference(implemented_syscalls)
else:
return set(syscalls.values()).difference(implemented_syscalls)


############################################################################
# Symbolic versions follows
Expand Down
6 changes: 3 additions & 3 deletions manticore/platforms/linux_syscalls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
#
# AUTOGENERATED, DO NOT EDIT
#
# From version: 4.11
#

LINUX_KERNEL_VERSION = "4.11"

i386 = {
0: "sys_restart_syscall",
Expand Down Expand Up @@ -426,7 +426,7 @@
53: "sys_socketpair",
54: "sys_setsockopt",
55: "sys_getsockopt",
56: "sys_clone_ptregs",
56: "sys_clone/ptregs",
57: "sys_fork/ptregs",
58: "sys_vfork/ptregs",
59: "sys_execve/ptregs",
Expand Down
4 changes: 2 additions & 2 deletions scripts/extract_syscalls.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ def write_without_includes(f, res):
args = parser.parse_args()

output = open(args.output, "w+")
output.write("#\n#\n# AUTOGENERATED, DO NOT EDIT\n#\n")
output.write(f"# From version: {args.linux_version}\n#\n\n")
output.write("#\n#\n# AUTOGENERATED, DO NOT EDIT\n#\n\n")
output.write(f'LINUX_KERNEL_VERSION = "{args.linux_version}"\n\n')

for arch, path in ARCH_TABLES:
url = BASE_URL.format(path, args.linux_version)
Expand Down
47 changes: 46 additions & 1 deletion tests/native/test_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
from manticore.native import Manticore
from manticore.platforms import linux, linux_syscalls
from manticore.utils.helpers import pickle_dumps
from manticore.platforms.linux import EnvironmentError, logger as linux_logger, SymbolicFile
from manticore.platforms.linux import (
EnvironmentError,
logger as linux_logger,
SymbolicFile,
Linux,
SLinux,
)


class LinuxTest(unittest.TestCase):
Expand Down Expand Up @@ -417,3 +423,42 @@ def post(state):

m.run()
self.assertTrue(m.context["success"])

def test_implemented_syscall_report(self) -> None:
concrete_syscalls = set(Linux.implemented_syscalls())
symbolic_syscalls = set(SLinux.implemented_syscalls())

# Make sure at least one known syscall implementation appears in both
assert "sys_read" in concrete_syscalls
assert "sys_read" in symbolic_syscalls

# Make sure an unimplemented syscall (taken from linux_syscall_stubs)
# does not appear in our list of concrete syscalls. This could change in
# the future
assert "sys_bpf" not in concrete_syscalls

# Make sure that a concretely implemented syscall does not have a (at
# this time) symbolic equivalent. This could change in the future
assert "sys_tgkill" in concrete_syscalls
assert "sys_tgkill" not in symbolic_syscalls

# This doesn't _need_ to be true, but our design says it should be true
assert symbolic_syscalls.issubset(concrete_syscalls)

def test_unimplemented_syscall_report(self) -> None:
"""This test is the inverse of test_implemented_syscall_report"""
from manticore.platforms.linux_syscalls import amd64

unimplemented_concrete_syscalls = set(Linux.unimplemented_syscalls(amd64))
unimplemented_symbolic_syscalls = set(SLinux.unimplemented_syscalls(set(amd64.values())))

assert "sys_read" not in unimplemented_concrete_syscalls
assert "sys_read" not in unimplemented_symbolic_syscalls

assert "sys_bpf" in unimplemented_concrete_syscalls

assert "sys_tgkill" not in unimplemented_concrete_syscalls
assert "sys_tgkill" in unimplemented_symbolic_syscalls

# This doesn't _need_ to be true, but our design says it should be true
assert unimplemented_concrete_syscalls.issubset(unimplemented_symbolic_syscalls)

0 comments on commit 522f8ba

Please sign in to comment.