Beispiel #1
0
def spec_lemma_nr_dmapages_refcnt(kernelstate):
    conj = []

    pid = util.FreshBitVec('pid', dt.pid_t)
    dmapn = util.FreshBitVec('dmapn', dt.dmapn_t)

    # General invariant -- a free dma page has no owner
    conj.append(
        z3.ForAll([dmapn],
                  z3.Implies(
                      is_dmapn_valid(dmapn),
                      is_pid_valid(kernelstate.dmapages[dmapn].owner) == (
                          kernelstate.dmapages[dmapn].type !=
                          dt.page_type.PAGE_TYPE_FREE))))

    # PROC_UNUSED state implies refcount is 0 (sys_clone needs this)
    conj.append(
        z3.ForAll(
            [pid],
            z3.Implies(
                is_pid_valid(pid),
                z3.Implies(
                    kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED,
                    kernelstate.procs[pid].nr_dmapages() == z3.BitVecVal(
                        0, dt.size_t)))))

    # # Correctness definition for `permutation` based refcount
    kernelstate.procs.nr_dmapages.check(
        conj,
        is_owner_valid=is_pid_valid,
        is_owned_valid=is_dmapn_valid,
        max_refs=dt.NDMAPAGE,
        ownerfn=lambda dmapn: kernelstate.dmapages[dmapn].owner)

    return z3.And(*conj)
Beispiel #2
0
def spec_lemma_nr_fds_refcnt(kernelstate):
    conj = []

    pid = util.FreshBitVec('pid', dt.pid_t)
    fd = util.FreshBitVec('fd', dt.fd_t)

    # unused procs do not have any fds.
    # valid(pid) & state(pid)== PROC_UNUSED ==> pid.nr_fds == 0
    conj.append(
        z3.ForAll(
            [pid],
            z3.Implies(
                is_pid_valid(pid),
                z3.Implies(
                    kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED,
                    kernelstate.procs[pid].nr_fds() == z3.BitVecVal(
                        0, dt.size_t)))))

    # unused procs do not have any opened files
    # valid(pid) & valid(fd) & state(pid) == PROC_UNUSED ==> !valid(openedfile(pid, fd))
    conj.append(
        z3.ForAll(
            [pid, fd],
            z3.Implies(
                z3.And(is_pid_valid(pid), is_fd_valid(fd)),
                z3.Implies(
                    kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED,
                    z3.Not(is_fn_valid(kernelstate.procs[pid].ofile(fd)))))))

    # a procs have opened a file, the state of this procs must not be UNUSED
    # valid(pid) & valid(fd) & valid(fn)  ==> state(pid) != PROC_UNUSED
    conj.append(
        z3.ForAll(
            [pid, fd],
            z3.Implies(
                z3.And(is_pid_valid(pid), is_fd_valid(fd),
                       is_fn_valid(kernelstate.procs[pid].ofile(fd))),
                kernelstate.procs[pid].state != dt.proc_state.PROC_UNUSED)))

    # Correctness definition for `permutation` based refcount
    kernelstate.procs.nr_fds.check(
        conj,
        is_owner_valid=is_pid_valid,
        is_owned_valid=is_fd_valid,
        max_refs=dt.NOFILE,
        ownedby=lambda pid, fd: is_fn_valid(kernelstate.procs[pid].ofile(fd)))

    return z3.And(*conj)
Beispiel #3
0
def spec_lemma_nr_pages_refcnt(kernelstate):
    conj = []

    pid = util.FreshBitVec('pid', dt.pid_t)

    # PROC_UNUSED state implies refcount is 0 (sys_clone needs this)
    conj.append(
        z3.ForAll(
            [pid],
            z3.Implies(
                is_pid_valid(pid),
                z3.Implies(
                    kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED,
                    kernelstate.procs[pid].nr_pages() == z3.BitVecVal(
                        0, dt.size_t)))))

    # Correctness definition for `permutation` based refcount
    kernelstate.procs.nr_pages.check(
        conj,
        is_owner_valid=is_pid_valid,
        is_owned_valid=is_pn_valid,
        max_refs=dt.NPAGE,
        ownerfn=lambda pn: kernelstate.pages[pn].owner)

    return z3.And(*conj)
