模型检查器Skill model-checker

实现有限状态系统的有界模型检查,用于验证并发程序、硬件和协议。关键词包括模型检查、SAT/SMT求解器、验证、并发程序、硬件验证、协议验证、时态逻辑、状态空间、错误检测、反例生成。

测试 0 次安装 0 次浏览 更新于 3/13/2026

名称: 模型检查器 描述: ‘实现有限状态系统的有界模型检查。使用场景: (1) 验证并发程序, (2) 硬件验证, (3) 协议验证.’ 版本: 1.0.0 标签:

  • 验证
  • 模型检查
  • 时态逻辑
  • 状态空间 难度: 高级 语言:
  • python
  • ocaml 依赖: []

模型检查器

使用SAT/SMT求解器实现有界模型检查。

使用场景

  • 并发程序验证
  • 协议验证
  • 硬件验证
  • 错误检测

技能功能

  1. 编码转换 - 转换为SAT/SMT
  2. 展开循环 - 有界探索
  3. 检查属性 - 安全性、活性
  4. 生成反例 - 针对违规

实现

from dataclasses import dataclass, field
from typing import Dict, List, Set, Optional
import z3

@dataclass
class State:
    """程序状态"""
    pc: int  # 程序计数器
    vars: Dict[str, int]  # 变量

@dataclass
class Transition:
    """状态转换"""
    from_state: State
    to_state: State
    condition: Optional[z3.BoolRef] = None

@dataclass
class Model:
    """转换系统"""
    states: Set[State]
    initial: Set[State]
    transitions: List[Transition]
    variables: List[str]

@dataclass
class Property:
    """时态属性"""
    pass

@dataclass
class Invariant(Property):
    """不变式: 始终P"""
    predicate: z3.BoolRef

@dataclass
class Reachability(Property):
    """最终可达: ◇P"""
    predicate: z3.BoolRef

