显式状态模型检验的挑战与方向

显式状态模型检验的去向

1 引言

确保各种软件和硬件产品质量的方法是开发过程中不可或缺的一部分。对于软件和硬件开发者而言,通常用于检测系统缺陷的唯一技术是测试;考虑到特定产品的质量不佳的成本,这通常是有效且合理的选择。然而,在某些情况下,可能的设计错误或实现缺陷所带来的后果过于严重,此时标准的测试方法便显得不足,这要么是因为错误检测本身存在固有的不完整性,要么是因为需要执行的测试数量过于庞大,才能将未发现错误的概率降低到可接受的水平。在这种情况下,形式化验证方法便应运而生。

模型检验[28]是一种形式化验证过程,它以待验证系统的模型和一条系统规范作为输入。该过程用于判定系统是否满足给定的规范。在否定情况下,即当存在违反规范的系统行为时,可选择性地返回此类违规的证据,即所谓的反例。

模型检验方法的显著优势在于,当验证过程成功完成时,它为模型检测器的用户提供了与数学证明相当的置信度,以确认系统满足给定规约的有效性。此外,一旦输入被转换为所使用的模型检测器可接受的形式,该决策过程完全是自动化的(由计算机完成)。

模型检验方法的这些显而易见的优势自然并非免费获得。模型检验的标准工作流程要求用户提供模型检测器可处理的系统和规范的形式化描述。不幸的是,模型检验的实际经验表明,将待验证系统以模型检测器可接受的形式进行描述,实际上相当于使用模型检测器的建模语言对系统的相关部分进行重新表述。尽管这可能并不十分困难,但这一过程几乎无法自动化,因而需要大量的人工投入。类似地,在模型检验的背景下形式化规范,要求系统工程师将各个系统属性表示为时态逻辑公式。根据所使用的时态逻辑不同,该技术被称为 LTL(线性时态逻辑)模型检验、CTL(计算树逻辑)模型检验等。然而,一旦输入到模型检测器中的内容已处于适当形式,关于单个系统属性是否成立的判断便可完全自动化。

自动化决策过程的原理是让计算机完全探索被验证系统的所有内部配置。所谓的可达状态空间是指系统从一组给定的初始状态可能演化到的所有系统配置的集合。通过对可达状态空间进行适当的分析,模型检测器可以证明错误的可达配置不存在,或者证明系统行为与以时态逻辑公式给出的规范的一致性。

不幸的是,现实世界系统的可达状态空间非常庞大,以至于其完整分析超出了当代计算平台的能力范围。因此,在许多情况下,由于计算资源不足(尤其是内存),通过模型检验进行的验证最终会失败。通常将状态空间的大小随着系统描述规模(例如某种编程语言)呈指数增长的现象称为状态空间爆炸问题。实际上,这种指数增长有两个根本原因:输入处理和控制流非确定性。

人们已将大量关注投入到应对自动化形式验证领域中的状态空间爆炸问题[31]的各种方法的开发上[48]。为了降低单个模型检验任务的内存需求,引入了许多技术,例如状态压缩[35],、压缩[37],、状态空间约简[29,33,47],、符号化状态空间表示[23],等。随着二叉决策图在模型检验中的发明与应用[46],模型检验领域被划分为符号化和显式状态(枚举式)两个分支。尽管CTL已成为符号化分支中的原生规范逻辑(主要归功于SMV模型检验器[27]),线性时序逻辑则由于良好的已知的显式状态模型检测器SPIN[37], 、DIVINE[3],或LTSmin[18], )所使用的接受环检测算法必须构造并存储整个乘积图。因此,这些模型检测工具主要受限于由状态空间爆炸问题引起的高度内存需求。

本文涉及最近关于线性时序逻辑显式状态模型检测的三个主要研究方向。在第2节中,我们简要回顾了通过并行和分布式内存处理来应对状态空间爆炸的研究工作。尽管状态空间爆炸是一个严重的问题,但令人惊讶的是,它并不总是阻碍模型检测在实践中应用的主要因素。在模型检测方案中另一个相当棘手的因素是需要进行形式化建模。为了解决这个问题,我们在第3节中讨论了将显式状态模型检测直接应用于LLVM位码的方法。LLVM位码用作程序的编译器内部表示,因此可以通过通用编译器从源代码自动生成。在第4节中,我们注意到显式状态模型检测通常被用作单元测试的一种实例,并讨论了一种朝向符号化表示的扩展方法,这将使显式状态模型检测重新回归到形式化验证的范畴。

