def pages_equiv(conj, ctx, kernelstate):
    pn = util.FreshBitVec('pn', dt.pn_t)
    idx = util.FreshBitVec('page_index', 64)

    conj.append(
        z3.ForAll([pn, idx],
                  z3.Implies(
                      z3.And(is_pn_valid(pn), z3.ULT(idx, 512)),
                      util.global_to_uf_dict(ctx, '@pages')[()](
                          util.i64(0), pn,
                          idx) == kernelstate.pages[pn].data(idx))))

    conj.append(
        z3.ForAll(
            [pn],
            z3.Implies(
                is_pn_valid(pn),
                util.global_field_element(ctx, '@page_desc_table', 'pid',
                                          pn) == kernelstate.pages[pn].owner)))

    conj.append(
        z3.ForAll(
            [pn],
            z3.Implies(
                is_pn_valid(pn),
                util.global_field_element(ctx, '@page_desc_table', 'type',
                                          pn) == kernelstate.pages[pn].type)))
Ejemplo n.º 2
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)
Ejemplo n.º 3
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)
Ejemplo n.º 4
0
def sys_map_pcipage(old, pt, index, pcipn, perm):
    cond = z3.And(
        # pt is a valid PT page
        is_pn_valid(pt),
        old.pages[pt].type == dt.page_type.PAGE_TYPE_X86_PT,
        old.pages[pt].owner == old.current,
        z3.ULT(index, 512),

        # pcipn is a valid pci page owned by current
        is_pcipn_valid(pcipn),
        old.pcipages[pcipn].valid,
        old.pci[old.pcipages[pcipn].owner].owner == old.current,

        # 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,

        # slot should be empty
        old.pages[pt].data(index) & dt.PTE_P == 0,
    )

    new = old.copy()

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

    # maintain the "shadow" pgtable
    new.pages[pt].pgtable_pn[index] = pcipn
    new.pages[pt].pgtable_perm[index] = perm
    new.pages[pt].pgtable_type[index] = dt.PGTYPE_PCIPAGE

    new.flush_tlb(old.current)

    return cond, util.If(cond, new, old)
Ejemplo n.º 5
0
def sys_alloc_iommu_frame(old, frm, index, to, perm):
    cond = z3.And(
        # to page is valid and free
        is_dmapn_valid(to),
        old.dmapages[to].type == dt.page_type.PAGE_TYPE_FREE,

        # from page is a valid page with correct type
        is_pn_valid(frm),
        old.pages[frm].type == dt.page_type.PAGE_TYPE_IOMMU_PT,
        old.pages[frm].owner == old.current,

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

        # permission bits check
        perm & (dt.MAX_INT64 ^ (dt.DMAR_PTE_R | dt.DMAR_PTE_W)) == 0,

        old.pages[frm].data(index) == 0,
    )

    new = old.copy()

    new.pages[frm].data[index] = (new.dmapages_ptr_to_int + to * dt.PAGE_SIZE) | perm
    new.pages[frm].pgtable_pn[index] = to
    new.pages[frm].pgtable_perm[index] = perm

    new.dmapages[to].type = dt.page_type.PAGE_TYPE_IOMMU_FRAME
    new.dmapages[to].owner = new.current
    new.procs[new.current].nr_dmapages[to] += 1

    new.flush_iotlb()

    return cond, util.If(cond, new, old)
Ejemplo n.º 6
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)
Ejemplo n.º 7
0
def sys_protect_frame(old, pt, index, frame, perm):
    cond = z3.And(
        is_pn_valid(pt),
        old.pages[pt].type == dt.page_type.PAGE_TYPE_X86_PT,
        old.pages[pt].owner == old.current,

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

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

        # index must be preset
        old.pages[pt].data(index) & dt.PTE_P != 0,

        # the entry in the pt must be the frame
        z3.Extract(63, 40, z3.UDiv(old.pages_ptr_to_int,
                                   util.i64(dt.PAGE_SIZE)) + frame) == z3.BitVecVal(0, 24),
        z3.Extract(39, 0, z3.UDiv(old.pages_ptr_to_int, util.i64(
            dt.PAGE_SIZE)) + frame) == z3.Extract(51, 12, old.pages[pt].data(index)),

        # no unsafe bits in perm is set
        perm & (dt.MAX_INT64 ^ dt.PTE_PERM_MASK) == 0,

        # P bit is set in perm
        perm & dt.PTE_P != 0
    )

    new = old.copy()

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

    # The only thing that changed is the permission.
    new.pages[pt].pgtable_perm[index] = perm

    new.flush_tlb(old.current)

    return cond, util.If(cond, new, old)