Beispiel #4
0
def spec_corollary_pgwalk(kernelstate):
    pid = util.FreshBitVec('pid', dt.pid_t)
    va = dt.FreshVA()

    pml4 = kernelstate.procs[pid].page_table_root
    # Abstract model of a page_walk starting from some root
    page, writable, present = page_walk(kernelstate, pml4, *va)

    # If a four level page-walk from some pid returns a page,
    # that page must be exclusively owned by the pid.
    # furthermore, if the page is writable,
    # it has to be a frame.
    isolation = util.Implies(
        present, kernelstate.pages[page].owner == pid,
        z3.Implies(
            writable,
            kernelstate.pages[page].type == dt.page_type.PAGE_TYPE_FRAME))
    # valid(pid) & pid.state == active & valid(va) ==> page.owner == pid && page.type == FRAME (active is either EMBRYO, RUNNABLE or RUNNING)

    return z3.ForAll([pid] + va,
                     z3.Implies(
                         z3.And(
                             is_pid_valid(pid),
                             is_status_live(kernelstate.procs[pid].state),
                             is_va_valid(va),
                         ), isolation))
Beispiel #5
0
def spec_lemma_nr_ports_refcnt(kernelstate):
    conj = []

    pid = util.FreshBitVec('pid', dt.pid_t)
    port = util.FreshBitVec('port', dt.uint16_t)

    # unused procs don't own any ports
    # valid([port:pid]) ==> [port:pid].state != UNUSED
    conj.append(
        z3.ForAll([port],
                  z3.Implies(
                      is_pid_valid(kernelstate.io[port].owner),
                      kernelstate.procs[kernelstate.io[port].owner].state !=
                      dt.proc_state.PROC_UNUSED)))
    # pid.state == UNUSED ==> pid.nr_ports == 0
    conj.append(
        z3.ForAll(
            [pid],
            z3.Implies(
                kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED,
                kernelstate.procs[pid].nr_ports() == z3.BitVecVal(
                    0, dt.size_t))))

    # Correctness definition for `permutation` based refcount
    kernelstate.procs.nr_ports.check(
        conj,
        is_owner_valid=is_pid_valid,
        is_owned_valid=lambda n: z3.BoolVal(True),
        max_refs=2**dt.uint16_t.size(),
        ownerfn=lambda port0: kernelstate.io[port0].owner)

    return z3.And(*conj)
Beispiel #6
0
def sys_reap(old, pid):
    cond = z3.And(
        is_pid_valid(pid),
        # Only the owner can reap a child
        old.procs[pid].ppid == old.current,

        # The pid to reap is a zombie
        old.procs[pid].state == dt.proc_state.PROC_ZOMBIE,

        # The proc has no children/open fds/pages/devices/ports
        old.procs[pid].nr_devs() == z3.BitVecVal(0, dt.size_t),
        old.procs[pid].nr_children() == z3.BitVecVal(0, dt.size_t),
        old.procs[pid].nr_fds() == z3.BitVecVal(0, dt.size_t),
        old.procs[pid].nr_pages() == z3.BitVecVal(0, dt.size_t),
        old.procs[pid].nr_dmapages() == z3.BitVecVal(0, dt.size_t),
        old.procs[pid].nr_ports() == z3.BitVecVal(0, dt.size_t),
        old.procs[pid].nr_vectors() == z3.BitVecVal(0, dt.size_t),
        old.procs[pid].nr_intremaps() == z3.BitVecVal(0, dt.size_t),
    )

    new = old.copy()

    new.procs[old.current].nr_children[pid] -= 1

    new.procs[pid].state = dt.proc_state.PROC_UNUSED
    new.procs[pid].ppid = z3.BitVecVal(0, dt.pid_t)
    new.procs[pid].page_table_root = z3.BitVecVal(0, dt.pn_t)
    new.procs[pid].stack = z3.BitVecVal(0, dt.pn_t)
    new.procs[pid].killed = z3.BoolVal(False)
    new.procs[pid].hvm = z3.BitVecVal(0, dt.pn_t)
    new.procs[pid].use_io_bitmap = z3.BoolVal(False)
    new.procs[pid].io_bitmap_a = z3.BitVecVal(0, dt.pn_t)
    new.procs[pid].io_bitmap_b = z3.BitVecVal(0, dt.pn_t)

    return cond, util.If(cond, new, old)