2 并行处理和状态空间爆炸

毫无疑问,在过去的几十年中,使用逻辑模型检验工具可以解决的验证问题范围已显著增加。尽管令人惊讶的是,这种增长不仅基于新发现的算法进步,而且在很大程度上归因于单个处理器核心速度以及可用主内存大小[39]的提升。鉴于显式状态模型检测的效率在很大程度上依赖于所使用的计算硬件的速度,并且考虑到单个CPU核心的速度在未来将不再继续提升,因此除了采用并行处理之外别无选择。

现有顺序式线性时序逻辑模型检测工具难以直接扩展到并行架构,其主要障碍在于:Büchi空性问题不太可能存在时间最优的并行可扩展算法[49]。(这仍然是一个未解决的问题。)因此,早期在并行和分布式内存的LTL模型检测[8]方面的研究采用了并行可扩展但非时间最优的接受环检测算法。尽管在顺序情况下,用于接受环检测的算法,例如嵌套深度优先搜索[32]或用于强连通分量分解的塔扬算法的各种版本[51],依赖于深度优先搜索后序;分布式内存算法则基于可达性过程、值传播或拓扑排序[9,21]构建。

分布式内存处理无法单独应对状态空间爆炸问题,必须与其他技术相结合。在显式状态模型检测中,应对状态空间爆炸最成功的技术之一是偏序约减[47]。事实上,为了在分布式内存环境中保持偏序约减的效率,必须开发新的拓扑排序条件[6]。另一项重要的算法改进涉及LTL公式[25]的分类。对于某些类别的LTL公式,可以显著改进并行算法[2]。对于弱LTL公式,OWCTY算法[24]甚至在复杂度上达到了最优顺序算法的水平。然而,该算法的缺点在于它不是一种在线验证算法。由于在线验证是一个重要的实际方面,因此也开发了一种改进版本,使其在大多数验证实例中能够支持在线验证[5]。

所有分布式内存算法均已作为并行和分布式内存线性时序逻辑模型检测器DIVINE[12]的一部分实现。然而,DIVINE专注于非基于DFS的算法和分布式内存处理,并不要求其仅在分布式内存环境中使用。事实上,DIVINE在共享内存环境[4]中也能顺利运行。

由于缺乏并行处理能力对于其他显式状态模型检测工具而言是一个显著的缺陷,尤其是在当前计算硬件环境下,因此它们也进行了并行处理方面的改进。具体来说,SPIN模型检测器已通过所谓的栈切片[38]和捎带[41]技术实现了对并行处理的支持。然而,在并行处理方面对SPIN最具创新性的扩展是所谓的群体验证[39,40],该方法将模型检验引向了Map‐Reduce模式。具体而言,单个验证任务会被复制成与可用处理器数量相同的多个副本,并且每个副本探索状态空间的顺序都会有所不同。在这种并行任务群中,早期发现接受环的概率显著提高。

LTSmin模型检测器[43]选择了完全不同的方法。其作者成功地将顺序嵌套深度优先搜索算法适配到并行共享内存处理中。该方法的核心思想是自由地并行运行嵌套深度优先搜索算法,然后检测并恢复可能破坏计算正确性的情况。尽管这种方法在一般情况下无法实现良好的扩展性,但实际测量表明其在共享内存架构上取得了优越的结果[34,42]。

还有另一种并行计算平台最近变得流行起来——通用图形处理单元(GPGPU)。尽管该平台从未用于加速内存密集型计算,但其原始计算能力相当具有吸引力。一系列关于加速LTL模型检测的研究成果最近已被发表,这些研究采用非基于DFS的算法进行接受环检测[1,10,11]以及加速状态空间生成[54]。

3 无需建模的模型检测

最近的形式化验证研究活动非常强调验证方法在实践中的直接适用性。例如,软件验证竞赛[17]就是程序分析社区中的一项主流活动。推动形式化方法应用被广大软件开发和工程界所接受的强烈需求表明,实践中使用形式化方法最重要的因素是其易用性。因此,形式化方法必须应用于软件工程师和开发人员自然使用的工件上,即源代码层面。

