前言
此博客为Rethinking Pointer Reasoning in Symbolic Execution论文的阅读笔记,本篇论文提出了一种新的符号内存处理方法,以减少符号爆炸和符号丢失的问题。本文将会以原论文的行文结构来分析本篇论文,并且对其基本情况进行了概述。以下就是本文的全部内容。
Rethinking Pointer Reasoning in Symbolic Execution
12.1、基本情况概述
2017 32nd IEEE/ACM International Conference on Automated Software Engineering (ASE——B类会议)
12.2、摘要
本文介绍了MEMSIGHT,这是一种新的符号内存处理方法,旨在减少对具体化的需求,从而提供更广泛的状态探索和更精确的指针推理机会。与之前的工具不同,MEMSIGHT不是将地址实例映射到数据,而是将符号地址表达式映射到数据,以紧凑、隐式的形式维护由符号地址引用的内存所导致的可能的替代状态。通过对DARPA Cyber Grand Challenge的知名基准进行初步实验调查,结果表明MEMSIGHT能够探索到先前技术无法达到的状态。
12.3、引言
在介绍本文所解决的问题和目标之前,我们先来了解一下符号执行技术。符号执行是一种主要用于软件测试和安全领域的程序属性验证技术。通过采用符号输入而非具体输入值,可以同时探索多个执行路径,其中每个路径描述程序是对一类明确定义的输入的行为。然而,要探索的路径数量可能非常庞大,例如,在存在无限循环或要解引用的指针由符号表达式表示时。我们基于下图的简单运行示例进行讨论。
该函数以数组 a a a和两个索引 i i i和 j j j作为输入。我们假设 a a a可以指向一个大的内存区域,可能是整个内存,并且我们对 i i i和 j j j没有任何约束。我们感兴趣的是消除 b o m b bomb bomb的输入,即不触发断言语句的输入(即断言成功,从而 b o m b bomb bomb的值应为 0 0 0)。先前的研究和最先进的工具通常将内存建模为具体地址和具体值或符号值表达式之间的映射。在这种情境下,处理引用内存时的符号地址有不同的可能性。
符号执行器可以通过在当前路径约束下使用一个有效的符号表达式模型来具体化地址。例如,这种策略自然会出现在动态测试生成中,其中同时维护来自具体执行的值。然而,先前的研究指出,具体化可能无法触发程序的分支和路径。另一方面,将内存视为完全符号化将允许执行器推理所有可能的地址,方法是分叉执行以解释与表达式匹配的每个具体地址,或者通过嵌套的 i t e ite ite(即 i f − t h e n − e l s e if-then-else if−then−else)表达式捕获地址的不确定性。
不幸的是,如上所述的完全符号化的内存在实践中很难扩展。因此,现代执行器通常通过实现部分内存模型来在性能和健壮性之间取得平衡。在这种模型中,写操作总是具体化的,而读操作则仅在面对可管理数量的可能地址值(例如,最多 1024 1024 1024个地址值)时被建模为完全符号化的内存,否则会被具体化。
为了简化我们示例的讨论,让我们假设涉及的内存最初被零填充,以避免从预先存在的存储中消除 b o m b bomb bomb。对 & a [ i ] \&a[i] &a[i]和 & a [ j ] \&a[j] &a[j]的完全具体化可能导致断言失败,除非 i i i和 j j j使用相同的值。部分内存模型也会对符号读取进行具体化,因为 & a [ j ] \&a[j] &a[j]跨越的内存很大。即使调用上下文信息产生了可管理大小的间隔,符号读取 a [ j ] a[j] a[j]仍会逐个考虑每个 a + j a+j a+j地址实例,导致执行器仅找到一个消除 b o m b bomb bomb的输入,即具体地址 a + j a+j a+j等于写具体化策略选择的 a + i a+i a+i值的输入。相反,完全符号化的方法将显示所有满足 i = = j i==j i==j条件的输入都可以消除 b o m b bomb bomb的属性。
基于以上,我们明确了,本文要解决的问题为:
- 传统的符号执行技术对内存进行符号执行时,由于具体化参数,可能会导致符号丢失的问题
- 如果对内存进行符号执行时使用完全符号化的方法,又将导致符号执行要探索的状态数量可能随指数级增长,那么符号执行器可能很快耗尽空间
为了解决以上问题,本文实现了以下几个目标:
- 讨论了符号指针推理设计的不同视角,即展示了如何紧凑地将值与符号地址表达式关联起来,而不是与具体地址
- 研究了使用分页区间树实现高效完全符号化内存的方法。此种方法被称为MEMSIGHT,此方法本能地考虑了状态合并,这是现代执行器中的主流性能增强方式,并已集成到ANGR框架中
- MEMSIGHT通过在著名的基准测试中进行更广泛的状态探索,揭示了以前的技术可能会忽略或使用过多资源来识别的行为
12.4、方法
12.4.1、基本版本
作者将符号内存化 M M M建模为一组元组 ( e , v , t , δ ) (e,v,t,δ) (e,v,t,δ),其中 e e e是表示地址的表达式, v v v是表示地址 e e e处的值的表达式。属性 t t t是创建元组的逻辑时间,加载操作使用它来确定在给定地址写入的最新值。为了支持内存的合并,作者考虑了反映元组有效的特定条件的谓词δ:δ通常由执行器根据要合并的状态之间的发散路径约束来计算。
作者的符号化内存数据结构的基本版本如 A l g o r i t h m 1 Algorithm \quad 1 Algorithm1所示。要解释它是如何工作的,请再次考虑刚刚介绍的上图中的示例。为了确定是否有任何消除 b o m b bomb bomb输入,作者设置了一个符号执行器,将指针 a a a与符号值 α a α_a αa相关联,并将索引 i i i和 j j j分别与符号值 α i α_i αi和 α j α_j αj相关联。
该程序对符号状态的影响如下图所示。为了跟踪逻辑时间,该状态包括一个从零开始的计时器 t t t。最初,内存 M M M包括由参数传递产生的地址值映射。为了紧凑起见,作者将元组 ( e , v , t , δ ) (e,v,t,δ) (e,v,t,δ)表示为 e ⟼ v ∣ t = ⋯ δ = ⋯ \left.e \longmapsto v\right|_{t=\cdots} ^{\delta=\cdots} e⟼v∣t=⋯δ=⋯,如果 t = 0 t=0 t=0,则省略 t t t,如果 δ = t r u e δ=true δ=true,则省略 δ δ δ。
12.4.1.1、内存加载和存储
为了执行 a [ i ] = 23 a[i] = 23 a[i]=23操作,程序首先加载变量 a a a和 i i i的值。 l o a d ( e ) load(e) loa