Beispiel #7
0
def spec_lemma_nr_devs_refcnt(kernelstate):
    conj = []

    pid = util.FreshBitVec('pid', dt.pid_t)
    devid = util.FreshBitVec('devid', dt.devid_t)

    # unused procs don't own any devices
    conj.append(
        z3.ForAll([devid],
                  z3.Implies(
                      is_pid_valid(kernelstate.pci[devid].owner),
                      kernelstate.procs[kernelstate.pci[devid].owner].state !=
                      dt.proc_state.PROC_UNUSED)))

    conj.append(
        z3.ForAll(
            [pid],
            z3.Implies(
                kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED,
                kernelstate.procs[pid].nr_devs() == z3.BitVecVal(0,
                                                                 dt.size_t))))

    # Correctness definition for `permutation` based refcount
    kernelstate.procs.nr_devs.check(
        conj,
        is_owner_valid=is_pid_valid,
        is_owned_valid=lambda n: z3.BoolVal(True),
        max_refs=2**dt.devid_t.size(),
        ownerfn=lambda devid0: kernelstate.pci[devid0].owner)

    return z3.And(*conj)
Beispiel #8
0
def sys_close(old, pid, fd):
    cond = z3.And(
        is_pid_valid(pid),
        is_fd_valid(fd),

        # pid is either current or a zombie
        z3.Or(
            pid == old.current,
            old.procs[pid].state == dt.proc_state.PROC_ZOMBIE),

        is_fn_valid(old.procs[pid].ofile(fd)),
    )
    new = old.copy()

    fn = new.procs[pid].ofile(fd)

    new.procs[pid].ofile[fd] = z3.BitVecVal(0, dt.fn_t)

    new.procs[pid].nr_fds[fd] -= 1

    # decrement file refcnt
    new.files[fn].refcnt[(pid, fd)] -= 1

    ref = new.files[fn].refcnt()

    # If the refcnt is zero, clear the file slot
    new2 = new.copy()
    new2.files[fn].type = dt.file_type.FD_NONE
    new2.files[fn].value = z3.BitVecVal(0, dt.uint64_t)
    new2.files[fn].offset = z3.BitVecVal(0, dt.off_t)
    new2.files[fn].omode = z3.BitVecVal(0, dt.uint64_t)

    return cond, util.If(cond, util.If(ref == 0, new2, new), old)
Beispiel #9
0
def sys_dup(old, oldfd, pid, newfd):
    cond = z3.And(
        is_pid_valid(pid),

        # the pid is either current or an embryo belonging to current
        z3.Or(pid == old.current,
              z3.And(
                  old.procs[pid].ppid == old.current,
                  old.procs[pid].state == dt.proc_state.PROC_EMBRYO)),

        is_fd_valid(oldfd),
        is_fn_valid(old.procs[old.current].ofile(oldfd)),

        is_fd_valid(newfd),
        z3.Not(is_fn_valid(old.procs[pid].ofile(newfd))),
    )

    new = old.copy()

    fn = new.procs[old.current].ofile(oldfd)

    new.procs[pid].ofile[newfd] = fn

    new.procs[pid].nr_fds[newfd] += 1

    # bump file refcnt
    new.files[fn].refcnt[(pid, newfd)] += 1

    return cond, util.If(cond, new, old)
Beispiel #10
0
def sys_recv(old, pid, pn, fd):
    cond = z3.And(
        is_pid_valid(pid),
        old.procs[pid].state == dt.proc_state.PROC_RUNNABLE,

        is_pn_valid(pn),
        old.pages[pn].owner == old.current,
        old.pages[pn].type == dt.page_type.PAGE_TYPE_FRAME,

        z3.Implies(is_fd_valid(fd),
                   z3.Not(is_fn_valid(old.procs[old.current].ofile(fd))))
    )

    new = old.copy()

    new.procs[old.current].ipc_from = z3.BitVecVal(0, dt.pid_t)
    new.procs[old.current].ipc_page = pn
    new.procs[old.current].ipc_size = z3.BitVecVal(0, dt.size_t)
    new.procs[old.current].ipc_fd = fd

    new.procs[old.current].state = dt.proc_state.PROC_SLEEPING
    new.procs[pid].state = dt.proc_state.PROC_RUNNING
    new.current = pid

    return cond, util.If(cond, new, old)