Ejemplo n.º 8
0
def sys_alloc_io_bitmap(old, pn1, pn2, pn3):
    cond = z3.And(
        pn1 + 1 == pn2,
        pn2 + 1 == pn3,

        z3.Not(old.procs[old.current].use_io_bitmap),

        is_pn_valid(pn1),
        old.pages[pn1].type == dt.page_type.PAGE_TYPE_FREE,

        is_pn_valid(pn2),
        old.pages[pn2].type == dt.page_type.PAGE_TYPE_FREE,

        is_pn_valid(pn3),
        old.pages[pn3].type == dt.page_type.PAGE_TYPE_FREE,
    )

    new = old.copy()

    new.pages[pn1].owner = old.current
    new.pages[pn1].type = dt.page_type.PAGE_TYPE_PROC_DATA
    new.pages[pn1].data = util.i64(0xffffffffffffffff)
    new.procs[old.current].nr_pages[pn1] += 1

    new.pages[pn2].owner = old.current
    new.pages[pn2].type = dt.page_type.PAGE_TYPE_PROC_DATA
    new.pages[pn2].data = util.i64(0xffffffffffffffff)
    new.procs[old.current].nr_pages[pn2] += 1

    new.pages[pn3].owner = old.current
    new.pages[pn3].type = dt.page_type.PAGE_TYPE_PROC_DATA
    new.pages[pn3].data = util.i64(0xffffffffffffffff)
    new.procs[old.current].nr_pages[pn3] += 1

    new.procs[old.current].io_bitmap_a = pn1
    new.procs[old.current].io_bitmap_b = pn2
    new.procs[old.current].use_io_bitmap = z3.BoolVal(True)

    return cond, util.If(cond, new, old)
Ejemplo n.º 9
0
def free_page_table_page(old, frm, index, to, from_type, to_type):
    cond = z3.And(
        # The frm pn has the correct type and owned by current
        is_pn_valid(frm),
        old.pages[frm].type == from_type,
        old.pages[frm].owner == old.current,

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

        # The to pn has the correct type and owned by current
        is_pn_valid(to),
        old.pages[to].type == to_type,
        old.pages[to].owner == old.current,

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

        # The current pgtable entry matches to...
        z3.Extract(63, 40, z3.UDiv(old.pages_ptr_to_int,
                                   util.i64(dt.PAGE_SIZE)) + to) == z3.BitVecVal(0, 24),
        z3.Extract(39, 0, z3.UDiv(old.pages_ptr_to_int, util.i64(
            dt.PAGE_SIZE)) + to) == z3.Extract(51, 12, old.pages[frm].data(index)),
    )

    new = old.copy()

    new.pages[frm].data[index] = util.i64(0)

    new.pages[to].owner = z3.BitVecVal(0, dt.pid_t)
    new.pages[to].type = dt.page_type.PAGE_TYPE_FREE

    new.procs[old.current].nr_pages[to] -= 1

    new.flush_tlb(old.current)

    return cond, util.If(cond, new, old)
