Example #1
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)
Example #2
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)
def dmapages_equiv(conj, ctx, kernelstate):
    dmapn = util.FreshBitVec('dmapn', dt.dmapn_t)

    conj.append(
        z3.ForAll([dmapn],
                  z3.Implies(
                      is_dmapn_valid(dmapn),
                      util.global_field_element(
                          ctx, '@dmapage_desc_table', 'type',
                          dmapn) == kernelstate.dmapages[dmapn].type)))

    conj.append(
        z3.ForAll([dmapn],
                  z3.Implies(
                      is_dmapn_valid(dmapn),
                      util.global_field_element(
                          ctx, '@dmapage_desc_table', 'pid',
                          dmapn) == kernelstate.dmapages[dmapn].owner)))
Example #4
0
def sys_reclaim_iommu_frame(old, dmapn):
    cond = z3.And(
        is_dmapn_valid(dmapn),
        old.dmapages[dmapn].type != dt.page_type.PAGE_TYPE_FREE,
        is_pid_valid(old.dmapages[dmapn].owner),
        old.procs[old.dmapages[dmapn].owner].state == dt.proc_state.PROC_ZOMBIE,
        old.procs[old.dmapages[dmapn].owner].nr_devs() == z3.BitVecVal(0, dt.size_t),
    )

    new = old.copy()

    new.procs[new.dmapages[dmapn].owner].nr_dmapages[dmapn] -= 1
    new.dmapages[dmapn].type = dt.page_type.PAGE_TYPE_FREE
    new.dmapages[dmapn].owner = z3.BitVecVal(0, dt.pid_t)

    return cond, util.If(cond, new, old)
Example #5
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)
Example #6
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)