Beispiel #11
0
def sys_copy_frame(old, frm, pid, to):
    cond = z3.And(
        # frm is a valid FRAME owned by current
        is_pn_valid(frm),
        old.pages[frm].type == dt.page_type.PAGE_TYPE_FRAME,
        old.pages[frm].owner == old.current,

        # to is a valid frame owned by pid
        is_pid_valid(pid),
        is_pn_valid(to),
        old.pages[to].type == dt.page_type.PAGE_TYPE_FRAME,
        old.pages[to].owner == pid,

        # the pid is either current or an embryo belonging to current
        z3.Or(pid == old.current,
              z3.And(
                  old.procs[pid].ppid == old.current,
                  old.procs[pid].state == dt.proc_state.PROC_EMBRYO)),
    )

    new = old.copy()

    # copy contents of page frm to page to
    new.pages.data = lambda pn, idx, oldfn: \
        util.If(pn == to,
                oldfn(frm, idx),
                oldfn(pn, idx))

    return cond, util.If(cond, new, old)
Beispiel #12
0
def kk_wait(old,pid,w_len):
    cond = z3.And(
        is_pid_valid(pid),
    )
    new = old.copy()
    new.esbs[pid].primitive=dt.K5_WAIT
    new.esbs[pid].dst_port=old.current
    return cond,util.If(cond,new,old)
Beispiel #13
0
def sys_dup2(old, oldfd, pid, newfd):
    cond = z3.And(
        is_pid_valid(pid),

        # the pid is either current or an embryo belonging to current
        z3.Or(pid == old.current,
              z3.And(
                  old.procs[pid].ppid == old.current,
                  old.procs[pid].state == dt.proc_state.PROC_EMBRYO)),

        is_fd_valid(oldfd),
        is_fn_valid(old.procs[old.current].ofile(oldfd)),

        is_fd_valid(newfd),
    )

    new1 = old.copy()

    newfn = new1.procs[pid].ofile(newfd)

    # If fn != 0

    new1.procs[pid].ofile[newfd] = z3.BitVecVal(0, dt.fn_t)

    new1.procs[pid].nr_fds[newfd] -= 1

    # decrement file refcnt
    new1.files[newfn].refcnt[(pid, newfd)] -= 1

    ref = new1.files[newfn].refcnt()

    # If the refcnt is zero, clear the file slot

    new1.files[newfn].type = util.If(ref == 0, dt.file_type.FD_NONE, new1.files[newfn].type)
    new1.files[newfn].value = util.If(ref == 0, z3.BitVecVal(0, dt.uint64_t), new1.files[newfn].value)
    new1.files[newfn].offset = util.If(ref == 0, z3.BitVecVal(0, dt.off_t), new1.files[newfn].offset)
    new1.files[newfn].omode = util.If(ref == 0, z3.BitVecVal(0, dt.uint64_t), new1.files[newfn].omode)

    new2 = util.If(is_fn_valid(old.procs[pid].ofile(newfd)), new1, old.copy())

    # un-conditional

    fn = new2.procs[old.current].ofile(oldfd)

    new2.procs[pid].ofile[newfd] = fn

    new2.procs[pid].nr_fds[newfd] += 1

    # bump file refcnt
    new2.files[fn].refcnt[(pid, newfd)] += 1

    # posix: if fds are the same, do nothing

    new3 = util.If(z3.And(old.current == pid, oldfd == newfd),
                   old.copy(), new2)

    return cond, util.If(cond, new3, old)