Ejemplo n.º 10
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)
Ejemplo n.º 11
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)
Ejemplo n.º 12
0
def spec_corollary_iommu_pgwalk(kernelstate):
    devid = util.FreshBitVec('devid', dt.devid_t)
    pid = kernelstate.pci[devid].owner
    va = dt.FreshVA()

    pml4 = kernelstate.pci[devid].page_table_root
    page, writable, present = page_walk(kernelstate, pml4, *va)

    isolation = util.Implies(
        present, kernelstate.dmapages[page].owner == pid,
        kernelstate.dmapages[page].type == dt.page_type.PAGE_TYPE_IOMMU_FRAME)

    return z3.ForAll([devid] + va,
                     z3.Implies(z3.And(
                         is_pn_valid(pml4),
                         is_va_valid(va),
                     ), isolation))
Ejemplo n.º 13
0
def spec_corollary_iommu_pgwalk(kernelstate):
    devid = util.FreshBitVec('devid', dt.devid_t)
    pid = kernelstate.pci[devid].owner
    va = dt.FreshVA()

    pml4 = kernelstate.pci[devid].page_table_root
    page, writable, present = page_walk(kernelstate, pml4, *va)

    # after iommu page walk, if p bit present and the page returned from page walk, then page.type must equals to FRAME
    # if P bit present && dmapage.owner == pid ==> page.type == PE_IOMMU_FRAME
    isolation = util.Implies(
        present, kernelstate.dmapages[page].owner == pid,
        kernelstate.dmapages[page].type == dt.page_type.PAGE_TYPE_IOMMU_FRAME)
    # valid(pml4) && valide(va) ==> isolation
    return z3.ForAll([devid] + va,
                     z3.Implies(z3.And(
                         is_pn_valid(pml4),
                         is_va_valid(va),
                     ), isolation))
Ejemplo n.º 14
0
def spec_lemma_iommu_tlb_ok(kernelstate, oldstate=None):
    if kernelstate is oldstate or oldstate is None:
        return z3.BoolVal(True)

    pn = util.FreshBitVec('pn', dt.pn_t)
    idx = util.FreshBitVec('page_index', dt.size_t)

    return z3.ForAll(
        [pn, idx],
        z3.Implies(
            z3.And(
                # Arguments are valid
                is_pn_valid(pn),
                z3.ULT(idx, 512),
                is_iommu_page_table_type(oldstate.pages[pn].type)),
            z3.And(
                z3.Implies(
                    oldstate.pages[pn].data(idx) !=
                    kernelstate.pages[pn].data(idx), kernelstate.iotlbinv))))
Ejemplo n.º 15
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)
Ejemplo n.º 16
0
def sys_alloc_iommu_root(old, devid, pn):
    cond = z3.And(
        old.pci[devid].owner == 0,
        is_pn_valid(pn),
        old.pages[pn].type == dt.page_type.PAGE_TYPE_FREE,
    )

    new = old.copy()

    new.pci[devid].owner = old.current
    new.pci[devid].page_table_root = pn

    new.pages[pn].owner = old.current
    new.pages[pn].type = dt.page_type.PAGE_TYPE_IOMMU_PML4
    # bzero page
    new.pages[pn].data = util.i64(0)
    new.procs[old.current].nr_pages[pn] += 1

    new.procs[new.current].nr_devs[devid] += 1

    new.flush_iotlb()

    return cond, util.If(cond, new, old)
