SMT简介

摘要


本文为初次遇到 SMT 的总结文档。这篇总结中,没有通过示例来讲解概念。具体示例,可以查看文中对应链接。

首先,通过一篇中文综述,我们简单了解 SMT 的名词解释, SMT 的应用场景。应用场景中,我们重点关注了测试用例的自动生成:通过动态符号执行获取执行路径;对前缀路径的最后一个节点取非操作;使用求解器查看是否有满足解,如果有,则为新的测试用例。

简单了解 SMT 之后,我们尝试使用其中一种 SMT 求解器 z3 。重点阅读《z3 python API Guide》。原文使用python2.x 演示程序。我们选择一个示例,使用python3.x运行,并解释运行过程。

大多数 SMT Solver都支持SMT-LIB。SMT-LIB其中一个目标便是促进SMT求解器采用通用语言。所以我们有必要了解下 SMT-LIB 标准。我们简单翻看了下《The SMT-LIB Standard Version 2.6》,简单知道有啥就好。

初次相遇,暂不深入了解,至此为止。


SMT 简介


来源:Satisfiability modulo theories --wiki | 王翀, 吕荫润, 陈力, 等。 SMT 求解技术的发展及最新应用研究综述[J]。 计算机研究与发展, 2017, 54(7): 1405。

论文摘要:可满足性模理论(satisfiability modulo theories, SMT)是判定一阶逻辑公式在组合背景理论下的可满足性问题。SMT的背景理论使其能很好地描述实际领域中的各种问题,结合高效的可满足性判定算法,SMT在测试用例自动生成、程序缺陷检测、RTL(register transfer level)验证、程序分析与验证、线性逻辑约束公式优化问题求解等一些最新研究领域中有着突出的优势。首先阐述SMT问题的基础SAT(satisfiability)问题及判定算法;其次对SMT问题、判定算法进行了总结,分析了主流的SMT求解器,包括Z3,Yices2,CVC4等;然后着重介绍了SMT求解技术在典型领域中的实际应用,对目前的研究热点进行了阐述;最后对SMT未来的发展前景进行了展望,目的是试图推动SMT的发展,为此领域的相关人员提供有益的参考。

名词解释


  1. SAT问题是命题逻辑公式(propositional logical formula)的可满足性判定问题。基于命题逻辑,SAT问题可以进一步被描述为:给定1个命题逻辑公式f,公式f由子句集S组成,其中S由1组布尔变量V组成,判定是否存在1组对于f的赋值使得f中所有子句取值为真,如果存在,则称公式f可满足;否则,f不可满足。判定f是否可满足是SAT问题的核心

  2. SAT问题判定算法可以分为2类:局部搜索算法和完备算法(回溯搜索算法)。局部搜索算法基于随机搜索策略,对于任意给定的问题,它不一定能判断该问题是否可解;而完备算法基于穷举和回溯的思想,可以判断给定的问题是否可满足,对于无解问题拥可给出无解的证明。判定SAT问题时,需要确定给出的问题是否可满足,因此完备算法是研究的重点。

  3. SMT问题的基础是一阶逻辑公式,在命题逻辑的基础上补充了项和量词,公式中的函数和谓词符号需要用对应的背景理论解释。通常情况下,SMT公式是无量词(存在、任意)的一阶逻辑公式(quantifier free formula),判定公式可满足性的问题称为SMT问题。

  4. SMT问题的惰性算法是目前大多数SMT求解器采用的算法,算法主要步骤如下:

    1. 对SMT公式进行预处理,把公式中的命题变量替换为布尔变量,再将SMT公式转化为可满足性意义上等价的SAT公式;

    2. 检查此SAT公式是否可满足,如果不可满足,那么SMT公式也不可满足,算法结束;

    3. 如果SAT公式可满足,则结合SMT背景理论去判断SMT公式的可满足性,返回判断结果,算法结束。

    惰性算法是SAT求解器与对应的背景理论相结合的产物,典型的惰性算法是DPLL(T)算法。

SMT应用


随着SMT判定算法的不断发展以及SMT求解器的逐渐成熟,人们开始使用SMT解决一些实际问题,例如测试用例自动生成、程序缺陷检测、RTL验证、程序分析与验证、线性逻辑约束公式优化问题求解等。

测试用例的自动生成