Beispiel #14
0
def alloc_page_table(old, pid, frm, index, to, perm, from_type, to_type):
    cond = z3.And(
        # The to argument is a valid page and is marked as free
        is_pn_valid(to),
        old.pages[to].type == dt.page_type.PAGE_TYPE_FREE,

        # The pid is valid and is either current running process or child embryo
        is_pid_valid(pid),
        z3.Or(pid == old.current,
              z3.And(
                  old.procs[pid].ppid == old.current,
                  old.procs[pid].state == dt.proc_state.PROC_EMBRYO)),

        # The from parameter is valid and of type PML4 and owned by pid
        is_pn_valid(frm),
        old.pages[frm].owner == pid,
        old.pages[frm].type == from_type,

        # Index is a valid page index
        z3.ULT(index, 512),

        # perm has no unsafe bits on it and it is present
        perm & (dt.MAX_INT64 ^ dt.PTE_PERM_MASK) == 0,
        perm & dt.PTE_P != 0,

        # index does not have the P bit in PML4
        old.pages[frm].data(index) & dt.PTE_P == 0,
    )

    new = old.copy()

    new.pages[to].owner = pid
    new.pages[to].type = to_type

    new.pages[frm].data[index] = (
        (z3.UDiv(new.pages_ptr_to_int, util.i64(dt.PAGE_SIZE)) + to) << dt.PTE_PFN_SHIFT) | perm

    # Zero out the new page
    new.pages[to].data = util.i64(0)

    # Maintain the "shadow" pgtable
    new.pages[frm].pgtable_pn[index] = to
    new.pages[to].pgtable_reverse_pn = frm
    new.pages[to].pgtable_reverse_idx = index

    new.pages[frm].pgtable_perm[index] = perm
    new.pages[frm].pgtable_type[index] = dt.PGTYPE_PAGE

    new.pages[to].pgtable_pn = util.i64(0)
    new.pages[to].pgtable_perm = util.i64(0)
    new.pages[to].pgtable_type = dt.PGTYPE_NONE

    new.procs[pid].nr_pages[to] += 1

    new.flush_tlb(pid)

    return cond, util.If(cond, new, old)
Beispiel #15
0
def sys_set_runnable(old, pid):
    cond = z3.And(
        is_pid_valid(pid),
        old.procs[pid].ppid == old.current,
        old.procs[pid].state == dt.proc_state.PROC_EMBRYO)

    new = old.copy()
    new.procs[pid].state = dt.proc_state.PROC_RUNNABLE
    return cond, util.If(cond, new, old)
Beispiel #16
0
def sys_reparent(old, pid):
    cond = z3.And(
        is_pid_valid(pid),
        is_pid_valid(old.procs[pid].ppid),
        old.procs[old.procs[pid].ppid].state == dt.proc_state.PROC_ZOMBIE,

        z3.Or(
            old.procs[dt.INITPID].state == dt.proc_state.PROC_RUNNABLE,
            old.procs[dt.INITPID].state == dt.proc_state.PROC_RUNNING,
        ),
    )

    new = old.copy()

    new.procs[dt.INITPID].nr_children[pid] += 1
    new.procs[old.procs[pid].ppid].nr_children[pid] -= 1

    new.procs[pid].ppid = dt.INITPID

    return cond, util.If(cond, new, old)
Beispiel #17
0
def kk_call(old,pid,service,c_len):
    cond = z3.And(
        is_pid_valid(pid),
        is_service_valid(service),
    )
    new = old.copy()
    #new.esbs[pid].primitive=dt.K5_CALL

    new.esbs[pid].src_port=old.to.src_port
    new.esbs[pid].dst_port=old.to.dst_port
    return cond,util.If(cond,new,old)
Beispiel #18
0
def kk_reply(old,pid,ack_err,s_len):
    cond = z3.And(
        is_pid_valid(pid),

    )
    new = old.copy()
    new.esbs[pid].primitive=dt.K5_REPLY
    new.esbs[pid].src_port=old.current
    new.esbs[pid].dst_port=pid
    #new.esbs[pid].head
    return cond,util.If(cond,new,old)
