分离式分离逻辑与残余运行时验证技术解析
分离式分离逻辑相关工作
在编程与验证领域,有多种方法和技术用于确保程序的正确性和效率。我们提出的组件(机器)和子组件概念与 B 机器的“包含”概念有相似之处,但我们的单个操作(在 B 中称为事件)不假定为原子操作,因为 B 不允许操作中存在递归、循环和顺序组合。
我们的方法与使用抽象关系(或函数)将指针结构映射到代数结构的标准技术相关,但又有所超越。例如 Verifast 支持这种抽象,Automath 也曾使用过。我们仅在验证从 RBTBASIC 到 RBTHEAP 的核心细化时使用这种抽象函数。
目前存在两种与我们方法不同的替代方案:
-
Cogent
:通过线性类型系统限制程序,以线性方式传播数据结构。这虽能立即生成破坏性代码,但对编程语言有严重限制。
-
代码生成器优化
:将代数类型转换为指针结构,并尝试使用线性使用检查将非破坏性操作优化为破坏性操作。这种方法还会生成证明义务,以表明破坏性实现与抽象实现行为一致。我们的方法有可能被编码到这种方法中,不过它更侧重于单个算法,而非基于状态的抽象数据类型。
我们在 KIV 中的代码生成器也有类似功能,它会将树转换为树形指针结构,并在数据流分析表明可行时对指针结构进行破坏性更新。但目前我们尚未验证这些转换的正确性,这将是未来的工作方向。这种策略通常足以生成高效代码,但如果没有从 RBTBASIC 到 RBTHEAP 的细化,路径将以双向链表表示,效率较低。
在红黑树验证方面,有多种不同程度的验证工作:
|验证情况|相关工作|特点|
| ---- | ---- | ---- |
|部分验证| [7]、[27] | [7] 证明插入操作时未确保到每个叶子节点的路径上黑节点数量相等;[27] 仅证明排序属性。 |
|完全验证(代数实现)| [2]、[3]、[16]、[29] | 生成功能性(非破坏性)代码。 |
|完全验证(破坏性代码)| [4]、[36] | [4] 使用 VerCors 进行验证,用 Java 实现,主要例程使用递归且无父指针;[36] 在 Isabelle 的 AFP 库中,算法使用递归且无父指针,修改为使用破坏性更新,重新平衡策略效率低于原始算法。 |
|部分验证(破坏性代码)| [12]、[11] | [12] 分析 C 代码的插入操作;[11] 分析 SPARKS(Ada 的子集)的插入操作,都未考虑 sameBlacks 属性。 |
mermaid 流程图如下:
graph LR
A[红黑树验证工作] --> B[部分验证]
A --> C[完全验证]
B --> B1([[7]、[27]])
C --> C1[代数实现]
C --> C2[破坏性代码]
C1 --> C11([[2]、[3]、[16]、[29]])
C2 --> C21([[4]])
C2 --> C22([[36]])
B --> B2([[12]、[11]])
残余运行时验证技术
运行时验证(RV)是一种检查系统运行是否符合规范的形式化方法。然而,运行时验证引入的运行时开销是其广泛应用的主要障碍之一。开销主要来自插入程序中生成跟踪的插桩代码,在线监测时还包括监视器对跟踪的评估。
将静态验证和运行时验证相结合是一种自然的选择。对于静态验证,它通过将某些属性的不可判定片段推迟到运行时验证来提高完整性;对于 RV,它通过修剪可静态分析的程序部分来减少监测开销。我们提出的残余运行时验证技术就是朝着减少监测开销的方向发展。
我们的技术旨在处理可以用有限状态自动机表达的属性,如类型状态错误。具体贡献如下:
- 提出一种高效且可靠的技术,用于在程序的过程内级别静态地找到控制流中的“安全”执行路径。这些路径能保证保留被监测的属性,因此在运行时可以忽略。
- 我们的方法核心不依赖数据流分析,也不依赖程序完整调用图的静态构建,从而将静态分析和残余分析的任务分开,便于与许多 RV 框架和开发流程无缝集成。
- 我们分析单个方法的控制流图,并依赖对程序行为的过度近似。假设一个方法内生成事件的变量可能别名,我们的分析会考虑所有可能的跟踪投影。同时,我们还会处理可能允许引用从方法中逃逸的指令,以确保分析的可靠性。
通过在 BISM 工具中实现我们的方法,并在 DaCapo 基准测试上进行实验,我们发现平均插桩点减少了 2.5 倍(最高达到 9 倍),相应地,运行时事件数量平均减少了 1.8 倍(最高达到 6 倍)。
下面通过一个具体的 Java 方法示例来说明我们的方法。假设我们要监测 SafeIterator 属性,即“当与集合关联的迭代器被创建并使用时,集合不应被更新”。有一个 Java 方法 m,它检索两个列表,更新它们,创建迭代器并调用迭代器的“next”方法。我们关注以下两个问题:
-
Q1
:能否完全静态验证这个程序?如果可以,就无需插桩和运行时监测。
-
Q2
:如果不能,能否静态验证其中一些部分?如果可以,如何找到这些部分,以便只监测残余部分?
通过手动检查程序及其控制流图,我们发现如果执行进入 if 块(标记为 (10 - 11)),可能会违反属性。具体来说,当满足以下两个条件时可能发生违反:(1) someflag 求值为 true;(2) 变量 l1 和 l2 别名,即它们引用内存中的同一个对象。由于条件 (1) 只能在运行时确定,而要静态确定条件 (2),需要对程序进行指针分析。但指针分析往往耗时过长甚至无法得出结果。要回答 Q1,需要确定 l1 和 l2 必须不别名;要回答 Q2,通过观察 Lines 15 - 16 可知,无论执行情况如何,这两条指令都是安全的,无需监测,Line 6 的指令也是安全的,因为它在创建迭代器之前更新列表。
在实际操作中,我们可以按照以下步骤进行残余运行时验证:
1.
静态分析
:对程序的控制流图进行分析,找出可能的安全执行路径。
2.
确定插桩点
:根据静态分析结果,选择需要在运行时观察的程序点。
3.
运行时监测
:对残余部分的程序进行运行时验证。
这种方法在减少运行时监测开销的同时,保证了程序验证的可靠性。
通过上述对分离式分离逻辑和残余运行时验证技术的介绍,我们可以看到不同方法在程序验证和优化中的应用和特点。这些技术的发展将有助于提高程序的正确性和效率,为软件开发提供更可靠的保障。
分离式分离逻辑与残余运行时验证技术解析
分离式分离逻辑的优势与实践效果
我们提出的分离式分离逻辑方法具有显著的优势。它将算法正确性的考量与破坏性更新、别名处理和内存分配的考量分离到两个独立的细化过程中。核心思路是将算法使用的指针抽象为代数结构上的路径,并使用一个接口封装对抽象结构和路径的基本操作。这样,验证工作就可以分为两个部分:在纯代数层面验证相关算法的功能正确性,以及证明基本操作可以正确地细化为指针更新。
这种方法并非旨在与最自动化的技术竞争,而是展示了在不影响最终算法效率的情况下,实现功能正确性和指针正确性的清晰分离的可能性。同时,它也为借助各种自动化证明技术提供了便利,例如利用分离逻辑的自动化证明技术处理底层细化,或针对上层细化中的代数树使用相关证明技术。
在实际效果方面,KIV 的代码生成器根据我们的实现生成的 C 代码表现出色。当随机插入、查找或删除元素时,其运行速度与 stdc++ 库中的代码相当(误差在 ±5% 以内)。为了进行对比,我们还在 KIV 中实现了 [36] 中经过验证的递归红黑算法(未进行任何模块化结构设计和验证)并生成 C 代码,结果显示该代码比我们的代码慢约 10%。相关的 KIV 程序、生成的 C 代码以及基准测试都可以在 [23] 中找到。
不过,我们的代码相比 [36] 中的代码缺少两个特性:一是没有指向最小和最大元素的额外指针,这使得访问最大值时需要遍历树到最右侧叶子节点;二是没有缓存已删除节点以避免内存的分配和再分配。但我们当前的模块化设计完全有能力添加这些特性。
残余运行时验证的进一步探讨
残余运行时验证技术为减少运行时验证的开销提供了有效的解决方案。通过静态分析找到控制流中的安全路径,我们可以有针对性地选择需要在运行时观察的程序点,从而显著减少插桩点和运行时事件的数量。
在实际应用中,我们可以进一步优化残余运行时验证的流程。以下是一个优化后的流程图:
graph TD
A[程序输入] --> B[静态分析控制流图]
B --> C{是否可完全静态验证?}
C -- 是 --> D[无需插桩和运行时监测]
C -- 否 --> E[确定安全路径]
E --> F[选择插桩点]
F --> G[运行时监测残余部分]
G --> H[结果反馈]
H --> I{是否满足规范?}
I -- 是 --> J[程序通过验证]
I -- 否 --> K[分析错误原因并修正]
K --> B
这个流程图展示了一个完整的残余运行时验证循环。首先对输入的程序进行静态分析,判断是否可以完全静态验证。如果可以,则无需进行插桩和运行时监测;如果不能,则确定安全路径并选择插桩点,在运行时对残余部分进行监测。根据监测结果反馈,如果程序满足规范则通过验证;如果不满足,则分析错误原因并修正程序,然后重新进行静态分析。
此外,残余运行时验证技术还可以与其他形式化验证方法相结合,进一步提高验证的完整性和效率。例如,可以将其与模型检查、演绎验证等方法结合使用,充分发挥各种方法的优势。
未来展望
分离式分离逻辑和残余运行时验证技术都有广阔的发展前景。在分离式分离逻辑方面,我们计划将该方法应用于其他数据结构,如 (wandering) B + 树,这是我们闪存文件系统中尚未验证的最后一个组件。目前,我们在这方面已经取得了部分成果。同时,虽然我们已经定义了将组件概念扩展到并发的方法,但如何将当前的方法扩展到并发数据结构仍需要进一步研究。
对于残余运行时验证技术,我们可以进一步探索如何更精确地进行静态分析,减少对过度近似的依赖,从而提高安全路径的识别准确率。此外,还可以研究如何将该技术应用于更多类型的程序和属性,扩大其适用范围。
总之,这些技术的不断发展和完善将为软件开发的可靠性和效率提供更有力的支持,推动整个软件行业朝着更加安全、高效的方向发展。
分离逻辑与残余运行时验证解析
超级会员免费看
1373

被折叠的 条评论
为什么被折叠?



