def eval_table(self, ctx): action_exprs = [] action_matches = [] forward_cond_copy = ctx.tmp_forward_cond # only bother to evaluate if the table can actually hit if not z3.is_false(self.locals["hit"]): # note: the action lists are pass by reference here # first evaluate all the constant entries self.eval_const_entries(ctx, action_exprs, action_matches) # then append dynamic table entries to the constant entries # only do this if the table can actually be manipulated if not self.immutable: self.eval_table_entries(ctx, action_exprs, action_matches) # finally start evaluating the default entry var_store = ctx.checkpoint() # this hits when the table is either missed, or no action matches cond = z3.Or(self.locals["miss"], z3.Not(z3.Or(*action_matches))) ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond) self.eval_default(ctx) if ctx.get_exited(): ctx.restore(var_store) ctx.set_exited(False) ctx.tmp_forward_cond = forward_cond_copy # generate a nested set of if expressions per available action for cond, then_vars in action_exprs: merge_attrs(ctx, cond, then_vars)
def eval_table(self, p4_state): action_exprs = [] action_matches = [] context = p4_state.current_context() forward_cond_copy = context.tmp_forward_cond # only bother to evaluate if the table can actually hit if not self.locals["hit"] == z3.BoolVal(False): # note: the action lists are pass by reference here # first evaluate all the constant entries self.eval_const_entries(p4_state, action_exprs, action_matches) # then append dynamic table entries to the constant entries self.eval_table_entries(p4_state, action_exprs, action_matches) # finally start evaluating the default entry var_store, contexts = p4_state.checkpoint() # this hits when the table is either missed, or no action matches cond = z3.Or(self.locals["miss"], z3.Not(z3.Or(*action_matches))) context.tmp_forward_cond = z3.And(forward_cond_copy, cond) self.eval_default(p4_state) if p4_state.has_exited: p4_state.restore(var_store, contexts) p4_state.has_exited = False context.tmp_forward_cond = forward_cond_copy # generate a nested set of if expressions per available action for cond, then_vars in action_exprs: merge_attrs(p4_state, cond, then_vars)
def handle_select(self, p4_state): switches = [] select_conds = [] context = p4_state.current_context() forward_cond_copy = context.tmp_forward_cond match_list = p4_state.resolve_expr(self.match) for parser_cond, parser_node in reversed(self.child): case_expr = p4_state.resolve_expr(parser_cond) cond = build_select_cond(p4_state, case_expr, match_list) # state forks here var_store, contexts = p4_state.checkpoint() context.tmp_forward_cond = z3.And(forward_cond_copy, cond) parser_node.eval(p4_state) select_conds.append(cond) if not p4_state.has_exited: switches.append( (context.tmp_forward_cond, p4_state.get_attrs())) p4_state.has_exited = False context.has_returned = False p4_state.restore(var_store, contexts) # this hits when the table is either missed, or no action matches cond = z3.Not(z3.Or(*select_conds)) context.tmp_forward_cond = z3.And(forward_cond_copy, cond) self.default.eval(p4_state) p4_state.has_exited = False context.has_returned = False context.tmp_forward_cond = forward_cond_copy for cond, then_vars in switches: merge_attrs(p4_state, cond, then_vars)
def handle_select(self, ctx): switches = [] select_conds = [] forward_cond_copy = ctx.tmp_forward_cond match_list = ctx.resolve_expr(self.match) for parser_cond, parser_node in self.child: case_expr = ctx.resolve_expr(parser_cond) cond = build_select_cond(ctx, case_expr, match_list) # state forks here var_store = ctx.checkpoint() ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond) parser_node.eval(ctx) select_conds.append(cond) if not (ctx.get_exited() or z3.is_false(cond)): switches.append((ctx.tmp_forward_cond, ctx.get_attrs())) ctx.set_exited(False) ctx.has_returned = False ctx.restore(var_store) # this hits when no select matches cond = z3.Not(z3.Or(*select_conds)) ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond) self.default.eval(ctx) ctx.set_exited(False) ctx.has_returned = False ctx.tmp_forward_cond = forward_cond_copy for cond, then_vars in reversed(switches): merge_attrs(ctx, cond, then_vars)
def eval(self, ctx): cond = z3.simplify( z3.And(z3.Not(z3.Or(*ctx.forward_conds)), ctx.tmp_forward_cond)) if not z3.is_false(cond): ctx.return_states.append((cond, ctx.copy_attrs())) ctx.has_returned = True ctx.forward_conds.append(ctx.tmp_forward_cond)
def eval(self, ctx): parser_state = self.parser_state try: parser_state.eval(ctx) except ParserException: RejectState().eval(ctx) return if self.is_terminal: key = self.parser_state.name tmp_forward_conds = [] sub_ctx = ctx while not isinstance(sub_ctx, StaticContext): tmp_forward_conds.append(sub_ctx.tmp_forward_cond) sub_ctx = sub_ctx.parent_ctx cond = z3.And(*tmp_forward_conds) attrs = ctx.get_attrs() # add the if key in self.parser_tree.terminal_nodes: orig_cond = self.parser_tree.terminal_nodes[key][0] orig_dict = self.parser_tree.terminal_nodes[key][1] merge_dicts(orig_dict, cond, attrs) cond = z3.Or(orig_cond, cond) attrs = orig_dict self.parser_tree.terminal_nodes[key] = (cond, attrs) return if isinstance(self.child, list): # there is a switch case try to untangle it. self.handle_select(ctx) elif isinstance(self.child, ParserNode): # direct descendant, continue the evaluation self.child.eval(ctx)
def eval_cases(self, p4_state, cases): case_exprs = [] case_matches = [] context = p4_state.current_context() forward_cond_copy = context.tmp_forward_cond for case in reversed(cases.values()): var_store, contexts = p4_state.checkpoint() context.tmp_forward_cond = z3.And(forward_cond_copy, case["match"]) case["case_block"].eval(p4_state) if not (context.has_returned or p4_state.has_exited): then_vars = p4_state.get_attrs() case_exprs.append((case["match"], then_vars)) context.has_returned = False p4_state.has_exited = False p4_state.restore(var_store, contexts) case_matches.append(case["match"]) var_store, contexts = p4_state.checkpoint() cond = z3.Not(z3.Or(*case_matches)) context.tmp_forward_cond = z3.And(forward_cond_copy, cond) self.default_case.eval(p4_state) if context.has_returned or p4_state.has_exited: p4_state.restore(var_store, contexts) context.has_returned = False p4_state.has_exited = False context.tmp_forward_cond = forward_cond_copy for cond, then_vars in case_exprs: merge_attrs(p4_state, cond, then_vars)
def eval(self, p4_state): context = p4_state.current_context() if self.expr is None: expr = None else: # resolve the expr before restoring the state expr = p4_state.resolve_expr(self.expr) if isinstance(context.return_type, z3.BitVecSortRef): expr = z3_cast(expr, context.return_type) # we return a complex typed expression list, instantiate if isinstance(expr, list): instance = gen_instance(p4_state, "undefined", context.return_type) instance.set_list(expr) expr = instance cond = z3.simplify( z3.And(z3.Not(z3.Or(*context.forward_conds)), context.tmp_forward_cond)) if not cond == z3.BoolVal(False): context.return_states.append((cond, p4_state.copy_attrs())) if expr is not None: context.return_exprs.append((cond, expr)) context.has_returned = True context.forward_conds.append(context.tmp_forward_cond)
def eval_switch_table_matches(self, ctx, table): cases = OrderedDict() if table.immutable: # if the table is immutable we can only match on const entries for c_keys, (action_name, _) in table.const_entries: const_matches = [] # check if the action of the entry is even present if action_name not in self.case_blocks: continue # compute the match key # FIXME: Deal with side effects here # Maybe just checkpoint and restore? Ugh. So expensive... match_cond = table.get_const_matches(ctx, c_keys) action = table.actions[action_name][0] if action_name in cases: prev_match, _ = cases[action_name] match_cond = z3.Or(match_cond, prev_match) const_matches.append(match_cond) cases[action_name] = ( match_cond, self.case_blocks[action_name]) # we also need to process the default action separately # this basically hits only if no other case matches _, action_name, _ = table.default_action match_cond = z3.Not(z3.Or(*const_matches)) if action_name in cases: prev_match, _ = cases[action_name] match_cond = z3.Or(match_cond, prev_match) const_matches.append(match_cond) cases[action_name] = (match_cond, self.case_blocks[action_name]) else: # otherwise we are dealing with a normal table # just insert the match entries combined with the hit expression add_default = None for case_name, case_block in self.case_blocks.items(): match_var = table.tbl_action action = table.actions[case_name][0] match_cond = z3.And(table.locals["hit"], (action == match_var)) cases[case_name] = (match_cond, case_block) # we need to check if the default is in the cases # this implies that the "default" case can never be executed if case_name == table.default_action[1]: add_default = (case_name, (z3.BoolVal(True), case_block)) if add_default and len(table.actions) == len(cases): cases[add_default[0]] = add_default[1] return cases
def eval_cases(self, ctx, cases): case_exprs = [] case_matches = [] forward_cond_copy = ctx.tmp_forward_cond fall_through_matches = [] for case_match, case_block in cases.values(): # there is no block for the switch # this expressions falls through to the next switch case if not case_block: fall_through_matches.append(case_match) continue # matches the condition OR all the other fall-through switches case_match = z3.Or(case_match, *fall_through_matches) fall_through_matches.clear() var_store = ctx.checkpoint() ctx.tmp_forward_cond = z3.And( forward_cond_copy, case_match) case_block.eval(ctx) if not (ctx.has_returned or ctx.get_exited()): then_vars = ctx.get_attrs() case_exprs.append((case_match, then_vars)) ctx.has_returned = False ctx.set_exited(False) ctx.restore(var_store) case_matches.append(case_match) var_store = ctx.checkpoint() # process the default expression cond = z3.Not(z3.Or(*case_matches)) if not z3.is_false(cond): ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond) self.default_case.eval(ctx) if ctx.has_returned or ctx.get_exited(): ctx.restore(var_store) ctx.has_returned = False ctx.set_exited(False) ctx.tmp_forward_cond = forward_cond_copy # merge all the expressions in reverse order for cond, then_vars in reversed(case_exprs): merge_attrs(ctx, cond, then_vars)
def eval(self, p4_state): # FIXME: This checkpointing should not be necessary # Figure out what is going on var_store, contexts = p4_state.checkpoint() forward_conds = [] tmp_forward_conds = [] for context in reversed(p4_state.contexts): context.copy_out(p4_state) forward_conds.extend(context.forward_conds) tmp_forward_conds.append(context.tmp_forward_cond) context = p4_state.current_context() cond = z3.simplify( z3.And(z3.Not(z3.Or(*forward_conds)), z3.And(*tmp_forward_conds))) if not cond == z3.BoolVal(False): p4_state.exit_states.append((cond, p4_state.get_z3_repr())) p4_state.has_exited = True p4_state.restore(var_store, contexts) context.forward_conds.append(context.tmp_forward_cond)
def eval(self, ctx): # FIXME: This checkpointing should not be necessary # Figure out what is going on var_store = ctx.checkpoint() forward_conds = [] tmp_forward_conds = [] sub_ctx = ctx while not isinstance(sub_ctx, StaticContext): sub_ctx.copy_out(ctx) forward_conds.extend(sub_ctx.forward_conds) tmp_forward_conds.append(sub_ctx.tmp_forward_cond) sub_ctx = sub_ctx.parent_ctx cond = z3.simplify( z3.And(z3.Not(z3.Or(*forward_conds)), z3.And(*tmp_forward_conds))) if not z3.is_false(cond): ctx.add_exit_state(cond, ctx.get_p4_state().get_members(ctx)) ctx.set_exited(True) ctx.restore(var_store) ctx.forward_conds.append(ctx.tmp_forward_cond)
def eval(self, p4_state): # FIXME: This checkpointing should not be necessary # Figure out what is going on forward_conds = [] tmp_forward_conds = [] for context in reversed(p4_state.contexts): forward_conds.extend(context.forward_conds) tmp_forward_conds.append(context.tmp_forward_cond) context = p4_state.current_context() cond = z3.And(z3.Not(z3.Or(*forward_conds)), z3.And(*tmp_forward_conds)) var_store, contexts = p4_state.checkpoint() for member_name, _ in p4_state.members: member_val = p4_state.resolve_reference(member_name) if isinstance(member_val, StructInstance): member_val.deactivate() p4_state.exit_states.append((cond, p4_state.get_z3_repr())) p4_state.restore(var_store, contexts) p4_state.has_exited = True context.forward_conds.append(context.tmp_forward_cond)
def eval(self, ctx): # FIXME: This checkpointing should not be necessary # Figure out what is going on forward_conds = [] tmp_forward_conds = [] sub_ctx = ctx while not isinstance(sub_ctx, StaticContext): forward_conds.extend(sub_ctx.forward_conds) tmp_forward_conds.append(sub_ctx.tmp_forward_cond) sub_ctx = sub_ctx.parent_ctx cond = z3.And(z3.Not(z3.Or(*forward_conds)), z3.And(*tmp_forward_conds)) var_store = ctx.checkpoint() for member_name, _ in ctx.get_p4_state().members: member_val = ctx.resolve_reference(member_name) if isinstance(member_val, StructInstance): member_val.deactivate() ctx.add_exit_state(cond, ctx.get_p4_state().get_members(ctx)) ctx.restore(var_store) ctx.set_exited(True) ctx.forward_conds.append(ctx.tmp_forward_cond)
def eval(self, ctx): if self.expr is None: expr = None else: # resolve the expr before restoring the state expr = ctx.resolve_expr(self.expr) if isinstance(ctx.return_type, z3.BitVecSortRef): expr = z3_cast(expr, ctx.return_type) # we return a complex typed expression list, instantiate if isinstance(expr, list): # name is meaningless here so keep it empty instance = ctx.gen_instance("", ctx.return_type) instance.set_list(expr) expr = instance cond = z3.simplify(z3.And(z3.Not(z3.Or(*ctx.forward_conds)), ctx.tmp_forward_cond)) if not z3.is_false(cond): ctx.return_states.append((cond, ctx.copy_attrs())) if expr is not None: ctx.return_exprs.append((cond, expr)) ctx.has_returned = True ctx.forward_conds.append(ctx.tmp_forward_cond)