Beispiel #19
0
def spec_lemma_nr_fds_refcnt(kernelstate):
    conj = []

    pid = util.FreshBitVec('pid', dt.pid_t)
    fd = util.FreshBitVec('fd', dt.fd_t)

    conj.append(
        z3.ForAll(
            [pid],
            z3.Implies(
                is_pid_valid(pid),
                z3.Implies(
                    kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED,
                    kernelstate.procs[pid].nr_fds() == z3.BitVecVal(
                        0, dt.size_t)))))

    conj.append(
        z3.ForAll(
            [pid, fd],
            z3.Implies(
                z3.And(is_pid_valid(pid), is_fd_valid(fd)),
                z3.Implies(
                    kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED,
                    z3.Not(is_fn_valid(kernelstate.procs[pid].ofile(fd)))))))

    conj.append(
        z3.ForAll(
            [pid, fd],
            z3.Implies(
                z3.And(is_pid_valid(pid), is_fd_valid(fd),
                       is_fn_valid(kernelstate.procs[pid].ofile(fd))),
                kernelstate.procs[pid].state != dt.proc_state.PROC_UNUSED)))

    kernelstate.procs.nr_fds.check(
        conj,
        is_owner_valid=is_pid_valid,
        is_owned_valid=is_fd_valid,
        max_refs=dt.NOFILE,
        ownedby=lambda pid, fd: is_fn_valid(kernelstate.procs[pid].ofile(fd)))

    return z3.And(*conj)
Beispiel #20
0
def sys_kill(old, pid):
    cond = z3.And(
        is_pid_valid(pid),
        old.procs[pid].state != dt.proc_state.PROC_UNUSED,
        old.procs[pid].state != dt.proc_state.PROC_ZOMBIE
    )

    new = old.copy()
    new.procs[pid].killed = z3.BoolVal(True)
    new.procs[pid].state = util.If(
        old.procs[pid].state != dt.proc_state.PROC_RUNNING, dt.proc_state.PROC_ZOMBIE, old.procs[pid].state)

    return cond, util.If(cond, new, old)
Beispiel #21
0
def sys_reclaim_port(old, port):
    pid = old.io[port].owner
    cond = z3.And(
        is_pid_valid(pid),
        old.procs[pid].state == dt.proc_state.PROC_ZOMBIE
    )

    new = old.copy()

    new.procs[pid].nr_ports[port] -= 1
    new.io[port].owner = z3.BitVecVal(0, dt.pid_t)

    return cond, util.If(cond, new, old)
Beispiel #22
0
def sys_reclaim_vector(old, vector):
    pid = old.vectors[vector].owner
    cond = z3.And(
        is_pid_valid(pid),
        old.procs[pid].state == dt.proc_state.PROC_ZOMBIE,
        old.procs[pid].nr_intremaps() == 0,
    )

    new = old.copy()

    new.vectors[vector].owner = z3.BitVecVal(0, dt.pid_t)
    new.procs[pid].nr_vectors[vector] -= 1

    return cond, util.If(cond, new, old)
Beispiel #23
0
def sys_send(old, pid, val, pn, size, fd):
    cond = z3.And(
        is_pid_valid(pid),
        old.procs[pid].state == dt.proc_state.PROC_SLEEPING,

        is_pn_valid(pn),
        old.pages[pn].owner == old.current,

        z3.ULE(size, dt.PAGE_SIZE),

        z3.Implies(is_fd_valid(fd),
                   is_fn_valid(old.procs[old.current].ofile(fd))),
    )

    new = old.copy()

    new.procs[pid].ipc_from = old.current
    new.procs[pid].ipc_val = val
    new.procs[pid].ipc_size = size

    # memcpy
    new.pages.data = lambda pn0, idx0, oldfn: \
        util.If(z3.And(pn0 == old.procs[pid].ipc_page, z3.ULT(idx0, size)),
                oldfn(pn, idx0),
                oldfn(pn0, idx0))

    ########
    new2 = new.copy()

    cond2 = z3.And(is_fd_valid(fd), is_fd_valid(new2.procs[pid].ipc_fd))

    fn = old.procs[old.current].ofile(fd)
    fd = old.procs[pid].ipc_fd

    new2.procs[pid].ofile[fd] = fn

    # bump proc nr_fds
    new2.procs[pid].nr_fds[fd] += 1

    # bump file refcnt
    new2.files[fn].refcnt[(pid, fd)] += 1

    new3 = util.If(cond2, new2, new)

    new3.procs[pid].state = dt.proc_state.PROC_RUNNING
    new3.procs[old.current].state = dt.proc_state.PROC_RUNNABLE
    new3.current = pid

    return cond, util.If(cond, new3, old)
