def intremaps_equiv(conj, ctx, kernelstate): index = util.FreshBitVec('index', dt.size_t) # intremap index conj.append( z3.ForAll([index], z3.Implies( is_intremap_valid(index), util.global_field_element( ctx, '@intremap_table', 'state', index) == kernelstate.intremaps[index].state))) conj.append( z3.ForAll([index], z3.Implies( is_intremap_valid(index), util.global_field_element( ctx, '@intremap_table', 'devid', index) == kernelstate.intremaps[index].devid))) conj.append( z3.ForAll([index], z3.Implies( is_intremap_valid(index), util.global_field_element( ctx, '@intremap_table', 'vector', index) == kernelstate.intremaps[index].vector)))
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 pcipages_equiv(conj, ctx, kernelstate): pcipn = util.FreshBitVec('pcipn', dt.pn_t) conj.append( z3.ForAll([pcipn], z3.Implies( is_pcipn_valid(pcipn), util.global_field_element( ctx, '@pcipages', 'devid', pcipn) == kernelstate.pcipages[pcipn].owner))) conj.append( z3.ForAll( [pcipn], z3.Implies( is_pcipn_valid(pcipn), (util.global_field_element(ctx, '@pcipages', 'valid', pcipn) != 0) == kernelstate.pcipages[pcipn].valid)))
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 proc_field_equiv(conj, pid, ctx, kernelstate, field_name): conj.append( z3.ForAll([pid], z3.Implies( is_pid_valid(pid), util.global_field_element(ctx, '@proc_table', field_name, pid) == getattr( kernelstate.procs[pid], field_name))))
def files_equiv(conj, ctx, kernelstate): fn = util.FreshBitVec('fn', dt.fn_t) conj.append( z3.ForAll( [fn], z3.Implies( is_fn_valid(fn), util.global_field_element(ctx, '@file_table', 'type', fn) == kernelstate.files[fn].type))) conj.append( z3.ForAll([fn], z3.Implies( is_fn_valid(fn), util.global_field_element( ctx, '@file_table', 'refcnt', fn) == kernelstate.files[fn].refcnt()))) conj.append( z3.ForAll( [fn], z3.Implies( is_fn_valid(fn), util.global_field_element(ctx, '@file_table', 'value', fn) == kernelstate.files[fn].value))) conj.append( z3.ForAll( [fn], z3.Implies( is_fn_valid(fn), util.global_field_element(ctx, '@file_table', 'omode', fn) == kernelstate.files[fn].omode))) conj.append( z3.ForAll([fn], z3.Implies( is_fn_valid(fn), util.global_field_element( ctx, '@file_table', 'offset', fn) == kernelstate.files[fn].offset)))
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 pn_has_owner_and_type(ctx, pn, pid, type): return z3.And( is_pn_valid(pn), util.global_field_element(ctx, '@page_desc_table', 'pid', pn) == pid, util.global_field_element(ctx, '@page_desc_table', 'type', pn) == type)
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)