Ejemplo n.º 1
0
class Var(simsym.tstruct(val=simsym.SInt)):
    @model.methodwrap(val=simsym.SInt)
    def set(self, val):
        self.val = val

    @model.methodwrap()
    def get(self):
        return self.val
Ejemplo n.º 2
0
class Watermark(simsym.tstruct(val=simsym.SInt)):
    @model.methodwrap(val=simsym.SInt)
    def put(self, val):
        if val > self.val:
            self.val = val

    @model.methodwrap()
    def max(self):
        return self.val
Ejemplo n.º 3
0
class Rename(simsym.tstruct(fname_to_inum=SymDir, inodes=SymIMap)):
    @model.methodwrap(src=SymFilename, dst=SymFilename)
    def rename(self, src, dst):
        if simsym.symnot(self.fname_to_inum.contains(src)):
            #return (-1, errno.ENOENT)
            return -1
        if src == dst:
            return 0
        if self.fname_to_inum.contains(dst):
            self.inodes[self.fname_to_inum[dst]].nlink -= 1
        self.fname_to_inum[dst] = self.fname_to_inum[src]
        del self.fname_to_inum[src]
        return 0
Ejemplo n.º 4
0
class Counter(simsym.tstruct(counter=simsym.SInt)):
    def _declare_assumptions(self, assume):
        super(Counter, self)._declare_assumptions(assume)
        assume(self.counter >= 0)

    @model.methodwrap()
    def inc(self):
        self.counter = self.counter + 1

    @model.methodwrap()
    def dec(self):
        simsym.assume(self.counter > 0)
        self.counter = self.counter - 1

    @model.methodwrap()
    def iszero(self):
        return self.counter == 0
Ejemplo n.º 5
0
class UPipe(simsym.tstruct(elems=SItembag, nitem=simsym.SInt)):
    def _declare_assumptions(self, assume):
        super(UPipe, self)._declare_assumptions(assume)
        assume(self.nitem >= 0)

    @model.methodwrap(elem=simsym.SInt)
    def u_write(self, elem):
        self.elems.add(elem)
        self.nitem = self.nitem + 1

    @model.methodwrap()
    def u_read(self):
        if self.nitem == 0:
            return None
        else:
            e = self.elems.take()
            self.nitem = self.nitem - 1
            return e
Ejemplo n.º 6
0
class SFd(
        simsym.tstruct(domain=simsym.SInt,
                       type=simsym.SInt,
                       prot=simsym.SInt,
                       can_read=simsym.SBool,
                       can_write=simsym.SBool,
                       is_bound=simsym.SBool,
                       is_connected=simsym.SBool,
                       local_addr=SAddr,
                       local_port=SPort,
                       remote_addr=SAddr,
                       remote_port=SPort,
                       send_buffer=SBuffer,
                       recv_buffer=SBuffer)):
    def _declare_assumptions(self, assume):
        super(SFd, self)._declare_assumptions(assume)
        assume(self.domain == AF_INET or self.domain == AF_INET6)
        assume(self.type == SOCK_DGRAM)
        assume(self.prot == 0)
Ejemplo n.º 7
0
def methodwrap(**arg_types):
    """Transform a method into a testable model method.

    This returns a decorator for model class methods to make them
    testable.  The keys of arg_types must correspond to the wrapped
    method's arguments (except 'self'), and its values must give the
    symbolic types of those arguments.  The decorator returns the
    function given to it, but augmented with an arg_struct_type
    attribute that gives the symbolic struct for its arguments.
    """

    # Create symbolic struct type for the arguments
    arg_struct_type = simsym.tstruct(**arg_types)

    def decorator(m):
        m.arg_struct_type = arg_struct_type
        m.is_model_function = True
        return m
    return decorator
Ejemplo n.º 8
0
class Tracker(simsym.tstruct(events=SEventList, forgot=simsym.SBool)):
    def _eq_internal(self, o):
        if type(self) != type(o):
            return NotImplemented
        return simsym.symand([
            self.forgot == o.forgot,
            simsym.symor([self.forgot, self.events == o.events])
        ])

    @model.methodwrap()
    def track(self):
        self.events.append(model.cur_thread())

    @model.methodwrap()
    def report(self):
        if self.forgot:
            return SEventList.var(_len=0)
        return self.events

    @model.methodwrap()
    def forget(self):
        self.forgot = True
Ejemplo n.º 9
0
class SVMA(
        simsym.tstruct(
            anon=simsym.SBool,
            writable=simsym.SBool,
            inum=SInum,
            # This offset is in datavals
            off=SOffset,
            anondata=SDataVal)):
    def _declare_assumptions(self, assume):
        super(SVMA, self)._declare_assumptions(assume)
        assume(self.off >= 0)
        assume(self.off % PAGE_DATAVALS == 0)

    def _eq_internal(self, o):
        if type(self) != type(o):
            return NotImplemented
        return simsym.symand([
            self.anon == o.anon, self.writable == o.writable,
            simsym.symif(
                self.anon, self.anondata == o.anondata,
                simsym.symand([self.off == o.off, self.inum == o.inum]))
        ])