Beispiel #24
0
def spec_lemma_nr_children_refcnt(kernelstate):
    conj = []

    pid = util.FreshBitVec('pid', dt.pid_t)

    # pid has a parent <=> pid is not PROC_UNUSED
    # valid(pid) & valid(ppid) ==> state(pid) != PROC_UNUSED
    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      is_pid_valid(kernelstate.procs[pid].ppid) == (
                          kernelstate.procs[pid].state !=
                          dt.proc_state.PROC_UNUSED))))

    # valid(pid) & state(pid) == PROC_UNUSED ==> !valid(ppid)
    conj.append(
        z3.ForAll(
            [pid],
            z3.Implies(
                is_pid_valid(pid),
                z3.Implies(
                    kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED,
                    z3.Not(is_pid_valid(kernelstate.procs[pid].ppid))))))

    # PROC_UNUSED state implies refcnt is 0
    # valid(pid) & state(pid) == PROC_UNUSED ==> pid.nr_children == 0
    conj.append(
        z3.ForAll(
            [pid],
            z3.Implies(
                is_pid_valid(pid),
                z3.Implies(
                    kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED,
                    kernelstate.procs[pid].nr_children() == z3.BitVecVal(
                        0, dt.size_t)))))

    # unused procs don't have a parent
    conj.append(
        z3.ForAll(
            [pid],
            z3.Implies(
                z3.And(
                    is_pid_valid(pid),
                    kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED),
                kernelstate.procs[pid].ppid == z3.BitVecVal(0, dt.pid_t))))

    # Correctness definition for `permutation` based refcount
    kernelstate.procs.nr_children.check(
        conj,
        is_owner_valid=is_pid_valid,
        is_owned_valid=is_pid_valid,
        max_refs=dt.NPROC - 1,
        ownerfn=lambda pid0: kernelstate.procs[pid0].ppid)

    return z3.And(*conj)
Beispiel #25
0
def sys_reclaim_page(old, pn):
    cond = z3.And(
        is_pn_valid(pn),
        old.pages[pn].type != dt.page_type.PAGE_TYPE_FREE,
        is_pid_valid(old.pages[pn].owner),
        old.procs[old.pages[pn].owner].state == dt.proc_state.PROC_ZOMBIE,
        old.procs[old.pages[pn].owner].nr_devs() == z3.BitVecVal(0, dt.size_t),
    )

    new = old.copy()

    new.procs[new.pages[pn].owner].nr_pages[pn] -= 1
    new.pages[pn].type = dt.page_type.PAGE_TYPE_FREE
    new.pages[pn].owner = z3.BitVecVal(0, dt.pid_t)

    return cond, util.If(cond, new, old)
Beispiel #26
0
def sys_switch(old, pid):
    cond = z3.And(
        is_pid_valid(pid),
        old.procs[pid].state == dt.proc_state.PROC_RUNNABLE,

        # This is implied by pid having state runnable,
        # current is always running
        old.current != pid,
    )

    new = old.copy()
    new.procs[old.current].state = util.If(
        old.procs[old.current].killed, dt.proc_state.PROC_ZOMBIE, dt.proc_state.PROC_RUNNABLE)
    new.procs[pid].state = dt.proc_state.PROC_RUNNING
    new.current = pid

    return cond, util.If(cond, new, old)
Beispiel #27
0
def sys_reclaim_iommu_root(old, devid):
    pid = old.pci[devid].owner
    cond = z3.And(
        is_pid_valid(pid),
        old.procs[pid].state == dt.proc_state.PROC_ZOMBIE,
        old.procs[pid].nr_intremaps() == 0,
    )

    new = old.copy()

    new.procs[pid].nr_devs[devid] -= 1
    # Clear the page_table_root
    new.pci[devid].page_table_root = z3.BitVecVal(-1, dt.pn_t)
    new.pci[devid].owner = z3.BitVecVal(0, dt.pid_t)

    new.flush_iotlb()

    return cond, util.If(cond, new, old)
