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