Ejemplo n.º 10
0
class SFd(
        simsym.tstruct(
            ispipe=simsym.SBool,
            pipeid=SPipeId,
            pipewriter=simsym.SBool,
            inum=SInum,
            # This offset is in datavals
            off=SOffset)):
    def _declare_assumptions(self, assume):
        super(SFd, self)._declare_assumptions(assume)
        assume(self.off >= 0)

    def _eq_internal(self, o):
        if type(self) != type(o):
            return NotImplemented
        return simsym.symand([
            self.ispipe == o.ispipe,
            simsym.symif(
                self.ispipe,
                simsym.symand(
                    [self.pipeid == o.pipeid,
                     self.pipewriter == o.pipewriter]),
                simsym.symand([self.inum == o.inum, self.off == o.off]))
        ])
Ejemplo n.º 11
0
SFn = simsym.tuninterpreted("SFn")
SInum = simsym.tuninterpreted("SInum")
SDataVal = simsym.tuninterpreted("SDataVal")
SVa = simsym.tuninterpreted("SVa")
SPipeId = simsym.tuninterpreted("SPipeId")

DATAVAL_BYTES = 4096
PAGE_BYTES = 4096
PAGE_DATAVALS = PAGE_BYTES / DATAVAL_BYTES
assert PAGE_BYTES % DATAVAL_BYTES == 0
DATA_MAX_LEN = 8

SPid = simsym.SBool
SOffset = simsym.tsynonym("SOffset", simsym.SInt)
SData = symtypes.tsmalllist(DATA_MAX_LEN, SDataVal, lenType=SOffset)
SPipe = simsym.tstruct(data=SData)
SPipeMap = symtypes.tmap(SPipeId, SPipe)


class SFd(
        simsym.tstruct(
            ispipe=simsym.SBool,
            pipeid=SPipeId,
            pipewriter=simsym.SBool,
            inum=SInum,
            # This offset is in datavals
            off=SOffset)):
    def _declare_assumptions(self, assume):
        super(SFd, self)._declare_assumptions(assume)
        assume(self.off >= 0)
Ejemplo n.º 12
0
# The simplified rename model from the SOSP '13 paper

import simsym
import symtypes
import model
import errno

SymByte = simsym.tuninterpreted('SymByte')
SymInode = simsym.tstruct(data=symtypes.tlist(SymByte), nlink=simsym.SInt)
SymIMap = simsym.tmap(simsym.SInt, SymInode)
SymFilename = simsym.tuninterpreted('Filename')
SymDir = symtypes.tdict(SymFilename, simsym.SInt)


class Rename(simsym.tstruct(fname_to_inum=SymDir, inodes=SymIMap)):
    @model.methodwrap(src=SymFilename, dst=SymFilename)
    def rename(self, src, dst):
        if simsym.symnot(self.fname_to_inum.contains(src)):
            #return (-1, errno.ENOENT)
            return -1
        if src == dst:
            return 0
        if self.fname_to_inum.contains(dst):
            self.inodes[self.fname_to_inum[dst]].nlink -= 1
        self.fname_to_inum[dst] = self.fname_to_inum[src]
        del self.fname_to_inum[src]
        return 0


model_class = Rename
Ejemplo n.º 13
0
# The simplified rename model from the SOSP '13 paper

import simsym
import symtypes
import model
import errno

SymByte     = simsym.tuninterpreted('SymByte')
SymInode    = simsym.tstruct(data  = symtypes.tlist(SymByte),
                             nlink = simsym.SInt)
SymIMap     = simsym.tmap(simsym.SInt, SymInode)
SymFilename = simsym.tuninterpreted('Filename')
SymDir      = symtypes.tdict(SymFilename, simsym.SInt)

class Rename(simsym.tstruct(
        fname_to_inum=SymDir,
        inodes=SymIMap)):

    @model.methodwrap(src=SymFilename, dst=SymFilename)
    def rename(self, src, dst):
        if simsym.symnot(self.fname_to_inum.contains(src)):
            #return (-1, errno.ENOENT)
            return -1
        if src == dst:
            return 0
        if self.fname_to_inum.contains(dst):
            self.inodes[self.fname_to_inum[dst]].nlink -= 1
        self.fname_to_inum[dst] = self.fname_to_inum[src]
        del self.fname_to_inum[src]
        return 0
Ejemplo n.º 14
0
SFn = simsym.tuninterpreted("SFn")
SInum = simsym.tuninterpreted("SInum")
SDataVal = simsym.tuninterpreted("SDataVal")
SVa = simsym.tuninterpreted("SVa")
SPipeId = simsym.tuninterpreted("SPipeId")

DATAVAL_BYTES = 4096
PAGE_BYTES = 4096
PAGE_DATAVALS = PAGE_BYTES / DATAVAL_BYTES
assert PAGE_BYTES % DATAVAL_BYTES == 0
DATA_MAX_LEN = 8