Beispiel #28
0
def extintr(old, vector):
    pid = old.vectors[vector].owner
    cond = is_pid_valid(pid)
    cond2 = z3.And(cond, old.procs[pid].state == dt.proc_state.PROC_SLEEPING)

    vector = z3.ZeroExt(64 - vector.size(), vector)
    idx = z3.UDiv(vector, 64)
    mask = 1 << (vector % 64)

    new = old.copy()
    new.procs[pid].intr[idx] = new.procs[pid].intr(idx) | mask

    new2 = new.copy()
    new2.procs[pid].state = dt.proc_state.PROC_RUNNABLE
    new2.procs[pid].ipc_from = z3.BitVecVal(0, dt.pid_t)
    new2.procs[pid].ipc_val = vector
    new2.procs[pid].ipc_size = z3.BitVecVal(0, dt.size_t)

    return cond, util.If(cond, util.If(cond2, new2, new), old)
Beispiel #29
0
def sys_map_pml4(old, pid, index, perm):

    cond = z3.And(
        is_pid_valid(pid),

        # the pid is either current or an embryo belonging to current
        z3.Or(pid == old.current,
              z3.And(
                  old.procs[pid].ppid == old.current,
                  old.procs[pid].state == dt.proc_state.PROC_EMBRYO)),

        # Index is a valid page index
        z3.ULT(index, 512),

        # perm has no unsafe bits on it and it is present and non-writable
        perm & (dt.MAX_INT64 ^ dt.PTE_PERM_MASK) == 0,
        perm & dt.PTE_P != 0,
        perm & dt.PTE_W == 0,

        # index does not have the P bit in the page table root at that index
        old.pages[old.procs[pid].page_table_root].data(
            index) & dt.PTE_P == 0,
    )

    new = old.copy()

    frm = old.procs[pid].page_table_root

    new.pages[frm].data[index] = (
        (z3.UDiv(new.pages_ptr_to_int, util.i64(dt.PAGE_SIZE)) + frm) << dt.PTE_PFN_SHIFT) | perm

    # maintain the "shadow" pgtable
    new.pages[frm].pgtable_pn[index] = frm
    new.pages[frm].pgtable_perm[index] = perm
    new.pages[frm].pgtable_type[index] = dt.PGTYPE_PAGE

    new.pages[frm].pgtable_reverse_pn = frm
    new.pages[frm].pgtable_reverse_idx = index

    new.flush_tlb(pid)

    return cond, util.If(cond, new, old)
Beispiel #30
0
def sys_map_file(old, pid, frm, index, n, perm):
    cond = z3.And(
        z3.ULT(n, dt.NPAGES_FILE_TABLE),

        is_pid_valid(pid),

        # the pid is either current or an embryo belonging to current
        z3.Or(pid == old.current,
              z3.And(
                  old.procs[pid].ppid == old.current,
                  old.procs[pid].state == dt.proc_state.PROC_EMBRYO)),

        # frm is a valid pn of type PT whose owner is pid
        is_pn_valid(frm),
        old.pages[frm].type == dt.page_type.PAGE_TYPE_X86_PT,
        old.pages[frm].owner == pid,

        # Index is a valid page index
        z3.ULT(index, 512),

        # perm has no unsafe bits on it and it is present and non-writable
        perm & (dt.MAX_INT64 ^ dt.PTE_PERM_MASK) == 0,
        perm & dt.PTE_P != 0,
        perm & dt.PTE_W == 0,

        # index does not have the P bit in the from page
        old.pages[frm].data(index) & dt.PTE_P == 0,
    )

    new = old.copy()

    new.pages[frm].data[index] = (
        (z3.UDiv(new.file_table_ptr_to_int, util.i64(dt.PAGE_SIZE)) + n) << dt.PTE_PFN_SHIFT) | perm

    # maintain the "shadow" pgtable
    new.pages[frm].pgtable_pn[index] = n
    new.pages[frm].pgtable_perm[index] = perm
    new.pages[frm].pgtable_type[index] = dt.PGTYPE_FILE_TABLE

    new.flush_tlb(pid)

    return cond, util.If(cond, new, old)