Esempio n. 1
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)
Esempio n. 2
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)
Esempio n. 3
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)
Esempio n. 4
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)
Esempio n. 5
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)
Esempio n. 6
0
def sys_create(old, fd, fn, type, value, omode):
    cond = z3.And(
        type != dt.file_type.FD_NONE,

        # fd is valid and empty
        is_fd_valid(fd),
        z3.Not(is_fn_valid(old.procs[old.current].ofile(fd))),

        # fn is valid and unused
        is_fn_valid(fn),
        old.files[fn].refcnt() == 0,
    )

    new = old.copy()

    new.files[fn].type = type
    new.files[fn].value = value
    new.files[fn].omode = omode

    new.files[fn].offset = z3.BitVecVal(0, dt.off_t)

    new.procs[old.current].ofile[fd] = fn

    new.procs[old.current].nr_fds[fd] += 1

    # bump file refcnt
    new.files[fn].refcnt[(old.current, fd)] += 1

    return cond, util.If(cond, new, old)
Esempio n. 7
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)
Esempio n. 8
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)
Esempio n. 9
0
def sys_lseek(old, fd, offset):
    cond = z3.And(
        is_fd_valid(fd),
        is_fn_valid(old.procs[old.current].ofile(fd)),
        old.files[old.procs[old.current].ofile(fd)].type == dt.file_type.FD_INODE,
        offset >= 0,
    )

    new = old.copy()

    fn = old.procs[old.current].ofile(fd)
    new.files[fn].offset = offset

    return cond, util.If(cond, new, old)
def procs_equiv(conj, ctx, kernelstate):
    pid = util.FreshBitVec('pid', dt.pid_t)
    fd = util.FreshBitVec('fd', dt.fd_t)
    idx = util.FreshBitVec('vector_index', 64)

    proc_field_equiv(conj, pid, ctx, kernelstate, 'state')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'ppid')

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(is_pid_valid(pid), (util.global_field_element(
                      ctx, '@proc_table', 'killed',
                      pid) == 0) == z3.Not(kernelstate.procs[pid].killed))))

    proc_field_equiv(conj, pid, ctx, kernelstate, 'ipc_from')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'ipc_val')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'ipc_page')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'ipc_size')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'ipc_fd')

    conj.append(
        z3.ForAll([pid, fd],
                  z3.Implies(
                      z3.And(is_pid_valid(pid), is_fd_valid(fd)),
                      util.global_field_element(
                          ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(
                              32, fd)) == kernelstate.procs[pid].ofile(fd))))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_children',
                          pid) == kernelstate.procs[pid].nr_children())))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_fds',
                          pid) == kernelstate.procs[pid].nr_fds())))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_pages',
                          pid) == kernelstate.procs[pid].nr_pages())))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_dmapages',
                          pid) == kernelstate.procs[pid].nr_dmapages())))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_devs',
                          pid) == kernelstate.procs[pid].nr_devs())))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_ports',
                          pid) == kernelstate.procs[pid].nr_ports())))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_vectors',
                          pid) == kernelstate.procs[pid].nr_vectors())))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_intremaps',
                          pid) == kernelstate.procs[pid].nr_intremaps())))

    proc_field_equiv(conj, pid, ctx, kernelstate, 'stack')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'hvm')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'page_table_root')

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(is_pid_valid(pid), (util.global_field_element(
                      ctx, '@proc_table', 'use_io_bitmap',
                      pid) == 0) == z3.Not(
                          kernelstate.procs[pid].use_io_bitmap))))

    proc_field_equiv(conj, pid, ctx, kernelstate, 'io_bitmap_a')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'io_bitmap_b')

    conj.append(
        z3.ForAll([pid, idx],
                  z3.Implies(
                      z3.And(is_pid_valid(pid), z3.ULT(idx, 4)),
                      util.global_field_element(
                          ctx, '@proc_table', 'intr', pid,
                          idx) == kernelstate.procs[pid].intr(idx))))

    conj.append(
        z3.ForAll(
            [pid],
            ctx.globals['#tlbinv'](pid) == kernelstate.procs[pid].tlbinv))

    conj.append(ctx.globals['#iotlbinv'] == kernelstate.iotlbinv)
