def set_verification(self, value): if value == LIVENESS: self.verification = VerificationType.LIVENESS return if value == EVENTUALLY: self.verification = VerificationType.EVENTUALLY return if value == SAFETY: self.verification = VerificationType.SAFETY return if value == EQUIVALENCE: self.verification = VerificationType.EQUIVALENCE return if value == DETERMINISTIC: self.verification = VerificationType.DETERMINISTIC return if value == SIMULATION: self.verification = VerificationType.SIMULATION return if value == LTL: self.verification = VerificationType.LTL return if value == PARAMETRIC: self.verification = VerificationType.PARAMETRIC return Logger.error("Unknown verification type \"%s\""%value)
def load_problems(self, problems_file): config = configparser.ConfigParser() config.optionxform=str with open(problems_file, "r") as f: config.read_string(u""+f.read()) self.relative_path = ("/".join(problems_file.split("/")[:-1])) if self.relative_path !="": self.relative_path += "/" for value in config: problem = dict(config[value]) if DEFAULT == value: continue if GENERAL == value: for attr,value in problem.items(): if hasattr(self, attr): setattr(self, attr, auto_convert(value)) else: if not hasattr(Problem(), attr): Logger.error("Attribute \"%s\" not found"%attr) continue pbm = self.generate_problem(value, problem) pbm.name = value self.add_problem(pbm)
def _add_assertion(self, solver, formula, comment=None): if not self.config.skip_solving: solver.solver.add_assertion(formula) if Logger.level(3): buf = cStringIO() printer = SmtPrinter(buf) printer.printer(formula) print(buf.getvalue() + "\n") if solver.trace_file is not None: if comment: self._write_smt2_comment(solver, "%s: START" % comment) formula_fv = get_free_variables(formula) for v in formula_fv: if v in solver.smt2vars: continue if v.symbol_type() == BOOL: self._write_smt2_log( solver, "(declare-fun %s () Bool)" % (v.symbol_name())) elif v.symbol_type().is_array_type(): st = v.symbol_type() assert st.index_type.is_bv_type(), "Expecting BV indices" assert st.elem_type.is_bv_type(), "Expecting BV elements" self._write_smt2_log( solver, "(declare-fun %s () (Array (_ BitVec %s) (_ BitVec %s)))" % (v.symbol_name(), st.index_type.width, st.elem_type.width)) elif v.symbol_type().is_bv_type(): self._write_smt2_log( solver, "(declare-fun %s () (_ BitVec %s))" % (v.symbol_name(), v.symbol_type().width)) else: Logger.error("Unhandled type in smt2 translation") self._write_smt2_log(solver, "") for v in formula_fv: solver.smt2vars.add(v) if formula.is_and(): for f in conjunctive_partition(formula): buf = cStringIO() printer = SmtPrinter(buf) printer.printer(f) self._write_smt2_log(solver, "(assert %s)" % buf.getvalue()) else: buf = cStringIO() printer = SmtPrinter(buf) printer.printer(formula) self._write_smt2_log(solver, "(assert %s)" % buf.getvalue()) if comment: self._write_smt2_comment(solver, "%s: END" % comment)
def __solve_problem(self, hts: HTS, prop: Optional[FNode], lemmas: Optional[List[FNode]], assumptions: Optional[List[FNode]], problem: NamedTuple) -> str: trace = None traces = None region = None # only used for parametric model checking accepted_ver = False assert hts.assumptions is None, "There should not be any left-over assumptions from previous problems" for assump in assumptions: hts.add_assumption(assump) for lemma in lemmas: hts.add_lemma(lemma) bmc_safety = BMCSafety(hts, problem) bmc_parametric = BMCParametric(hts, problem) bmc_ltl = BMCLTL(hts, problem) res = VerificationStatus.UNC bmc_length = problem.bmc_length bmc_length_min = problem.bmc_length_min if problem.verification == VerificationType.SAFETY: accepted_ver = True Logger.log("Property: %s" % (prop.serialize(threshold=100)), 2) res, trace, _ = bmc_safety.safety(prop, bmc_length, bmc_length_min, problem.processes) if problem.verification == VerificationType.LTL: accepted_ver = True res, trace, _ = bmc_ltl.ltl(prop, bmc_length, bmc_length_min) if problem.verification == VerificationType.SIMULATION: accepted_ver = True res, trace = bmc_safety.simulate(prop, bmc_length) if problem.verification == VerificationType.PARAMETRIC: accepted_ver = True Logger.log("Property: %s" % (prop.serialize(threshold=100)), 2) res, traces, region = bmc_parametric.parametric_safety( prop, bmc_length, bmc_length_min, ModelExtension.get_parameters(hts), at_most=problem.cardinality) if problem.verification == VerificationType.EQUIVALENCE: accepted_ver = True bmcseq = BMCSafety(hts, problem) res, trace, t = bmcseq.safety(prop, bmc_length, bmc_length_min) if not accepted_ver: Logger.error("Invalid verification type") Logger.log("\n*** Problem \"%s\" is %s ***" % (problem.name, res), 1) return res, trace, traces, region
def _define_var(self, varname, vartype): if vartype == T_BOOL: return Symbol(varname, BOOL) if vartype[0] == T_BV: vartype, size = vartype[0], vartype[1] return Symbol(varname, BVType(int(size))) Logger.error("Unsupported type: %s" % vartype)
def solve_liveness_inc(self, hts, prop, k, k_min, eventually=False): if self.config.strategy in [ VerificationStrategy.FWD, VerificationStrategy.AUTO ]: return self.solve_liveness_inc_fwd(hts, prop, k, k_min, eventually) Logger.error("Invalid configuration strategy") return None
def _get_type(self, strtype): (vartype, size) = strtype if vartype == T_BV: return BVType(int(size[0])) if vartype == T_BOOL: return BOOL Logger.error("Unsupported type: %s" % vartype)
def Dec2BV(self, left, right): if right.is_int_constant(): size = right.constant_value() else: size = get_type(right).width if not left.is_int_constant(): Logger.error("Left argument of dec2bv should be a number") return BV(left.constant_value(), size)
def run_problems(problems_config: ProblemsManager): if sys.version_info[0] < 3: if config.devel: Logger.warning( "This software is not tested for Python 2, we recommend to use Python 3 instead" ) else: Logger.error( "This software is not tested for Python 2, please use Python 3 instead. To avoid this error run in developer mode" ) reset_env() # Named tuple representing all the general configuration options # (things that don't change between problems) general_config = problems_config.general_config Logger.verbosity = general_config.verbosity Logger.time = general_config.time psol = ProblemSolver() psol.solve_problems(problems_config) global_status = 0 traces = [] if len(problems_config.problems) > 0: Logger.log("\n*** SUMMARY ***", 0) else: if not general_config.translate: Logger.log("No problems to solve", 0) return 0 formulae = [] for pbm in problems_config.problems: (status, trace) = print_problem_result(pbm, problems_config) if status != 0: global_status = status traces += trace formulae.append(pbm.properties) if len(traces) > 0: Logger.log("\n*** TRACES ***\n", 0) for trace in traces: Logger.log("[%d]:\t%s" % (traces.index(trace) + 1, trace), 0) if general_config.translate: translate(problems_config.hts, general_config, formulae) if global_status != 0: Logger.log("", 0) Logger.warning("Verifications with unexpected result") return global_status
def solve_safety_inc_zz(self, hts, prop, k): solver = self.solver.copy("inc_zz") self._reset_assertions(solver) if TS.has_next(prop): Logger.error("Invariant checking with next variables only supports FWD strategy") init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() initt = self.at_time(And(init, invar), 0) Logger.log("Add init at_0", 2) self._add_assertion(solver, initt) propt = self.at_ptime(And(Not(prop), invar), -1) Logger.log("Add property pat_%d"%0, 2) self._add_assertion(solver, propt) t = 0 while (t < k+1): self._push(solver) even = (t % 2) == 0 th = int(t/2) if even: eq = And([EqualsOrIff(self.at_time(v, th), self.at_ptime(v, th-1)) for v in hts.vars]) else: eq = And([EqualsOrIff(self.at_time(v, th+1), self.at_ptime(v, th-1)) for v in hts.vars]) Logger.log("Add equivalence time %d"%t, 2) self._add_assertion(solver, eq) if self._solve(solver): Logger.log("Counterexample found with k=%s"%(t), 1) model = self._get_model(solver) return (t, model) else: Logger.log("No counterexample found with k=%s"%(t), 1) Logger.msg(".", 0, not(Logger.level(1))) self._pop(solver) if even: trans_t = self.unroll(trans, invar, th+1, th) else: trans_t = self.unroll(trans, invar, th, th+1) self._add_assertion(solver, trans_t) t += 1 return (t-1, None)
def walk(self, ast, modulename): description = ast.children()[0] modules = description.children() self.reset_structures(modulename) for m in modules: if type(m) == ModuleDef: self.modulesdic[m.name] = m if modulename not in self.modulesdic: Logger.error("Undefined module \"%s\"" % (modulename)) return self.walk_module(self.modulesdic[modulename], \ modulename if self.preserve_main_name else "")
def _define_var(self, var, prefix=""): varname, (vartype, size) = var fullname = self._concat_names(prefix, varname) if vartype == T_BV: return Symbol(fullname, BVType(int(size[0]))) if vartype == T_BOOL: return Symbol(fullname, BOOL) Logger.error("Unsupported type: %s" % vartype)
def convert(status): if type(status) == bool: return VerificationStatus.TRUE if status else VerificationStatus.FALSE if status.upper() in [VerificationStatus.TRUE,\ VerificationStatus.FALSE,\ VerificationStatus.UNK,\ VerificationStatus.UNC]: return status.upper() Logger.error("Invalid Verification Status \"%s\"" % status)
def run_problems(problems_file, config, problems=None): if sys.version_info[0] < 3: if config.devel: Logger.warning( "This software is not tested for Python 2, we recommend to use Python 3 instead" ) else: Logger.error( "This software is not tested for Python 2, please use Python 3 instead. To avoid this error run in developer mode" ) reset_env() Logger.verbosity = config.verbosity Logger.time = config.time psol = ProblemSolver() if problems is None: problems = Problems() problems.load_problems(problems_file) psol.solve_problems(problems, config) global_status = 0 traces = [] if len(problems.problems) > 0: Logger.log("\n*** SUMMARY ***", 0) else: if not config.translate: Logger.log("No problems to solve", 0) return 0 formulae = [] for pbm in problems.problems: (status, trace) = print_problem_result(pbm, config, len(traces) + 1) if status != 0: global_status = status traces += trace formulae.append(pbm.formula) if len(traces) > 0: Logger.log("\n*** TRACES ***\n", 0) for trace in traces: Logger.log("[%d]:\t%s" % (traces.index(trace) + 1, trace), 0) if config.translate: translate(problems.get_hts(), config, formulae) if global_status != 0: Logger.log("", 0) Logger.warning("Verifications with unexpected result") return global_status
def parse_file(self, strfile, config, flags=None): if flags is None: Logger.error("Top module not provided") topmodule = flags[0] absstrfile = os.path.abspath(strfile) directory = "/".join(absstrfile.split("/")[:-1]) filename = absstrfile.split("/")[-1] if self.single_file: files = [absstrfile] else: if self.files_from_dir: files = [ "%s/%s" % (directory, f) for f in os.listdir(directory) if self._get_extension(f) in self.extensions ] else: files = [ "%s/%s" % (directory, f) for f in list( set(self._collect_dependencies(directory, filename))) ] files.append(absstrfile) command = "%s -p \"%s\"" % (CMD, "; ".join(COMMANDS)) command = command.format(FILES=" ".join(files), \ TARGET=topmodule, \ PASSES="; ".join(PASSES), \ BTORFILE=TMPFILE) Logger.log("Command: %s" % command, 2) print_level = 3 if not Logger.level(print_level): saved_stdout = suppress_output() retval = os.system(command) if not Logger.level(print_level): restore_output(saved_stdout) if retval != 0: Logger.error("Error in Verilog conversion") parser = BTOR2Parser() ret = parser.parse_file(TMPFILE, config) if not Logger.level(1): os.remove(TMPFILE) return ret
def _check_parameters(self, module, modulesdic, vars_): vartypes = dict([(v.symbol_name(), v.symbol_type()) for v in vars_]) for sub in module.subs: formal_pars = [t[1] for t in modulesdic[sub[1]].pars] actual_pars = [ vartypes[self._concat_names(module.name, v[0])] for v in sub[2] ] if formal_pars != actual_pars: Logger.error( "Not matching types for instance \"%s\" of type \"%s\"" % (sub[0], sub[1]))
def solve_problems(self, problems, config): if len(problems.problems) == 0: Logger.error("No problems defined") if VerificationType.LTL in [ problem.verification for problem in problems.problems ]: ltl_reset_env() # generate systems for each problem configuration systems = {} for si in problems.symbolic_inits: (systems[('hts', si)], _, _) = self.parse_model(problems.relative_path, \ problems.model_file, \ problems.abstract_clock, si, "System 1", \ boolean=problems.boolean, \ no_clock=problems.no_clock, \ run_passes=problems.run_coreir_passes) if problems.equivalence is not None: (systems[('hts2', si)], _, _) = self.parse_model(problems.relative_path, \ problems.equivalence, \ problems.abstract_clock, si, "System 2", \ boolean=problems.boolean, \ no_clock=problems.no_clock, \ run_passes=problems.run_coreir_passes) else: systems[('hts2', si)] = None for problem in problems.problems: problem.hts = systems[('hts', problem.symbolic_init)] problem.hts2 = systems[('hts2', problem.symbolic_init)] problem.abstract_clock = problems.abstract_clock problem.no_clock = problems.no_clock problem.run_coreir_passes = problems.run_coreir_passes problem.relative_path = problems.relative_path if config.time or problems.time: timer_solve = Logger.start_timer("Problem %s" % problem.name, False) try: self.solve_problem(problem, config) Logger.msg(" %s\n" % problem.status, 0, not (Logger.level(1))) if config.time or problems.time: problem.time = Logger.get_timer(timer_solve, False) except KeyboardInterrupt as e: Logger.msg("\b\b Skipped!\n", 0)
def generate_problem(self, name, pbm_values): pbm = Problem() if VERIFICATION not in pbm_values: Logger.error("Verification type missing in problem \"%s\""%(name)) else: pbm.set_verification(pbm_values[VERIFICATION].lower()) del(pbm_values[VERIFICATION]) for attr,value in pbm_values.items(): if hasattr(pbm, attr): setattr(pbm, attr, auto_convert(value)) else: Logger.error("Attribute \"%s\" not found"%attr) return pbm
def BVVar(self, name, width): if width <= 0 or not isinstance(width, int): Logger.error("Bit Vector undefined for width = {}".format(width)) orname = name.replace(CSEP, SEP) if not self.anonimize_names: name = orname else: name = self.__new_var_name() self.map_an2or[name] = orname self.map_or2an[orname] = name if self.config.boolean and (width == 1): return Symbol(name, BOOL) return Symbol(name, BVType(width))
def analyze_element(self, modulename, el, args): if not VPARSER: Logger.error("Pyverilog is not available") Logger.log("(%d) Processing Node: %s ---> %s"%(el.lineno, \ class_name(el), \ None if el.children() is None else [class_name(c) for c in el.children()]), 3) Logger.log("(%d) Args: %s" % (el.lineno, str(args)), 4) self.__init_methods() classname = class_name(el) if classname in self.methods: local_handler = getattr(self, classname) return local_handler(modulename, el, args) Logger.error("Unmanaged Node type \"%s\", line %d" % (classname, el.lineno)) return el
def solve_safety_inc_bwd(self, hts, prop, k, assert_property=False): self._reset_assertions(self.solver) if TS.has_next(prop): Logger.error( "Invariant checking with next variables only supports FWD strategy" ) init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() formula = self.at_ptime(And(Not(prop), invar), -1) Logger.log("Add not property at time %d" % 0, 2) self._add_assertion(self.solver, formula) t = 0 while (t < k + 1): self._push(self.solver) pinit = self.at_ptime(init, t - 1) Logger.log("Add init at time %d" % t, 2) self._add_assertion(self.solver, pinit) if self._solve(self.solver): Logger.log("Counterexample found with k=%s" % (t), 1) model = self._get_model(self.solver) return (t, model) else: Logger.log("No counterexample found with k=%s" % (t), 1) Logger.msg(".", 0, not (Logger.level(1))) self._pop(self.solver) trans_t = self.unroll(trans, invar, t, t + 1) self._add_assertion(self.solver, trans_t) if assert_property and t > 0: prop_t = self.unroll(TRUE(), prop, t - 1, t) self._add_assertion(self.solver, prop_t) Logger.log("Add property at time %d" % t, 2) t += 1 return (t - 1, None)
def _remap_model(self, vars, model, k): if model is None: return model if self.config.strategy == VerificationStrategy.BWD: return self._remap_model_bwd(vars, model, k) if self.config.strategy == VerificationStrategy.ZZ: return self._remap_model_zz(vars, model, k) if self.config.strategy in [VerificationStrategy.AUTO, \ VerificationStrategy.FWD, \ VerificationStrategy.NU, \ VerificationStrategy.INT, \ VerificationStrategy.LTL, VerificationStrategy.ALL]: return self._remap_model_fwd(vars, model, k) Logger.error("Invalid configuration strategy") return None
def init_parsers(): from cosa.encoders.btor2 import BTOR2Parser from cosa.encoders.coreir import CoreIRParser from cosa.encoders.explicit_transition_system import ExplicitTSParser from cosa.encoders.symbolic_transition_system import SymbolicTSParser, SymbolicSimpleTSParser from cosa.encoders.verilog_yosys import VerilogYosysBtorParser ModelParsersFactory.register_parser(BTOR2Parser()) ModelParsersFactory.register_parser(CoreIRParser()) ModelParsersFactory.register_parser(ExplicitTSParser()) ModelParsersFactory.register_parser(SymbolicTSParser()) ModelParsersFactory.register_parser(SymbolicSimpleTSParser()) if ModelParsersFactory.verilog_encoder == VerilogEncoder.INTERNAL: Logger.error("Internal verilog parser support is deprecated.") if ModelParsersFactory.verilog_encoder == VerilogEncoder.YOSYS_BTOR: ModelParsersFactory.register_parser(VerilogYosysBtorParser()) if ModelParsersFactory.verilog_encoder == VerilogEncoder.YOSYS_COREIR: Logger.error("Not supported")
def MemAcc(self, left, right): primed = False if TS.is_prime(left): primed = True left = TS.get_ref_var(left) timed_symbol = lambda v: v if not primed else TS.get_prime(v) memname = left.symbol_name() allsymbols = list(get_env().formula_manager.get_all_symbols()) memsymbols = [(v.symbol_name(), timed_symbol(v)) for v in allsymbols \ if (not TS.is_prime(v)) and (not TS.is_timed(v)) and (v.symbol_name()[:len(memname)] == memname) \ and v.symbol_name() != memname] memsymbols.sort() memsize = len(memsymbols) if memsize < 1: Logger.error("Memory \"%s\" has size 1"%(memname)) if right.is_int_constant(): location = right.constant_value() if location > memsize-1: Logger.error("Out of bound access for memory \"%s\", size %d"%(memname, memsize)) return memsymbols[location][1] else: if not (right.is_symbol() and right.symbol_type().is_bv_type()): Logger.error("Symbolic memory access requires Bitvector indexing") width_idx = right.symbol_type().width width_mem = min(memsize, width_idx) return mem_access(right, [m[1] for m in memsymbols], width_idx)
def solve_safety_ninc(self, hts, prop, k): if self.config.strategy == VerificationStrategy.ALL: res = self.solve_safety_fwd(hts, prop, k) if res[1] is not None: return res if self.config.prove: res = self.solve_safety_int(hts, prop, k) if res[1] is not None: return res return res if self.config.strategy in [ VerificationStrategy.FWD, VerificationStrategy.AUTO ]: return self.solve_safety_fwd(hts, prop, k) if self.config.strategy == VerificationStrategy.INT: return self.solve_safety_int(hts, prop, k) Logger.error("Invalid configuration strategy") return None
def solve_safety_inc(self, hts, prop, k, k_min): if self.config.strategy == VerificationStrategy.ALL: res = self.solve_safety_inc_fwd(hts, prop, k, k_min) if res[1] is not None: return res if self.config.prove and not TS.has_next(prop): res = self.solve_safety_int(hts, prop, k) if res[1] is not None: return res res = self.solve_safety_inc_bwd(hts, prop, k) if res[1] is not None: self.config.strategy == VerificationStrategy.BWD return res res = self.solve_safety_inc_zz(hts, prop, k) self.config.strategy == VerificationStrategy.ZZ return res if self.config.strategy in [ VerificationStrategy.FWD, VerificationStrategy.AUTO ]: return self.solve_safety_inc_fwd(hts, prop, k, k_min) if self.config.strategy == VerificationStrategy.BWD: return self.solve_safety_inc_bwd(hts, prop, k) if self.config.strategy == VerificationStrategy.ZZ: return self.solve_safety_inc_zz(hts, prop, k) # Redirecting strategy selection error if self.config.strategy == VerificationStrategy.INT: Logger.warning( "Interpolation is not available in incremental mode. Switching to not incremental" ) return self.solve_safety_ninc(hts, prop, k) Logger.error("Invalid configuration strategy") return None
def get_const(val: FNode, match: FNode = None) -> FNode: ''' Returns a bit-vector constant based on the input value. If match is an FNode instead of None, tries to match the bit-width ''' if type(val) == FNode: return val elif type(val) == int: if match is not None: if type(match) != FNode: Logger.error( "Expecting an FNode in get_const, but got {}".format( type(match))) match_width = get_type(match).width if val.bit_length() > match_width: Logger.error( "Trying to match bit-width of {} but can't express {} in {} bits" .format(match, val, match_width)) return BV(val, match_width) return BV(val, DEFAULTINT) else: raise RuntimeError("Unhandled case in get_const: {}".format(type(val)))
def compile_sts(self, name, params): ts = TS() parsize = params[0] size = None if type(parsize) == str: sparser = StringParser() parsize = sparser.parse_formula(parsize) if parsize.is_constant(): size = parsize.constant_value() if get_type(parsize).is_bv_type(): size = get_type(parsize).width if size is None: Logger.error("Undefined size for symbol \"%s\"" % (params[0])) value = Symbol("%s.value" % name, BVType(size)) ts.add_var(value) ts.trans = EqualsOrIff(value, TS.get_prime(value)) return ts
def parse_file(self, strfile, config, flags=None): if flags is None: Logger.error("Top module not provided") topmodule = flags[0] absstrfile = os.path.abspath(strfile) directory = "/".join(absstrfile.split("/")[:-1]) filename = absstrfile.split("/")[-1] files = [absstrfile] with open(strfile, "r") as f: if topmodule not in f.read(): Logger.error("Module \"%s\" not found"%topmodule) commands = "\n".join(COMMANDS) commands = commands.format(FILES=" ".join(files), \ TARGET=topmodule, \ FILE=TMPFILE) command = "%s -script_file %s"%(CMD, TMPCMDFILE) with open(TMPCMDFILE, "w") as f: f.write(commands) Logger.log("Commands: %s"%commands, 2) print_level = 3 if not Logger.level(print_level): saved_stdout = suppress_output() retval = os.system(command) if not Logger.level(print_level): restore_output(saved_stdout) if retval != 0: Logger.error("Error in SystemVerilog conversion") parser = VerilogHTSParser() ret = parser.parse_file(TMPFILE, config, flags=flags) self.model_info = parser.get_model_info() if (not Logger.level(1)) and (not config.devel): os.remove(TMPFILE) os.remove(TMPCMDFILE) return ret
def get_sts(self, params): if len(params) != len(self.interface.split()): Logger.error("Invalid parameters for clock behavior \"%s\"" % (self.name)) clk = params[0] valuepar = params[1] if (not type(clk) == FNode) or (not clk.is_symbol()): Logger.error("Clock symbol \"%s\" not found" % (str(clk))) if (type(valuepar) == FNode) and (valuepar.is_bv_constant()): value = valuepar.constant_value() else: try: value = int(valuepar) except: Logger.error( "Clock value should be an integer number instead of \"%s\"" % valuepar) init = [] invar = [] trans = [] vars = set([]) if clk.symbol_type().is_bv_type(): pos_clk = EqualsOrIff(clk, BV(1, 1)) neg_clk = EqualsOrIff(clk, BV(0, 1)) else: pos_clk = clk neg_clk = Not(clk) if value == 1: invar.append(pos_clk) else: invar.append(neg_clk) ts = TS("Clock Behavior") ts.vars, ts.init, ts.invar, ts.trans = vars, And(init), And( invar), And(trans) Logger.log( "Adding clock behavior \"%s(%s)\"" % (self.name, ", ".join([str(p) for p in params])), 1) return ts