Smack:软件验证工具与软件验证工具链

SMACK: Software Verifier and Verification Toolchain

Reading:

Smack是一个软件验证工具,它可以在给定的循环迭代和递归深度情况下验证程序的正确性。用Smack进行程序验证的整体流程图如下图所示,Smack将程序用LLVM中间表示(IR),在LLVM IR上做优化,做data structure analysis,并根据内存访问点和断言自动插入规约。之后将LLVM IR 转换为Boogie中间验证语言(IVL),SMACK的功能就是精确和高效地将丰富的LLVM-IR特性集(包括动态内存分配和指针算法)转换为相对简单的Boogie IVL,而Boogie IVL不包括这些特性。最后利用Boogie和Corral等验证工具在Boogie IVL上做程序验证。
在这里插入图片描述
Smack其实也是也是一个模块化的软件验证工具链。如下图所示,Smack可以分成前端和后端,前端将语言程序转换成LLVM IR后,做一些优化和静态分析(Data structure analysis),然后把LLVM IR转成Boogie IVL,而后端则尝试验证翻译后的程序(bounded verification),最后调用Z3等工具。这提供了“关注点分离”,这样做的好处是:

  • 前端确实需要关心使用什么方法来验证程序;
  • 后端不需要关心几种不同编程语言的语义。它只需要关心Boogie IVL。
    在这里插入图片描述

下图给出了一个简单的的例子,左上角的C语言程序首先会被编译成右侧的LLVM中间表示,在这个过程中,对C中malloc的调用被编译成LLVM IR中相应的函数调用。结构字段访问被编译为getelementptr和load/store指令的组合,其中getelementptr执行结构字段地址计算,随后使用load/store进行访问。请注意,虽然LLVM IR是一种简单的表示形式,但它确实包括动态内存分配,指针算术和复杂的数据类型。 这些特征是Boogie IVL中不具备的。
之后SMACK利用LLVM的静态数据结构分析(DSA)来生成Boogie IVL程序,将对象划分到不相交的内存区域(region),每个区域都是用map来描述,这样,指向两个不同区域的指针就永远不会出现别名。在下图中,基于DSA分析的结果,可以得知LLVM IR中的指针%5和{%6,%7}是不想交的。因此SMACK静态地引入了两个内存区域映射 M . 0 和 M.0和 M.0M.1。该转换还定义了$ pa函数,以对getelementptr建模,并定义$ malloc过程,以对内存分配进行建模,方法是精确跟踪内存的已分配和未分配部分。然后将load和store指令转换为对相应区域map的访问。 最后,C语言中的断言最终会转换为Boogie断言,并使用后端验证程序进行检查。
在这里插入图片描述
笔者后面使用Smack试了一个结构体内存在数组的情况,故意写了一个heap based buffer-overflow,但是验证居然通过了(检测不到结构内数组的缓冲区溢出)。。。emm

Tool:

安装完 Vagrant 和 VirtualBox 之后,使用下面这种运行方法是最简便的。

# Installation:

# fetch vsmack and set executable permission
wget -O ~/bin/vsmack https://raw.githubusercontent.com/smackers/smack/develop/bin/vsmack
chmod u+x ~/bin/vsmack

# fetch a source file
wget https://raw.githubusercontent.com/smackers/smack/develop/test/basic/simple.c

# run vsmack
cd ~/bin
./vsmack simple.c

//You may need to download and install some extra packages. An instruction will be given on the screen if the vsmack failed to run.

笔者尝试验证了多个简单程序,Smack挺厉害的,基本都能符合笔者的预期设想,唯一的缺点就是验证效率低,特别是当递归和循环的次数设置大一点就明显慢很多。一个直观的感觉就是函数多,调用关系越复杂,执行的时间就更长。单个函数的规模对效率影响不大。

最后笔者写了一个结构内部缓冲区溢出的例子,Smack终于还是没检测出来,该例子如下图所示,结构体内有两个数组变量a[9]和b[9]。结构体的地址实际上跟a的地址相同。代码的第14,17,21行分别存在缓冲区溢出错误。

#include <stdio.h>
#include <stdlib.h>

struct overflow{
	int a[9];
	float b[9];
	double c;
};

int main() {
	struct overflow* s = (struct overflow*)malloc(sizeof(struct overflow));
	
	int i = 10;
	s->a[i] = 0; //buffer-over-write

	for (i = 0; i < 10; i++)
  		s->b[i] = 1.0; //buffer-over-write

	s->c = 2.0;

	printf("%d,%f", s->a[i], s->b[i]); //buffer-over-read
	
	free(s);
	return 0;
}

使用如下命令进行验证,选项包括限定10次循环和验证memory safety

# verify memory safety
./vsmack --memory-safety buffer-overflow.c --unroll 10

笔者猜测Smack没有检测出这个缓冲区溢出错误,可能是由于Data structure Analysis不够精确引起的。似乎在在Boogie中,SMACK缺少对C结构内部结构的建模。结构体被建模为单个分配的堆对象,忽略了内部结构。内存检查函数“SMACK”“check”“memory”“safety”只检查指针访问是否在单个堆对象内。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值