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)
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)))
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)
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_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)