Esempio n. 11
0
def send_recv(old, pid, val, inpn, size, infd, outpn, outfd):
    cond = z3.And(
        is_pid_valid(pid),

        old.procs[pid].state == dt.proc_state.PROC_SLEEPING,

        # inpn is a valid pn and belongs to current
        is_pn_valid(inpn),
        old.pages[inpn].owner == old.current,

        z3.ULE(size, dt.PAGE_SIZE),

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

        # outpn is a valid pn and belongs to current
        is_pn_valid(outpn),
        old.pages[outpn].owner == old.current,
        old.pages[outpn].type == dt.page_type.PAGE_TYPE_FRAME,

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

        # if ipc from is set, it must be set to current
        z3.Implies(old.procs[pid].ipc_from != 0,
                   old.procs[pid].ipc_from == old.current)
    )

    new = old.copy()

    new.procs[old.current].ipc_page = outpn
    new.procs[old.current].ipc_fd = outfd

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

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

    new.procs[pid].ipc_size = size

    new2 = new.copy()

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

    fn = old.procs[old.current].ofile(infd)
    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[old.current].state = dt.proc_state.PROC_SLEEPING
    new3.procs[pid].state = dt.proc_state.PROC_RUNNING

    return cond, util.If(cond, new3, old)
def impl_invariants_py(ctx):
    conj = []

    pid = util.FreshBitVec('pid', dt.pid_t)
    pn = util.FreshBitVec('pn', dt.pn_t)
    fd = util.FreshBitVec('fd', dt.fd_t)
    # fn = util.FreshBitVec('fn', dt.fn_t)

    # embryos, runnable or running processes own the pages in their structs
    conj.append(z3.ForAll([pid], z3.Implies(
        z3.Or(util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_EMBRYO,
              util.global_field_element(
                  ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_RUNNING,
              util.global_field_element(
                  ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_RUNNABLE,
              util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_SLEEPING),
        z3.And(
            pn_has_owner_and_type(ctx, util.global_field_element(
                ctx, '@proc_table', 'page_table_root', pid), pid, dt.page_type.PAGE_TYPE_X86_PML4),
            pn_has_owner_and_type(ctx, util.global_field_element(
                ctx, '@proc_table', 'hvm', pid), pid, dt.page_type.PAGE_TYPE_PROC_DATA),
            pn_has_owner_and_type(ctx, util.global_field_element(
                ctx, '@proc_table', 'stack', pid), pid, dt.page_type.PAGE_TYPE_PROC_DATA),
        ))))

    # sleeping processes own their ipc_page
    conj.append(z3.ForAll([pid], z3.Implies(z3.And(is_pid_valid(pid),
        util.global_field_element(ctx, '@proc_table', 'state', pid) != dt.proc_state.PROC_ZOMBIE),
        z3.Implies(util.global_field_element(ctx, '@proc_table', 'use_io_bitmap', pid) != 0,
            z3.And(
                is_pn_valid(util.global_field_element(ctx, '@proc_table', 'io_bitmap_a', pid)),
                is_pn_valid(util.global_field_element(ctx, '@proc_table', 'io_bitmap_b', pid)),
                pn_has_owner_and_type(ctx, util.global_field_element(ctx, '@proc_table', 'io_bitmap_a', pid), pid, dt.page_type.PAGE_TYPE_PROC_DATA),
                pn_has_owner_and_type(ctx, util.global_field_element(ctx, '@proc_table', 'io_bitmap_b', pid), pid, dt.page_type.PAGE_TYPE_PROC_DATA))))))

    # sleeping processes own their ipc_page
    conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid),
        z3.Implies(util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_SLEEPING,
            pn_has_owner_and_type(ctx, util.global_field_element(ctx, '@proc_table', 'ipc_page', pid), pid, dt.page_type.PAGE_TYPE_FRAME)))))

    conj.append(z3.ForAll([pid],
                          z3.And(
        is_pn_valid(util.global_field_element(
                ctx, '@proc_table', 'page_table_root', pid)),
        is_pn_valid(util.global_field_element(
            ctx, '@proc_table', 'hvm', pid)),
        is_pn_valid(util.global_field_element(ctx, '@proc_table', 'stack', pid)))))

    # sleeping processes' ipc fd are empty if valid
    conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid),
        z3.Implies(util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_SLEEPING,
            z3.Implies(is_fd_valid(util.global_field_element(ctx, '@proc_table', 'ipc_fd', pid)),
                util.global_field_element(ctx, '@proc_table', 'ofile', pid,
                    z3.ZeroExt(32, util.global_field_element(ctx, '@proc_table', 'ipc_fd', pid))) == z3.BitVecVal(0, dt.fn_t))))))

    conj.append(z3.ForAll([pid],
                          z3.And(
        is_pn_valid(util.global_field_element(
                ctx, '@proc_table', 'page_table_root', pid)),
        is_pn_valid(util.global_field_element(
            ctx, '@proc_table', 'hvm', pid)),
        is_pn_valid(util.global_field_element(ctx, '@proc_table', 'stack', pid)))))

    # page has an owner <=> page is not free
    conj.append(z3.ForAll([pn], z3.Implies(is_pn_valid(pn),
        is_pid_valid(util.global_field_element(ctx, '@page_desc_table', 'pid', pn)) ==
        (util.global_field_element(ctx, '@page_desc_table', 'type', pn) != dt.page_type.PAGE_TYPE_FREE))))

    # unused procs have zero refcnt
    conj.append(z3.ForAll([pid], z3.Implies(
        z3.And(
            is_pid_valid(pid),
            util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_UNUSED),
        z3.And(
            util.global_field_element(ctx, '@proc_table', 'nr_children', pid) == z3.BitVecVal(0, dt.size_t),
            util.global_field_element(ctx, '@proc_table', 'nr_fds', pid) == z3.BitVecVal(0, dt.size_t),
            util.global_field_element(ctx, '@proc_table', 'nr_pages', pid) == z3.BitVecVal(0, dt.size_t),
            util.global_field_element(ctx, '@proc_table', 'nr_dmapages', pid) == z3.BitVecVal(0, dt.size_t),
            util.global_field_element(ctx, '@proc_table', 'nr_devs', pid) == z3.BitVecVal(0, dt.size_t),
            util.global_field_element(ctx, '@proc_table', 'nr_ports', pid) == z3.BitVecVal(0, dt.size_t),
            util.global_field_element(ctx, '@proc_table', 'nr_vectors', pid) == z3.BitVecVal(0, dt.size_t),
            util.global_field_element(ctx, '@proc_table', 'nr_intremaps', pid) == 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(
    #         util.global_field_element(ctx, '@proc_table', 'nr_fds', pid) == z3.BitVecVal(0, dt.size_t),
    #         z3.Not(is_fn_valid(util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd))))))))

    # # unused procs have zero fds
    # conj.append(z3.ForAll([pid, fd], z3.Implies(z3.And(is_pid_valid(pid), is_fd_valid(fd)),
    #     z3.Implies(
    #         util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_UNUSED,
    #         util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd)) == z3.BitVecVal(0, dt.fn_t)))))

    # fds valid
    conj.append(z3.ForAll([pid, fd], z3.Implies(z3.And(is_pid_valid(pid), is_fd_valid(fd)),
        z3.Or(
            util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd)) == z3.BitVecVal(0, dt.fn_t),
            is_fn_valid(util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd)))))))

    # # FD_NONE's refcount is 0
    # conj.append(z3.ForAll([fn], z3.Implies(
    #     z3.And(
    #         is_fn_valid(fn),
    #         util.global_field_element(ctx, '@file_table', 'type', fn) == dt.file_type.FD_NONE),
    #     util.global_field_element(ctx, '@file_table', 'refcnt', fn) == z3.BitVecVal(0, dt.size_t))))

    # # FD never points to FD_NONE
    # conj.append(z3.ForAll([pid, fd], z3.Implies(
    #     z3.And(
    #         is_pid_valid(pid),
    #         is_fd_valid(fd),
    #         is_fn_valid(util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd)))),
    #     util.global_field_element(ctx, '@file_table', 'type',
    #         util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd))) != dt.file_type.FD_NONE)))

    # page freelist is well formed
    conj.append(z3.ForAll([pn], z3.Implies(is_pn_valid(pn),
        z3.And(
            is_pn_valid(util.global_field_element(ctx, '@page_desc_table', ['link', 'prev'], pn)),
            is_pn_valid(util.global_field_element(ctx, '@page_desc_table', ['link', 'next'], pn))))))

    # ready queue is well formed
    # don't use is_pid_valid as some ->prev and ->next are zeros
    conj.append(z3.ForAll([pid], z3.Implies(is_pid_bounded(pid),
        z3.And(
            is_pid_bounded(util.global_field_element(ctx, '@proc_table', ['ready', 'prev'], pid)),
            is_pid_bounded(util.global_field_element(ctx, '@proc_table', ['ready', 'next'], pid))))))

    # Current is always a valid and running
    conj.append(is_pid_valid(util.global_value(ctx, '@current')))

    conj.append(util.global_field_element(ctx, '@proc_table', 'state', util.global_value(ctx, '@current')) == dt.proc_state.PROC_RUNNING)

    return z3.And(*conj)
def spec_invariants(kernelstate):
    conj = []

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

    #
    # procs' page table, hvm and stack are
    #
    # 1) valid
    conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid),
        z3.And(
            is_pn_valid(kernelstate.procs[pid].page_table_root),
            is_pn_valid(kernelstate.procs[pid].hvm),
            is_pn_valid(kernelstate.procs[pid].stack)))))
    # 2) owned by that proc
    conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid),
        z3.Implies(
            is_status_live(kernelstate.procs[pid].state),
            z3.And(
                kernelstate.pages[kernelstate.procs[pid].page_table_root].owner == pid,
                kernelstate.pages[kernelstate.procs[pid].hvm].owner == pid,
                kernelstate.pages[kernelstate.procs[pid].stack].owner == pid)))))

    # 3) have the correct type
    conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid),
        z3.Implies(
            is_status_live(kernelstate.procs[pid].state),
            z3.And(
                kernelstate.pages[kernelstate.procs[pid].page_table_root].type == dt.page_type.PAGE_TYPE_X86_PML4,
                kernelstate.pages[kernelstate.procs[pid].hvm].type == dt.page_type.PAGE_TYPE_PROC_DATA,
                kernelstate.pages[kernelstate.procs[pid].stack].type == dt.page_type.PAGE_TYPE_PROC_DATA)))))

    ##

    # Sleeping PROC's ipc_page is a frame owned by that pid
    conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid),
        z3.Implies(
            kernelstate.procs[pid].state == dt.proc_state.PROC_SLEEPING,
            z3.And(
                is_pn_valid(kernelstate.procs[pid].ipc_page),
                kernelstate.pages[kernelstate.procs[pid]
                    .ipc_page].type == dt.page_type.PAGE_TYPE_FRAME,
                kernelstate.pages[kernelstate.procs[pid].ipc_page].owner == pid)))))

    ## Non-zombie procs with use_io_bitmaps own their (valid) bitmap pages
    conj.append(z3.ForAll([pid],
        z3.Implies(
            z3.And(
                is_pid_valid(pid),
                kernelstate.procs[pid].use_io_bitmap,
                kernelstate.procs[pid].state != dt.proc_state.PROC_ZOMBIE),
            z3.And(
                is_pn_valid(kernelstate.procs[pid].io_bitmap_a),
                is_pn_valid(kernelstate.procs[pid].io_bitmap_b),
                kernelstate.pages[kernelstate.procs[pid].io_bitmap_a].owner == pid,
                kernelstate.pages[kernelstate.procs[pid].io_bitmap_b].owner == pid,
                kernelstate.pages[kernelstate.procs[pid].io_bitmap_a].type == dt.page_type.PAGE_TYPE_PROC_DATA,
                kernelstate.pages[kernelstate.procs[pid].io_bitmap_b].type == dt.page_type.PAGE_TYPE_PROC_DATA))))

    # page has an owner <=> page is not free
    conj.append(z3.ForAll([pn], z3.Implies(is_pn_valid(pn),
        is_pid_valid(kernelstate.pages[pn].owner) == (kernelstate.pages[pn].type != dt.page_type.PAGE_TYPE_FREE))))

    conj.append(z3.ForAll([pn], z3.Implies(is_pn_valid(pn),
        z3.Implies(kernelstate.pages[pn].type == dt.page_type.PAGE_TYPE_FREE,
            z3.Not(is_pid_valid(kernelstate.pages[pn].owner))))))

    # a sleeping proc's ipc_fd is either invalid or empty
    conj.append(z3.ForAll([pid], z3.Implies(z3.And(
        is_pid_valid(pid),
        kernelstate.procs[pid].state == dt.proc_state.PROC_SLEEPING),

        z3.Or(z3.Not(is_fd_valid(kernelstate.procs[pid].ipc_fd)),
              z3.Not(is_fn_valid(kernelstate.procs[pid].ofile(kernelstate.procs[pid].ipc_fd)))))))

    ##############
    # Unused procs's refcount is all zero
    # conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid),
    #     z3.Implies(kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED,
    #         z3.And(
    # kernelstate.procs[pid].nr_pages(dt.NPAGE - 1) == z3.BitVecVal(0, dt.size_t))))))
    # kernelstate.procs[pid].nr_children(dt.NPROC - 1) == z3.BitVecVal(0, dt.size_t),
    # kernelstate.procs[pid].nr_fds(dt.NOFILE - 1) == z3.BitVecVal(0, dt.size_t),
    # kernelstate.procs[pid].nr_devs(dt.NPCIDEV - 1) == 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))))

    # # unused procs don't have fds
    # conj.append(z3.ForAll([pid, fd], z3.Implies(
    #     z3.And(
    #         is_pid_valid(pid),
    #         kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED),
    #     z3.Not(is_fn_valid(kernelstate.procs[pid].ofile(fd))))))

    # unused fn has refcount == 0
    # conj.append(z3.ForAll([fn], z3.Implies(is_fn_valid(fn),
    #     z3.Implies(kernelstate.files[fn].type == dt.file_type.FD_NONE,
    #         kernelstate.files[fn].refcnt(
    #             z3.Concat(
    #                 z3.BitVecVal(dt.NPROC - 1, dt.pid_t),
    #                 z3.BitVecVal(dt.NOFILE - 1, dt.fd_t))) == z3.BitVecVal(0, dt.size_t)))))

    ##############

    # disjointed-ness of memory regions
    conj.append(z3.And(
        z3.Extract(63, 40, z3.UDiv(kernelstate.pages_ptr_to_int, util.i64(4096)) + dt.NPAGES_PAGES) == z3.BitVecVal(0, 24),
        z3.Extract(63, 40, z3.UDiv(kernelstate.proc_table_ptr_to_int, util.i64(4096)) + dt.NPAGES_PROC_TABLE) == z3.BitVecVal(0, 24),
        z3.Extract(63, 40, z3.UDiv(kernelstate.page_desc_table_ptr_to_int, util.i64(4096)) + dt.NPAGES_PAGE_DESC_TABLE) == z3.BitVecVal(0, 24),
        z3.Extract(63, 40, z3.UDiv(kernelstate.file_table_ptr_to_int, util.i64(4096)) + dt.NPAGES_FILE_TABLE) == z3.BitVecVal(0, 24),
        z3.Extract(63, 40, z3.UDiv(kernelstate.devices_ptr_to_int,util.i64(4096)) + dt.NPAGES_DEVICES) == z3.BitVecVal(0, 24),
        z3.Extract(63, 40, z3.UDiv(kernelstate.dmapages_ptr_to_int,util.i64(4096)) + dt.NDMAPAGE) == z3.BitVecVal(0, 24),

        z3.Extract(63, 40, z3.UDiv(kernelstate.pages_ptr_to_int, util.i64(4096))) == z3.BitVecVal(0, 24),
        z3.Extract(63, 40, z3.UDiv(kernelstate.proc_table_ptr_to_int, util.i64(4096))) == z3.BitVecVal(0, 24),
        z3.Extract(63, 40, z3.UDiv(kernelstate.page_desc_table_ptr_to_int, util.i64(4096))) == z3.BitVecVal(0, 24),
        z3.Extract(63, 40, z3.UDiv(kernelstate.file_table_ptr_to_int, util.i64(4096))) == z3.BitVecVal(0, 24),
        z3.Extract(63, 40, z3.UDiv(kernelstate.devices_ptr_to_int, util.i64(4096))) == z3.BitVecVal(0, 24),
        z3.Extract(63, 40, z3.UDiv(kernelstate.dmapages_ptr_to_int, util.i64(4096))) == z3.BitVecVal(0, 24),

        z3.ULT(z3.UDiv(kernelstate.pages_ptr_to_int, util.i64(4096)) + dt.NPAGES_PAGES, z3.UDiv(kernelstate.proc_table_ptr_to_int, util.i64(4096))),
        z3.ULT(z3.UDiv(kernelstate.proc_table_ptr_to_int, util.i64(4096)) + dt.NPAGES_PROC_TABLE, z3.UDiv(kernelstate.page_desc_table_ptr_to_int, util.i64(4096))),
        z3.ULT(z3.UDiv(kernelstate.page_desc_table_ptr_to_int, util.i64(4096)) + dt.NPAGES_PAGE_DESC_TABLE, z3.UDiv(kernelstate.file_table_ptr_to_int, util.i64(4096))),
        z3.ULT(z3.UDiv(kernelstate.file_table_ptr_to_int, util.i64(4096)) + dt.NPAGES_FILE_TABLE, z3.UDiv(kernelstate.devices_ptr_to_int, util.i64(4096))),
        z3.ULT(z3.UDiv(kernelstate.devices_ptr_to_int, util.i64(4096)) + dt.NPCIDEV, z3.UDiv(kernelstate.dmapages_ptr_to_int, util.i64(4096))),
        z3.ULT(z3.UDiv(kernelstate.dmapages_ptr_to_int, util.i64(4096)) + dt.NDMAPAGE, z3.UDiv(dt.PCI_START, util.i64(4096))),
    ))

    # Current is a valid pid
    conj.append(is_pid_valid(kernelstate.current))
    # Current is always running
    conj.append(kernelstate.procs[kernelstate.current].state == dt.proc_state.PROC_RUNNING),
    # A running proc must be current
    conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid),
        z3.Implies(kernelstate.procs[pid].state == dt.proc_state.PROC_RUNNING,
            pid == kernelstate.current))))

    return z3.And(*conj)