测试用例自动生成是设计和编写软件测试用例一种方法,也是软件测试的一种重要手段,常用于检测程序缺陷。基于SMT的测试用例自动生成技术主要分为获取程序执行路径和检查路径可满足性这2个部分获取程序的执行路径主要依赖于动态符号执行,即在不执行程序的前提下,使用具体数值代替程序变量作为程序的输入,模拟程序的执行,分析1条指定路径会触发程序中哪些代码的执行,并记录下此路径。检查路径可满足性需要将程序执行路径转化为SMT公式,然后使用SMT求解器判断公式的可满足性。具体思想如下:首先利用基于动态符号执行的代码模拟执行器模拟一条具体的程序执行路径,同时记录路径中的条件语句和赋值语句,再通过SMT背景理论将这些语句转化为SMT公式f,例如可以用未解释函数背景理论将赋值语句表示为等式的合取形式,而数组赋值语句则需要数组理论的支持,利用SMT求解器对判断f的可满足性,此时会出现2种情况:

  1. 如果f可满足,f的具体可满足性赋值可作为该条路径的输入(测试用例)。通过修改f中的某个条件,例如将分支语句if(a=0)中的表达式(a=0)改为(a!=0),可以构建出1条新的执行路径,将新的路径转化为公式f′,通过SMT求解器求解f′,得到新的输入(测试用例),利用新的输入再进行新一轮的路径构造、约束求解。通过这种迭代的路径生成方法,动态符号执行可以持续遍历程序的可执行路径,直到所得到的测试用例数量达到预期值,从而实现了测试用例的自动生成。

  2. 当f不可满足时,说明当前执行路径不正确,修改某一分支语句的分支条件后进行新一轮的路径构造、约束求解。

[这篇论文在动态符号执行这里描述并不清楚,我简单做下补充。]

原文:“通过修改f中的某个条件。。。。。。可以创建出一条新的执行路径。。。。。。”

问题:如果记录的是一条完整的路径。试图相反的修改路径中的某条节点,然后使用约束求解器求解。这意味着,在路径中某个位置取反之后,还可以直接回到原路径。然而,现实的程序中,上游的判断走向可能会直接决定之后的路径走向。所以,这样操作,可能会约束求解失败且不是一种系统操作,有大量重复运行。。

正确的描述一:在执行路径的过程中,应当不断的记录前缀路径(这里是详细的介绍: Concolic Fuzzing)。前缀路径不是一个完整的路径。所以对前缀路径的最后一个节点进行取反的时候,不用考虑之后路径的可满足性,进而探索取反之后的新路径。(优势是能更加系统且高效地得到所有的路径信息以及对应的测试用例, 避免重复性搜索过程; 其缺点是内存空间耗费较大)

正确描述二:另一种方式是,从运行过程中不保存前缀路径。一条完整的路径执行完成后,总体从下向上取反最后一个约束,约束组合的长度总体在不断变短。

这两种思路一样,具体参见:叶志斌, 严波. 符号执行研究综述[J]. 计算机科学, 2018, 45(6A): 28-35.

不知道为什么,我总是看到这样的描述:“混合测试以一定的策略选择其中的一项分支判定条件进行取反”。不是应该总是最后一个约束取反吗?

程序缺陷检测

基于SMT的有界模型检测(bounded model checking,BMC)方法成为了新的研究热点。主要思想是检测程序在界限K内是否满足给定的安全属性(property),给定系统I,1个安全属性P,以及1个界限(bound)K,BMC会将系统I展开K次得到验证条件VV是可满足的当且仅当P在界限K内有1个反例。这里的界限K是指将源程序中的循环结构(比如for循环)展开K次。V是源程序所转化成的等价SMT公式F。基于SMT的有界模型检测是指将上述FP的反 ( F ∧ ¬ P ) (F \wedge \neg P) (F¬P)送入SMT求解器,如果 ( F ∧ ¬ P ) (F \wedge \neg P) (F¬P)可满足,证明程序的某一条执行路径会违反安全属性,通过SMT求解器返回的具体可满足赋值可以得到使得程序出错的具体输入,如果 ( F ∧ ¬ P ) (F \wedge \neg P) (F¬P)不可满足,则证明程序在界限K内不违反安全属性。

大概意思可能是:源程序在有限次循环展开之后,转换成等价的SMT公式。之后,将某个安全属性取反,然后进行约束求解,看能否得到满足解。如果有满足解,说明程序存在缺陷。

程序分析与验证

基于SMT的程序分析与验证是一种形式化方法,基础思想来源于Floyd和Hoare提出的弗洛伊德-霍尔逻辑(Floyd-Hoare logic)。该方法将前置条件(pre-condition)、循环不变量(loop invariant)和后置条件(post-condition)以断言(assert)的形式引入程序验证中,三者分别判断程序在运行前、运行中以及运行结束时的正确性,通过判定断言得成立情况检验程序的正确性。

SMT求解器

随着SMT背景理论的逐渐成熟以及SMT问题判定算法的不断发展,许多SMT求解器能够满足学术界的研究需求,一些成熟的SMT求解器也能处理大规模工业化的应用问题。目前主要的求解器有:略。

这里,可以简单参考本节开头两个链接中的相关内容。


z3求解器


官网: z3 – github | z3的document