Ejemplo n.º 17
0
def sys_map_iommu_frame(old, pt, index, to, perm):
    cond = z3.And(
        # to is a valid IOMMU_FRAME owned by current
        is_dmapn_valid(to),
        old.dmapages[to].type == dt.page_type.PAGE_TYPE_IOMMU_FRAME,
        old.dmapages[to].owner == old.current,

        # pt is a valid X86_PT page owned by current
        is_pn_valid(pt),
        old.pages[pt].type == dt.page_type.PAGE_TYPE_X86_PT,
        old.pages[pt].owner == old.current,

        # Index valid
        z3.ULT(index, 512),

        # permissions contain no unsafe bits
        perm & (dt.MAX_INT64 ^ dt.PTE_PERM_MASK) == 0,
        perm & dt.PTE_P != 0,


        # index slot is unused in pt
        old.pages[pt].data(index) & dt.PTE_P == 0,
    )

    new = old.copy()

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

    new.pages[pt].pgtable_pn[index] = to
    new.pages[pt].pgtable_perm[index] = perm
    new.pages[pt].pgtable_type[index] = dt.PGTYPE_IOMMU_FRAME

    new.flush_tlb(old.current)

    return cond, util.If(cond, new, old)
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)
Ejemplo n.º 19
0
def spec_lemma_isolation(kernelstate):
    conj = []

    conj.append(spec_lemma_nr_pages_refcnt(kernelstate))

    pn = util.FreshBitVec('pn', dt.pn_t)
    idx = util.FreshBitVec('page_index', dt.size_t)

    conj.append(
        z3.ForAll(
            [pn, idx],
            z3.Implies(
                z3.And(
                    # Arguments are valid
                    is_pn_valid(pn),
                    z3.ULT(idx, 512),

                    # This is a page table and the idx entry does have the P bit set
                    is_page_table_type(kernelstate.pages[pn].type),
                    (kernelstate.pages[pn].data(idx) & dt.PTE_P) != 0,

                    # The owner of this page has a valid pid and is either EMBRYO, RUNNABLE or RUNNING
                    is_status_live(
                        kernelstate.procs[kernelstate.pages[pn].owner].state),
                    is_pid_valid(kernelstate.pages[pn].owner),
                ),
                z3.And(
                    z3.ULT(kernelstate.pages[pn].pgtable_type(idx),
                           dt.PGTYPE_NONE),

                    # The contents of the shadow pgatble correspond to the *actual*
                    # contents of those pages according to our pn->pfn mapping fn.
                    kernelstate.pages[pn].data(idx) == pgentry2pfn(
                        kernelstate,
                        kernelstate.pages[pn].pgtable_pn(
                            idx
                        ),  # page numbers. represent counts not a number
                        kernelstate.pages[pn].pgtable_perm(idx),  # permission
                        kernelstate.pages[pn].pgtable_type(idx)),  # page type
                    z3.Extract(62, 12, kernelstate.pages[pn].pgtable_perm(idx))
                    == z3.BitVecVal(0, 51),  # 62 - 12 must 0

                    # For the specification each entry in the pgtable has a type.
                    # For each of those type, we must specify how it can appear in a
                    # the page table.

                    # If the entry is a page
                    z3.Implies(
                        kernelstate.pages[pn].pgtable_type(idx) ==
                        dt.PGTYPE_PAGE,
                        z3.And(
                            # The entry must be a valid pn
                            is_pn_valid(kernelstate.pages[pn].pgtable_pn(idx)),

                            # The pn must be owned by the owner of the pgtable
                            kernelstate.pages[
                                kernelstate.pages[pn].pgtable_pn(idx)
                            ].owner == kernelstate.pages[pn].owner,

                            # Either the subtypes match (PML4 => PDPT, PDPT => PD, etc) and is a unique mapping.
                            # Or PML4 points to a *read_only* PML4 page.
                            z3.Or(
                                z3.And(
                                    get_sub_type(kernelstate.pages[pn].type) ==
                                    kernelstate.pages[kernelstate.pages[pn].
                                                      pgtable_pn(idx)].type,
                                    # The mapping from a pgtable <pn, idx> to a page is unique
                                    kernelstate.pages[
                                        kernelstate.pages[pn].pgtable_pn(idx)
                                    ].pgtable_reverse_pn == pn,
                                    kernelstate.pages[
                                        kernelstate.pages[pn].pgtable_pn(
                                            idx)].pgtable_reverse_idx == idx,
                                ),
                                z3.And(
                                    kernelstate.pages[pn].type ==
                                    dt.page_type.PAGE_TYPE_X86_PML4,
                                    kernelstate.pages[kernelstate.pages[pn].
                                                      pgtable_pn(idx)].type ==
                                    dt.page_type.PAGE_TYPE_X86_PML4,
                                    kernelstate.pages[pn].pgtable_perm(idx)
                                    & dt.PTE_W == 0)))),

                    # If the entry is a proc
                    z3.Implies(
                        kernelstate.pages[pn].pgtable_type(idx) ==
                        dt.PGTYPE_PROC,
                        z3.And(
                            # This must be at the PT level in the pgtable
                            kernelstate.pages[pn].type ==
                            dt.page_type.PAGE_TYPE_X86_PT,

                            # There are 3 proc pages (PS:dt.NPAGES_PROC_TABLE = 6) use 6 page tables keep process information.
                            z3.ULT(kernelstate.pages[pn].pgtable_pn(idx),
                                   dt.NPAGES_PROC_TABLE),

                            # proc pages can not be mapped writable
                            kernelstate.pages[pn].pgtable_perm(idx)
                            & dt.PTE_W == 0)),

                    # If the entry is a page_desc
                    z3.Implies(
                        kernelstate.pages[pn].pgtable_type(idx) ==
                        dt.PGTYPE_PAGE_DESC,
                        z3.And(
                            # This must be at the PT level in the pgtable
                            kernelstate.pages[pn].type ==
                            dt.page_type.PAGE_TYPE_X86_PT,

                            # There are 15 page desc pages (ps: dt.NPAGES_PAGE_DESC_TABLE = 60)
                            z3.ULT(kernelstate.pages[pn].pgtable_pn(idx),
                                   dt.NPAGES_PAGE_DESC_TABLE),

                            # page desc pages can not be mapped writable
                            kernelstate.pages[pn].pgtable_perm(idx)
                            & dt.PTE_W == 0)),

                    # If the entry is file table
                    z3.Implies(
                        kernelstate.pages[pn].pgtable_type(idx) ==
                        dt.PGTYPE_FILE_TABLE,
                        z3.And(
                            # This must be at the PT level in the pgtable
                            kernelstate.pages[pn].type ==
                            dt.page_type.PAGE_TYPE_X86_PT,

                            # There are 2 page desc pages
                            z3.ULT(kernelstate.pages[pn].pgtable_pn(idx),
                                   dt.NPAGES_FILE_TABLE),

                            # file tables can not be mapped writable
                            kernelstate.pages[pn].pgtable_perm(idx)
                            & dt.PTE_W == 0)),

                    # If the entry is devices
                    z3.Implies(
                        kernelstate.pages[pn].pgtable_type(idx) ==
                        dt.PGTYPE_DEVICES,
                        z3.And(
                            # This must be at the PT level in the pgtable
                            kernelstate.pages[pn].type ==
                            dt.page_type.PAGE_TYPE_X86_PT,

                            # There are 64 page desc pages
                            z3.ULT(kernelstate.pages[pn].pgtable_pn(idx),
                                   dt.NPAGES_DEVICES),

                            # page desc pages can not be mapped writable
                            kernelstate.pages[pn].pgtable_perm(idx)
                            & dt.PTE_W == 0)),

                    # If the entry is an IOMMU_FRAME
                    z3.Implies(
                        kernelstate.pages[pn].pgtable_type(idx) ==
                        dt.PGTYPE_IOMMU_FRAME,
                        z3.And(
                            # This must be at the PT level in the pgtable
                            kernelstate.pages[pn].type ==
                            dt.page_type.PAGE_TYPE_X86_PT,

                            # There are NDMAPAGES many dma pages that can be mapped
                            z3.ULT(kernelstate.pages[pn].pgtable_pn(idx),
                                   dt.NDMAPAGE),

                            # must be an IOMMU_FRAME
                            kernelstate.dmapages[
                                kernelstate.pages[pn].pgtable_pn(idx)
                            ].type == dt.page_type.PAGE_TYPE_IOMMU_FRAME,

                            # the dma page must belong to the owner of the pt page
                            kernelstate.dmapages[
                                kernelstate.pages[pn].pgtable_pn(idx)
                            ].owner == kernelstate.pages[pn].owner)),

                    # If the entry is a pci page
                    z3.Implies(
                        kernelstate.pages[pn].pgtable_type(idx) ==
                        dt.PGTYPE_PCIPAGE,
                        z3.And(
                            # This must be at the PT level in the pgtable
                            kernelstate.pages[pn].type ==
                            dt.page_type.PAGE_TYPE_X86_PT,

                            # There are NPCIPAGE many pci pages that can be mapped
                            z3.ULT(kernelstate.pages[pn].pgtable_pn(idx),
                                   dt.NPCIPAGE),

                            # the pci page must belong to a device owned by the
                            # proc that owns the pgtable page
                            kernelstate.pci[
                                kernelstate.pcipages[kernelstate.pages[pn].
                                                     pgtable_pn(idx)].owner
                            ].owner == kernelstate.pages[pn].owner)),
                ))))

    return z3.And(*conj)