此外,如果模型检测方法得到大规模推广,并且模型检测工具成为软件开发人员的常规实用工具,那么它必须实现完整的编程语言规范,以便软件开发人员编写和运行的程序也能作为模型检测器的有效输入。编程语言在其完整规范中相当复杂,而工程师们在追求更优雅、更易维护的代码时,通常需要权衡特定编程语言中允许与不允许的边界。因此,为了实现模型检验而在编程语言上引入重大约束,通常会导致模型检验过程完全被排除在开发周期之外。

一种无需显式建模即可验证C/C++程序的新方法已在[7]中提出。该解决方案通过LLVM[44]架构,特别是利用中间字节码表示(LLVM IR),有效地将我们的并行分布式内存模型检测工具DIVINE与Clang编译器连接起来。尽管LLVM IR没有精确的语义,但事实表明,尽管实际编译器都实现了大量优化过程,它们在此方面仍达到了令人羡慕的一致水平。

使用LLVM IR作为模型检验的输入,不仅能够实现无需繁琐的系统建模过程的模型检验,还提供了一种具有合理且明确定义语义的稳定建模语言。在这种设置下,诸如DIVINE之类的模型检测工具可以对几乎未经修改的C/C++源代码提供完整的LTL模型检测。

关于输入到模型检测器的唯一限制是需要C/C++程序描述的完整性。实际上,模型检测器无法验证那些调用了外部库但没有可用源代码的程序。同样,任何对操作系统内核的调用(例如输入和输出处理)也超出了此方法的范围,除非将外部环境以某种方式添加到程序中,并在不实际执行输入/输出指令的情况下进行模拟。原则上,这种使用场景类似于众所周知的单元测试方法。

请注意,DIVINE内部提供了大多数POSIX线程API(pthread.h)的实现,从而能够对未修改的程序进行验证已验证的多线程程序。特别是,DIVINE在单个字节码指令级别上系统地探索所有可能的线程交错。这使得DIVINE能够几乎证明给定多线程代码中不存在死锁或断言违规,而这是标准测试技术无法实现的壮举。

在LLVM位码级别对需要验证的系统进行建模的主要缺点是,该语言的粒度非常细,容易导致严重的状态空间爆炸问题。然而,LLVM的低级特性所导致的操作粒度过细,实际上超出了验证所需的程度。τ-reduction[7]提出了一组启发式方法,能够在不丧失整体验证正确性的前提下,实现更粗略的粒度。

τ-reduction背后的基本思想是,指令的效果仅当线程通过加载或存储指令访问主内存时,才对其他线程可见。模型检测器可以利用这一事实,将大多数指令视为不可见,并在一步中执行更多指令。

我们最新的LLVM位码模型检测器扩展中,包含了对完整的C++11异常处理机制和C++11线程[50]的内部支持。

4 显式状态模型检测中的符号数据

对于不处理输入数据的程序,待探索的整个状态空间均源自程序源代码本身。我们将这类程序称为封闭系统。对于封闭系统而言,模型检验等同于形式化验证,因为它能够保证没有任何系统执行会违反经模型检验的属性。如上所述,这一点对于多线程程序尤其重要,因为由于线程调度的非确定性,常规测试方法不足以检测出这些程序中所有与并发相关的问题。

然而,对于读取输入数据的程序(所谓的开放系统),显式状态模型检测方法面临困难。请注意,即使是一个仅读取单个32位整数值的简单程序,枚举所有可能的输入值也会导致无法管理的状态空间爆炸。因此,在显式状态模型检测的情况下,通过一个环境进程来为程序提供所有可能输入以闭合开放系统的想法是不可行的。不过,对于在某些具体输入数据上执行的开放系统,即系统通常被测试的方式,应用显式状态模型检测可能具有一定优势。在这些情况下,模型检验可以检测到系统调用失败后系统的不当行为、异常处理中的错误,以及与控制流非确定性相关的其他错误。

开放系统仍然是一个验证挑战。显式状态模型检测解决此问题的方法是所谓的控制显式,数据符号化(CEDS)方法[13]。其思想是让模型检测器仅显式地跟踪系统配置的控制流部分,而数据值以符号化方式存储。换句话说,对于每个控制流点,模型检测器会保存在该特定控制流点有效的可能数据值集合,即所谓的多状态。这种基于集合的数据表示方法能够有效处理状态空间图中存在的两种非确定性来源。

显然,值集的表示方式可能有所不同。显式集合(即显式枚举集合成员)对于小范围情况非常高效,但如上所述,其可扩展性较差。另一方面,符号集合(例如通过位向量理论中的一阶公式表示)能够很好地适应任意范围的输入变量,但其使用使模型检测器依赖于SMT求解器的效率。