class BoundedModelChecker:
    """有界模型检查器"""
    
    def __init__(self):
        self.solver = z3.Solver()
        self.model: Optional[Model] = None
        self.bound: int = 0
    
    def check(self, model: Model, property: Property, 
              bound: int = 10) -> 'CheckResult':
        """
        有界模型检查
        
        返回: (可满足/不可满足, 如果可满足则返回反例)
        """
        
        self.model = model
        self.bound = bound
        
        match property:
            case Invariant(pred):
                return self.check_invariant(pred, bound)
            
            case Reachability(pred):
                return self.check_reachability(pred, bound)
        
        raise ValueError(f"未知属性: {property}")
    
    def check_invariant(self, invariant: z3.BoolRef, 
                       bound: int) -> 'CheckResult':
        """
        检查不变式: ⊡P (始终P)
        
        编码: ∧_{i=0}^{k} (状态_i → P)
        """
        
        self.solver = z3.Solver()
        
        # 创建状态变量
        state_vars = self.create_state_variables(bound)
        
        # 编码初始状态
        self.encode_initial(state_vars[0])
        
        # 编码转换
        for i in range(bound):
            self.encode_transition(state_vars[i], state_vars[i+1])
        
        # 编码不变式违规
        for i in range(bound + 1):
            violation = z3.Not(self.encode_state_predicate(state_vars[i], invariant))
            self.solver.add(violation)
        
        # 检查
        if self.solver.check() == z3.sat:
            model = self.solver.model()
            return CheckResult(
                sat=True,
                counterexample=self.extract_trace(state_vars, model),
                message="不变式被违反"
            )
        
        return CheckResult(
            sat=False,
            counterexample=None,
            message="在边界内不变式成立"
        )
    
    def check_reachability(self, target: z3.BoolRef,
                          bound: int) -> 'CheckResult':
        """
        检查可达性: ◇P
        
        编码: ∃i. 状态_i → P
        """
        
        self.solver = z3.Solver()
        
        state_vars = self.create_state_variables(bound)
        self.encode_initial(state_vars[0])
        
        for i in range(bound):
            self.encode_transition(state_vars[i], state_vars[i+1])
        
        # 目标可达
        target_reached = z3.Bool('target_reached')
        for i in range(bound + 1):
            self.solver.add(
                target_reached == self.encode_state_predicate(state_vars[i], target)
            )
        
        self.solver.add(target_reached)
        
        if self.solver.check() == z3.sat:
            model = self.solver.model()
            return CheckResult(
                sat=True,
                counterexample=self.extract_trace(state_vars, model),
                message="目标可达"
            )
        
        return CheckResult(
            sat=False,
            counterexample=None,
            message="在边界内目标不可达"
        )
    
    def create_state_variables(self, bound: int) -> List[Dict[str, z3.Expr]]:
        """创建符号状态变量"""
        
        states = []
        
        for i in range(bound + 1):
            state = {}
            for var in self.model.variables:
                state[var] = z3.Int(f"s{i}_{var}")
            states.append(state)
        
        return states
    
    def encode_initial(self, state: Dict[str, z3.Expr]):
        """编码初始状态约束"""
        
        for init in self.model.initial:
            for var, val in init.vars.items():
                self.solver.add(state[var] == val)
    
    def encode_transition(self, from_state: Dict[str, z3.Expr],
                        to_state: Dict[str, z3.Expr]):
        """编码转换关系"""
        
        for trans in self.model.transitions:
            # 匹配: from_state 匹配 trans.from_state
            # 结果: to_state 匹配 trans.to_state
            
            conditions = []
            
            for var in self.model.variables:
                # 简单: 如果不改变则复制
                conditions.append(to_state[var] == from_state[var])
            
            # 添加特定转换的约束
            for var, val in trans.to_state.vars.items():
                if var in from_state and from_state[var] != val:
                    conditions.append(to_state[var] == val)
            
            if trans.condition:
                conditions.append(trans.condition)
            
            self.solver.add(z3.Or(conditions))
    
    def encode_state_predicate(self, state: Dict[str, z3.Expr],
                              pred: z3.BoolRef) -> z3.BoolRef:
        """为状态编码谓词"""
        
        # 将状态变量替换到谓词中
        subst = {z3.Int(var): val for var, val in state.items()}
        
        return z3.substitute(pred, subst)
    
    def extract_trace(self, states: List[Dict[str, z3.Expr]], 
                     model: z3.Model) -> List[Dict[str, int]]:
        """提取反例轨迹"""
        
        trace = []
        
        for state in states:
            concrete = {}
            for var, expr in state.items():
                val = model.eval(expr)
                concrete[var] = val.as_long()
            trace.append(concrete)
        
        return trace

@dataclass
class CheckResult:
    """模型检查结果"""
    sat: bool
    counterexample: Optional[List[Dict[str, int]]]
    message: str

并发程序验证

class ConcurrentModelChecker:
    """并发程序的模型检查器"""
    
    def __init__(self):
        self.checker = BoundedModelChecker()
    
    def verify_two_thread(self, program: 'Program', 
                        property: Property) -> CheckResult:
        """
        验证两个线程的程序
        
        交错语义:
        - 所有可能的线程调度
        - 共享内存一致性
        """
        
        # 建模为转换系统
        model = self.create_model(program)
        
        # 使用递增边界检查
        for bound in range(1, 20):
            result = self.checker.check(model, property, bound)
            
            if result.sat:
                return result
        
        return CheckResult(
            sat=False,
            counterexample=None,
            message="属性成立(或边界太小)"
        )
    
    def create_model(self, program: 'Program') -> Model:
        """创建转换系统模型"""
        
        # 提取状态和转换
        states = set()
        initial = set()
        transitions = []
        
        # 初始状态
        init_state = State(pc=0, vars=program.init_vars)
        initial.add(init_state)
        states.add(init_state)
        
        # 从CFG构建转换
        for stmt in program.statements:
            from_state = State(stmt.from_pc, stmt.from_vars)
            to_state = State(stmt.to_pc, stmt.to_vars)
            
            trans = Transition(from_state, to_state, stmt.condition)
            transitions.append(trans)
            
            states.add(from_state)
            states.add(to_state)
        
        return Model(
            states=states,
            initial=initial,
            transitions=transitions,
            variables=list(program.variables)
        )