SPid = simsym.SBool
SOffset = simsym.tsynonym("SOffset", simsym.SInt)
SData = symtypes.tsmalllist(DATA_MAX_LEN, SDataVal, lenType=SOffset)
SPipe = simsym.tstruct(data = SData)
SPipeMap = symtypes.tmap(SPipeId, SPipe)
class SFd(simsym.tstruct(ispipe = simsym.SBool,
                         pipeid = SPipeId,
                         pipewriter = simsym.SBool,
                         inum = SInum,
                         # This offset is in datavals
                         off = SOffset)):
    def _declare_assumptions(self, assume):
        super(SFd, self)._declare_assumptions(assume)
        assume(self.off >= 0)

    def _eq_internal(self, o):
        if type(self) != type(o):
            return NotImplemented
        return simsym.symand(
Ejemplo n.º 15
0
class Fs(
        simsym.tstruct(
            # Process state
            proc0=SProc,
            proc1=SProc,
            pipes=SPipeMap,
            # In-memory file system state
            i_map=SIMap,
            root_dir=SDirMap,  ## XXX Non-directories impl
            # "On-disk" file system state
            durable_root_dir=SDirMap,
            durable_i_map=SIMap)):
    def getproc(self, pid):
        if pid == False:
            return self.proc0
        return self.proc1

    def iused(self, inum):
        dir = SInum.var('dir')
        fn = SFn.var('fn')
        fd = SFdNum.var('fd')
        pid = SPid.var('pid')

        # If we try to simply index into dirmap, its __getitem__
        # won't have access to the supposition that it contains the right
        # key, and throw an exception.  Thus, we use _map directly.
        return simsym.symor([
            ## XXX Directories impl:
            # simsym.exists(dir,
            #     simsym.symand([
            #         self.i_map[dir].isdir,
            #         simsym.exists(fn,
            #             simsym.symand([self.i_map[dir].dirmap.contains(fn),
            #                            self.i_map[dir].dirmap._map[fn] == inum]))])),

            ## XXX Non-directories impl:
            simsym.exists(
                fn,
                simsym.symand([
                    self.root_dir.contains(fn), self.root_dir._map[fn] == inum
                ])),
            simsym.exists(
                fd,
                simsym.symand([
                    self.proc0.fd_map.contains(fd),
                    simsym.symnot(self.proc0.fd_map._map[fd].ispipe),
                    self.proc0.fd_map._map[fd].inum == inum
                ])),
            simsym.exists(
                fd,
                simsym.symand([
                    self.proc1.fd_map.contains(fd),
                    simsym.symnot(self.proc1.fd_map._map[fd].ispipe),
                    self.proc1.fd_map._map[fd].inum == inum
                ])),
        ])

    def add_selfpid(self, pid):
        ## XXX hack due to our simplified PID model
        ## without loss of generality, assume syscall "a" happens in proc0
        if str(pid).startswith('a.'):
            simsym.assume(pid == False)

    def nameiparent(self, pn):
        ## XXX Non-directories impl:
        return 0, self.root_dir, pn

        ## XXX Directories impl:
        # simsym.assume(self.i_map[self.root_inum].isdir)
        # return self.root_inum, self.i_map[self.root_inum].dirmap, pn.last

    # Each model method must return a dictionary representing its the
    # return value of the implemented call.  The keys in these
    # dictionaries correspond to variables declared in the code
    # generated by fs_testgen, with one exception: in order for
    # spec.test to be able to correctly compare these dictionaries,
    # they must be well-typed.  So if, for example, a return value can
    # either be a literal -1 or some symbolic value, we name the
    # latter something like 'r:data' and handle it specially in
    # fs_testgen.

    @model.methodwrap(
        pn=SPathname,
        creat=simsym.SBool,
        excl=simsym.SBool,
        trunc=simsym.SBool,
        anyfd=simsym.SBool,
        pid=SPid,
    )
    def open(self, pn, creat, excl, trunc, anyfd, pid):
        # XXX O_RDONLY, O_WRONLY, O_RDWR
        self.add_selfpid(pid)
        internal_time = STime.var('internal_time*')
        created = False
        anyfd = False
        _, pndirmap, pnlast = self.nameiparent(pn)
        if creat:
            if not pndirmap.contains(pnlast):
                internal_alloc_inum = SInum.var('internal_alloc_inum*')
                simsym.assume(simsym.symnot(self.iused(internal_alloc_inum)))

                simsym.assume(
                    internal_time >= self.i_map[internal_alloc_inum].atime)
                simsym.assume(
                    internal_time >= self.i_map[internal_alloc_inum].mtime)
                simsym.assume(
                    internal_time >= self.i_map[internal_alloc_inum].ctime)

                inode = self.i_map[internal_alloc_inum]
                inode.data._len = 0
                inode.nlink = 1
                inode.atime = inode.mtime = inode.ctime = internal_time
                pndirmap[pnlast] = internal_alloc_inum

                created = True
            else:
                if excl: return {'r': -1, 'errno': errno.EEXIST}
        if not pndirmap.contains(pnlast):
            return {'r': -1, 'errno': errno.ENOENT}

        inum = pndirmap[pnlast]
        if trunc:
            if not created:
                simsym.assume(internal_time >= self.i_map[inum].mtime)
                simsym.assume(internal_time >= self.i_map[inum].ctime)
                self.i_map[inum].mtime = internal_time
                self.i_map[inum].ctime = internal_time
            self.i_map[inum].data._len = 0

        internal_ret_fd = SFdNum.var('internal_ret_fd*')
        simsym.assume(internal_ret_fd >= 0)
        simsym.assume(
            simsym.symnot(self.getproc(pid).fd_map.contains(internal_ret_fd)))

        ## Lowest FD
        otherfd = SFdNum.var('fd')
        simsym.assume(
            simsym.symor([
                anyfd,
                simsym.symnot(
                    simsym.exists(
                        otherfd,
                        simsym.symand([
                            otherfd >= 0, otherfd < internal_ret_fd,
                            self.getproc(pid).fd_map.contains(otherfd)
                        ])))
            ]))

        fd_data = self.getproc(pid).fd_map.create(internal_ret_fd)
        fd_data.inum = inum
        fd_data.off = 0
        fd_data.ispipe = False

        return {'r': internal_ret_fd}

    @model.methodwrap(pid=SPid)
    def pipe(self, pid):
        self.add_selfpid(pid)
        internal_pipeid = SPipeId.var('internal_pipeid*')

        xfd = SFdNum.var('xfd')
        simsym.assume(
            simsym.symnot(
                simsym.symor([
                    simsym.exists(
                        xfd,
                        simsym.symand([
                            self.proc0.fd_map.contains(xfd),
                            self.proc0.fd_map._map[xfd].ispipe,
                            self.proc0.fd_map._map[xfd].pipeid ==
                            internal_pipeid
                        ])),
                    simsym.exists(
                        xfd,
                        simsym.symand([
                            self.proc1.fd_map.contains(xfd),
                            self.proc1.fd_map._map[xfd].ispipe,
                            self.proc1.fd_map._map[xfd].pipeid ==
                            internal_pipeid
                        ]))
                ])))

        empty_pipe = self.pipes[internal_pipeid]
        empty_pipe.data._len = 0

        ## lowest FD for read end
        internal_fd_r = SFdNum.var('internal_fd_r*')
        simsym.assume(internal_fd_r >= 0)
        simsym.assume(
            simsym.symnot(self.getproc(pid).fd_map.contains(internal_fd_r)))
        simsym.assume(
            simsym.symnot(
                simsym.exists(
                    xfd,
                    simsym.symand([
                        xfd >= 0, xfd < internal_fd_r,
                        self.getproc(pid).fd_map.contains(xfd)
                    ]))))
        fd_r_data = self.getproc(pid).fd_map.create(internal_fd_r)
        fd_r_data.ispipe = True
        fd_r_data.pipeid = internal_pipeid
        fd_r_data.pipewriter = False

        ## lowest FD for write end
        internal_fd_w = SFdNum.var('internal_fd_w*')
        simsym.assume(internal_fd_w >= 0)
        simsym.assume(
            simsym.symnot(self.getproc(pid).fd_map.contains(internal_fd_w)))
        simsym.assume(
            simsym.symnot(
                simsym.exists(
                    xfd,
                    simsym.symand([
                        xfd >= 0, xfd < internal_fd_w,
                        self.getproc(pid).fd_map.contains(xfd)
                    ]))))
        fd_w_data = self.getproc(pid).fd_map.create(internal_fd_w)
        fd_w_data.ispipe = True
        fd_w_data.pipeid = internal_pipeid
        fd_w_data.pipewriter = True

        return {'r': 0, 'fds[0]': internal_fd_r, 'fds[1]': internal_fd_w}

    @model.methodwrap(src=SPathname, dst=SPathname)
    def rename(self, src, dst):
        internal_time = STime.var('internal_time*')
        srcdiri, srcdirmap, srclast = self.nameiparent(src)
        dstdiri, dstdirmap, dstlast = self.nameiparent(dst)
        if not srcdirmap.contains(srclast):
            return {'r': -1, 'errno': errno.ENOENT}
        if srcdiri == dstdiri and srclast == dstlast:
            return {'r': 0}
        if dstdirmap.contains(dstlast):
            dstinum = dstdirmap[dstlast]
        else:
            dstinum = None
        dstdirmap[dstlast] = srcdirmap[srclast]
        del srcdirmap[srclast]
        if dstinum is not None:
            self.i_map[dstinum].nlink = self.i_map[dstinum].nlink - 1
            simsym.assume(internal_time >= self.i_map[dstinum].ctime)
            self.i_map[dstinum].ctime = internal_time
        return {'r': 0}

    @model.methodwrap(pn=SPathname)
    def unlink(self, pn):
        internal_time = STime.var('internal_time*')
        _, dirmap, pnlast = self.nameiparent(pn)
        if not dirmap.contains(pnlast):
            return {'r': -1, 'errno': errno.ENOENT}
        inum = dirmap[pnlast]
        del dirmap[pnlast]
        self.i_map[inum].nlink = self.i_map[inum].nlink - 1
        simsym.assume(internal_time >= self.i_map[inum].ctime)
        self.i_map[inum].ctime = internal_time
        return {'r': 0}

    @model.methodwrap(oldpn=SPathname, newpn=SPathname)
    def link(self, oldpn, newpn):
        internal_time = STime.var('internal_time*')
        olddiri, olddirmap, oldlast = self.nameiparent(oldpn)
        newdiri, newdirmap, newlast = self.nameiparent(newpn)
        if not olddirmap.contains(oldlast):
            return {'r': -1, 'errno': errno.ENOENT}
        if newdirmap.contains(newlast):
            return {'r': -1, 'errno': errno.EEXIST}
        inum = olddirmap[oldlast]
        newdirmap[newlast] = inum
        self.i_map[inum].nlink = self.i_map[inum].nlink + 1
        simsym.assume(internal_time >= self.i_map[inum].ctime)
        self.i_map[inum].ctime = internal_time
        return {'r': 0}

    def iread(self, inum, off, time=None):
        simsym.assume(off >= 0)
        if off >= self.i_map[inum].data._len:
            return {'r': 0}
        if time is None:
            time = STime.var('internal_time*')
        if time is not False:
            simsym.assume(time >= self.i_map[inum].atime)
            self.i_map[inum].atime = time
        return {'r': DATAVAL_BYTES, 'data': self.i_map[inum].data[off]}

    @model.methodwrap(fd=SFdNum, pid=SPid)
    def read(self, fd, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        if self.getproc(pid).fd_map[fd].ispipe:
            if self.getproc(pid).fd_map[fd].pipewriter:
                return {'r': -1, 'errno': errno.EBADF}
            pipeid = self.getproc(pid).fd_map[fd].pipeid
            pipe = self.pipes[pipeid]
            if pipe.data.len() == 0:
                otherfd = SFdNum.var('otherfd')
                if simsym.symor([
                        simsym.exists(
                            otherfd,
                            simsym.symand([
                                self.proc0.fd_map.contains(otherfd),
                                self.proc0.fd_map._map[otherfd].ispipe,
                                self.proc0.fd_map._map[otherfd].pipewriter,
                                self.proc0.fd_map._map[otherfd].pipeid ==
                                pipeid
                            ])),
                        simsym.exists(
                            otherfd,
                            simsym.symand([
                                self.proc1.fd_map.contains(otherfd),
                                self.proc1.fd_map._map[otherfd].ispipe,
                                self.proc1.fd_map._map[otherfd].pipewriter,
                                self.proc1.fd_map._map[otherfd].pipeid ==
                                pipeid
                            ]))
                ]):
                    return {'r': -1, 'errno': errno.EAGAIN}
                else:
                    # XXX The above condition can always be satisfied
                    # by making up some other FD, but testgen may
                    # never see that FD, so the real test may not
                    # reflect its presence.
                    return {'r': 0}
            d = pipe.data[0]
            pipe.data.shift()
            return {'r': DATAVAL_BYTES, 'data': d}
        off = self.getproc(pid).fd_map[fd].off
        r = self.iread(self.getproc(pid).fd_map[fd].inum, off)
        if 'data' in r:
            self.getproc(pid).fd_map[fd].off = off + 1
        return r

    @model.methodwrap(fd=SFdNum, off=SOffset, pid=SPid)
    def pread(self, fd, off, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        if self.getproc(pid).fd_map[fd].ispipe:
            return {'r': -1, 'errno': errno.ESPIPE}
        return self.iread(self.getproc(pid).fd_map[fd].inum, off)

    def iwrite(self, inum, off, databyte, time=None):
        simsym.assume(off >= 0)
        ## Avoid overly-long files.  fs-test.py caps file size at 16 units.
        simsym.assume(off < DATA_MAX_LEN)
        ## XXX Handle sparse files?
        simsym.assume(off <= self.i_map[inum].data._len)

        if off == self.i_map[inum].data._len:
            self.i_map[inum].data.append(databyte)
        else:
            self.i_map[inum].data[off] = databyte
        if time is None:
            time = STime.var('internal_time*')
        if time is not False:
            simsym.assume(time >= self.i_map[inum].mtime)
            simsym.assume(time >= self.i_map[inum].ctime)
            self.i_map[inum].mtime = time
            self.i_map[inum].ctime = time
        return {'r': DATAVAL_BYTES}

    @model.methodwrap(fd=SFdNum, databyte=SDataVal, pid=SPid)
    def write(self, fd, databyte, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        if self.getproc(pid).fd_map[fd].ispipe:
            if not self.getproc(pid).fd_map[fd].pipewriter:
                return {'r': -1, 'errno': errno.EBADF}
            pipeid = self.getproc(pid).fd_map[fd].pipeid
            pipe = self.pipes[pipeid]

            otherfd = SFdNum.var('otherfd')
            if simsym.symnot(
                    simsym.symor([
                        simsym.exists(
                            otherfd,
                            simsym.symand([
                                self.proc0.fd_map.contains(otherfd),
                                self.proc0.fd_map._map[otherfd].ispipe,
                                simsym.symnot(self.proc0.fd_map._map[otherfd].
                                              pipewriter),
                                self.proc0.fd_map._map[otherfd].pipeid ==
                                pipeid
                            ])),
                        simsym.exists(
                            otherfd,
                            simsym.symand([
                                self.proc1.fd_map.contains(otherfd),
                                self.proc1.fd_map._map[otherfd].ispipe,
                                simsym.symnot(self.proc1.fd_map._map[otherfd].
                                              pipewriter),
                                self.proc1.fd_map._map[otherfd].pipeid ==
                                pipeid
                            ]))
                    ])):
                # XXX This condition has the same problem as the one
                # in read.
                return {'r': -1, 'errno': errno.EPIPE}

            simsym.assume(pipe.data.len() < DATA_MAX_LEN)
            pipe.data.append(databyte)
            return {'r': DATAVAL_BYTES}
        off = self.getproc(pid).fd_map[fd].off
        self.getproc(pid).fd_map[fd].off = off + 1
        return self.iwrite(self.getproc(pid).fd_map[fd].inum, off, databyte)

    @model.methodwrap(fd=SFdNum, off=SOffset, databyte=SDataVal, pid=SPid)
    def pwrite(self, fd, off, databyte, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        if self.getproc(pid).fd_map[fd].ispipe:
            return {'r': -1, 'errno': errno.ESPIPE}
        return self.iwrite(self.getproc(pid).fd_map[fd].inum, off, databyte)

    def istat(self, inum):
        len = self.i_map[inum].data._len
        nlink = self.i_map[inum].nlink
        atime = self.i_map[inum].atime
        mtime = self.i_map[inum].mtime
        ctime = self.i_map[inum].ctime
        return {
            'r': 0,
            'st.st_ino': inum,
            'st.st_size': len,
            'st.st_nlink': nlink,
            'st.st_atime': atime,
            'st.st_mtime': mtime,
            'st.st_ctime': ctime
        }

    @model.methodwrap(pn=SPathname)
    def stat(self, pn):
        _, dirmap, pnlast = self.nameiparent(pn)
        if not dirmap.contains(pnlast):
            return {'r': -1, 'errno': errno.ENOENT}
        return self.istat(dirmap[pnlast])

    @model.methodwrap(fd=SFdNum, pid=SPid)
    def fstat(self, fd, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        if self.getproc(pid).fd_map[fd].ispipe:
            return {'r': 0, '!!S_ISFIFO(st.st_mode)': 1}
        return self.istat(self.getproc(pid).fd_map[fd].inum)

    @model.methodwrap(fd=SFdNum, pid=SPid)
    def close(self, fd, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        del self.getproc(pid).fd_map[fd]
        return {'r': 0}

    @model.methodwrap(fd=SFdNum,
                      off=SOffset,
                      whence_set=simsym.SBool,
                      whence_cur=simsym.SBool,
                      whence_end=simsym.SBool,
                      pid=SPid)
    def lseek(self, fd, off, whence_set, whence_cur, whence_end, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        fdm = self.getproc(pid).fd_map[fd]
        if fdm.ispipe:
            return {'r': -1, 'errno': errno.EINVAL}
            ## POSIX says ESPIPE, but Linux returns EINVAL
        if whence_set:
            new_off = off
        elif whence_cur:
            new_off = fdm.off + off
        elif whence_end:
            new_off = self.i_map[fdm.inum].data._len + off
        else:
            return {'r': -1, 'errno': errno.EINVAL}
        if new_off < 0:
            return {'r': -1, 'errno': errno.EINVAL}
        fdm.off = new_off
        return {'r': fdm.off}

    @model.methodwrap(anon=simsym.SBool,
                      writable=simsym.SBool,
                      fixed=simsym.SBool,
                      va=SVa,
                      fd=SFdNum,
                      off=SOffset,
                      pid=SPid)
    def mmap(self, anon, writable, fixed, va, fd, off, pid):
        ## TODO: MAP_SHARED/MAP_PRIVATE for files
        ##       -> how to model delayed file read?
        ## TODO: MAP_SHARED/MAP_PRIVATE for anon (with fork)
        ## TODO: zeroing anon memory
        self.add_selfpid(pid)
        myproc = self.getproc(pid)
        if not fixed:
            va = SVa.var('internal_freeva*')
            simsym.assume(simsym.symnot(myproc.va_map.contains(va)))
        if not anon:
            if not myproc.fd_map.contains(fd):
                return {'r': -1, 'errno': errno.EBADF}
            if myproc.fd_map[fd].ispipe:
                # The Linux manpage is misleading, but POSIX is clear
                # about ENODEV and this is what Linux does.
                return {'r': -1, 'errno': errno.ENODEV}
        vma = myproc.va_map.create(va)
        vma.anon = anon
        vma.writable = writable
        if anon:
            vma.anondata = SDataVal.var()
        else:
            simsym.assume(off >= 0)
            simsym.assume(off % PAGE_DATAVALS == 0)
            vma.off = off
            vma.inum = myproc.fd_map[fd].inum
        # This has to be well-typed, so we use a different variable to
        # represent VAs.
        return {'r:va': va}

    @model.methodwrap(va=SVa, pid=SPid)
    def munmap(self, va, pid):
        self.add_selfpid(pid)
        del self.getproc(pid).va_map[va]
        return {'r': 0}

    @model.methodwrap(va=SVa, writable=simsym.SBool, pid=SPid)
    def mprotect(self, va, writable, pid):
        self.add_selfpid(pid)
        myproc = self.getproc(pid)
        if not myproc.va_map.contains(va):
            return {'r': -1, 'errno': errno.ENOMEM}
        myproc.va_map[va].writable = writable
        return {'r': 0}

    @model.methodwrap(va=SVa, pid=SPid)
    def memread(self, va, pid):
        self.add_selfpid(pid)
        myproc = self.getproc(pid)
        if not myproc.va_map.contains(va):
            return {'r': -1, 'signal': signal.SIGSEGV}
        if myproc.va_map[va].anon:
            return {'r:data': myproc.va_map[va].anondata, 'signal': 0}
        ## TODO: memory-mapped reads don't bump atime?
        res = self.iread(myproc.va_map[va].inum, myproc.va_map[va].off, False)
        if res['r'] == 0:
            # This means there was no page here
            return {'r': -1, 'signal': signal.SIGBUS}
        elif res['r'] == DATAVAL_BYTES:
            return {'r:data': res['data'], 'signal': 0}
        else:
            raise RuntimeError('Unexpected result from iread: %r' % res)

    @model.methodwrap(va=SVa, databyte=SDataVal, pid=SPid)
    def memwrite(self, va, databyte, pid):
        self.add_selfpid(pid)
        myproc = self.getproc(pid)
        if not myproc.va_map.contains(va):
            return {'r': -1, 'signal': signal.SIGSEGV}
        if not myproc.va_map[va].writable:
            return {'r': -1, 'signal': signal.SIGSEGV}
        if myproc.va_map[va].anon:
            myproc.va_map[va].anondata = databyte
            return {'r': 0, 'signal': 0}
        vma = myproc.va_map[va]
        if vma.off >= self.i_map[vma.inum].data._len:
            return {'r': -1, 'signal': signal.SIGBUS}
        ## TODO: memory-mapped writes don't bump mtime/ctime?
        res = self.iwrite(myproc.va_map[va].inum, myproc.va_map[va].off,
                          databyte, False)
        if res['r'] == DATAVAL_BYTES:
            return {'r': 0, 'signal': 0}
        else:
            raise RuntimeError('Unexpected result from iwrite: %r' % res)

    @model.methodwrap()
    def sync(self):
        # Update the "on-disk" state to match the "in-memory" file
        # system state.  If only the real sync were this easy!
        self.durable_i_map = self.i_map
        self.durable_root_dir = self.root_dir
        return {'r': 0}

    @model.methodwrap(fd=SFdNum, pid=SPid)
    def fsync(self, fd, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        inum = self.getproc(pid).fd_map[fd].inum
        self.durable_i_map[inum] = self.i_map[inum]
        return {'r': 0}

    @model.methodwrap()
    def reboot(self):
        # Undo "in-memory" file system state back to "durable" state
        self.i_map = self.durable_i_map
        self.root_dir = self.durable_root_dir
        # XXX We should probably do something with the process state.
        # We can't reset it to "nothing" because we don't have a way
        # to represent nothing.  We could de-constrain it as below,
        # but I'm not sure that's right either (and it sends Z3 into
        # an infinite loop).
        # self.proc0 = SProc.var()
        # self.proc1 = SProc.var()
        # self.pipes = SPipeMap.var()
        return {'r': 0}
Ejemplo n.º 16
0
SHUT_RDWR = 2

SDataVal = simsym.tuninterpreted("SDataVal")

DATAVAL_BYTES = 4096
PAGE_BYTES = 4096
PAGE_DATAVALS = PAGE_BYTES / DATAVAL_BYTES
assert PAGE_BYTES % DATAVAL_BYTES == 0
DATA_MAX_LEN = 8

SPid = simsym.SBool  # TODO: bool ???
SOffset = simsym.tsynonym("SOffset", simsym.SInt)
SAddr = simsym.tsynonym("SAddr", simsym.SInt)
SPort = simsym.tsynonym("SPort", simsym.SInt)
SData = symtypes.tsmalllist(DATA_MAX_LEN, SDataVal, lenType=SOffset)
SBuffer = simsym.tstruct(data=SData)
SBindRecord = symtypes.tdict(SPort, symtypes.tdict(SPid, simsym.SBool))


class SFd(
        simsym.tstruct(domain=simsym.SInt,
                       type=simsym.SInt,
                       prot=simsym.SInt,
                       can_read=simsym.SBool,
                       can_write=simsym.SBool,
                       is_bound=simsym.SBool,
                       is_connected=simsym.SBool,
                       local_addr=SAddr,
                       local_port=SPort,
                       remote_addr=SAddr,
                       remote_port=SPort,
Ejemplo n.º 17
0
class Socket(
        simsym.tstruct(
            # Process state
            proc0=SProc,
            proc1=SProc,
            # Bind records
            bind_rec=SBindRecord)):
    def getproc(self, pid):
        if pid == False:
            return self.proc0
        return self.proc1

    def add_selfpid(self, pid):
        ## XXX hack due to our simplified PID model
        ## without loss of generality, assume syscall "a" happens in proc0
        if str(pid).startswith('a.'):
            simsym.assume(pid == False)

    def enqueue_buffer(self, buffer, databyte):
        simsym.assume(buffer.data.len() < DATA_MAX_LEN)
        buffer.data.append(databyte)

    def dequeue_buffer(self, buffer):
        d = buffer.data[0]
        buffer.data.shift()
        return d

    def is_bound(self, port, pid):
        if not self.bind_rec.contains(port):
            return False
        if not self.bind_rec[port].contains(pid):
            return False
        return self.bind_rec[port][pid]

    def set_bound(self, port, pid):
        if not self.bind_rec.contains(port):
            self.bind_rec.create(port)
        if not self.bind_rec[port].contains(pid):
            self.bind_rec[port].create(pid)
        self.bind_rec[port][pid] = True

    def unset_bound(self, port, pid):
        if not self.bind_rec.contains(port):
            return
        if not self.bind_rec[port].contains(pid):
            return
        self.bind_rec[port][pid] = False

    # transport layer
    def read_transport(self, sock):
        if sock.recv_buffer.data.len() > 0:
            d = self.dequeue_buffer(sock.recv_buffer)
            return {'r': DATAVAL_BYTES, 'data': d}
        else:
            return {'r': 0}

    # transport layer
    def write_transport(self, sock, databyte):
        self.enqueue_buffer(sock.send_buffer, databyte)
        return {'r': DATAVAL_BYTES}

    # network layer
    def read_net(self, sock):
        return {'r': DATAVAL_BYTES, 'data': '???'}

    # network layer
    def write_net(self, sock, databyte):
        return {'r': DATAVAL_BYTES}

    # datalink layer
    def read_datalink(self, sock):
        return {'r': DATAVAL_BYTES, 'data': '???'}

    # not a system call
    @model.methodwrap(from_addr=SAddr,
                      from_port=SPort,
                      to_addr=SAddr,
                      to_port=SPort,
                      pid=SPid)
    def on_delivery(self, from_addr, from_port, to_addr, to_port, pid):
        self.add_selfpid(pid)
        for rpid in [False, True]:
            if self.is_bound(to_port, rpid):
                # TODO
                pass
        return {'r': 0}

    @model.methodwrap(
        domain=simsym.SInt,
        type=simsym.SInt,
        prot=simsym.SInt,
        anyfd=simsym.SBool,
        pid=SPid,
    )
    def socket(self, domain, type, prot, anyfd, pid):
        self.add_selfpid(pid)

        if not ((domain == AF_INET or domain == AF_INET6)
                and type == SOCK_DGRAM and prot == 0):
            return {'r': -1, 'errno': errno.EAFNOSUPPORT}

        internal_ret_fd = SFdNum.var('internal_ret_fd*')
        simsym.assume(internal_ret_fd >= 0)
        simsym.assume(
            simsym.symnot(self.getproc(pid).fd_map.contains(internal_ret_fd)))

        ## Lowest FD
        otherfd = SFdNum.var('fd')
        simsym.assume(
            simsym.symor([
                anyfd,
                simsym.symnot(
                    simsym.exists(
                        otherfd,
                        simsym.symand([
                            otherfd >= 0, otherfd < internal_ret_fd,
                            self.getproc(pid).fd_map.contains(otherfd)
                        ])))
            ]))

        sock = self.getproc(pid).fd_map.create(internal_ret_fd)
        sock.domain = domain
        sock.type = type
        sock.prot = prot
        sock.can_read = True
        sock.can_write = True
        sock.is_bound = False
        sock.is_connected = False
        sock.local_addr = 0
        sock.local_port = 0
        sock.remote_addr = 0
        sock.remote_port = 0

        return {'r': internal_ret_fd}

    @model.methodwrap(fd=SFdNum, pid=SPid)
    def read(self, fd, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        sock = self.getproc(pid).fd_map[fd]
        if not sock.can_read:
            return {'r': 0}
        if not sock.is_bound or not sock.is_connected:
            return {'r': -1, 'errno': errno.ENOTCONN}
        return self.read_transport(sock)

    @model.methodwrap(fd=SFdNum, databyte=SDataVal, pid=SPid)
    def write(self, fd, databyte, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        sock = self.getproc(pid).fd_map[fd]
        if not sock.can_write:
            # TODO: signal
            return {'r': 0}
        if not sock.is_bound or not sock.is_connected:
            return {'r': -1, 'errno': errno.ENOTCONN}
        return self.write_transport(sock, databyte)

    @model.methodwrap(fd=SFdNum, addr=SAddr, port=simsym.SInt, pid=SPid)
    def connect(self, fd, addr, port, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        sock = self.getproc(pid).fd_map[fd]
        if sock.is_connected:
            return {'r': -1, 'errno': errno.EISCONN}
        sock.is_connected = True
        sock.remote_addr = addr
        sock.remote_port = port
        # TODO: register?
        return {'r': 0}

    @model.methodwrap(fd=SFdNum, addr=SAddr, port=simsym.SInt, pid=SPid)
    def bind_(self, fd, addr, port, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        sock = self.getproc(pid).fd_map[fd]
        if sock.is_bound:
            return {'r': -1, 'errno': errno.EINVAL}
        sock.is_bound = True
        sock.local_addr = addr
        sock.local_port = port
        self.set_bound(port, pid)
        return {'r': 0}

    @model.methodwrap(fd=SFdNum, backlog=simsym.SInt, pid=SPid)
    def listen(self, fd, backlog, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        sock = self.getproc(pid).fd_map[fd]
        return {'r': -1, 'errno': errno.EOPNOTSUPP}

    @model.methodwrap(fd=SFdNum, pid=SPid)
    def accept(self, fd, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        sock = self.getproc(pid).fd_map[fd]
        return {'r': -1, 'errno': errno.EOPNOTSUPP}

    @model.methodwrap(fd=SFdNum, how=simsym.SInt, pid=SPid)
    def shutdown(self, fd, how, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        sock = self.getproc(pid).fd_map[fd]
        if how == SHUT_RD or how == SHUT_RDWR:
            sock.can_read = False
        if how == SHUT_WR or how == SHUT_RDWR:
            sock.can_write = False
        return {'r': 0}

    @model.methodwrap(fd=SFdNum, pid=SPid)
    def close(self, fd, pid):
        self.add_selfpid(pid)
        if not self.getproc(pid).fd_map.contains(fd):
            return {'r': -1, 'errno': errno.EBADF}
        sock = self.getproc(pid).fd_map[fd]
        if sock.is_connected:
            # TODO: unconnect
            sock.is_connected = False
        if sock.is_bound:
            sock.is_bound = False
            self.unset_bound(sock.local_port, pid)
        del self.getproc(pid).fd_map[fd]
        return {'r': 0}