Ejemplo n.º 20
0
def sys_clone(old, pid, pml4, stack, hvm):
    cond = z3.And(
        is_pid_valid(pid),
        old.procs[pid].state == dt.proc_state.PROC_UNUSED,

        is_pn_valid(pml4),
        old.pages[pml4].type == dt.page_type.PAGE_TYPE_FREE,

        is_pn_valid(stack),
        old.pages[stack].type == dt.page_type.PAGE_TYPE_FREE,

        is_pn_valid(hvm),
        old.pages[hvm].type == dt.page_type.PAGE_TYPE_FREE,

        z3.Distinct(pml4, stack, hvm),
    )
    new = old.copy()

    # Initialize the proc
    new.procs[pid].ppid = new.current
    new.procs[pid].state = dt.proc_state.PROC_EMBRYO
    new.procs[pid].killed = z3.BoolVal(False)
    new.procs[pid].ipc_from = z3.BitVecVal(0, dt.pid_t)
    new.procs[pid].ipc_val = z3.BitVecVal(0, dt.uint64_t)
    new.procs[pid].ipc_page = z3.BitVecVal(0, dt.pn_t)
    new.procs[pid].ipc_size = z3.BitVecVal(0, dt.size_t)
    new.procs[pid].ipc_fd = z3.BitVecVal(0, dt.fd_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)

    # all refcnts should be zero at this point (according to invariants):
    # no need to zero them
    # new.proc_nr_pages = 0
    # new.proc_nr_children = 0
    # new.procs.nr_fds = 0
    # new.proc_nr_devs = 0

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

    # Maintain the "shadow" pgtable
    new.pages[pml4].pgtable_pn = util.i64(0)
    new.pages[pml4].pgtable_perm = util.i64(0)
    new.pages[pml4].pgtable_type = dt.PGTYPE_NONE

    # Claim the root pml4, the stack and hvm pages
    # We need to do four things to claim a page.
    # 1) Change the type from free to something else
    # 2) change the owner
    # 3) zero the page contents
    # 4) bump the refcount for the owner
    new.pages[pml4].type = dt.page_type.PAGE_TYPE_X86_PML4
    new.pages[pml4].owner = pid
    new.pages[pml4].data = util.i64(0)
    new.procs[pid].nr_pages[pml4] += 1

    new.pages[stack].type = dt.page_type.PAGE_TYPE_PROC_DATA
    new.pages[stack].owner = pid
    new.pages[stack].data = util.i64(0)
    new.procs[pid].nr_pages[stack] += 1

    new.pages[hvm].type = dt.page_type.PAGE_TYPE_PROC_DATA
    new.pages[hvm].owner = pid
    new.pages[hvm].data = util.i64(0)
    new.procs[pid].nr_pages[hvm] += 1

    new.procs[pid].page_table_root = pml4
    new.procs[pid].stack = stack
    new.procs[pid].hvm = hvm

    new.procs[new.current].nr_children[pid] += 1

    # Copy parent's hvm to child's hvm
    new.pages.data = lambda pn, idx, oldfn: \
        util.If(pn == hvm,
                oldfn(new.procs[new.current].hvm, idx),
                oldfn(pn, idx))

    # Copy parent's stack to child's stack
    new.pages.data = lambda pn, idx, oldfn: \
        util.If(pn == stack,
                oldfn(new.procs[new.current].stack, idx),
                oldfn(pn, idx))

    return cond, util.If(cond, new, old)
