From 275a85e08eec9caf1fb9538fd6c074b2a9a0f282 Mon Sep 17 00:00:00 2001 From: Mark Mossberg Date: Wed, 1 Mar 2017 13:47:35 -0500 Subject: [PATCH] Clean CGC artifacts from linux model (#36) * Use errno instead of CGC constants for linux * Rm linux.sys_allocate * Rm linux.sys_deallocate * Rm linux.sys_fdwait * Rm comments * Clean sys_read * Clean sys_write * Fix syscall trace spots * Clean exit_group --- manticore/core/executor.py | 4 +- manticore/models/linux.py | 253 +++---------------------------------- 2 files changed, 22 insertions(+), 235 deletions(-) diff --git a/manticore/core/executor.py b/manticore/core/executor.py index b5b6714..3566470 100644 --- a/manticore/core/executor.py +++ b/manticore/core/executor.py @@ -481,9 +481,9 @@ class Executor(object): receive_size = 0 transmit_size = 0 for sysname, fd, data in state.model.syscall_trace: - if sysname == '_receive': + if sysname in ('_receive', '_read'): receive_size += len(data) - if sysname == '_transmit': + if sysname in ('_transmit', '_write'): transmit_size += len(data) # if we did *not* concretize PC, this will be expression diff --git a/manticore/models/linux.py b/manticore/models/linux.py index 690701c..b10b654 100644 --- a/manticore/models/linux.py +++ b/manticore/models/linux.py @@ -1,5 +1,6 @@ import cgcrandom import weakref +import errno import sys, os, struct from ..utils import qemu from ..utils.helpers import issymbolic @@ -256,16 +257,6 @@ class Linux(object): A simple Linux Operating System Model. This class emulates the most common Linux system calls ''' - CGC_EBADF=1 - CGC_EFAULT=2 - CGC_EINVAL=3 - CGC_ENOMEM=4 - CGC_ENOSYS=5 - CGC_EPIPE=6 - CGC_SSIZE_MAX=2147483647 - CGC_SIZE_MAX=4294967295 - CGC_FD_SETSIZE=32 - ARM_GET_TLS=0xffff0fe0 ARM_CMPXCHG=0xffff0fc0 ARM_MEM_BARRIER=0xffff0fa0 @@ -869,52 +860,6 @@ class Linux(object): def _is_open(self, fd): return fd >= 0 and fd < len(self.files) and self.files[fd] is not None - def sys_allocate(self, cpu, length, isX, addr): - ''' allocate - allocate virtual memory - - The allocate system call creates a new allocation in the virtual address - space of the calling process. The length argument specifies the length of - the allocation in bytes which will be rounded up to the hardware page size. - - The kernel chooses the address at which to create the allocation; the - address of the new allocation is returned in *addr as the result of the call. - - All newly allocated memory is readable and writeable. In addition, the - is_X argument is a boolean that allows newly allocated memory to be marked - as executable (non-zero) or non-executable (zero). - - The allocate function is invoked through system call number 5. - - @param cpu current CPU - @parm length the length of the allocation in bytes - @parm isX boolean that allows newly allocated memory to be marked - as executable - @parm addr the address of the new allocation is returned in *addr - - @return On success, allocate returns zero and a pointer to the allocated area - is returned in *addr. Otherwise, an error code is returned - and *addr is undefined. - EINVAL length is zero. - EINVAL length is too large. - EFAULT addr points to an invalid address. - ENOMEM No memory is available or the process' maximum number of allocations - would have been exceeded. - ''' - #TODO: check 4 bytes from addr - if not cpu.memory.isValid(addr): - logger.info("ALLOCATE: addr points to invalid address. Rerurning EFAULT") - return Linux.CGC_EFAULT - - perms = [ 'rw ', 'rwx'][bool(isX)] - try: - result = cpu.memory.mmap(None, length, perms) - except Exception,e: - logger.info("ALLOCATE exception %s. Returning ENOMEM", str(e)) - return Linux.CGC_ENOMEM - cpu.write_int(addr, result, cpu.address_bit_size) - logger.debug("ALLOCATE(%d, %s, 0x%08x) -> 0x%08x"%(length, perms, addr, result)) - - return 0 def sys_lseek(self, cpu, fd, offset, whence): ''' lseek - reposition read/write file offset @@ -936,11 +881,11 @@ class Linux(object): ''' if not self._is_open(fd): logger.info("LSEEK: Not valid file descriptor on lseek. Returning EBADF") - return Linux.CGC_EBADF + return errno.EBADF if isinstance(self.files[fd], Socket): logger.info("LSEEK: Not valid file descriptor on lseek. Fd not seekable. Returning EBADF") - return Linux.CGC_EBADF + return errno.EBADF # Read the data and put in tin memory self.files[fd].seek(offset) @@ -950,47 +895,32 @@ class Linux(object): return 0 def sys_read(self, cpu, fd, buf, count): - ''' receive - receive bytes from a file descriptor - - The receive system call reads up to count bytes from file descriptor fd to the - buffer pointed to by buf. If count is zero, receive returns 0 and optionally - dets *rx_bytes to zero. - - @param self current CPU. - @param fd a valid file descripor - @param buf a memory buffer - @param count max number of bytes to receive - @param rx_bytes if valid, points to the actual number of bytes received - @result 0 Success - EBADF fd is not a valid file descriptor or is not open - EFAULT buf or rx_bytes points to an invalid address. - ''' data = '' if count != 0: if not self._is_open(fd): - logger.info("RECEIVE: Not valid file descriptor on receive. Returning EBADF") - return Linux.CGC_EBADF + logger.info("READ: Not valid file descriptor on read. Returning EBADF") + return errno.EBADF # TODO check count bytes from buf if not buf in cpu.memory: # or not cpu.memory.isValid(buf+count): - logger.info("RECEIVE: buf points to invalid address. Returning EFAULT") - return Linux.CGC_EFAULT + logger.info("READ: buf points to invalid address. Returning EFAULT") + return errno.EFAULT if isinstance(self.files[fd],Socket) and self.files[fd].is_empty(): return 0 # Read the data and put in tin memory data = self.files[fd].read(count) - self.syscall_trace.append(("_receive", fd, data)) + self.syscall_trace.append(("_read", fd, data)) cpu.write_bytes(buf, data) - logger.debug("RECEIVE(%d, 0x%08x, %d, 0x%08x) -> <%s> (size:%d)"%(fd, buf, count, len(data), repr(data)[:min(count,10)],len(data))) + logger.debug("READ(%d, 0x%08x, %d, 0x%08x) -> <%s> (size:%d)"%(fd, buf, count, len(data), repr(data)[:min(count,10)],len(data))) return len(data) def sys_write(self, cpu, fd, buf, count): - ''' transmit - send bytes through a file descriptor - The transmit system call writes up to count bytes from the buffer pointed - to by buf to the file descriptor fd. If count is zero, transmit returns 0 + ''' write - send bytes through a file descriptor + The write system call writes up to count bytes from the buffer pointed + to by buf to the file descriptor fd. If count is zero, write returns 0 and optionally sets *tx_bytes to zero. @param cpu current CPU @@ -1005,13 +935,13 @@ class Linux(object): if count != 0: if not self._is_open(fd): - logger.error("TRANSMIT: Not valid file descriptor. Returning EBADFD %d", fd) - return Linux.CGC_EBADF + logger.error("WRITE: Not valid file descriptor. Returning EBADFD %d", fd) + return errno.EBADF # TODO check count bytes from buf if buf not in cpu.memory or buf+count not in cpu.memory: - logger.debug("TRANSMIT: buf points to invalid address. Rerurning EFAULT") - return Linux.CGC_EFAULT + logger.debug("WRITE: buf points to invalid address. Rerurning EFAULT") + return errno.EFAULT if fd > 2 and self.files[fd].is_full(): cpu.PC -= cpu.instruction.size @@ -1022,8 +952,8 @@ class Linux(object): self.files[fd].transmit(data) for line in ''.join([str(x) for x in data]).split('\n'): - logger.debug("TRANSMIT(%d, 0x%08x, %d) -> <%.48r>"%(fd, buf, count, line)) - self.syscall_trace.append(("_transmit", fd, data)) + logger.debug("WRITE(%d, 0x%08x, %d) -> <%.48r>"%(fd, buf, count, line)) + self.syscall_trace.append(("_write", fd, data)) self.signal_transmit(fd) return len(data) @@ -1380,129 +1310,12 @@ class Linux(object): procid = self.procs.index(cpu) self.sched() self.running.remove(procid) - #self.procs[procid] = None # TODO(mark) ask felipe why this commented - logger.debug("TERMINATE PROC_%02d %s", procid, error_code) + #self.procs[procid] = None + logger.debug("EXIT_GROUP PROC_%02d %s", procid, error_code) if len(self.running) == 0 : raise ProcessExit(error_code) return error_code - def sys_deallocate(self, cpu, addr, size): - ''' deallocate - remove allocations - The deallocate system call deletes the allocations for the specified - address range, and causes further references to the addresses within the - range to generate invalid memory accesses. The region is also - automatically deallocated when the process is terminated. - - The address addr must be a multiple of the page size. The length parameter - specifies the size of the region to be deallocated in bytes. All pages - containing a part of the indicated range are deallocated, and subsequent - references will terminate the process. It is not an error if the indicated - range does not contain any allocated pages. - - The deallocate function is invoked through system call number 6. - - @param cpu: current CPU - @param addr: the starting address to unmap. - @param size: the size of the portion to unmap. - @return 0 On success - EINVAL addr is not page aligned. - EINVAL length is zero. - EINVAL any part of the region being deallocated is outside the valid - address range of the process. - - @param cpu: current CPU. - @return: C{0} on success. - ''' - logger.debug("DEALLOCATE(0x%08x, %d)"%(addr, size)) - - if addr & 0xfff != 0: - logger.info("DEALLOCATE: addr is not page aligned") - return Linux.CGC_EINVAL - if size == 0 : - logger.info("DEALLOCATE:length is zero") - return Linux.CGC_EINVAL - #unlikely AND WRONG!!! - #if addr > Decree.CGC_SSIZE_MAX or addr+size > Decree.CGC_SSIZE_MAX: - # logger.info("DEALLOCATE: part of the region being deallocated is outside the valid address range of the process") - # return Decree.CGC_EINVAL - - cpu.memory.munmap(addr, size) - return 0 - - def sys_fdwait(self, cpu, nfds, readfds, writefds, timeout, readyfds): - ''' fdwait - wait for file descriptors to become ready - ''' - logger.debug("FDWAIT(%d, 0x%08x, 0x%08x, 0x%08x, 0x%08x)"%(nfds, readfds, writefds, timeout, readyfds)) - - if timeout: - if timeout not in cpu.memory: #todo: size - logger.info("FDWAIT: timeput is pointing to invalid memory. Returning EFAULT") - return Linux.CGC_EFAULT - - if readyfds: - if readyfds not in cpu.memory: - logger.info("FDWAIT: readyfds pointing to invalid memory. Returning EFAULT") - return Linux.CGC_EFAULT - - writefds_wait = set() - writefds_ready = set() - - if writefds: - if writefds not in cpu.memory: - logger.info("FDWAIT: writefds pointing to invalid memory. Returning EFAULT") - return Linux.CGC_EFAULT - bits = cpu.read_int(writefds, (nfds + 7) / 8) - - for fd in range(nfds): - if (bits & 1<> byte) & 0xff, 8) - if writefds: - bits = 0 - for fd in writefds_ready: - bits |= 1<> byte) & 0xff, 8) - - logger.debug("FDWAIT: continuing. Some file is ready Readyfds: %08x", readyfds) - if readyfds: - cpu.write_int(readyfds, n, 32) - return 0 - def sys_ptrace(self, cpu, request, pid, addr, data): logger.debug("sys_ptrace(%016x, %d, %016x, %016x) -> 0", request, pid, addr, data) return 0 @@ -2010,8 +1823,6 @@ class SLinux(Linux): def sys_read(self, cpu, fd, buf, count): - ''' Symbolic version of Decree.sys_receive - ''' if issymbolic(fd): logger.debug("Ask to read from a symbolic file descriptor!!") raise SymbolicSyscallArgument(0) @@ -2147,8 +1958,6 @@ class SLinux(Linux): def sys_write(self, cpu, fd, buf, count): - ''' Symbolic version of Decree.sys_receive - ''' if issymbolic(fd): logger.debug("Ask to write to a symbolic file descriptor!!") raise SymbolicSyscallArgument(0) @@ -2163,28 +1972,6 @@ class SLinux(Linux): return super(SLinux, self).sys_write(cpu, fd, buf, count) - - def sys_allocate(self, cpu, length, isX, address_p): - if issymbolic(length): - logger.debug("Ask to ALLOCATE a symbolic number of bytes ") - raise SymbolicSyscallArgument(0) - if issymbolic(address_p): - logger.debug("Ask to ALLOCATE potentially executable or not executable memory") - raise SymbolicSyscallArgument(1) - if issymbolic(address_p): - logger.debug("Ask to return ALLOCATE result to a symbolic reference ") - raise SymbolicSyscallArgument(2) - return super(SLinux, self).sys_allocate(cpu, length, isX, address_p) - - def sys_deallocate(self, cpu, addr, size): - if issymbolic(addr): - logger.debug("Ask to DEALLOCATE a symbolic pointer?!") - raise SymbolicSyscallArgument(0) - if issymbolic(size): - logger.debug("Ask to DEALLOCATE a symbolic size?!") - raise SymbolicSyscallArgument(1) - return super(SLinux, self).sys_deallocate(cpu, addr, size) - class DecreeEmu(object): RANDOM = 0