def sys_dup(old, oldfd, pid, newfd): cond = z3.And( is_pid_valid(pid), # the pid is either current or an embryo belonging to current z3.Or(pid == old.current, z3.And( old.procs[pid].ppid == old.current, old.procs[pid].state == dt.proc_state.PROC_EMBRYO)), is_fd_valid(oldfd), is_fn_valid(old.procs[old.current].ofile(oldfd)), is_fd_valid(newfd), z3.Not(is_fn_valid(old.procs[pid].ofile(newfd))), ) new = old.copy() fn = new.procs[old.current].ofile(oldfd) new.procs[pid].ofile[newfd] = fn new.procs[pid].nr_fds[newfd] += 1 # bump file refcnt new.files[fn].refcnt[(pid, newfd)] += 1 return cond, util.If(cond, new, old)
def sys_dup2(old, oldfd, pid, newfd): cond = z3.And( is_pid_valid(pid), # the pid is either current or an embryo belonging to current z3.Or(pid == old.current, z3.And( old.procs[pid].ppid == old.current, old.procs[pid].state == dt.proc_state.PROC_EMBRYO)), is_fd_valid(oldfd), is_fn_valid(old.procs[old.current].ofile(oldfd)), is_fd_valid(newfd), ) new1 = old.copy() newfn = new1.procs[pid].ofile(newfd) # If fn != 0 new1.procs[pid].ofile[newfd] = z3.BitVecVal(0, dt.fn_t) new1.procs[pid].nr_fds[newfd] -= 1 # decrement file refcnt new1.files[newfn].refcnt[(pid, newfd)] -= 1 ref = new1.files[newfn].refcnt() # If the refcnt is zero, clear the file slot new1.files[newfn].type = util.If(ref == 0, dt.file_type.FD_NONE, new1.files[newfn].type) new1.files[newfn].value = util.If(ref == 0, z3.BitVecVal(0, dt.uint64_t), new1.files[newfn].value) new1.files[newfn].offset = util.If(ref == 0, z3.BitVecVal(0, dt.off_t), new1.files[newfn].offset) new1.files[newfn].omode = util.If(ref == 0, z3.BitVecVal(0, dt.uint64_t), new1.files[newfn].omode) new2 = util.If(is_fn_valid(old.procs[pid].ofile(newfd)), new1, old.copy()) # un-conditional fn = new2.procs[old.current].ofile(oldfd) new2.procs[pid].ofile[newfd] = fn new2.procs[pid].nr_fds[newfd] += 1 # bump file refcnt new2.files[fn].refcnt[(pid, newfd)] += 1 # posix: if fds are the same, do nothing new3 = util.If(z3.And(old.current == pid, oldfd == newfd), old.copy(), new2) return cond, util.If(cond, new3, old)
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 spec_lemma_nr_fds_refcnt(kernelstate): conj = [] pid = util.FreshBitVec('pid', dt.pid_t) fd = util.FreshBitVec('fd', dt.fd_t) # unused procs do not have any fds. # valid(pid) & state(pid)== PROC_UNUSED ==> pid.nr_fds == 0 conj.append( z3.ForAll( [pid], z3.Implies( is_pid_valid(pid), z3.Implies( kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED, kernelstate.procs[pid].nr_fds() == z3.BitVecVal( 0, dt.size_t))))) # unused procs do not have any opened files # valid(pid) & valid(fd) & state(pid) == PROC_UNUSED ==> !valid(openedfile(pid, fd)) conj.append( z3.ForAll( [pid, fd], z3.Implies( z3.And(is_pid_valid(pid), is_fd_valid(fd)), z3.Implies( kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED, z3.Not(is_fn_valid(kernelstate.procs[pid].ofile(fd))))))) # a procs have opened a file, the state of this procs must not be UNUSED # valid(pid) & valid(fd) & valid(fn) ==> state(pid) != PROC_UNUSED conj.append( z3.ForAll( [pid, fd], z3.Implies( z3.And(is_pid_valid(pid), is_fd_valid(fd), is_fn_valid(kernelstate.procs[pid].ofile(fd))), kernelstate.procs[pid].state != dt.proc_state.PROC_UNUSED))) # Correctness definition for `permutation` based refcount kernelstate.procs.nr_fds.check( conj, is_owner_valid=is_pid_valid, is_owned_valid=is_fd_valid, max_refs=dt.NOFILE, ownedby=lambda pid, fd: is_fn_valid(kernelstate.procs[pid].ofile(fd))) return z3.And(*conj)
def sys_close(old, pid, fd): cond = z3.And( is_pid_valid(pid), is_fd_valid(fd), # pid is either current or a zombie z3.Or( pid == old.current, old.procs[pid].state == dt.proc_state.PROC_ZOMBIE), is_fn_valid(old.procs[pid].ofile(fd)), ) new = old.copy() fn = new.procs[pid].ofile(fd) new.procs[pid].ofile[fd] = z3.BitVecVal(0, dt.fn_t) new.procs[pid].nr_fds[fd] -= 1 # decrement file refcnt new.files[fn].refcnt[(pid, fd)] -= 1 ref = new.files[fn].refcnt() # If the refcnt is zero, clear the file slot new2 = new.copy() new2.files[fn].type = dt.file_type.FD_NONE new2.files[fn].value = z3.BitVecVal(0, dt.uint64_t) new2.files[fn].offset = z3.BitVecVal(0, dt.off_t) new2.files[fn].omode = z3.BitVecVal(0, dt.uint64_t) return cond, util.If(cond, util.If(ref == 0, new2, new), old)
def sys_create(old, fd, fn, type, value, omode): cond = z3.And( type != dt.file_type.FD_NONE, # fd is valid and empty is_fd_valid(fd), z3.Not(is_fn_valid(old.procs[old.current].ofile(fd))), # fn is valid and unused is_fn_valid(fn), old.files[fn].refcnt() == 0, ) new = old.copy() new.files[fn].type = type new.files[fn].value = value new.files[fn].omode = omode new.files[fn].offset = z3.BitVecVal(0, dt.off_t) new.procs[old.current].ofile[fd] = fn new.procs[old.current].nr_fds[fd] += 1 # bump file refcnt new.files[fn].refcnt[(old.current, fd)] += 1 return cond, util.If(cond, new, old)
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 spec_lemma_nr_fds_refcnt(kernelstate): conj = [] pid = util.FreshBitVec('pid', dt.pid_t) fd = util.FreshBitVec('fd', dt.fd_t) conj.append( z3.ForAll( [pid], z3.Implies( is_pid_valid(pid), z3.Implies( kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED, kernelstate.procs[pid].nr_fds() == z3.BitVecVal( 0, dt.size_t))))) conj.append( z3.ForAll( [pid, fd], z3.Implies( z3.And(is_pid_valid(pid), is_fd_valid(fd)), z3.Implies( kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED, z3.Not(is_fn_valid(kernelstate.procs[pid].ofile(fd))))))) conj.append( z3.ForAll( [pid, fd], z3.Implies( z3.And(is_pid_valid(pid), is_fd_valid(fd), is_fn_valid(kernelstate.procs[pid].ofile(fd))), kernelstate.procs[pid].state != dt.proc_state.PROC_UNUSED))) kernelstate.procs.nr_fds.check( conj, is_owner_valid=is_pid_valid, is_owned_valid=is_fd_valid, max_refs=dt.NOFILE, ownedby=lambda pid, fd: is_fn_valid(kernelstate.procs[pid].ofile(fd))) return z3.And(*conj)
def sys_lseek(old, fd, offset): cond = z3.And( is_fd_valid(fd), is_fn_valid(old.procs[old.current].ofile(fd)), old.files[old.procs[old.current].ofile(fd)].type == dt.file_type.FD_INODE, offset >= 0, ) new = old.copy() fn = old.procs[old.current].ofile(fd) new.files[fn].offset = offset return cond, util.If(cond, new, old)
def procs_equiv(conj, ctx, kernelstate): pid = util.FreshBitVec('pid', dt.pid_t) fd = util.FreshBitVec('fd', dt.fd_t) idx = util.FreshBitVec('vector_index', 64) proc_field_equiv(conj, pid, ctx, kernelstate, 'state') proc_field_equiv(conj, pid, ctx, kernelstate, 'ppid') conj.append( z3.ForAll([pid], z3.Implies(is_pid_valid(pid), (util.global_field_element( ctx, '@proc_table', 'killed', pid) == 0) == z3.Not(kernelstate.procs[pid].killed)))) proc_field_equiv(conj, pid, ctx, kernelstate, 'ipc_from') proc_field_equiv(conj, pid, ctx, kernelstate, 'ipc_val') proc_field_equiv(conj, pid, ctx, kernelstate, 'ipc_page') proc_field_equiv(conj, pid, ctx, kernelstate, 'ipc_size') proc_field_equiv(conj, pid, ctx, kernelstate, 'ipc_fd') conj.append( z3.ForAll([pid, fd], z3.Implies( z3.And(is_pid_valid(pid), is_fd_valid(fd)), util.global_field_element( ctx, '@proc_table', 'ofile', pid, z3.ZeroExt( 32, fd)) == kernelstate.procs[pid].ofile(fd)))) conj.append( z3.ForAll([pid], z3.Implies( is_pid_valid(pid), util.global_field_element( ctx, '@proc_table', 'nr_children', pid) == kernelstate.procs[pid].nr_children()))) conj.append( z3.ForAll([pid], z3.Implies( is_pid_valid(pid), util.global_field_element( ctx, '@proc_table', 'nr_fds', pid) == kernelstate.procs[pid].nr_fds()))) conj.append( z3.ForAll([pid], z3.Implies( is_pid_valid(pid), util.global_field_element( ctx, '@proc_table', 'nr_pages', pid) == kernelstate.procs[pid].nr_pages()))) conj.append( z3.ForAll([pid], z3.Implies( is_pid_valid(pid), util.global_field_element( ctx, '@proc_table', 'nr_dmapages', pid) == kernelstate.procs[pid].nr_dmapages()))) conj.append( z3.ForAll([pid], z3.Implies( is_pid_valid(pid), util.global_field_element( ctx, '@proc_table', 'nr_devs', pid) == kernelstate.procs[pid].nr_devs()))) conj.append( z3.ForAll([pid], z3.Implies( is_pid_valid(pid), util.global_field_element( ctx, '@proc_table', 'nr_ports', pid) == kernelstate.procs[pid].nr_ports()))) conj.append( z3.ForAll([pid], z3.Implies( is_pid_valid(pid), util.global_field_element( ctx, '@proc_table', 'nr_vectors', pid) == kernelstate.procs[pid].nr_vectors()))) conj.append( z3.ForAll([pid], z3.Implies( is_pid_valid(pid), util.global_field_element( ctx, '@proc_table', 'nr_intremaps', pid) == kernelstate.procs[pid].nr_intremaps()))) proc_field_equiv(conj, pid, ctx, kernelstate, 'stack') proc_field_equiv(conj, pid, ctx, kernelstate, 'hvm') proc_field_equiv(conj, pid, ctx, kernelstate, 'page_table_root') conj.append( z3.ForAll([pid], z3.Implies(is_pid_valid(pid), (util.global_field_element( ctx, '@proc_table', 'use_io_bitmap', pid) == 0) == z3.Not( kernelstate.procs[pid].use_io_bitmap)))) proc_field_equiv(conj, pid, ctx, kernelstate, 'io_bitmap_a') proc_field_equiv(conj, pid, ctx, kernelstate, 'io_bitmap_b') conj.append( z3.ForAll([pid, idx], z3.Implies( z3.And(is_pid_valid(pid), z3.ULT(idx, 4)), util.global_field_element( ctx, '@proc_table', 'intr', pid, idx) == kernelstate.procs[pid].intr(idx)))) conj.append( z3.ForAll( [pid], ctx.globals['#tlbinv'](pid) == kernelstate.procs[pid].tlbinv)) conj.append(ctx.globals['#iotlbinv'] == kernelstate.iotlbinv)
def send_recv(old, pid, val, inpn, size, infd, outpn, outfd): cond = z3.And( is_pid_valid(pid), old.procs[pid].state == dt.proc_state.PROC_SLEEPING, # inpn is a valid pn and belongs to current is_pn_valid(inpn), old.pages[inpn].owner == old.current, z3.ULE(size, dt.PAGE_SIZE), z3.Implies(is_fd_valid(infd), is_fn_valid(old.procs[old.current].ofile(infd))), # outpn is a valid pn and belongs to current is_pn_valid(outpn), old.pages[outpn].owner == old.current, old.pages[outpn].type == dt.page_type.PAGE_TYPE_FRAME, z3.Implies(is_fd_valid(outfd), z3.Not(is_fn_valid(old.procs[old.current].ofile(outfd)))), # if ipc from is set, it must be set to current z3.Implies(old.procs[pid].ipc_from != 0, old.procs[pid].ipc_from == old.current) ) new = old.copy() new.procs[old.current].ipc_page = outpn new.procs[old.current].ipc_fd = outfd new.procs[pid].ipc_from = old.current new.procs[pid].ipc_val = val # memcpy new.pages.data = lambda pn0, idx0, oldfn=new.pages.data: \ util.If(z3.And(pn0 == old.procs[pid].ipc_page, z3.ULT(idx0, size)), oldfn(inpn, idx0), oldfn(pn0, idx0)) new.procs[pid].ipc_size = size new2 = new.copy() cond2 = z3.And(is_fd_valid(infd), is_fd_valid(new2.procs[pid].ipc_fd)) fn = old.procs[old.current].ofile(infd) fd = old.procs[pid].ipc_fd new2.procs[pid].ofile[fd] = fn # bump proc nr_fds new2.procs[pid].nr_fds[fd] += 1 # bump file refcnt new2.files[fn].refcnt[(pid, fd)] += 1 new3 = util.If(cond2, new2, new) new3.procs[old.current].state = dt.proc_state.PROC_SLEEPING new3.procs[pid].state = dt.proc_state.PROC_RUNNING return cond, util.If(cond, new3, old)
def impl_invariants_py(ctx): conj = [] pid = util.FreshBitVec('pid', dt.pid_t) pn = util.FreshBitVec('pn', dt.pn_t) fd = util.FreshBitVec('fd', dt.fd_t) # fn = util.FreshBitVec('fn', dt.fn_t) # embryos, runnable or running processes own the pages in their structs conj.append(z3.ForAll([pid], z3.Implies( z3.Or(util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_EMBRYO, util.global_field_element( ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_RUNNING, util.global_field_element( ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_RUNNABLE, util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_SLEEPING), z3.And( pn_has_owner_and_type(ctx, util.global_field_element( ctx, '@proc_table', 'page_table_root', pid), pid, dt.page_type.PAGE_TYPE_X86_PML4), pn_has_owner_and_type(ctx, util.global_field_element( ctx, '@proc_table', 'hvm', pid), pid, dt.page_type.PAGE_TYPE_PROC_DATA), pn_has_owner_and_type(ctx, util.global_field_element( ctx, '@proc_table', 'stack', pid), pid, dt.page_type.PAGE_TYPE_PROC_DATA), )))) # sleeping processes own their ipc_page conj.append(z3.ForAll([pid], z3.Implies(z3.And(is_pid_valid(pid), util.global_field_element(ctx, '@proc_table', 'state', pid) != dt.proc_state.PROC_ZOMBIE), z3.Implies(util.global_field_element(ctx, '@proc_table', 'use_io_bitmap', pid) != 0, z3.And( is_pn_valid(util.global_field_element(ctx, '@proc_table', 'io_bitmap_a', pid)), is_pn_valid(util.global_field_element(ctx, '@proc_table', 'io_bitmap_b', pid)), pn_has_owner_and_type(ctx, util.global_field_element(ctx, '@proc_table', 'io_bitmap_a', pid), pid, dt.page_type.PAGE_TYPE_PROC_DATA), pn_has_owner_and_type(ctx, util.global_field_element(ctx, '@proc_table', 'io_bitmap_b', pid), pid, dt.page_type.PAGE_TYPE_PROC_DATA)))))) # sleeping processes own their ipc_page conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid), z3.Implies(util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_SLEEPING, pn_has_owner_and_type(ctx, util.global_field_element(ctx, '@proc_table', 'ipc_page', pid), pid, dt.page_type.PAGE_TYPE_FRAME))))) conj.append(z3.ForAll([pid], z3.And( is_pn_valid(util.global_field_element( ctx, '@proc_table', 'page_table_root', pid)), is_pn_valid(util.global_field_element( ctx, '@proc_table', 'hvm', pid)), is_pn_valid(util.global_field_element(ctx, '@proc_table', 'stack', pid))))) # sleeping processes' ipc fd are empty if valid conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid), z3.Implies(util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_SLEEPING, z3.Implies(is_fd_valid(util.global_field_element(ctx, '@proc_table', 'ipc_fd', pid)), util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, util.global_field_element(ctx, '@proc_table', 'ipc_fd', pid))) == z3.BitVecVal(0, dt.fn_t)))))) conj.append(z3.ForAll([pid], z3.And( is_pn_valid(util.global_field_element( ctx, '@proc_table', 'page_table_root', pid)), is_pn_valid(util.global_field_element( ctx, '@proc_table', 'hvm', pid)), is_pn_valid(util.global_field_element(ctx, '@proc_table', 'stack', pid))))) # page has an owner <=> page is not free conj.append(z3.ForAll([pn], z3.Implies(is_pn_valid(pn), is_pid_valid(util.global_field_element(ctx, '@page_desc_table', 'pid', pn)) == (util.global_field_element(ctx, '@page_desc_table', 'type', pn) != dt.page_type.PAGE_TYPE_FREE)))) # unused procs have zero refcnt conj.append(z3.ForAll([pid], z3.Implies( z3.And( is_pid_valid(pid), util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_UNUSED), z3.And( util.global_field_element(ctx, '@proc_table', 'nr_children', pid) == z3.BitVecVal(0, dt.size_t), util.global_field_element(ctx, '@proc_table', 'nr_fds', pid) == z3.BitVecVal(0, dt.size_t), util.global_field_element(ctx, '@proc_table', 'nr_pages', pid) == z3.BitVecVal(0, dt.size_t), util.global_field_element(ctx, '@proc_table', 'nr_dmapages', pid) == z3.BitVecVal(0, dt.size_t), util.global_field_element(ctx, '@proc_table', 'nr_devs', pid) == z3.BitVecVal(0, dt.size_t), util.global_field_element(ctx, '@proc_table', 'nr_ports', pid) == z3.BitVecVal(0, dt.size_t), util.global_field_element(ctx, '@proc_table', 'nr_vectors', pid) == z3.BitVecVal(0, dt.size_t), util.global_field_element(ctx, '@proc_table', 'nr_intremaps', pid) == z3.BitVecVal(0, dt.size_t))))) # conj.append(z3.ForAll([pid, fd], z3.Implies(z3.And(is_pid_valid(pid), is_fd_valid(fd)), # z3.Implies( # util.global_field_element(ctx, '@proc_table', 'nr_fds', pid) == z3.BitVecVal(0, dt.size_t), # z3.Not(is_fn_valid(util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd)))))))) # # unused procs have zero fds # conj.append(z3.ForAll([pid, fd], z3.Implies(z3.And(is_pid_valid(pid), is_fd_valid(fd)), # z3.Implies( # util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_UNUSED, # util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd)) == z3.BitVecVal(0, dt.fn_t))))) # fds valid conj.append(z3.ForAll([pid, fd], z3.Implies(z3.And(is_pid_valid(pid), is_fd_valid(fd)), z3.Or( util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd)) == z3.BitVecVal(0, dt.fn_t), is_fn_valid(util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd))))))) # # FD_NONE's refcount is 0 # conj.append(z3.ForAll([fn], z3.Implies( # z3.And( # is_fn_valid(fn), # util.global_field_element(ctx, '@file_table', 'type', fn) == dt.file_type.FD_NONE), # util.global_field_element(ctx, '@file_table', 'refcnt', fn) == z3.BitVecVal(0, dt.size_t)))) # # FD never points to FD_NONE # conj.append(z3.ForAll([pid, fd], z3.Implies( # z3.And( # is_pid_valid(pid), # is_fd_valid(fd), # is_fn_valid(util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd)))), # util.global_field_element(ctx, '@file_table', 'type', # util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd))) != dt.file_type.FD_NONE))) # page freelist is well formed conj.append(z3.ForAll([pn], z3.Implies(is_pn_valid(pn), z3.And( is_pn_valid(util.global_field_element(ctx, '@page_desc_table', ['link', 'prev'], pn)), is_pn_valid(util.global_field_element(ctx, '@page_desc_table', ['link', 'next'], pn)))))) # ready queue is well formed # don't use is_pid_valid as some ->prev and ->next are zeros conj.append(z3.ForAll([pid], z3.Implies(is_pid_bounded(pid), z3.And( is_pid_bounded(util.global_field_element(ctx, '@proc_table', ['ready', 'prev'], pid)), is_pid_bounded(util.global_field_element(ctx, '@proc_table', ['ready', 'next'], pid)))))) # Current is always a valid and running conj.append(is_pid_valid(util.global_value(ctx, '@current'))) conj.append(util.global_field_element(ctx, '@proc_table', 'state', util.global_value(ctx, '@current')) == dt.proc_state.PROC_RUNNING) return z3.And(*conj)
def spec_invariants(kernelstate): conj = [] pid = util.FreshBitVec('pid', dt.pid_t) pn = util.FreshBitVec('pn', dt.pn_t) # # procs' page table, hvm and stack are # # 1) valid conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid), z3.And( is_pn_valid(kernelstate.procs[pid].page_table_root), is_pn_valid(kernelstate.procs[pid].hvm), is_pn_valid(kernelstate.procs[pid].stack))))) # 2) owned by that proc conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid), z3.Implies( is_status_live(kernelstate.procs[pid].state), z3.And( kernelstate.pages[kernelstate.procs[pid].page_table_root].owner == pid, kernelstate.pages[kernelstate.procs[pid].hvm].owner == pid, kernelstate.pages[kernelstate.procs[pid].stack].owner == pid))))) # 3) have the correct type conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid), z3.Implies( is_status_live(kernelstate.procs[pid].state), z3.And( kernelstate.pages[kernelstate.procs[pid].page_table_root].type == dt.page_type.PAGE_TYPE_X86_PML4, kernelstate.pages[kernelstate.procs[pid].hvm].type == dt.page_type.PAGE_TYPE_PROC_DATA, kernelstate.pages[kernelstate.procs[pid].stack].type == dt.page_type.PAGE_TYPE_PROC_DATA))))) ## # Sleeping PROC's ipc_page is a frame owned by that pid conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid), z3.Implies( kernelstate.procs[pid].state == dt.proc_state.PROC_SLEEPING, z3.And( is_pn_valid(kernelstate.procs[pid].ipc_page), kernelstate.pages[kernelstate.procs[pid] .ipc_page].type == dt.page_type.PAGE_TYPE_FRAME, kernelstate.pages[kernelstate.procs[pid].ipc_page].owner == pid))))) ## Non-zombie procs with use_io_bitmaps own their (valid) bitmap pages conj.append(z3.ForAll([pid], z3.Implies( z3.And( is_pid_valid(pid), kernelstate.procs[pid].use_io_bitmap, kernelstate.procs[pid].state != dt.proc_state.PROC_ZOMBIE), z3.And( is_pn_valid(kernelstate.procs[pid].io_bitmap_a), is_pn_valid(kernelstate.procs[pid].io_bitmap_b), kernelstate.pages[kernelstate.procs[pid].io_bitmap_a].owner == pid, kernelstate.pages[kernelstate.procs[pid].io_bitmap_b].owner == pid, kernelstate.pages[kernelstate.procs[pid].io_bitmap_a].type == dt.page_type.PAGE_TYPE_PROC_DATA, kernelstate.pages[kernelstate.procs[pid].io_bitmap_b].type == dt.page_type.PAGE_TYPE_PROC_DATA)))) # page has an owner <=> page is not free conj.append(z3.ForAll([pn], z3.Implies(is_pn_valid(pn), is_pid_valid(kernelstate.pages[pn].owner) == (kernelstate.pages[pn].type != dt.page_type.PAGE_TYPE_FREE)))) conj.append(z3.ForAll([pn], z3.Implies(is_pn_valid(pn), z3.Implies(kernelstate.pages[pn].type == dt.page_type.PAGE_TYPE_FREE, z3.Not(is_pid_valid(kernelstate.pages[pn].owner)))))) # a sleeping proc's ipc_fd is either invalid or empty conj.append(z3.ForAll([pid], z3.Implies(z3.And( is_pid_valid(pid), kernelstate.procs[pid].state == dt.proc_state.PROC_SLEEPING), z3.Or(z3.Not(is_fd_valid(kernelstate.procs[pid].ipc_fd)), z3.Not(is_fn_valid(kernelstate.procs[pid].ofile(kernelstate.procs[pid].ipc_fd))))))) ############## # Unused procs's refcount is all zero # conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid), # z3.Implies(kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED, # z3.And( # kernelstate.procs[pid].nr_pages(dt.NPAGE - 1) == z3.BitVecVal(0, dt.size_t)))))) # kernelstate.procs[pid].nr_children(dt.NPROC - 1) == z3.BitVecVal(0, dt.size_t), # kernelstate.procs[pid].nr_fds(dt.NOFILE - 1) == z3.BitVecVal(0, dt.size_t), # kernelstate.procs[pid].nr_devs(dt.NPCIDEV - 1) == z3.BitVecVal(0, dt.size_t)))))) # # unused procs don't have a parent # conj.append(z3.ForAll([pid], z3.Implies( # z3.And( # is_pid_valid(pid), # kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED), # kernelstate.procs[pid].ppid == z3.BitVecVal(0, dt.pid_t)))) # # unused procs don't have fds # conj.append(z3.ForAll([pid, fd], z3.Implies( # z3.And( # is_pid_valid(pid), # kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED), # z3.Not(is_fn_valid(kernelstate.procs[pid].ofile(fd)))))) # unused fn has refcount == 0 # conj.append(z3.ForAll([fn], z3.Implies(is_fn_valid(fn), # z3.Implies(kernelstate.files[fn].type == dt.file_type.FD_NONE, # kernelstate.files[fn].refcnt( # z3.Concat( # z3.BitVecVal(dt.NPROC - 1, dt.pid_t), # z3.BitVecVal(dt.NOFILE - 1, dt.fd_t))) == z3.BitVecVal(0, dt.size_t))))) ############## # disjointed-ness of memory regions conj.append(z3.And( z3.Extract(63, 40, z3.UDiv(kernelstate.pages_ptr_to_int, util.i64(4096)) + dt.NPAGES_PAGES) == z3.BitVecVal(0, 24), z3.Extract(63, 40, z3.UDiv(kernelstate.proc_table_ptr_to_int, util.i64(4096)) + dt.NPAGES_PROC_TABLE) == z3.BitVecVal(0, 24), z3.Extract(63, 40, z3.UDiv(kernelstate.page_desc_table_ptr_to_int, util.i64(4096)) + dt.NPAGES_PAGE_DESC_TABLE) == z3.BitVecVal(0, 24), z3.Extract(63, 40, z3.UDiv(kernelstate.file_table_ptr_to_int, util.i64(4096)) + dt.NPAGES_FILE_TABLE) == z3.BitVecVal(0, 24), z3.Extract(63, 40, z3.UDiv(kernelstate.devices_ptr_to_int,util.i64(4096)) + dt.NPAGES_DEVICES) == z3.BitVecVal(0, 24), z3.Extract(63, 40, z3.UDiv(kernelstate.dmapages_ptr_to_int,util.i64(4096)) + dt.NDMAPAGE) == z3.BitVecVal(0, 24), z3.Extract(63, 40, z3.UDiv(kernelstate.pages_ptr_to_int, util.i64(4096))) == z3.BitVecVal(0, 24), z3.Extract(63, 40, z3.UDiv(kernelstate.proc_table_ptr_to_int, util.i64(4096))) == z3.BitVecVal(0, 24), z3.Extract(63, 40, z3.UDiv(kernelstate.page_desc_table_ptr_to_int, util.i64(4096))) == z3.BitVecVal(0, 24), z3.Extract(63, 40, z3.UDiv(kernelstate.file_table_ptr_to_int, util.i64(4096))) == z3.BitVecVal(0, 24), z3.Extract(63, 40, z3.UDiv(kernelstate.devices_ptr_to_int, util.i64(4096))) == z3.BitVecVal(0, 24), z3.Extract(63, 40, z3.UDiv(kernelstate.dmapages_ptr_to_int, util.i64(4096))) == z3.BitVecVal(0, 24), z3.ULT(z3.UDiv(kernelstate.pages_ptr_to_int, util.i64(4096)) + dt.NPAGES_PAGES, z3.UDiv(kernelstate.proc_table_ptr_to_int, util.i64(4096))), z3.ULT(z3.UDiv(kernelstate.proc_table_ptr_to_int, util.i64(4096)) + dt.NPAGES_PROC_TABLE, z3.UDiv(kernelstate.page_desc_table_ptr_to_int, util.i64(4096))), z3.ULT(z3.UDiv(kernelstate.page_desc_table_ptr_to_int, util.i64(4096)) + dt.NPAGES_PAGE_DESC_TABLE, z3.UDiv(kernelstate.file_table_ptr_to_int, util.i64(4096))), z3.ULT(z3.UDiv(kernelstate.file_table_ptr_to_int, util.i64(4096)) + dt.NPAGES_FILE_TABLE, z3.UDiv(kernelstate.devices_ptr_to_int, util.i64(4096))), z3.ULT(z3.UDiv(kernelstate.devices_ptr_to_int, util.i64(4096)) + dt.NPCIDEV, z3.UDiv(kernelstate.dmapages_ptr_to_int, util.i64(4096))), z3.ULT(z3.UDiv(kernelstate.dmapages_ptr_to_int, util.i64(4096)) + dt.NDMAPAGE, z3.UDiv(dt.PCI_START, util.i64(4096))), )) # Current is a valid pid conj.append(is_pid_valid(kernelstate.current)) # Current is always running conj.append(kernelstate.procs[kernelstate.current].state == dt.proc_state.PROC_RUNNING), # A running proc must be current conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid), z3.Implies(kernelstate.procs[pid].state == dt.proc_state.PROC_RUNNING, pid == kernelstate.current)))) return z3.And(*conj)