Ejemplo n.º 21
0
def spec_lemma_iommu_isolation(kernelstate):
    conj = []

    conj.append(spec_lemma_nr_pages_refcnt(kernelstate))
    conj.append(spec_lemma_nr_devs_refcnt(kernelstate))

    pn = util.FreshBitVec('pn', dt.pn_t)
    idx = util.FreshBitVec('page_index', dt.size_t)
    devid = util.FreshBitVec('devid', dt.devid_t)

    # valid device can ensures valid device.owner, page table root's type must IOMMU_PML4
    conj.append(
        z3.ForAll(
            [devid],
            z3.Implies(
                is_pn_valid(kernelstate.pci[devid].page_table_root),
                z3.And(
                    is_pid_valid(kernelstate.pci[devid].owner),
                    kernelstate.pages[kernelstate.pci[devid].page_table_root].
                    type == dt.page_type.PAGE_TYPE_IOMMU_PML4,
                    kernelstate.pages[kernelstate.pci[devid].page_table_root].
                    owner == kernelstate.pci[devid].owner))))

    conj.append(
        z3.ForAll(
            [pn, idx],
            z3.Implies(
                z3.And(
                    is_pn_valid(pn),
                    z3.ULT(idx, 512),
                    is_iommu_page_table_type(kernelstate.pages[pn].type),
                    dt.has_bit(kernelstate.pages[pn].data(idx), dt.PTE_P),
                    is_pid_valid(kernelstate.pages[pn].owner),

                    # If this proc has a device, nr_devs is non-zero.
                    # Devices can write regardless of proc status.
                    z3.Or(
                        is_status_live(kernelstate.procs[
                            kernelstate.pages[pn].owner].state),
                        kernelstate.procs[
                            kernelstate.pages[pn].owner].nr_devs() != 0),
                ),
                z3.And(
                    # The iommu page table is highly-regular and there are basically
                    # two cases.
                    # The page table it self is made from pages in the `pages` array.
                    # And the last level are all dmapages from the `dmapages` array.
                    z3.Implies(
                        kernelstate.pages[pn].type !=
                        dt.page_type.PAGE_TYPE_IOMMU_PT,
                        z3.And(
                            kernelstate.pages[pn].data(idx) == (
                                kernelstate.pages_ptr_to_int +
                                kernelstate.pages[pn].pgtable_pn(idx) * 4096)
                            | kernelstate.pages[pn].pgtable_perm(idx),
                            is_pn_valid(kernelstate.pages[pn].pgtable_pn(idx)),
                            get_iommu_sub_type(
                                kernelstate.pages[pn].type) == kernelstate.
                            pages[kernelstate.pages[pn].pgtable_pn(idx)].type,
                            kernelstate.pages[kernelstate.pages[pn].pgtable_pn(
                                idx)].owner == kernelstate.pages[pn].owner,
                        )),
                    z3.Implies(
                        kernelstate.pages[pn].type ==
                        dt.page_type.PAGE_TYPE_IOMMU_PT,
                        z3.And(
                            kernelstate.pages[pn].data(idx) == (
                                kernelstate.dmapages_ptr_to_int +
                                kernelstate.pages[pn].pgtable_pn(idx) * 4096)
                            | kernelstate.pages[pn].pgtable_perm(idx),
                            is_dmapn_valid(
                                kernelstate.pages[pn].pgtable_pn(idx)),
                            kernelstate.dmapages[
                                kernelstate.pages[pn].pgtable_pn(idx)].type ==
                            dt.page_type.PAGE_TYPE_IOMMU_FRAME,
                            kernelstate.dmapages[
                                kernelstate.pages[pn].pgtable_pn(
                                    idx)].owner == kernelstate.pages[pn].owner,
                        )),
                ))))

    return z3.And(*conj)
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)
Ejemplo n.º 23
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)