Various bug fixes. (#530)

* Fixes #503

* Fixes #515

* Fixes 272. Concretize data written to a fd.

* Revert debug hack and fix overly broad exception

* Update did/will_exec_instr semantics

* Clean up imports

* Update logging and improve sys_open return

* Update variable names+doc for consistency
This commit is contained in:
Yan 2017-10-25 10:53:11 -04:00 committed by GitHub
parent 08e8c3b7d8
commit baf2d769a6
5 changed files with 123 additions and 32 deletions

View File

@ -756,10 +756,21 @@ class Cpu(Eventful):
for l in self.render_registers():
register_logger.debug(l)
implementation(*insn.operands)
self._icount += 1
#FIXME(yan): In the case the instruction implementation invokes a system call, we would not be able to
# publish the did_execute_instruction event from here, so we capture and attach it to the syscall
# exception for the platform to emit it for us once the syscall has successfully been executed.
def did_exec():
self._icount += 1
self._publish('did_execute_instruction', self._last_pc, self.PC, insn)
try:
implementation(*insn.operands)
except (Interruption, Syscall) as e:
e.on_handled = did_exec
raise e
else:
did_exec()
self._publish('did_execute_instruction', self._last_pc, self.PC, insn)
def emulate(self, insn):
'''

View File

@ -192,7 +192,7 @@ class State(Eventful):
introduce it into the program state.
:param int nbytes: Length of the new buffer
:param str name: (keyword arg only) The name to assign to the buffer
:param str label: (keyword arg only) The label to assign to the buffer
:param bool cstring: (keyword arg only) Whether or not to enforce that the buffer is a cstring
(i.e. no \0 bytes, except for the last byte). (bool)
:param taint: Taint identifier of the new buffer
@ -200,9 +200,9 @@ class State(Eventful):
:return: :class:`~manticore.core.smtlib.expression.Expression` representing the buffer.
'''
name = options.get('label', 'buffer')
label = options.get('label', 'buffer')
taint = options.get('taint', frozenset())
expr = self._constraints.new_array(name=name, index_max=nbytes, taint=taint)
expr = self._constraints.new_array(name=label, index_max=nbytes, taint=taint)
self._input_symbols.append(expr)
if options.get('cstring', False):

View File

@ -18,7 +18,7 @@ from ..core.memory import SMemory32, SMemory64, Memory32, Memory64
from ..core.smtlib import Operators, ConstraintSet, SolverException, solver
from ..core.cpu.arm import *
from ..core.executor import TerminateState
from ..platforms.platform import Platform
from ..platforms.platform import Platform, SyscallNotImplemented
from ..utils.helpers import issymbolic, is_binja_disassembler
from . import linux_syscalls
@ -39,6 +39,9 @@ def perms_from_elf(elf_flags):
def perms_from_protflags(prot_flags):
return [' ', 'r ', ' w ', 'rw ', ' x', 'r x', ' wx', 'rwx'][prot_flags&7]
def mode_from_flags(file_flags):
return {os.O_RDWR: 'r+', os.O_RDONLY: 'r', os.O_WRONLY: 'w'}[file_flags&7]
class File(object):
def __init__(self, *args, **kwargs):
@ -747,7 +750,7 @@ class Linux(Platform):
elf = self.elf
arch = self.arch
addressbitsize = {'x86':32, 'x64':64, 'ARM': 32}[elf.get_machine_arch()]
logger.debug("Loading %s as a %s elf"%(filename, arch))
logger.debug("Loading %s as a %s elf",filename, arch)
assert elf.header.e_type in ['ET_DYN', 'ET_EXEC', 'ET_CORE']
@ -839,8 +842,8 @@ class Linux(Platform):
#cpu.write_bytes(elf_bss, '\x00'*((elf_bss | (align-1))-elf_bss))
logger.debug("Zeroing main elf fractional pages. From %x to %x.", elf_bss, elf_brk)
logger.debug("Main elf bss:%x"%elf_bss)
logger.debug("Main elf brk %x:"%elf_brk)
logger.debug("Main elf bss:%x",elf_bss)
logger.debug("Main elf brk %x:",elf_brk)
#FIXME Need a way to inspect maps and perms so
#we can rollback all to the initial state after zeroing
@ -932,7 +935,7 @@ class Linux(Platform):
try:
cpu.memory[elf_bss:elf_brk] = '\x00'*(elf_brk-elf_bss)
except Exception, e:
logger.debug("Exception zeroing Interpreter fractional pages: %s"%str(e))
logger.debug("Exception zeroing Interpreter fractional pages: %s",str(e))
#TODO #FIXME mprotect as it was before zeroing?
@ -1025,6 +1028,17 @@ class Linux(Platform):
else:
return self.files[fd]
def _transform_write_data(self, data):
'''
Implement in subclass to transform data written by write(2)/writev(2)
Nop by default.
:param list data: Anything being written to a file descriptor
:rtype list
:return: Transformed data
'''
return data
def sys_umask(self, mask):
'''
umask - Set file creation mode mask
@ -1166,6 +1180,7 @@ class Linux(Platform):
raise RestartSyscall()
data = cpu.read_bytes(buf, count)
data = self._transform_write_data(data)
write_fd.write(data)
for line in ''.join([str(x) for x in data]).split('\n'):
@ -1279,8 +1294,8 @@ class Linux(Platform):
else:
return -errno.EINVAL
def _sys_open_get_file(self, filename, flags, mode):
f = File(filename, mode) # TODO (theo) modes, flags
def _sys_open_get_file(self, filename, flags):
f = File(filename, mode_from_flags(flags))
return f
def sys_open(self, buf, flags, mode):
@ -1296,15 +1311,16 @@ class Linux(Platform):
filename = os.path.abspath(self.program)
else:
logger.info("FIXME!")
mode = {os.O_RDWR: 'r+', os.O_RDONLY: 'r', os.O_WRONLY: 'w'}[flags&7]
f = self._sys_open_get_file(filename, flags, mode)
logger.debug("Opening file %s for %s real fd %d",
filename, mode, f.fileno())
# FIXME(theo) generic exception
except Exception as e:
logger.info("Could not open file %s. Reason %s" % (filename, str(e)))
return -1
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 self._open(f)
@ -1650,6 +1666,12 @@ class Linux(Platform):
ptrsize = cpu.address_bit_size
sizeof_iovec = 2 * (ptrsize // 8)
total = 0
try:
write_fd = self._get_fd(fd)
except BadFd:
logger.error("writev: Not a valid file descriptor ({})".format(fd))
return -errno.EBADF
for i in xrange(0, count):
buf = cpu.read_int(iov + i * sizeof_iovec, ptrsize)
size = cpu.read_int(iov + i * sizeof_iovec + (sizeof_iovec // 2), ptrsize)
@ -1657,8 +1679,9 @@ class Linux(Platform):
data = ""
for j in xrange(0,size):
data += Operators.CHR(cpu.read_int(buf + j, 8))
logger.debug("WRITEV(%r, %r, %r) -> <%r> (size:%r)"%(fd, buf, size, data, len(data)))
self.files[fd].write(data)
logger.debug("WRITEV(%r, %r, %r) -> <%r> (size:%r)",fd, buf, size, data, len(data))
data = self._transform_write_data(data)
write_fd.write(data)
self.syscall_trace.append(("_write", fd, data))
total+=size
return total
@ -1874,7 +1897,10 @@ class Linux(Platform):
name = table.get(index, None)
implementation = getattr(self, name)
except (AttributeError, KeyError):
raise Exception("SyscallNotImplemented %d %d"%(self.current.address_bit_size, index))
if name is not None:
raise SyscallNotImplemented(index, name)
else:
raise Exception("Bad syscall index, {}".format(index))
return self._syscall_abi.invoke(implementation)
@ -2029,9 +2055,11 @@ class Linux(Platform):
if self.clocks % 10000 == 0:
self.check_timers()
self.sched()
except (Interruption, Syscall):
except (Interruption, Syscall) as e:
try:
self.syscall()
if hasattr(e, 'on_handled'):
e.on_handled()
except RestartSyscall:
pass
@ -2316,17 +2344,30 @@ class SLinux(Linux):
self.symbolic_files = state['symbolic_files']
super(SLinux, self).__setstate__(state)
def _sys_open_get_file(self, filename, flags, mode):
def _sys_open_get_file(self, filename, flags):
if filename in self.symbolic_files:
logger.debug("%s file is considered symbolic", filename)
assert flags & 7 == os.O_RDWR or flags & 7 == os.O_RDONLY, (
"Symbolic files should be readable?")
f = SymbolicFile(self.constraints, filename, mode)
f = SymbolicFile(self.constraints, filename, mode_from_flags(flags))
else:
f = super(SLinux, self)._sys_open_get_file(filename, flags, mode)
f = super(SLinux, self)._sys_open_get_file(filename, flags)
return f
def _transform_write_data(self, data):
bytes_concretized = 0;
concrete_data = []
for c in data:
if issymbolic(c):
bytes_concretized += 1
c = chr(solver.get_value(self.constraints, c))
concrete_data.append(c)
if bytes_concretized > 0:
logger.debug("Concretized {} written bytes.".format(bytes_concretized))
return super(SLinux, self)._transform_write_data(concrete_data)
#Dispatchers...
def sys_read(self, fd, buf, count):

View File

@ -11,7 +11,9 @@ class SyscallNotImplemented(OSException):
''' Exception raised when you try to call a not implemented
system call. Go to linux.py and add it!
'''
pass
def __init__(self, idx, name):
msg = 'Syscall index "{}" ({}) not implemented.'.format(idx, name)
super(SyscallNotImplemented, self).__init__(msg)
class ConcretizeSyscallArgument(OSException):
def __init__(self, reg_num, message='Concretizing syscall argument', policy='SAMPLED'):
@ -20,7 +22,6 @@ class ConcretizeSyscallArgument(OSException):
self.policy = policy
super(ConcretizeSyscallArgument, self).__init__(message)
class Platform(Eventful):
'''
Base class for all operating system platforms.

View File

@ -90,3 +90,41 @@ class LinuxTest(unittest.TestCase):
self.assertIn('stderr', files)
self.assertIn('net', files)
def test_syscall_events(self):
nr_fstat64 = 197
class Receiver(object):
def __init__(self):
self.nevents = 0
def will_exec(self, pc, i):
self.nevents += 1
def did_exec(self, last_pc, pc, i):
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)
r = Receiver()
model.current.subscribe('will_execute_instruction', r.will_exec)
model.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)
stat = model.current.SP - 0x100
model.current.R0 = fd
model.current.R1 = stat
model.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
self.assertEquals(pre_icount+1, post_icount)
self.assertEquals(r.nevents, 2)