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:
parent
08e8c3b7d8
commit
baf2d769a6
@ -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):
|
||||
'''
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user