System calls addition - sys_getrandom and sys_openat (#640)
* version argument in manticore * Fix 591 - Exception Handler * Issue 597 - Remove the unused policy argument * getrandom system call - Initial commit * openat system call initial commit * getrandom system call fixes * openat and getrandom system call * Update sys_getrandom * Make BadFd more generic as FdError * Resolve mode inside File * Support opening directory with open(2) * Fix fd leak * add sys_openat tests
This commit is contained in:
committed by
Yan Ivnitskiy
parent
843a4f399e
commit
fca3179dd0
@@ -30,9 +30,14 @@ class RestartSyscall(Exception):
|
||||
class Deadlock(Exception):
|
||||
pass
|
||||
|
||||
class BadFd(Exception):
|
||||
class EnvironmentError(RuntimeError):
|
||||
pass
|
||||
|
||||
class FdError(Exception):
|
||||
def __init__(self, message='', err=errno.EBADF):
|
||||
self.err = err
|
||||
super(FdError, self).__init__(message)
|
||||
|
||||
def perms_from_elf(elf_flags):
|
||||
return [' ', ' x', ' w ', ' wx', 'r ', 'r x', 'rw ', 'rwx'][elf_flags&7]
|
||||
|
||||
@@ -44,10 +49,11 @@ def mode_from_flags(file_flags):
|
||||
|
||||
|
||||
class File(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
def __init__(self, path, flags):
|
||||
# TODO: assert file is seekable otherwise we should save what was
|
||||
# read/write to the state
|
||||
self.file = file(*args,**kwargs)
|
||||
mode = mode_from_flags(flags)
|
||||
self.file = file(path, mode)
|
||||
|
||||
def __getstate__(self):
|
||||
state = {}
|
||||
@@ -111,6 +117,52 @@ class File(object):
|
||||
'''
|
||||
return
|
||||
|
||||
class Directory(File):
|
||||
def __init__(self, path, flags):
|
||||
assert(os.path.isdir(path))
|
||||
|
||||
self.fd = os.open(path, flags)
|
||||
self.path = path
|
||||
self.flags = flags
|
||||
|
||||
def __getstate__(self):
|
||||
state = {}
|
||||
state['path'] = self.path
|
||||
state['flags'] = self.flags
|
||||
return state
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.path = state['path']
|
||||
self.flags = state['flags']
|
||||
self.fd = os.open(self.path, self.flags)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.path
|
||||
|
||||
@property
|
||||
def mode(self):
|
||||
return mode_from_flags(self.flags)
|
||||
|
||||
def tell(self, *args):
|
||||
return 0
|
||||
|
||||
def seek(self, *args):
|
||||
return 0
|
||||
|
||||
def write(self, buf):
|
||||
raise FdError("Is a directory", errno.EBADF)
|
||||
|
||||
def read(self, *args):
|
||||
raise FdError("Is a directory", errno.EISDIR)
|
||||
|
||||
def close(self, *args):
|
||||
return os.close(self.fd)
|
||||
|
||||
def fileno(self, *args):
|
||||
return self.fd
|
||||
|
||||
|
||||
class SymbolicFile(File):
|
||||
'''
|
||||
Represents a symbolic file.
|
||||
@@ -290,10 +342,10 @@ class Socket(object):
|
||||
return len(buf)
|
||||
|
||||
def sync(self):
|
||||
raise BadFd("Invalid sync() operation on Socket")
|
||||
raise FdError("Invalid sync() operation on Socket", errno.EINVAL)
|
||||
|
||||
def seek(self, *args):
|
||||
raise BadFd("Invalid lseek() operation on Socket")
|
||||
raise FdError("Invalid lseek() operation on Socket", errno.EINVAL)
|
||||
|
||||
class Linux(Platform):
|
||||
'''
|
||||
@@ -303,6 +355,7 @@ class Linux(Platform):
|
||||
|
||||
# from /usr/include/asm-generic/resource.h
|
||||
RLIMIT_NOFILE = 7 #/* max number of open files */
|
||||
FCNTL_FDCWD = -100 #/* Special value used to indicate openat should use the cwd */
|
||||
|
||||
def __init__(self, program, argv=None, envp=None, disasm='capstone', **kwargs):
|
||||
'''
|
||||
@@ -963,6 +1016,17 @@ class Linux(Platform):
|
||||
'AT_EXECFN' : at_execfn, # Filename of executable.
|
||||
}
|
||||
|
||||
def _to_signed_dword(self, dword):
|
||||
arch_width = self.current.address_bit_size
|
||||
if arch_width == 32:
|
||||
sdword = ctypes.c_int32(dword).value
|
||||
elif arch_width == 64:
|
||||
sdword = ctypes.c_int64(dword).value
|
||||
else:
|
||||
raise EnvironmentError("Corrupted internal CPU state (arch width is {})".format(arch_width))
|
||||
return sdword
|
||||
|
||||
|
||||
def _open(self, f):
|
||||
'''
|
||||
Adds a file descriptor to the current file descriptor list
|
||||
@@ -986,7 +1050,11 @@ class Linux(Platform):
|
||||
:param fd: the file descriptor to close.
|
||||
:return: C{0} on success.
|
||||
'''
|
||||
self.files[fd] = None
|
||||
try:
|
||||
self.files[fd].close()
|
||||
self.files[fd] = None
|
||||
except IndexError:
|
||||
raise FdError("Bad file descriptor ({})".format(fd))
|
||||
|
||||
def _dup(self, fd):
|
||||
'''
|
||||
@@ -1006,7 +1074,7 @@ class Linux(Platform):
|
||||
|
||||
def _get_fd(self, fd):
|
||||
if not self._is_fd_open(fd):
|
||||
raise BadFd()
|
||||
raise FdError
|
||||
else:
|
||||
return self.files[fd]
|
||||
|
||||
@@ -1094,17 +1162,13 @@ class Linux(Platform):
|
||||
:return: 0 (Success), or EBADF (fd is not a valid file descriptor or is not open)
|
||||
|
||||
'''
|
||||
if self.current.address_bit_size == 32:
|
||||
signed_offset = ctypes.c_int32(offset).value
|
||||
else:
|
||||
signed_offset = ctypes.c_int64(offset).value
|
||||
|
||||
signed_offset = self._to_signed_dword(offset)
|
||||
try:
|
||||
self._get_fd(fd).seek(signed_offset, whence)
|
||||
except BadFd:
|
||||
except FdError as e:
|
||||
logger.info(("LSEEK: Not valid file descriptor on lseek."
|
||||
"Fd not seekable. Returning EBADF"))
|
||||
return -errno.EBADF
|
||||
return -e.err
|
||||
|
||||
return 0
|
||||
|
||||
@@ -1119,10 +1183,10 @@ class Linux(Platform):
|
||||
try:
|
||||
# Read the data and put in tin memory
|
||||
data = self._get_fd(fd).read(count)
|
||||
except BadFd:
|
||||
except FdError as e:
|
||||
logger.info(("READ: Not valid file descriptor on read."
|
||||
" Returning EBADF"))
|
||||
return -errno.EBADF
|
||||
return -e.err
|
||||
self.syscall_trace.append(("_read", fd, data))
|
||||
self.current.write_bytes(buf, data)
|
||||
|
||||
@@ -1146,9 +1210,9 @@ class Linux(Platform):
|
||||
if count != 0:
|
||||
try:
|
||||
write_fd = self._get_fd(fd)
|
||||
except BadFd:
|
||||
logger.error("WRITE: Not valid file descriptor. Returning EBADFD %d", fd)
|
||||
return -errno.EBADF
|
||||
except FdError as e:
|
||||
logger.error("WRITE: Not valid file descriptor (%d). Returning -%d", fd, e.err)
|
||||
return -e.err
|
||||
|
||||
# TODO check count bytes from buf
|
||||
if buf not in cpu.memory or buf + count not in cpu.memory:
|
||||
@@ -1269,7 +1333,18 @@ class Linux(Platform):
|
||||
return -errno.EINVAL
|
||||
|
||||
def _sys_open_get_file(self, filename, flags):
|
||||
f = File(filename, mode_from_flags(flags))
|
||||
# TODO(yan): Remove this special case
|
||||
if os.path.abspath(filename).startswith('/proc/self'):
|
||||
if filename == '/proc/self/exe':
|
||||
filename = os.path.abspath(self.program)
|
||||
else:
|
||||
raise EnvironmentError("/proc/self is largely unsupported")
|
||||
|
||||
if os.path.isdir(filename):
|
||||
f = Directory(filename, flags)
|
||||
else:
|
||||
f = File(filename, flags)
|
||||
|
||||
return f
|
||||
|
||||
def sys_open(self, buf, flags, mode):
|
||||
@@ -1280,24 +1355,56 @@ class Linux(Platform):
|
||||
'''
|
||||
filename = self.current.read_string(buf)
|
||||
try:
|
||||
if os.path.abspath(filename).startswith('/proc/self'):
|
||||
if filename == '/proc/self/exe':
|
||||
filename = os.path.abspath(self.program)
|
||||
else:
|
||||
logger.info("FIXME!")
|
||||
|
||||
f = self._sys_open_get_file(filename, flags)
|
||||
logger.debug("Opening file %s for real fd %d",
|
||||
filename, f.fileno())
|
||||
except IOError as e:
|
||||
logger.info("Could not open file %s. Reason: %s", filename, str(e))
|
||||
if e.errno is not None:
|
||||
return -e.errno
|
||||
else:
|
||||
return -errno.EINVAL
|
||||
return -e.errno if e.errno is not None else -errno.EINVAL
|
||||
|
||||
return self._open(f)
|
||||
|
||||
def sys_openat(self, dirfd, buf, flags, mode):
|
||||
'''
|
||||
Openat SystemCall - Similar to open system call except dirfd argument
|
||||
when path contained in buf is relative, dirfd is referred to set the relative path
|
||||
Special value AT_FDCWD set for dirfd to set path relative to current directory
|
||||
|
||||
:param dirfd: directory file descriptor to refer in case of relative path at buf
|
||||
:param buf: address of zero-terminated pathname
|
||||
:param flags: file access bits
|
||||
:param mode: file permission mode
|
||||
'''
|
||||
|
||||
filename = self.current.read_string(buf)
|
||||
dirfd = self._to_signed_dword(dirfd)
|
||||
|
||||
if os.path.isabs(filename) or dirfd == self.FCNTL_FDCWD:
|
||||
return self.sys_open(buf, flags, mode)
|
||||
|
||||
try:
|
||||
dir_entry = self._get_fd(dirfd)
|
||||
except FdError as e:
|
||||
logger.info("openat: Not valid file descriptor. Returning EBADF")
|
||||
return -e.err
|
||||
|
||||
if not isinstance(dir_entry, Directory):
|
||||
logger.info("openat: Not directory descriptor. Returning ENOTDIR")
|
||||
return -errno.ENOTDIR
|
||||
|
||||
dir_path = dir_entry.name
|
||||
|
||||
filename = os.path.join(dir_path, filename)
|
||||
try:
|
||||
f = self._sys_open_get_file(filename, flags)
|
||||
logger.debug("Opening file %s for real fd %d", filename, f.fileno())
|
||||
except IOError as e:
|
||||
logger.info("Could not open file %s. Reason: %s", filename, str(e))
|
||||
return -e.errno if e.errno is not None else -errno.EINVAL
|
||||
|
||||
return self._open(f)
|
||||
|
||||
|
||||
def sys_rename(self, oldnamep, newnamep):
|
||||
'''
|
||||
Rename filename `oldnamep` to `newnamep`.
|
||||
@@ -1326,7 +1433,7 @@ class Linux(Platform):
|
||||
self.files[fd].sync()
|
||||
except IndexError:
|
||||
ret = -errno.EBADF
|
||||
except BadFd:
|
||||
except FdError:
|
||||
ret = -errno.EINVAL
|
||||
|
||||
return ret
|
||||
@@ -1392,9 +1499,9 @@ class Linux(Platform):
|
||||
'''
|
||||
try:
|
||||
file = self._get_fd(fd)
|
||||
except BadFd:
|
||||
except FdError as e:
|
||||
logger.info("DUP2: Passed fd is not open. Returning EBADF")
|
||||
return -errno.EBADF
|
||||
return -e.err
|
||||
|
||||
soft_max, hard_max = self._rlimits[self.RLIMIT_NOFILE]
|
||||
if newfd >= soft_max:
|
||||
@@ -1402,7 +1509,7 @@ class Linux(Platform):
|
||||
return -errno.EBADF
|
||||
|
||||
if self._is_fd_open(newfd):
|
||||
self.sys_close(newfd)
|
||||
self._close(newfd)
|
||||
|
||||
if newfd >= len(self.files):
|
||||
self.files.extend([None]*(newfd+1-len(self.files)))
|
||||
@@ -1418,8 +1525,10 @@ class Linux(Platform):
|
||||
:param fd: the file descriptor to close.
|
||||
:return: C{0} on success.
|
||||
'''
|
||||
if fd > 0 :
|
||||
if self._is_fd_open(fd):
|
||||
self._close(fd)
|
||||
else:
|
||||
return -errno.EBADF
|
||||
logger.debug('sys_close(%d)', fd)
|
||||
return 0
|
||||
|
||||
@@ -1494,12 +1603,12 @@ class Linux(Platform):
|
||||
address = None
|
||||
|
||||
cpu = self.current
|
||||
if flags & 0x10 != 0:
|
||||
if flags & 0x10:
|
||||
cpu.memory.munmap(address,size)
|
||||
|
||||
perms = perms_from_protflags(prot)
|
||||
|
||||
if flags & 0x20 != 0:
|
||||
if flags & 0x20:
|
||||
result = cpu.memory.mmap(address, size, perms)
|
||||
elif fd == 0:
|
||||
assert offset == 0
|
||||
@@ -1625,9 +1734,9 @@ class Linux(Platform):
|
||||
total = 0
|
||||
try:
|
||||
write_fd = self._get_fd(fd)
|
||||
except BadFd:
|
||||
except FdError as e:
|
||||
logger.error("writev: Not a valid file descriptor ({})".format(fd))
|
||||
return -errno.EBADF
|
||||
return -e.err
|
||||
|
||||
for i in xrange(0, count):
|
||||
buf = cpu.read_int(iov + i * sizeof_iovec, ptrsize)
|
||||
@@ -1816,6 +1925,38 @@ class Linux(Platform):
|
||||
|
||||
return count
|
||||
|
||||
def sys_getrandom(self, buf, size, flags):
|
||||
'''
|
||||
The getrandom system call fills the buffer with random bytes of buflen.
|
||||
The source of random (/dev/random or /dev/urandom) is decided based on
|
||||
the flags value.
|
||||
|
||||
Manticore's implementation simply fills a buffer with zeroes -- chosing
|
||||
determinism over true randomness.
|
||||
|
||||
:param buf: address of buffer to be filled with random bytes
|
||||
:param size: number of random bytes
|
||||
:param flags: source of random (/dev/random or /dev/urandom)
|
||||
:return: number of bytes copied to buf
|
||||
'''
|
||||
|
||||
GRND_NONBLOCK = 0x0001
|
||||
GRND_RANDOM = 0x0002
|
||||
|
||||
if size == 0:
|
||||
return 0
|
||||
|
||||
if buf not in self.current.memory:
|
||||
logger.info("getrandom: Provided an invalid address. Returning EFAULT")
|
||||
return -errno.EFAULT
|
||||
|
||||
if flags & ~(GRND_NONBLOCK|GRND_RANDOM):
|
||||
return -errno.EINVAL
|
||||
|
||||
self.current.write_bytes(buf, '\x00' * size)
|
||||
|
||||
return size
|
||||
|
||||
#Distpatchers...
|
||||
def syscall(self):
|
||||
'''
|
||||
@@ -2011,9 +2152,9 @@ class Linux(Platform):
|
||||
|
||||
try:
|
||||
stat = self._get_fd(fd).stat()
|
||||
except BadFd:
|
||||
logger.info("Calling fstat with invalid fd, returning EBADF")
|
||||
return -errno.EBADF
|
||||
except FdError as e:
|
||||
logger.info("Calling fstat with invalid fd")
|
||||
return -e.err
|
||||
|
||||
def add(width, val):
|
||||
fformat = {2:'H', 4:'L', 8:'Q'}[width]
|
||||
@@ -2056,9 +2197,9 @@ class Linux(Platform):
|
||||
|
||||
try:
|
||||
stat = self._get_fd(fd).stat()
|
||||
except BadFd:
|
||||
except FdError as e:
|
||||
logger.info("Calling fstat with invalid fd, returning EBADF")
|
||||
return -errno.EBADF
|
||||
return -e.err
|
||||
|
||||
def add(width, val):
|
||||
fformat = {2:'H', 4:'L', 8:'Q'}[width]
|
||||
@@ -2099,9 +2240,9 @@ class Linux(Platform):
|
||||
|
||||
try:
|
||||
stat = self._get_fd(fd).stat()
|
||||
except BadFd:
|
||||
except FdError as e:
|
||||
logger.info("Calling fstat with invalid fd, returning EBADF")
|
||||
return -errno.EBADF
|
||||
return -e.err
|
||||
|
||||
def add(width, val):
|
||||
fformat = {2:'H', 4:'L', 8:'Q'}[width]
|
||||
@@ -2273,7 +2414,7 @@ class SLinux(Linux):
|
||||
def _sys_open_get_file(self, filename, flags):
|
||||
if filename in self.symbolic_files:
|
||||
logger.debug("%s file is considered symbolic", filename)
|
||||
f = SymbolicFile(self.constraints, filename, mode_from_flags(flags))
|
||||
f = SymbolicFile(self.constraints, filename, flags)
|
||||
else:
|
||||
f = super(SLinux, self)._sys_open_get_file(filename, flags)
|
||||
|
||||
@@ -2393,6 +2534,54 @@ class SLinux(Linux):
|
||||
|
||||
return rv
|
||||
|
||||
def sys_openat(self, dirfd, buf, flags, mode):
|
||||
'''
|
||||
A version of openat that includes a symbolic path and symnbolic directory file descriptor
|
||||
|
||||
:param dirfd: directory file descriptor
|
||||
:param buf: address of zero-terminated pathname
|
||||
:param flags: file access bits
|
||||
:param mode: file permission mode
|
||||
'''
|
||||
|
||||
if issymbolic(dirfd):
|
||||
logger.debug("Ask to read from a symbolic directory file descriptor!!")
|
||||
# Constrain to a valid fd and one past the end of fds
|
||||
self.constraints.add(dirfd >= 0)
|
||||
self.constraints.add(dirfd <= len(self.files))
|
||||
raise ConcretizeArgument(self, 0)
|
||||
|
||||
if issymbolic(buf):
|
||||
logger.debug("Ask to read to a symbolic buffer")
|
||||
raise ConcretizeArgument(self, 1)
|
||||
|
||||
return super(SLinux, self).sys_openat(dirfd, buf, flags, mode)
|
||||
|
||||
def sys_getrandom(self, buf, size, flags):
|
||||
'''
|
||||
The getrandom system call fills the buffer with random bytes of buflen.
|
||||
The source of random (/dev/random or /dev/urandom) is decided based on the flags value.
|
||||
|
||||
:param buf: address of buffer to be filled with random bytes
|
||||
:param size: number of random bytes
|
||||
:param flags: source of random (/dev/random or /dev/urandom)
|
||||
:return: number of bytes copied to buf
|
||||
'''
|
||||
|
||||
if issymbolic(buf):
|
||||
logger.debug("sys_getrandom: Asked to generate random to a symbolic buffer address")
|
||||
raise ConcretizeArgument(self, 0)
|
||||
|
||||
if issymbolic(size):
|
||||
logger.debug("sys_getrandom: Asked to generate random of symbolic number of bytes")
|
||||
raise ConcretizeArgument(self, 1)
|
||||
|
||||
if issymbolic(flags):
|
||||
logger.debug("sys_getrandom: Passed symbolic flags")
|
||||
raise ConcretizeArgument(self, 2)
|
||||
|
||||
return super(SLinux, self).sys_getrandom(buf, size, flags)
|
||||
|
||||
def generate_workspace_files(self):
|
||||
def solve_to_fd(data, fd):
|
||||
try:
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from manticore.platforms import linux, linux_syscalls
|
||||
from manticore.core.smtlib import *
|
||||
from manticore.core.smtlib import *
|
||||
from manticore.core.cpu.abstractcpu import ConcretizeRegister
|
||||
|
||||
|
||||
class LinuxTest(unittest.TestCase):
|
||||
@@ -64,23 +69,23 @@ class LinuxTest(unittest.TestCase):
|
||||
nr_fstat64 = 197
|
||||
|
||||
# Create a minimal state
|
||||
model = self.symbolic_linux
|
||||
model.current.memory.mmap(0x1000, 0x1000, 'rw ')
|
||||
model.current.SP = 0x2000-4
|
||||
platform = self.symbolic_linux
|
||||
platform.current.memory.mmap(0x1000, 0x1000, 'rw ')
|
||||
platform.current.SP = 0x2000-4
|
||||
|
||||
# open a file
|
||||
filename = model.current.push_bytes('/bin/true\x00')
|
||||
fd = model.sys_open(filename, os.O_RDONLY, 0600)
|
||||
filename = platform.current.push_bytes('/bin/true\x00')
|
||||
fd = platform.sys_open(filename, os.O_RDONLY, 0600)
|
||||
|
||||
stat = model.current.SP - 0x100
|
||||
model.current.R0 = fd
|
||||
model.current.R1 = stat
|
||||
model.current.R7 = nr_fstat64
|
||||
stat = platform.current.SP - 0x100
|
||||
platform.current.R0 = fd
|
||||
platform.current.R1 = stat
|
||||
platform.current.R7 = nr_fstat64
|
||||
self.assertEquals(linux_syscalls.armv7[nr_fstat64], 'sys_fstat64')
|
||||
|
||||
model.syscall()
|
||||
platform.syscall()
|
||||
|
||||
print ''.join(model.current.read_bytes(stat, 100)).encode('hex')
|
||||
print ''.join(platform.current.read_bytes(stat, 100)).encode('hex')
|
||||
|
||||
def test_linux_workspace_files(self):
|
||||
files = self.symbolic_linux.generate_workspace_files()
|
||||
@@ -102,29 +107,80 @@ class LinuxTest(unittest.TestCase):
|
||||
self.nevents += 1
|
||||
|
||||
# Create a minimal state
|
||||
model = self.symbolic_linux
|
||||
model.current.memory.mmap(0x1000, 0x1000, 'rw ')
|
||||
model.current.SP = 0x2000-4
|
||||
model.current.memory.mmap(0x2000, 0x2000, 'rwx')
|
||||
model.current.PC = 0x2000
|
||||
model.current.write_int(model.current.PC, 0x050f)
|
||||
platform = self.symbolic_linux
|
||||
platform.current.memory.mmap(0x1000, 0x1000, 'rw ')
|
||||
platform.current.SP = 0x2000-4
|
||||
platform.current.memory.mmap(0x2000, 0x2000, 'rwx')
|
||||
platform.current.PC = 0x2000
|
||||
platform.current.write_int(platform.current.PC, 0x050f)
|
||||
|
||||
r = Receiver()
|
||||
model.current.subscribe('will_execute_instruction', r.will_exec)
|
||||
model.current.subscribe('did_execute_instruction', r.did_exec)
|
||||
platform.current.subscribe('will_execute_instruction', r.will_exec)
|
||||
platform.current.subscribe('did_execute_instruction', r.did_exec)
|
||||
|
||||
filename = model.current.push_bytes('/bin/true\x00')
|
||||
fd = model.sys_open(filename, os.O_RDONLY, 0600)
|
||||
filename = platform.current.push_bytes('/bin/true\x00')
|
||||
fd = platform.sys_open(filename, os.O_RDONLY, 0600)
|
||||
|
||||
stat = model.current.SP - 0x100
|
||||
model.current.R0 = fd
|
||||
model.current.R1 = stat
|
||||
model.current.R7 = nr_fstat64
|
||||
stat = platform.current.SP - 0x100
|
||||
platform.current.R0 = fd
|
||||
platform.current.R1 = stat
|
||||
platform.current.R7 = nr_fstat64
|
||||
self.assertEquals(linux_syscalls.armv7[nr_fstat64], 'sys_fstat64')
|
||||
|
||||
pre_icount = model.current.icount
|
||||
model.execute()
|
||||
post_icount = model.current.icount
|
||||
pre_icount = platform.current.icount
|
||||
platform.execute()
|
||||
post_icount = platform.current.icount
|
||||
|
||||
self.assertEquals(pre_icount+1, post_icount)
|
||||
self.assertEquals(r.nevents, 2)
|
||||
|
||||
def _create_openat_state(self):
|
||||
nr_openat = 322
|
||||
|
||||
# Create a minimal state
|
||||
platform = self.symbolic_linux
|
||||
platform.current.memory.mmap(0x1000, 0x1000, 'rw ')
|
||||
platform.current.SP = 0x2000-4
|
||||
|
||||
dir_path = tempfile.mkdtemp()
|
||||
file_name = "file"
|
||||
file_path = os.path.join(dir_path, file_name)
|
||||
with open(file_path, 'w') as f:
|
||||
f.write('test')
|
||||
|
||||
# open a file + directory
|
||||
dirname = platform.current.push_bytes(dir_path+'\x00')
|
||||
dirfd = platform.sys_open(dirname, os.O_RDONLY, 0700)
|
||||
filename = platform.current.push_bytes(file_name+'\x00')
|
||||
|
||||
stat = platform.current.SP - 0x100
|
||||
platform.current.R0 = dirfd
|
||||
platform.current.R1 = filename
|
||||
platform.current.R2 = os.O_RDONLY
|
||||
platform.current.R3 = 0700
|
||||
platform.current.R7 = nr_openat
|
||||
self.assertEquals(linux_syscalls.armv7[nr_openat], 'sys_openat')
|
||||
|
||||
return platform
|
||||
|
||||
def test_syscall_openat_concrete(self):
|
||||
platform = self._create_openat_state()
|
||||
|
||||
platform.syscall()
|
||||
|
||||
self.assertGreater(platform.current.R0, 2)
|
||||
|
||||
def test_syscall_openat_symbolic(self):
|
||||
platform = self._create_openat_state()
|
||||
|
||||
platform.current.R0 = BitVecVariable(32, 'fd')
|
||||
|
||||
with self.assertRaises(ConcretizeRegister) as cm:
|
||||
platform.syscall()
|
||||
|
||||
e = cm.exception
|
||||
|
||||
_min, _max = solver.minmax(platform.constraints, e.cpu.read_register(e.reg_name))
|
||||
self.assertLess(_min, len(platform.files))
|
||||
self.assertGreater(_max, len(platform.files)-1)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user