活性检查

class LivenessChecker:
    """检查活性属性"""
    
    def check_liveness(self, model: Model, property: Reachability) -> CheckResult:
        """
        检查: ◇P (最终P)
        
        使用公平性约束
        """
        
        # 检查: 我们能无限次达到P吗?
        # 添加公平性: 每个线程无限执行
        
        # 目前, 简单的有界版本
        checker = BoundedModelChecker()
        
        # 尝试逐渐增大的边界
        for bound in range(1, 50):
            result = checker.check(model, property, bound)
            
            if result.sat:
                return result
        
        return CheckResult(sat=False, message="属性可能不成立")

关键概念

概念 描述
有界模型检查 展开到有界深度
SAT/SMT编码 编码为布尔公式
反例 违反的轨迹
公平性 调度器的假设

属性

属性 符号 描述
不变式 ⊡P 始终P
可达性 ◇P 最终P
活性 □◇P 无限次P
安全性 □(P → Q) 如果P则Q

技巧

  • 从小边界开始
  • 使用谓词抽象
  • 谨慎处理无限状态
  • 考虑公平性

相关技能

  • symbolic-execution-engine - 符号执行
  • weak-memory-model-verifier - 内存模型
  • smt-solver-interface - SMT求解

权威参考文献

参考文献 重要性
Clarke, Grumberg, Kroening, Peled, Veith, “Model Checking” (MIT Press, 2018) 模型检查的权威教科书
Biere, Cimatti, Clarke, Zhu, “Symbolic Model Checking without BDDs” (TACAS 1999) BMC论文
Ball & Rajamani, “The SLAM Project: Debugging System Software via Static Analysis” (POPL 2002) 软件模型检查
Kurshan, “Computer-Aided Verification of Coordinating Processes” (1994) 硬件模型检查起源
Holzmann, “The Model Checker SPIN” (IEEE TSE 1997) SPIN模型检查器

权衡与限制

模型检查方法权衡

方法 优点 缺点
显式状态 简单, 完整 状态爆炸
符号(BDD) 处理大状态 可能爆炸
有界(SMT) 适合错误检测 不能证明活性
谓词抽象 扩展到软件 精度损失

何时不使用模型检查

  • 对于大型系统: 状态爆炸; 使用抽象
  • 对于参数化系统: 无界验证困难
  • 对于概率系统: 使用概率模型检查
  • 对于实时系统: 使用时序自动机模型检查

复杂性考虑

  • 显式状态: O(n)状态, 变量指数级
  • 符号: 最坏情况指数级, 通常实用
  • BMC: 线性于边界×状态大小
  • SMT求解: NP难, 但Z3在实践中快速

限制

  • 状态爆炸: 主要瓶颈; 组件指数级
  • 无限状态: 需要抽象或有界检查
  • 可扩展性: 不能处理大型软件系统
  • 活性: 难以验证; 需要公平性假设
  • 反例: 可能太长难以理解
  • 参数化: N进程系统困难

研究工具与成果

模型检查工具:

工具 应用 学习内容
SPIN Promela 模型检查
CBMC C有界 有界验证
BLAST C 谓词抽象
SLAM Windows驱动 软件模型检查
Java PathFinder Java 显式状态
Alloy 关系型 轻量级建模

关键论文

  • Clarke et al. - 模型检查论文
  • Biere et al. - BMC论文

研究前沿

1. 软件模型检查

  • 目标: 扩展到大型代码库
  • 方法: 谓词抽象, 抽象精炼
  • 工具: CBMC, ESBMC

实现陷阱

陷阱 实际后果 解决方案
状态爆炸 无法探索所有状态 抽象
无限状态 不终止 有界检查