Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for proc/self/maps #1639

Merged
merged 11 commits into from
Mar 30, 2020
64 changes: 64 additions & 0 deletions manticore/native/memory.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from abc import ABCMeta, abstractmethod
from weakref import WeakValueDictionary
from dataclasses import dataclass, field
from ..core.smtlib import (
Operators,
ConstraintSet,
Expand Down Expand Up @@ -534,6 +535,32 @@ def _publish(self, *args, **kwargs):
return None


@dataclass
class ProcSelfMapInfo:
start: int
end: int
rwx_perms: str
shared_perms: str = "-"
offset: int = 0
device: str = "00:00"
inode: int = 0
pathname: str = ""

perms: str = field(init=False)

def __post_init__(self):
self.perms = self.rwx_perms.replace(" ", "-") + self.shared_perms
if self.pathname == "stack":
self.pathname = "[" + self.pathname + "]"

def __str__(self):
return f"{self.address} {self.perms:>4s} {self.offset:08x} {self.device} {self.inode:9} {self.pathname}"

@property
def address(self):
return f"{self.start:016x}-{self.end:016x}"


class Memory(object, metaclass=ABCMeta):
"""
The memory manager.
Expand Down Expand Up @@ -814,6 +841,43 @@ def __str__(self):
]
)

def proc_self_mappings(self):
"""
Returns a sorted list of all the mappings for this memory for /proc/self/maps.
sschriner marked this conversation as resolved.
Show resolved Hide resolved
Device, inode, and private/shared permissions are unsupported.
Stack is the only memory section supported in the memory map (heap, vdso, etc.)
are unsupported.
Pathname is substituted by filename

:return: a list of mappings.
:rtype: list
"""
result = []
# TODO: Device, inode, and private/shared permissions are unsupported
# TODO: Add complete paths
for m in self.maps:
if isinstance(m, AnonMap):
if m.name is not None:
result.append(
ProcSelfMapInfo(m.start, m.end, rwx_perms=m.perms, pathname=m.name)
)
else:
result.append(ProcSelfMapInfo(m.start, m.end, rwx_perms=m.perms))
elif isinstance(m, FileMap):
result.append(
ProcSelfMapInfo(
m.start, m.end, rwx_perms=m.perms, offset=m._offset, pathname=m._filename
)
)
else:
result.append(ProcSelfMapInfo(m.start, m.end, rwx_perms=m.perms, pathname=m.name))
return sorted(result, key=lambda m: m.start)

@property
def __proc_self__(self):
self.proc_self_mappings()
return "\n".join([f"{m}" for m in self.proc_self_mappings()])

def _maps_in_range(self, start, end):
"""
Generates the list of maps that overlaps with the range [start:end]
Expand Down
14 changes: 14 additions & 0 deletions manticore/platforms/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import struct
import time
import resource
import tempfile
from typing import Union, List, TypeVar, cast

import io
Expand Down Expand Up @@ -152,6 +153,17 @@ def sync(self):
return


class ProcSelfMaps(File):
def __init__(self, flags, linux):
self.file = tempfile.NamedTemporaryFile(mode="w", delete=False)
self.file.write(linux.current.memory.__proc_self__)
self.file.close()
mode = mode_from_flags(flags)
if mode != "rb":
raise EnvironmentError("/proc/self/maps is only supported in read only mode")
self.file = open(self.file.name, mode)


class Directory(File):
def __init__(self, path, flags):
assert os.path.isdir(path)
Expand Down Expand Up @@ -1531,6 +1543,8 @@ def _sys_open_get_file(self, filename, flags):
if os.path.abspath(filename).startswith("/proc/self"):
if filename == "/proc/self/exe":
filename = os.path.abspath(self.program)
elif filename == "/proc/self/maps":
return ProcSelfMaps(flags, self)
else:
raise EnvironmentError("/proc/self is largely unsupported")

Expand Down
Binary file added tests/native/binaries/hello_world
Binary file not shown.
37 changes: 37 additions & 0 deletions tests/native/test_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
import os
import shutil
import tempfile
import re


from manticore.native.cpu.abstractcpu import ConcretizeRegister
from manticore.core.smtlib.solver import Z3Solver
from manticore.core.smtlib import BitVecVariable, issymbolic
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


class LinuxTest(unittest.TestCase):
Expand Down Expand Up @@ -70,6 +73,40 @@ def test_load_maps(self):
self.assertEqual(first_map_name, "basic_linux_amd64")
self.assertEqual(second_map_name, "basic_linux_amd64")

def test_load_proc_self_maps(self):
proc_maps = self.linux.current.memory.proc_self_mappings()

# check that proc self raises error when not being read created as read only
maps = self.linux.current.push_bytes("/proc/self/maps\x00")
self.assertRaises(EnvironmentError, self.linux.sys_open, maps, os.O_RDWR, None)
self.assertRaises(EnvironmentError, self.linux.sys_open, maps, os.O_WRONLY, None)

# addresses should be in ascending order
for i in range(1, len(proc_maps)):
self.assertLess(proc_maps[i - 1].start, proc_maps[i].start)
self.assertLess(proc_maps[i - 1].end, proc_maps[i].end)

for m in proc_maps:
# check all properties are initialized
self.assertNotEqual(m.start, None)
self.assertNotEqual(m.end, None)
self.assertNotEqual(m.perms, None)
self.assertNotEqual(m.offset, None)
self.assertNotEqual(m.device, None)
self.assertNotEqual(m.inode, None)
self.assertNotEqual(m.pathname, None)

# check that address and perms properties are working and properly formatted
self.assertNotEqual(re.fullmatch(r"[0-9a-f]{16}\-[0-9a-f]{16}", m.address), None)
self.assertNotEqual(re.fullmatch(r"[r-][w-][x-][sp-]", m.perms), None)
self.assertNotEqual(
re.fullmatch(
r"[0-9a-f]{16}-[0-9a-f]{16} [r-][w-][x-][sp-] [0-9a-f]{8} [0-9a-f]{2}:[0-9a-f]{2} (?=.{9})\ *\d+ [^\n]*",
str(m),
),
None,
)

def test_aarch64_syscall_write(self):
nr_write = 64

Expand Down
15 changes: 15 additions & 0 deletions tests/native/test_rust.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import unittest

import os

from manticore.native import Manticore


class RustTest(unittest.TestCase):
BIN_PATH = os.path.join(os.path.dirname(__file__), "binaries", "hello_world")

def setUp(self):
self.m = Manticore.linux(self.BIN_PATH)

def test_hello_world(self):
self.m.run()