此外,根据基于自动机的方法进行LTL模型检测时,需要在状态空间遍历过程中判断两个多状态的相等性以检测接受环。在显式状态空间中,该操作可通过哈希表高效地实现。然而,当值集以逻辑公式表示时,由于这些公式缺乏唯一的规范形式,多状态等价性的判定变得相当困难。由于两个相等的多状态可能具有不同的内存表示,因此无法使用高效哈希。

对于那些至少允许对多状态进行线性排序的符号化表示,可以采用对数搜索,然而,在使用位向量公式时情况并非如此。唯一明显可用的选择是线性搜索,即把与给定控制流点相关联的潜在新多状态与迄今为止生成并关联到同一控制流点的每一个其他多状态进行比较。请注意,多状态的等价操作的复杂性可能非常高[14]。

不幸的是,CEDS方法并非没有局限性。例如,目前尚不清楚如何处理大小由符号化处理的输入变量规定的内存块的动态分配。尽管如此,对于避免此类分配的程序,CEDS方法提供了一种完整且高效的自动验证过程。事实上,我们通过集成DIVINE模型检测器和Z3 SMT求解器实现了CEDS方法[36],并将我们的新工具成功应用于验证一些带输入的多线程程序[14]。我们还能够应用线性时序逻辑模型检测来验证开放嵌入式系统,特别是Simulink图[15]。

关于LTL性质的验证,存在其他符号化方法,其中标准符号模型检测[26]、插值[45]、和IC3方法[19,20]最为相关。根据我们的实验比较[16],在验证速度和适用性方面,这些方法之间尚无明显优劣。换句话说,通过引入符号数据表示功能来扩展显式状态模型检测,使该技术在形式化验证领域成为一种具有竞争力的方法。

5 结论与未来方向

形式化验证的部署成本(将形式化验证方法集成到开发周期中)以及常常确实值得怀疑的性能,是阻碍LTL模型检测等形式化验证方法在实践中大规模应用的关键因素。尽管从学术角度来看,易用性和即刻可用性往往价值不大,但对于工业界而言,这些因素在许多情况下都是最重要的考虑因素。事实上,如果一项服务包含繁琐的手动建模步骤,则会被许多实践者立即拒绝。

对于学术工具而言,另一个阻碍其应用的问题是对所处理输入施加的限制。一个无法处理动态内存分配的工具,永远无法期望其在软件系统的实际验证中发挥作用。如果要将形式化验证工具用于大规模应用,该工具所依赖的方法必须足够完备,能够处理完整的编程语言,包括异常处理机制、动态内存分配、面向对象原则等。

然而,一定程度上受限的形式化验证工具仍可在许多特定场景中成功应用。实践者和学术界都应学会如何发现并沟通这些特定的应用场景,以避免因实践者期望过高而导致模型检测器部署失败,同时也应避免因缺乏对模型检测工具在实践中成功应用的宣传和报告而错失机会。

对于显式状态模型检测方法,本文已识别出一些阻碍显式状态模型检测工具易用性和效率的问题。我们提出了两个应对这些问题的方向:一是连接到 LLVM中间表示,二是通过数据的符号表示来扩展模型检测器。未来仍有大量工作需要完成。

理论性较少,但对于验证较大程序而言同样重要的是输入预处理。验证工作必须首先剪除输入程序中无法影响正确性判断的部分。特别是考虑到LLVM的低层次特性,用于检测无关代码的巧妙启发式方法可能会显著减小控制流图的规模。切片或自动抽象等方法将成为模型检验工作流中的常见组成部分,以尽可能减轻状态空间爆炸的负担。

技术的发展也不容忽视。如果显式状态模型检测还有未来,我们预测它必须能够充分利用未来的计算平台(如网络集群和云)的计算能力。历史表明,原始计算能力不容低估,因此我们预测将需要新的方法和技术,以实现空间需求与计算时间之间的权衡。

最后,很明显,在形式化验证领域并不存在一种占优的方法。将显式状态模型检测、符号执行和抽象解释等技术进行集成,是迈向未来形式化验证方法的下一个合乎逻辑的步骤。然而,这种组合成功的关键因素在于保持处理某种全规模编程语言形式输入的通用能力。至于本文所介绍的方法,LLVM模型检验与CEDS方法的完整结合仍有待实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值