def test_msat_preferred_variable(self): a, b, c = [Symbol(x) for x in "abc"] na, nb, nc = [Not(Symbol(x)) for x in "abc"] f = And(Implies(a, And(b, c)), Implies(na, And(nb, nc))) s1 = Solver("msat") s1.add_assertion(f) s1.set_preferred_var(a, True) self.assertTrue(s1.solve()) self.assertTrue(s1.get_value(a).is_true()) s2 = Solver("msat") s2.add_assertion(f) s2.set_preferred_var(a, False) self.assertTrue(s2.solve()) self.assertTrue(s2.get_value(a).is_false()) # Show that calling without polarity still works # This case is harder to test, because we only say # that the split will occur on that variable first. s1.set_preferred_var(a)
def test_msat_preferred_variable(self): a, b, c = [Symbol(x) for x in "abc"] na, nb, nc = [Not(Symbol(x)) for x in "abc"] f = And(Implies(a, And(b,c)), Implies(na, And(nb,nc))) s1 = Solver("msat") s1.add_assertion(f) s1.set_preferred_var(a, True) self.assertTrue(s1.solve()) self.assertTrue(s1.get_value(a).is_true()) s2 = Solver("msat") s2.add_assertion(f) s2.set_preferred_var(a, False) self.assertTrue(s2.solve()) self.assertTrue(s2.get_value(a).is_false()) # Show that calling without polarity still works # This case is harder to test, because we only say # that the split will occur on that variable first. s1.set_preferred_var(a)
def solve_soduku_model(model): solver = Solver() solver.add_assertion(model.extractConstraints()) if solver.solve(): # solution found solution = tabletools.generateEmptyTable(model.n) for y in range(0, model.n): # retrive the values from the sloved model for x in range(0, model.n): solution[y][x] = solver.get_value(model.getSymbol(x, y)) return solution else: return None # couldn't solve model :(
class Reluzy: def __init__(self, filename, violationfile, logger): self.logger = logger self.nnet2smt = Nnet2Smt(filename, violationfile) self.nnet2smt.convert(True) self.input_vars = self.nnet2smt.input_vars self.output_vars = self.nnet2smt.output_vars self.formulae = self.nnet2smt.formulae self.relus = self.nnet2smt.relus self.relus_level = self.nnet2smt.relus_level self.solver = Solver(name='yices') self.sat_checker = Solver(name='yices') self.init() def init(self): self.solver.add_assertion(And(self.formulae)) self.sat_checker.add_assertion(And(self.formulae)) for r1, r2 in self.relus: self.sat_checker.add_assertion(Equals(r1, Max(r2, Real(0)))) #lemmas = self.refine_zero_lb(False) #self.solver.add_assertion(And(lemmas)) #lemmas = self.refine_slope_lb(False) #self.solver.add_assertion(And(lemmas)) def solve(self): while True: self.logger.info('Solving') res = self.solver.solve() if not res: print('unsat') break else: lemmas = self.refine() if not lemmas: print('sat') break else: self.solver.add_assertion(And(lemmas)) def check_sat(self): self.logger.info('Checking for Sat') self.sat_checker.push() for v in self.input_vars: self.sat_checker.add_assertion(Equals(v, self.solver.get_value(v))) res = self.sat_checker.solve() if res: for x in self.input_vars: print(v, self.sat_checker.get_value(x)) return True else: self.sat_checker.pop() return False def refine(self): self.logger.info('Refining') lemmas = self.refine_zero_lb() if not lemmas: lemmas = self.refine_slope_lb() if not lemmas: lemmas = self.refine_zero_ub() if not lemmas: lemmas = self.refine_slope_ub() if not lemmas: for v in self.input_vars: print(v, self.solver.get_value(v)) #elif self.check_sat(): # return [] return lemmas def refine_zero_lb(self, check=True): lemmas = [] zero = Real(0) for r1, _ in self.relus: l = GE(r1, zero) if check: tval = self.solver.get_value(l) if tval.is_false(): lemmas.append(l) self.logger.debug('Adding %s' % l ) else: lemmas.append(l) return lemmas def refine_zero_ub(self): lemmas = [] zero = Real(0) for s in self.relus_level: for r1, r2 in s: l = Implies(LE(r2, zero), LE(r1, zero)) tval = self.solver.get_value(l) if tval.is_false(): lemmas.append(l) self.logger.debug('Adding %s' % l ) if lemmas: break return lemmas def refine_slope_lb(self, check=True): lemmas = [] for r1, r2 in self.relus: l = GE(r1, r2) if check: tval = self.solver.get_value(l) if tval.is_false(): lemmas.append(l) self.logger.debug('Adding %s' % l ) else: lemmas.append(l) return lemmas def refine_slope_ub(self): lemmas = [] zero = Real(0) for s in self.relus_level: for r1, r2 in s: l = Implies(GE(r2, zero), LE(r1, r2)) tval = self.solver.get_value(l) if tval.is_false(): lemmas.append(l) self.logger.debug('Adding %s' % l ) if lemmas: break return lemmas
class PDR(object): def __init__(self, system): self.system = system self.frames = [system.init] self.solver = Solver() self.prime_map = dict([(v, next_var(v)) for v in self.system.variables]) def check_property(self, prop): """Property Directed Reachability approach without optimizations.""" print("Checking property %s..." % prop) while True: cube = self.get_bad_state(prop) if cube is not None: # Blocking phase of a bad state if self.recursive_block(cube): print("--> Bug found at step %d" % (len(self.frames))) break else: print(" [PDR] Cube blocked '%s'" % str(cube)) else: # Checking if the last two frames are equivalent i.e., are inductive if self.inductive(): print("--> The system is safe!") break else: print(" [PDR] Adding frame %d..." % (len(self.frames))) self.frames.append(TRUE()) def get_bad_state(self, prop): """Extracts a reachable state that intersects the negation of the property and the last current frame""" return self.solve(And(self.frames[-1], Not(prop))) def solve(self, formula): """Provides a satisfiable assignment to the state variables that are consistent with the input formula""" if self.solver.solve([formula]): return And([ EqualsOrIff(v, self.solver.get_value(v)) for v in self.system.variables ]) return None def recursive_block(self, cube): """Blocks the cube at each frame, if possible. Returns True if the cube cannot be blocked. """ for i in range(len(self.frames) - 1, 0, -1): cubeprime = cube.substitute( dict([(v, next_var(v)) for v in self.system.variables])) cubepre = self.solve( And(self.frames[i - 1], self.system.trans, Not(cube), cubeprime)) if cubepre is None: for j in range(1, i + 1): self.frames[j] = And(self.frames[j], Not(cube)) return False cube = cubepre return True def inductive(self): """Checks if last two frames are equivalent """ if len(self.frames) > 1 and \ self.solve(Not(EqualsOrIff(self.frames[-1], self.frames[-2]))) is None: return True return False def __del__(self): self.solver.exit()
def analyze_rv32_interpreter(program: List[Instruction], bbs: List[BasicBlock]): #print("analyzing rv32 interpreter ...") mk_dot(dot_cfg(bbs), filename="cfg.pdf") #for bb in program: print(bb) # start at MainStart @ 0x0056 start_pc = 0x56 # symbolic instruction: ADD rs2, rs1, rd funct7 = BitVecVal(0, 7) rs2 = Symbol("RV32I_ADD_rs2", BVType(5)) rs1 = Symbol("RV32I_ADD_rs1", BVType(5)) funct3 = BitVecVal(0b00, 3) # ADD rd = Symbol("RV32I_ADD_rd", BVType(5)) opcode = BitVecVal(0b0110011, 7) # OP #RV32I_instr = Symbol("RV32IInstruction", BVType(32)) RV32I_instr = cat(funct7, rs2, rs1, funct3, rd, opcode) print(f"Symbolically executing: {RV32I_instr}") # interpreter orig_state = MachineState().update(PC=BitVecVal(start_pc, 16)) def place_instr(loc, instr, st) -> MachineState: # make sure PC fits into two registers assert loc & 0xffff == loc msb, lsb = BitVecVal(loc >> 8, 8), BitVecVal(loc & 0xff, 8) st = st.update(R=st.R.update(10, lsb).update(11, msb)) instr_parts = [ BVExtract(instr, *jj) for jj in ((jj * 8, jj * 8 + 7) for jj in range(4)) ] if isinstance(loc, int): instr_locs = [loc + ii for ii in range(4)] else: assert False, "TODO: support symbolic address" mem = st.MEM for loc, val in zip(instr_locs, instr_parts): mem = mem.update(loc, val) return st.update(MEM=mem) orig_state = place_instr(loc=0, instr=RV32I_instr, st=orig_state) mf8_ex = SymExec() ex = SymbolicExecutionEngine(program=program, start_state=orig_state, semantics=mf8_ex) print() print() print("SYM EXEC") print("--------") done, end_state = ex.run(max_steps=2000) #ex.print_state() #ex.print_mem(ex.st) #ex.print_path() print(ex.taken) print(f"DONE? {done}") #print("PATHS:") for ii, (cond, st) in enumerate(end_state): print(str(ii) + ") " + cond.serialize()) #ex.print_mem(st) solver = Solver(name="z3", logic=QF_AUFBV) # check for completeness conds = reduce(Or, (cond for cond, st in end_state)) complete = not solver.is_sat(Not(conds)) print(f"Complete? {complete}") # check result of every path: def to_mem_addrs(reg_index): return reversed([0xf100 + reg_index * 8 + jj for jj in range(4)]) def relate_regs(mem, regs): def relate_loc(ii): mem_locs = [ Select(mem, BitVecVal(addr, 16)) for addr in to_mem_addrs(ii) ] return Equals(cat(*mem_locs), Select(regs, BitVecVal(ii, 5))) return reduce(And, [relate_loc(ii) for ii in range(32)]) def name_value(solver, name, val): sym = Symbol(name, val.get_type()) solver.add_assertion(Equals(sym, val)) def locs_to_str(name, array, locs): return "; ".join(f"{name}[{ii:04x}] = 0x{array[ii]:02x}" for ii in sorted(list(set(locs)))) for ii, (cond, end_st) in enumerate(end_state): # create clean slate solver solver = Solver(name="cvc4", logic=QF_AUFBV, generate_models=True) # symbolically execute the RISC-V add regs = Symbol('RV32I_REGS', ArrayType(BVType(5), BVType(32))) regs_n = sym_exec_rsicv_add(rs1=rs1, rs2=rs2, rd=rd, regs=regs) name_value(solver, "DBG_RV32I_REGS_N", regs_n) # add mem to regs relation mem_orig = orig_state.MEM.array() pre = And(And(cond, relate_regs(mem_orig, regs)), Equals(Select(regs, BitVecVal(0, 5)), BitVecVal(0, 32))) mem_n = end_st.MEM.array() post = relate_regs(mem_n, regs_n) # DEBUG: add symbols for every memory write mem_data = end_st._mem._data mem_write_locs = [ Symbol(f"DBG_MF8_MEM_WRITE_LOC_{ii}", BVType(16)) for ii in range(len(mem_data)) ] for sym, (expr, _) in zip(mem_write_locs, mem_data): solver.add_assertion(Equals(sym, expr)) # now check for validity formula = Implies(pre, post) write_smtlib(Not(formula), f"path_{ii:02}.smt2") correct = solver.is_valid(formula) print(f"Correct? {correct}") if not correct: print("Path condition:") print(cond.serialize()) print("Symbolic Mem:") ex.print_mem(end_st) print("Model:") rs1_val = solver.get_value(rs1).bv_unsigned_value() rs2_val = solver.get_value(rs2).bv_unsigned_value() rd_val = solver.get_value(rd).bv_unsigned_value() regs_val = ArrayValue(solver.get_value(regs)) regs_n_val = ArrayValue(solver.get_value(regs_n)) mem_val = ArrayValue(solver.get_value(mem_orig)) mem_n_val = ArrayValue(solver.get_value(mem_n)) reg_addrs = [rd_val, rs1_val, rs2_val] mem_write_locs_vals = [ solver.get_value(ll).bv_unsigned_value() for ll in mem_write_locs ] mem_addrs = reduce(operator.add, [list(to_mem_addrs(ii)) for ii in reg_addrs]) + mem_write_locs_vals print(f"R[{rd_val}] <- R[{rs1_val}] + R[{rs2_val}]") print(f"Pre: {locs_to_str('R', regs_val, reg_addrs)}") print(f" {locs_to_str('M', mem_val, mem_addrs)}") print(f"Post: {locs_to_str('R', regs_n_val, reg_addrs)}") print(f" {locs_to_str('M', mem_n_val, mem_addrs)}") print( f"MEM write addresses: {[f'0x{loc:04x}' for loc in mem_write_locs_vals]}" ) #print(regs_n_val) #print(mem_val) # TODO: check PC post-condition # TODO: add pre and post conditions for program memory equivalence # TODO: add pre and post conditions for data memory equivalence break return
class PDR(object): def __init__(self, system): self.system = system self.frames = [system.init] self.solver = Solver() self.prime_map = dict([(v, next_var(v)) for v in self.system.variables]) def check_property(self, prop): """Property Directed Reachability approach without optimizations.""" print("Checking property %s..." % prop) while True: cube = self.get_bad_state(prop) if cube is not None: # Blocking phase of a bad state if self.recursive_block(cube): print("--> Bug found at step %d" % (len(self.frames))) break else: print(" [PDR] Cube blocked '%s'" % str(cube)) else: # Checking if the last two frames are equivalent i.e., are inductive if self.inductive(): print("--> The system is safe!") break else: print(" [PDR] Adding frame %d..." % (len(self.frames))) self.frames.append(TRUE()) def get_bad_state(self, prop): """Extracts a reachable state that intersects the negation of the property and the last current frame""" return self.solve(And(self.frames[-1], Not(prop))) def solve(self, formula): """Provides a satisfiable assignment to the state variables that are consistent with the input formula""" if self.solver.solve([formula]): return And([EqualsOrIff(v, self.solver.get_value(v)) for v in self.system.variables]) return None def recursive_block(self, cube): """Blocks the cube at each frame, if possible. Returns True if the cube cannot be blocked. """ for i in range(len(self.frames)-1, 0, -1): cubeprime = cube.substitute(dict([(v, next_var(v)) for v in self.system.variables])) cubepre = self.solve(And(self.frames[i-1], self.system.trans, Not(cube), cubeprime)) if cubepre is None: for j in range(1, i+1): self.frames[j] = And(self.frames[j], Not(cube)) return False cube = cubepre return True def inductive(self): """Checks if last two frames are equivalent """ if len(self.frames) > 1 and \ self.solve(Not(EqualsOrIff(self.frames[-1], self.frames[-2]))) is None: return True return False