重点需要:z3的python API | Z3Py Guide |Z3 - guide - SMT表达式求解

其他: Programming Z3 | Z3 API in PYTHON 中文文档 (官方文档翻译)

# Z3的python binding
pip install z3-solver

这里,简单运行一个示例,详细见上述链接文档。

import z3

x = z3.Int('x')
y = z3.Int('y')

s = z3.Solver()
print(s)

s.add(x > 10, y == x + 2)
print(s)
print("Solving constraints in the solver s ...")
if s.check() == z3.sat:
    print(s.model())
else:
    print("un-solved")

print("Create a new scope...")
s.push()
s.add(y < 11)
print(s)
print("Solving updated set of constraints...")
if s.check() == z3.sat:
    print(s.model())
else:
    print("un-solved")

print("Restoring state...")
s.pop()
print(s)
print("Solving restored set of constraints...")
if s.check() == z3.sat:
    print(s.model())
else:
    print("un-solved")
# 输出如下
[]
[x > 10, y == x + 2]
Solving constraints in the solver s ...
[x = 11, y = 13]
Create a new scope...
[x > 10, y == x + 2, y < 11]
Solving updated set of constraints...
un-solved
Restoring state...
[x > 10, y == x + 2]
Solving restored set of constraints...
[x = 11, y = 13]

可以看到,一开始求解器为空,后来加上两个断言之后,求解器的context就有了那两个断言。check求解器得到结果。sat 意味着满足(satisfied)。接下来创建了一个新的范围,可以看到新增了一个断言,这时候check的结果就是unsat,意味着不可满足(unsatisfied). 再把新增的assert 弹出(pop)之后,可以看到又sat了。

Solver()命令创建一个通用求解器。约束可以使用方法add添加。方法check()解决了断言的约束。如果找到解决方案,结果是sat(满足)。如果不存在解决方案,结果unsat(不可满足)。我们也可以说,所声明的约束系统是不可行的(infeasible)。最后,求解器可能无法解决约束系统并返回unknown(未知)。

在一些应用中,我们想要探索几个共享几个约束的类似问题。我们可以使用pushpop命令来做到这一点。每个求解器维护一堆断言。命令push通过保存当前堆栈大小来创建一个新的作用域。命令pop删除它与匹配推送之间执行的任何断言。检查方法始终对求解器断言堆栈的内容进行操作。


SMT-LIB


官网:SMT-LIB

其他:The SMT-LIBv2 Language and Tools: A Tutorial | The SMT-LIBv2 Language and Tools: A Tutorial – 中文速览

下面是《The SMT-LIB Standard Version 2.6》中的一点内容。

介绍

  1. SMT-LIB计划是一项国际性的努力,得到了全球多个研究小组的支持,其双重目标是建立一个广泛的基准测试在线库,并促进SMT求解器采用通用语言和界面。 本文档指定SMT-LIB标准的2.6版,它是2.5版的向后兼容扩展。

  2. Informally speaking, SMT-LIB calls an SMT solver any software system that implements a
    procedure for satisfiability modulo some given theory. In general, one can distinguish among
    a solver’s.(非正式地说,SMT-LIB调用一个SMT求解器,该求解器可以为任意的软件系统,该软件系统可以用来满足某种给定的理论模型来。 通常,可以如下区分求解器的)

    • 基本逻辑,例如,一阶,模态,时间,二阶等;
    • 背景理论,用于检查可满足性的理论;
    • 输入公式,求解器接受作为输入的公式类别
    • 接口,由求解器提供的功能集。

    例如,在用于线性算术的求解器中,基础逻辑是具有相等性的一阶逻辑,背景理论是实数理论,并且输入语言可能会限于线性多项式之间的不等式的合取。 该接口可以简单到接受一个不等式系统并返回一个二进制响应,指示该系统是否可满足。 更复杂的界面包括以下能力:返回满足要求的输入的具体解决方案,满足要求的输入的返回证明,允许增量和可追溯的输入等。 为了更好的清晰度和模块化,以上各方面在SMT-LIB中保持分开。 下面将描述SMTLIB对它们每个人的承诺。

语法

本章定义并解释了SMT-LIB标准的具体语法,我们将其统称为SMT-LIB语言。 SMT-LIB语言具有三个主要组件:理论声明,逻辑声明和脚本。 它的语法类似于LISP编程语言。 实际上,此版本中的每个表达式都是Common Lisp 的合法 S-expression。 S表达式语法的选择和具体语法的设计主要是由简化解析的目标驱动的,而不是为了促进人类可读性。

这三个主要部分,使用BNF-style制作规则。 附录B中还提供了这些规则以及更多详细信息。这些规则生成的语言实际上是SMT-LIB语言的超集。 该语言的合法表达必须满足本文档中还指定的其他约束,例如分类良好。

等等等…

  • 12
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

da1234cao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值