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
58 changes: 58 additions & 0 deletions manticore/native/memory.py
Expand Up @@ -814,6 +814,64 @@ 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

:return: a list of mappings.
:rtype: list
"""
result = []
# TODO: Device inode and private/shared permissions are unsupported
device = "00:00"
inode = 0
private_shared_perms = "-"
for m in self.maps:
if isinstance(m, AnonMap):
if m.name is not None:
result.append(
(
m.start,
m.end,
m.perms + private_shared_perms,
0,
device,
inode,
"[" + m.name + "]",
)
)
ekilmer marked this conversation as resolved.
Show resolved Hide resolved
else:
result.append(
(m.start, m.end, m.perms + private_shared_perms, 0, device, inode, "")
)
elif isinstance(m, FileMap):
result.append(
(
m.start,
m.end,
m.perms + private_shared_perms,
m._offset,
device,
inode,
m._filename,
)
)
else:
result.append(
(m.start, m.end, m.perms + private_shared_perms, 0, device, inode, m.name)
)

return sorted(result)

@property
def __proc_self__(self):
return "\n".join(
[
f'{start:016x}-{end:016x} {p.replace(" ", "-"):>4s} {offset:08x} {device} {inode:9} {name or ""}'
for start, end, p, offset, device, inode, name 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
46 changes: 46 additions & 0 deletions manticore/platforms/linux.py
Expand Up @@ -152,6 +152,50 @@ def sync(self):
return


class ProcSelfMaps(File):
def __init__(self, flags, linux):
self.filename = "_proc_self_maps_"
sschriner marked this conversation as resolved.
Show resolved Hide resolved
with open(self.filename, "w") as mapsFile:
print(linux.current.memory.__proc_self__, file=mapsFile)
sschriner marked this conversation as resolved.
Show resolved Hide resolved
mode = mode_from_flags(flags)
if mode != "rb":
raise EnvironmentError("/proc/self/maps is only supported in read only mode")
self.file = open(self.filename, mode)

def __getstate__(self):
state = {"name": self.name, "mode": self.mode}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For classes that have a superclass, it's generally best to start off the __getstate__ method with state = super().__getstate__() and then update state from there

state["pos"] = None if self.closed else self.tell()
return state

def __setstate__(self, state):
name = state["name"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As with __getstate__, __setstate__ should typically start or end with super().__setstate__(state) as well. Looks like most of this method is duplicated from File.__setstate__, so you can probably cut out a lot of it, right? Unless there's some other functionality I'm missing.

Copy link
Contributor Author

@sschriner sschriner Mar 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it's essentially the same. Originally I had the .name property returning '/proc/self/maps', in which case I would've needed to modify __getstate__ and __setstate__, but I realized that probably wasn't necessary. It's probably best to just remove these and rely on the superclass.

mode = state["mode"]
closed = state["pos"] is None
pos = state["pos"]
try:
self.file = open(name, mode)
if closed:
self.file.close()
except IOError:
# If the file can't be opened anymore (should not typically happen)
self.file = None
if pos is not None:
self.seek(pos)

def stat(self):
sschriner marked this conversation as resolved.
Show resolved Hide resolved
try:
return os.fstat(self.fileno())
except OSError as e:
return -e.errno

def ioctl(self, request, argp):
try:
return fcntl.fcntl(self, request, argp)
except OSError as e:
logger.error(f"Invalid Fcntl request: {request}")
return -e.errno


class Directory(File):
def __init__(self, path, flags):
assert os.path.isdir(path)
Expand Down Expand Up @@ -1531,6 +1575,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