首页> 中国专利> 一种基于Go语言可执行形式化语义的程序漏洞分析方法

一种基于Go语言可执行形式化语义的程序漏洞分析方法

摘要

本发明提供了一种基于Go语言可执行形式化语义的程序漏洞分析方法,包括:(1)对GO语言的官方给出的拓展巴克斯范式进行研究,并结合K框架可支持的BNF语法格式,提出把GO[EBNF]转化为一般巴克斯范式的方法,得到了GO[BNF];(2)依据GO[BNF]和Go语言的特点,应用K框架,基于重写逻辑,定义Go语言的可执行语义GO[KS];(3)采用基于测试用例的方法分析GO[KS]的正确性,分别在K框架和IntelliJ IDEA工具中单个执行测试用例,通过比较每个测试用例的结果的一致性来分析语义的正确性;(4)基于K框架,应用GO[KS],查找Go程序漏洞。本发明提高了Go语言语义的完整性,并实现了对程序漏洞的分析。

著录项

  • 公开/公告号CN113868136A

    专利类型发明专利

  • 公开/公告日2021-12-31

    原文格式PDF

  • 申请/专利号CN202111141171.2

  • 发明设计人 孟博;刘琴;王德军;赵璨;

    申请日2021-09-28

  • 分类号G06F11/36(20060101);G06F21/57(20130101);

  • 代理机构42222 武汉科皓知识产权代理事务所(特殊普通合伙);

  • 代理人罗飞

  • 地址 430074 湖北省武汉市洪山区民族大道182号

  • 入库时间 2023-06-19 13:29:16

说明书

技术领域

本发明涉及计算机技术领域,尤其涉及一种基于Go语言可执行形式化语义的程序漏洞分析方法。

背景技术

Go语言是谷歌在2009年推出的一种开源的编译型的程序编程语言,也称作GOLang,由C语言发展而来,专为并发而设计,不仅具备C的简单高效、java的跨平台、python的方便等一系列优点,同时可基于协程(goroutine)和信道(channel)实现程序的高效并发,其中协程负责执行代码,信道负责在协程之间传递事件,对于协程任务的分配与调度,由runtime自行完成。除此之外,Go语言还拥有垃圾收集器,能够使内存管理自动化,其强静态类型的特点也使得在程序编译的时候就能检查出来隐藏大多数类型不匹配的操作,同时也支持继承、重载、对象等许多常见的系统编程方法。相比其他高级编程语言,Go语言具有简洁、并发、安全、编译迅速等优点,其强大的功能已广泛应用于各个领域。

Go语言在区块链中的应用。随着区块链的快速发展和大规模的部署与应用,智能合约作为区块链的核心部分也受到了人们的重点关注。区块链目前已经发展有公有链,私有链和联盟链三种链模式,由于联盟链具有数据保密性强、易达成共识、交易速度快等优点,更贴合现实应用场景,在未来有着巨大的发展潜力。以联盟链Hyperledger Fabric为首,由于其Docker容器本身是用Go语言开发的,使Go语言成为Fabric区块链中兼容性最好、稳定性最强的智能合约开发语言,同时也成为智能约链上代码的核心和关键开发语言。Go语言由于其支持并发,能更好的处理智能合约的并发问题,同时具有强大的功能和高安全性,是未来智能合约的主要开发语言之一。

Go语言在分布式系统上的应用。随着并发业务的快速发展,基于Go语言开发并发业务需求较高的分布式系统或者平台已经成为首选之一。Go语言可以基于golang runtime实现并发协程的调度,降低分布式系统中的并发阻塞,提高并发效率。

基于形式化语义方法分析程序编程语言,在程序并分析和验证等方面都有着重要的意义。形式化语义是以数学逻辑为基础,利用符号和符号相关的公式,将程序语言处理数据的过程及其结果形式化,实现精确地定义和解释某种语言的语义,目前已经发展有操作语义、指称语义、代数语义和公理语义。程序编程语言都有其形式化语义,Go语言作为一种被广泛应用的并发语言,已经有众多学者对其进行形式化分析。

对于Go语言的形式化语义研究现状。目前对于Go语言的形式化语义研究,分析内容主要围绕Go语言并发机制和内存模型等方面,主要是使用操作语义表示,并能够验证程序的相关属性。对于并发机制的形式化研究,2016年,Prasertsang等使用CSP形式化描述语言对GO语言的并发机制提出了一种半自动化的验证方法,基于CSP模型形式化分析的内容包括GO语言的赋值语句、声明语句、条件语句、循环语句、函数、GOroutine和信道等内容,用于排除程序由于并发导致的故障,保证程序的正确性;同年,Nicholas Ng等提出了一个静态分析器,将Go源码对应的静态单赋值(Single Static Assignment,简称SSA),形式化表示为通信有限状态机(Communicating Finite State Machines,简称CFSM),来检测并发时的通信死锁。SSA将Go程序的语法简化为有限的指令集,并将程序的控制流简化为用于分析的指令块之间的跳转;同年,Bodden等使用一种抽象语法来形式化表示Go语言的子集,包含类型、结构、通道操作等重要的基本功能,同时分析了闭包,通过分析数据在程序中运行时,达到静态分析Go语言程序的信息流,以防止恶意数据导致的程序执行违规,并验证了语义在处理Go程序并发时的执行结果;Julien Lange等同样对Go源码的SSA进行分析,应用控制流分析来获得程序的行为类型,并使用一种操作语义建模,提出一个静态分析工具探索Go程序并发时安全漏洞,包括死锁自由和通信安全等方面。对于内存模型的形式化分析,Steffen将重点放在运行时行为和操作语义上,针对Go编程语言的关键字deffer、panic、recover相关结构构成的小型并发语言提出了一种小步骤操作语义(Small-StepSemantics),目的是使用deffer函数捕获Go的并发性和非标准控制流;同年,Valle使用一种结构化操作语义形式化分析Go语言内存模型的各个部分,包含读、写和信道通信三部分内容,可以通过集合对happen-before事件进行建模,并通过一些示例说明如何使用语义规则逐步完成程序;Stadtmüller等提出了一种新的基于迹(trace-based)的静态死锁检测方法,使用一种操作语义对Go语言的并发锁进行了形式化分析,弥补了Valle的研究内容,并开发了一个分析Go程序死锁的原型工具,实现了对语义的验证工作,

基于K框架定义程序语言的可执行语义研究现状。K是基于重写逻辑实现的用于定义可执行语义的框架,主要是将程序语言的执行过程基于符号和公式形式化表示,形成的可执行语义是一种操作语义。K框架包含各种工具,比如符号执行框架、模型检查、编译器、解释器、测试用例生成器等。K框架除了定义可执行的形式化语义,还能定义类型系统,尽管有些功能目前还还完善中,但依然有着强大的应用前景。目前已经有众多学者基于K框架实现了对程序编程语言的可执行形式化研究。Charles等基于K框架实现了对C语言的可执行的形式化语义分析,语义包含基本语义、静态语义和并发语义,并形成了分析工具KCC,不仅能够作为程序的解释器,它还能够调试、捕捉程序的未定义行为、状态空间搜索和模型检查,并验证了语义的正确性和覆盖率。Jiao等基于K框架实现了以太坊智能合约开发语言Solidity语言的可执行语义的形式化分析,具体分析了合约中的基本判断语句、类型声明和函数调用,覆盖了Solidity语言的大部分语义,并验证了语义在智能合约漏洞分析中的重要应用。Fava等[16]参照Go语言的信道通信机制,基于K框架对π演算表示的一种遵循happens-before原则的弱内存模型实现了可执行形式化分析,包含内存模型中的读、写和发送等操作,并验证了该模型遵循无数据竞争时的顺序一致性。Bogdanas等在中定义了Java的可执行形式语义,分为静态语义和动态语义,并基于语义生成的解释器对官方的测试套件进行了广泛的测试,同时演示了使用K的内置功能和该语义对Java程序代码进行符号执行和模型检查的示例。另外,更多的语言同样也实现了可执行形式化语义定义,如Python、PHP、JavaScript、Rust、KEVM等语言,并基于K框架的解释器和与程序语言相关的编译器对语义进行了正确性验证。

2015年,Andrei Arusoaie等基于重写逻辑理论提出的语言独立的符号执行框架,对K版本为3.5的功能拓展,增加了符号执行分析功能。由于K本身是一个基于重写逻辑的可执行语义框架,提供了程序状态空间和语义规则的表示方法,可以方便用来定义一门语言的可执行语义,该工作的核心是基于重写逻辑和匹配逻辑的可执行语义定义框架,可以对任何程序开发语言的语义进行定义,如特定领域语言、命令式语言等,然后对程序代码进行符号执行和具体执行,并输出路径约束及状态信息等。可以基于该语言独立的符号执行框架,在编译K定义时使用--symbolic选项来启用符号分析模式。当启用符号模式时,程序可以给定K工具原生支持的任何类型的符号输入。

本申请发明人在实施本发明的过程中,现有技术中至少存在如下技术问题:

(1)对于Go语言的形式化分析与研究,首先,存在不是直接对Go语言进行形式化分析,而是分析中间语言的情况;其次,较少关注语义的可执行性,无法基于该语义生成相应的工具,限制了语义的应用价值;最后,语义内容大都不完整,对Go语言的特点覆盖面比较少;

(2)经过对基于K框架定义语言的形式化语义的应用分析发现,目前没有基于K框架定义具有分布式并发对象的Go语言的可执行形式化语义,无法基于K框架形成用于分析程序漏洞的工具。

发明内容

本发明提出了一种基于Go语言可执行形式化语义的程序漏洞分析方法,用于解决或者至少部分解决现有技术中的方法存在的Go语言语义完整性不够以及分析程序漏洞的技术局限问题。

为了解决上述技术问题,本发明提供了一种基于Go语言可执行形式化语义的程序漏洞分析方法,包括:

S1:将Go语言的官方拓展的巴科斯范式语法格式GO[EBNF]转换为K框架可支持的BNF语法格式GO[BNF];

S2:根据GO[BNF]和Go语言的特点,应用K框架,基于重写逻辑,描述Go语言的可执行语义GO[KS],包括语法、配置和规则,GO[KS]包含基本语义和并发语义,其中基本语义涵盖Go语言的基本类型定义、基本表达式和基本语句,并发语义包含基于信道实现数据读写;

S3:采用基于测试用例的方法分析GO[KS]的正确性,分别在K框架和IntelliJIDEA工具中单个执行测试用例,通过比较每个测试用例的结果的一致性分析GO[KS]的正确性;

S4:应用Go语言的可执行语义GO[KS],基于K框架,对Go程序进行符号执行分析,查找程序漏洞。

在一种实施方式中,步骤S2中Go语言的可执行语义GO[KS]的语法,是基于K框架定义的,

GO[KS]的配置基于K框架使用格单元描述Go程序的状态,不同的格单元用于描述Go程序不同的状态信息,格单元包含全局配置格单元、协程配置格单元、信道配置格单元,全局配置格单元包括用于表示程序计算的格单元、用于存储全局变量的格单元;协程配置格单元包括用于表示协程的计算的格单元、用于表示协程唯一标志的格单元;信道配置格单元包括用于表示信道唯一标志的格单元、用于表示信道操作次数的格单元,用于表示信道类型的格单元,用于表示信道状态的格单元以及用于表示信道队列的格单元。

在一种实施方式中,步骤S2中Go语言的可执行语义GO[KS]的规则,是基于K框架应用重写逻辑对Go程序的状态转换关系进行重写后得到,重写内容包含格单元内的信息转换前和转换后的信息。

在一种实施方式中,步骤S2的基本语义包含基本类型定义、基本表达式和基本语句,其中,

变量声明,包含无类型声明、有类型声明、无类型声明且赋值、有类型声明且赋值、批量声明、批量声明且赋值;

数组声明,针对有类型的一维数组声明定义;

函数声明和定义,针对带参数的函数声明,包括有类型返回和无类型返回;

结构体定义,结构体包含结构体内参数和参数类型,参数类型包含整型、字符型、布尔型;

基本表达式,包含简单运算、复杂运算、逻辑运算和变量自增自减;

基本语句,一部分在是基本类型定义的基础上得到的基本语句,包含变量赋值,数组赋值,一部分是一般语句,包含函数调用、return语句、if语句、for语句、println语句、switch语句、select语句、函数定义、package和import。

在一种实施方式中,步骤S2的并发语义中基于信道实现数据读写,包含go语句、make语句、信道发送、信道接收、信道关闭,其中,go语句用于描述协程创建的静态语义,make语句用于描述信道创建的静态语义,信道发送、信道接收和信道关闭用于描述信道的状态变化、信道内的数据变化。

在一种实施方式中,步骤S3包括:

基于K框架编译GO[KS];

对GO[KS]的每个语义规则从官方测试套件中选择合适的测试用例,基于K框架批量运行测试用例,检查是否全部涵盖GO[KS],如果没有,则开发新的测试用例,实现测试用例全部涵盖GO[KS],形成一个语义覆盖率为100%的测试集;

通过比较每个测试用例的结果的一致性来分析语义的正确性,具体为:基于语义覆盖率为100%的测试集中的测试用例,分别在K框架和工具IntelliJ IDEA中单个运行测试集中的测试用例,对在K框架执行的结果和在工具IntelliJ运行的程序结果进行比较分析,验证GO[KS]正确性,若每个测试用例的运行结果都一致,则表示GO[KS]正确,否则不正确。

在一种实施方式中,在K框架中编译GO[KS]后,通过K框架启用符号分析模型,对Go程序进行符号执行分析,对程序漏洞进行查找与分析。

本申请实施例中的上述一个或多个技术方案,至少具有如下一种或多种技术效果:

本发明提供的一种基于Go语言可执行形式化语义的程序漏洞分析方法,提出了一种基于重写逻辑的可执行形式化语义,可执行的形式化语义是在K框架中实现的。首先,基于K框架可以模块化地指定语言规则,并准确表示真正的并发编程语言,尤其适合用于对并发语言Go语言的分析,其次,K中的语义作为项重写系统具有严格的意义,支持完全的形式化证明。实际上,语义被定义后,首先使用kompile工具对语义进行编译,K框架会自动生成相应的解释器,且自动支持对程序进行模型检查和符号执行分析,然后基于该解释器使用krun工具运行程序代码,通过符号执行等技术可以帮助发现程序中隐藏的各种问题,解决了现有技术中的方法存在的Go语言语义完整性不够以及分析程序漏洞的技术局限问题。

附图说明

为了更清楚地说明本发明实施例或现有技术中的技术方案,下面将对实施例或现有技术描述中所需要使用的附图作简单地介绍,显而易见地,下面描述中的附图是本发明的一些实施例,对于本领域普通技术人员来讲,在不付出创造性劳动的前提下,还可以根据这些附图获得其他的附图。

图1为本发明实施例中GO[KS]语义框架示意图;

图2为本发明实施例中GO[EBNF]到GO[BNF]的转换方法示意图;

图3为本发明实施例中GO[KS]配置的示例图;

图4为本发明实施例中GO[KS]全部内容的框架图;

图5为本发明实施例中select语句的重写规则定义流程;

图6为本发明实施例中GO[KS]整体验证框架;

图7为本发明实施例中GO[KS]正确性验证方法的流程图;

图8为本发明实施例中GO[KS]符号执行技术应用原理图。

具体实施方式

为了解决现有技术中关于Go语言形式化语义在全面性和验证方面、用于程序漏洞分析的技术局限问题,本发明提出一种基于重写逻辑的可执行形式化语义的程序漏洞分析方法,可执行的形式化语义是在K框架中实现的。首先,基于K框架可以模块化地指定语言规则,并准确表示真正的并发编程语言,尤其适合用于对并发语言Go语言的分析,其次,K中的语义作为项重写系统具有严格的意义,支持完全的形式化证明。实际上,语义被定义后,首先使用kompile工具对语义进行编译,K框架会自动生成相应的解释器,且自动支持对程序进行模型检查和符号执行分析,然后基于该解释器使用krun工具运行程序代码,通过符号执行等技术可以帮助发现程序中隐藏的各种问题。

本发明的主要构思如下:

本发明公开了一种基于Go语言可执行形式化语义的程序漏洞分析方法,首先提出了一种Go语言的可执行语义GO[KS]的定义方法,然后基于该语义可帮助分析Go语言编写的程序的更多特性帮助查找Go程序漏洞。本发明涉及到的内容包括:(1)对GO语言的官方给出的拓展巴克斯范式(简称GO[EBNF])进行研究,并结合K框架可支持的BNF语法格式,提出把GO[EBNF]转化为一般巴克斯范式(简称GO[BNF])的方法,得到了GO[BNF];(2)依据GO[BNF]和Go语言的特点,应用K框架,基于重写逻辑,定义Go语言的可执行语义GO[KS],包括语法、配置和规则;(3)采用基于测试用例的方法分析GO[KS]的正确性,分别在K框架和IntelliJIDEA工具中单个执行测试用例,通过比较每个测试用例的结果的一致性来分析语义的正确性;(4)基于K框架,应用GO[KS],查找Go程序漏洞。

为使本发明实施例的目的、技术方案和优点更加清楚,下面将结合本发明实施例中的附图,对本发明实施例中的技术方案进行清楚、完整地描述,显然,所描述的实施例是本发明一部分实施例,而不是全部的实施例。基于本发明中的实施例,本领域普通技术人员在没有做出创造性劳动前提下所获得的所有其他实施例,都属于本发明保护的范围。

本发明实施例提供了一种基于Go语言可执行形式化语义的程序漏洞分析方法,包括:

S1:将Go语言的官方语法格式GO[EBNF]转换为K框架可支持的BNF语法格式GO[BNF];

S2:根据GO[BNF]和Go语言的特点,应用K框架,基于重写逻辑,描述Go语言的可执行语义GO[KS],包括语法、配置和规则,GO[KS]包含基本语义和并发语义,其中基本语义涵盖Go语言的基本类型定义、基本表达式和基本语句,并发语义包含基于信道实现数据读写;

S3:采用基于测试用例的方法分析GO[KS]的正确性,分别在K框架和IntelliJIDEA工具中单个执行测试用例,通过比较每个测试用例的结果的一致性分析GO[KS]的正确性;

S4:应用Go语言的可执行语义GO[KS],基于K框架,对Go程序进行符号执行分析,查找程序漏洞。

具体来说,步骤S1~S3是提出了GO[KS]可执行语义,步骤S4是基于可执行语义GO[KS]的具体应用,具体用于程序漏洞的查找与分析。请参见图1,为本发明实施例中GO[KS]语义框架示意图。

步骤S1中,对GO语言的官方给出的拓展巴克斯范式(本发明简称GO[EBNF])进行研究,并结合K框架可支持的BNF语法格式,提出将GO[EBNF]转化为一般巴克斯范式(本发明简称GO[BNF])的方法,得到了GO[BNF]。图2概括了从GO[EBNF]到GO[BNF]的基本转换方法。

首先,将GO[EBNF]中涉及到“=”、“|”、“()”、“[]”、“{}”四种符号统一简化为只包含“|”的形式,主要转换方法如下:

(1)转换语法的定义符号,将GO[EBNF]中“=”换成“::=”,两个符号都表示等价的意思,转换的原因是K框架中语法只支持“::=”的表示法。

(2)将GO[EBNF]中可选性符号“[]”换成以符号“|”表示的语法。由于[]表示里面的项可重复0次或者1次,则在GO[BNF]中,直接将一个包含该项和不包含该项的两个内容都表示出来,并以“|”隔开。

(3)将GO[EBNF]中可重复性符号“{}”换成以符号“|”表示的语法;由于{}表示项可重复多次,则在GO[BNF]中,使用两个表达式,并以“|”隔开,比如将X=E{A}表示为X::=E|XA;

(4)将GO[EBNF]中分组符号“()”换成以符号“|”表示的语法,比如将X=(E1|E2)A转为X=E1A|E2A;

在整个语法转换过程中,必要时会简化部分语法类的命名,但并不影响每个语法内容的转换方法。if语句的转换为例,说明具体转换过程。在GO[EBNF]中,IfStmt="if"[SimpleStmt";"]Expression Block["else"(IfStmt|Block)],为了得到GO[BNF]中,将上述EBNF中的项SimpleStmt以AExp表示,项Expression拆分为AExp和BExp,AExp是返回类型为整型的表达式,BExp是返回类型为布尔类型的表达式,最终转换为如下较复杂的形式。在K中,关键字syntax后跟着是一个K类,比如在GO[BNF]中的IfStmt可称为一个K类,为了便于说明转换的过程,对IfStmt的K语法内容的每一项以序号标识,具体表示为如下六种形式:

1)"if"AExp";"BExp Block"else"Block

2)"if"BExp Block"else"Block

3)"if"AExp";"BExp Block

4)"if"BExp Block

5)"if"AExp";"BExp Body"else"IfStmt

6)"if"BExp Body"else"IfStmt

第一步,去掉表示可重复0或1次的“[]”的项,这里有项SimpleStmt和else语句两个,将SimpleStmt拆分为两项,同样将else拆分两项,这就出现第1种表达方式;

第二步,去掉分组“()”,产生第5、6条表示;

第三步,将以上6种表示以符号“|”分开,表示或的关系,即如果程序代码中的if语句满足以上六种中的其中一种,说明语法是满足要求的,K框架可以解析成功,否则解析失败。

然后,根据看K框架的特点对语法增加具体的属性。K框架属性众多,在GO[BNF]中,主要涉及到属性有严格性属性、函数属性、token属性、自定义标签属性。

最后,得到GO[BNF],内容包含Go语言的四个基本类型、四种基本表达式和十二个基本语句。四个基本类型有:变量声明、数组声明、函数声明和结构体定义;四种基本表达式有:简单运算、复杂运算、逻辑运算和变量自增自减;十二个基本语句有:变量赋值,数组赋值,函数调用,return,if,for,println,switch,select,函数定义,package和import,主函数main。

表1从GO[EBNF]到GO[BNF]部分转换结果

表1展示了GO语言的EBNF和BNF部门转换结果,由于篇幅的限制,该表简化了部分语法显示的内容,对于语法省略的部分,使用“…”表示。

在一种实施方式中,步骤S2中Go语言的可执行语义GO[KS]的语法,是基于K框架定义的,

GO[KS]的配置基于K框架使用格单元描述Go程序的状态,不同的格单元用于描述Go程序不同的状态信息,格单元包含全局配置格单元、协程配置格单元、信道配置格单元,全局配置格单元包括用于表示程序计算的格单元、用于存储全局变量的格单元;协程配置格单元包括用于表示协程的计算的格单元、用于表示协程唯一标志的格单元;信道配置格单元包括用于表示信道唯一标志的格单元、用于表示信道操作次数的格单元,用于表示信道类型的格单元,用于表示信道状态的格单元以及用于表示信道队列的格单元。

在一种实施方式中,步骤S2中Go语言的可执行语义GO[KS]的规则,是基于K框架应用重写逻辑对Go程序的状态转换关系进行重写后得到,重写内容包含格单元内的信息转换前和转换后的信息。

在一种实施方式中,步骤S2的基本语义包含基本类型定义、基本表达式和基本语句,其中,

变量声明,包含无类型声明、有类型声明、无类型声明且赋值、有类型声明且赋值、批量声明、批量声明且赋值;

数组声明,针对有类型的一维数组声明定义;

函数声明和定义,针对带参数的函数声明,包括有类型返回和无类型返回;

结构体定义,结构体包含结构体内参数和参数类型,参数类型包含整型、字符型、布尔型;

基本表达式,包含简单运算、复杂运算、逻辑运算和变量自增自减;

基本语句,一部分在是基本类型定义的基础上得到的基本语句,包含变量赋值,数组赋值,一部分是一般语句,包含函数调用、return语句、if语句、for语句、println语句、switch语句、select语句、函数定义、package和import。

在一种实施方式中,步骤S2的并发语义中基于信道实现数据读写,包含go语句、make语句、信道发送、信道接收、信道关闭,其中,go语句用于描述协程创建的静态语义,make语句用于描述信道创建的静态语义,信道发送、信道接收和信道关闭用于描述信道的状态变化、信道内的数据变化。

具体来说,步骤S2,依据GO[BNF]和Go语言的特点,应用K框架,基于重写逻辑,描述Go语言的可执行语义GO[KS]。

具体实施过程中,对于GO[KS]中的配置定义。K配置是由带标记的格单元的嵌套表示的,用于描述程序正在运行时的状态信息。图3介绍了GO[KS]的配置,每个单元描述一个完整执行快照所需的重要信息,包含全局配置、函数配置、协程和信道配置。

全局配置。格单元T涵盖了整个配置,即是一个包含所有格单元的顶层单元,以便在必要时可以将其作为一个单元进行操作。其中格单元k用于表示程序的计算,格单元env代表全局环境,用于存储全局变量,格单元genv表示对env的全量备份。由于传递和接受参数使用的都是栈,格单元control包含函数堆栈以及关于当前对象和类的信息,控制单元内是fstack单元,它存储了调用和返回的函数的调用堆栈,使用数据结构列表的形式表示,主要对堆栈帧进行编码,其中每个元素包含一个地址、一些计算和一个返回类型。格单元typeEnv用于记录给定变量位置的类型,格单元store用于存储所有已定义变量的值,nextLoc表示当前变量所在位置的下一个位置,当声明一个新的变量时,将从nextLoc格单元中分配一个新的整数值位置,然后nextLoc中的整数值加1,作为下一项的开始地址。格单元state表示运行中的程序总是处于某种状态,描述了对正在执行的程序状态的所有期望,比如变量的值,以及在程序中执行的时间点。程序的状态是状态空间的一个元素,保存运行程序的所有可能状态。格单元out,输出缓冲区,主要用于print语句;格单元error用于返回错误信息,主要用于显示panic函数返回的信息;格单元scount,类似于nextLoc,用于记录switch语句中当前case语句所在位置的下一个位置;格单元nextcase,类似于nextLoc,用于记录select语句中当前case语句的下一个case语句的位置;格单元package,用于记录包的名称,包含包的唯一标志pid和包名。

协程和信道的配置。协程使用goroutine格单元表示,主要包含两个信息,一个用于表示协程的计算k,一个是协程唯一标志gid,由K自动生成。使用channel格单元表示。对于信道的语义重写,主要涉及到五个格单元,分别是信道唯一标志cid,信道操作次数cnun,默认为0,信道类型ctype,信道状态cstate和信道队列cseq。详细介绍为:

(1)信道cid,由K框架统一生成唯一的整数标志。

(2)信道操作次数cnum,为整型,发送(往信道写入数据)时加1,接收(从信道读取数据)时减一:当为正数时,说明发送操作大于接收操作,发送方阻塞,当为负数时,说明接收操作大于发送的次数,接收方堵塞。当num为0时,表明堵塞解除。

(3)信道类型ctype,在信道创建时写入,一旦写入,不得更改。

(4)信道状态cstate,0表示异常,这里为关闭状态,1表示正常。

(5)信道队列cseq,属于信道的缓冲区列表,用于存放需要传递的内容。

图4显示了GO[KS]的全部内容,对于GO[KS]中基本语义的定义,其中select语句是Go语言的特色语句之一,主要是对信道进行监听,一旦监听到消息,便对信道进行读写操作完成消息的处理。图5为本发明中的select语句的重写规则定义流程,下面以select语句为例详细说明语义的定义方法。

select语句的执行过程遵循以下步骤:

1)首先检查每个case语句中的信道是否可进行读或者取的操作,若其中只有一个满足,则执行该case语句;

2)若有多个case满足,会从多个可执行case语句中的随机选择一个执行,即当多个信道都准备好的时候,随机选择执行一个信道用于发送或者接收操作;

3)若每个case语句都不能执行,若有default语句则执行default语句;

4)若每个case语句都不能执行,同时也没有default语句,则会造成整个select语句阻塞。由于select语句通常与for循环语句联合使用,通过for循环继续检查信道的可操作情况,直到有其中一个case语句可执行(即有一个信道可操作时)时为止。

表2 Select语句的语义

需要注意的是,select中的case语句不会按顺序测试,且select默认是阻塞的,但会循环检测case条件,如果有满足则执行对应的语句并退出select语句,否则会一直循环检测,只有当监听的信道中有发送或接收可以进行时才会运行。判断select中的每个case语句的通信是否可以执行或者会阻塞,是使用select语句的难点,也是研究Go语言并发特点的重点语句之一。

针对表2的语义详细描述:

规则①:select语句包含两部分内容,一个是case语句中判断条件,使用C表示,ci是第i个case语句中的判断条件,另一个是case语句中条件后面的执行表达式,使用S表示,si是第i个case语句中的表达式主体,I用于记录最后执行的第I个case表达式。整体上,首先将select语句重写为if语句和select语句的组合。其中的bool(ci)表示第i个case语句中的判断条件返回的布尔值,根据信道状态判断写入或者读取是否成功,成功为true,失败为false,当返回为真时,使用变量赋值的规则,将I的值V存储在store中,当返回为假,继续执行select语句,且无论满足哪种情况,都需要将格单元nextcase加1,然后继续判断下一个case语句中的判断条件ci+1,当if语句返回为假,则继续执行select语句。若都不满足,则执行default语句,若没有default语句,执行规则(2);

规则②:主要是用于随机选择一个case语句执行,首先随机获得1到N中随机数据I,然后开始执行第I个case语句。

对于GO[KS]中并发语义的定义,这里以信道的发送和接收操作、关闭信道为例进行详细的说明。基于信道对数据的发送和接收,使用符号“<-”表示,例如,“ch<-v”表示把数据v发送到通道ch(该过程也称写数据),“v:=<-ch”则表示从信道ch中接收数据,并把值赋给v(该过程也称读数据),通道一次只能接收一个数据元素。<-ch表示接收任意数据,执行该语句时将会发生阻塞,直到接收到数据,但接收到的数据会被忽略。对信道的写入和信道的读取语义如表3所示:

表3基于信道的写入和读取语义

(2)关闭信道。在数据接收或者发送完成后,可以通过close语句关闭信道(如表4所示),信道关闭后,不能像该信道发送数据,且从已关闭的通道接收数据时将不会发生阻塞。

表4 close语句的语义

在一种实施方式中,步骤S3包括:

基于K框架编译GO[KS];

对GO[KS]的每个语义规则从官方测试套件中选择合适的测试用例,基于K框架批量运行测试用例,检查是否全部涵盖GO[KS],如果没有,则开发新的测试用例,实现测试用例全部涵盖GO[KS],形成一个语义覆盖率为100%的测试集;

通过比较每个测试用例的结果的一致性来分析语义的正确性,具体为:基于语义覆盖率为100%的测试集中的测试用例,分别在K框架和工具IntelliJ IDEA中单个运行测试集中的测试用例,对在K框架执行的结果和在工具IntelliJ运行的程序结果进行比较分析,验证GO[KS]正确性,若每个测试用例的运行结果都一致,则表示GO[KS]正确,否则不正确。

具体来说,步骤S3,验证GO[KS]的正确性,整体验证框架图6所示。首先,基于K框架编译GO[KS];其次,对GO[KS]的每个语义规则从官方测试套件中选择合适的测试用例,批量运行测试用例,检查是否全部涵盖GO[KS],如果没有,则开发新的测试用例,实现测试用例全部涵盖GO[KS],形成一个语义覆盖率为100%的测试集;最后,分别在K框架和工具IntelliJ IDEA中单个运行测试集中的测试用例,对在K框架执行的结果和在工具IntelliJ运行的结果进行比较分析,验证GO[KS]正确性。

具体实施过程中,对于GO[]KS]的语义正确性验证,验证目标包含两个方面,一是验证测试用例的语义覆盖率,保证测试用例全部覆盖所定义的语义规则,二是验证语义的结果正确性。整体的验证方法如图7所示。

首先,编译GO[KS]语义。kompile是K的编译器,接受用K编写的以.k文件存储的程序或规范,并生成相应的解析器(parse)和解释器(Interpreter),编译k文件时使用命令“kompile文件名--coverage”,其中--coverage是编译器的后端,用于输出程序执行过程中覆盖的规则汇总。

然后,批量运行测试集中的测试用例,分析测试用例的语义覆盖率。语义的覆盖率指的是测试用例对所定义的语义涵盖范围,在实际的语义编写中,以关键字“rule”开头表示语义定义的开始,也称为规则。语义覆盖率越高,表示定义的规则利用率越高,相关的测试用例涵盖的语言语义内容越多,也即语义覆盖面更全。反之,说明定义的规则利用率较低,需要更多的测试用例来验证未覆盖的规则。基于更完善的测试用例集来验证本发明的语义,是验证GO[KS]正确性的基础。语义覆盖率的计算公式为:

在上面的公式中,covered表示覆盖的规则,rules表示所有的规则,len()统计对应规则的个数,coverage_frac表示最终得到的语义覆盖率。为了使统计更精确,coverage_frac结果保留两位小数。当得到的覆盖率达到100%以上时,说明测试用例的选择范围是达标的,测试用例涵盖了本发明的所定义的所有语义。

当编译时使用—coverage时,文件目录中会生成一个coverage文件,同时使用krun运行程序代码时,会增加一个以随机数字开头、以coverage结尾的文档,该文档记录了该程序使用到的所有规则,包括K框架本有的规则和自定义的规则,如果在统计语义覆盖率时将K框架本有的规则统计在内,统计得到的语义覆盖率会大大降低,且并不能真实反映测试用例对GO[KS]覆盖情况。为了实现只统计测试用例对本发明自定义规则的覆盖情况,本发明对官方给定的用于统计规则覆盖情况的python文件进行了修改,形成新的文件test_coverage.py,用于重新统计规则的覆盖情况,运行完所有的测试用例后执行,在命令窗口执行“python3 test_coverage.py”,从生成的文件coverage.txt文档中获得规则覆盖率。当覆盖率小于100%时,说明测试用例不能全部覆盖所有的语义,需要增加测试用例,覆盖未测试的规则。当覆盖率为100%时,表明验证语义的测试用例集形成。

最后,单个运行测试用例,通过结果一致性验证GO[KS]的正确性。在K框架中,使用命令“krun测试用例文件名”,krun工具将调用解析器和解释器运行程序代码。每运行一个测试用例都会输出一个文件目录,每个目录包含了执行程序或使用该定义执行证明所需的所有内容,同时在命令窗口中输出运行结果信息。同样,在IntelLij IDEA工具中分别测试用例集中的每一个测试用例,获得每个测试用例对应的执行结果,通过比较K框架和Intellij IDEA工具的结果,来分析语义的正确性。

基于测试用例的执行结果验证语义正确性,分两种情况,一是对于以print语句展示输出结果的测试用例,在K中运行后可直接查看缓冲区的内容,也即格单元out的内容,并与在IntelliJ IDEA工具的运行结果相比较,将两者内容进行比较。二是对于在Intellij中以exitcode返回编码表示的执行成功或失败的测试用例,需要手工将K中的运行结果与程序中panic函数前面的判断条件中的变量的内容是否一致。

测试用例如果执行失败,会使用panic函数返回一些信息,或者是变量的值,或者任意字符串,无论输出任何值,都表现程序运行出现异常,执行结果不一致,K框架会将错误信息返回到格单元error中,需要找出原因。

在一种实施方式中,在K框架中编译GO[KS]后,通过K框架启用符号分析模型,对Go程序进行符号执行分析,对程序漏洞进行查找与分析。

具体来说,在Andrei提出的K框架中编译GO[KS]语义,该框架增加了符号执行技术,与Z3 SMT求解器的部分连接是在K本身中完成的,不仅支持GO[KS]提到的所有的语义和规则,同时可基于该工具,通过使用后端--backend symbolic对Go语言的程序进行符号分析,获得程序的符号执行路径和程序的所有的状态,并可以通过检测程序执行过程,获得程序的求值顺序,输出所有的过程数据,实现检查分析程序漏洞。

请参见图8,为本发明实施例中GO[KS]符号执行技术应用原理图。

具体实施过程中,在Andrei提出的K框架中编译GO[KS]语义,该框架增加了符号执行技术,然后执行带有if、while和for的控制流语句的程序代码,并在执行代码时,指定控制流语句中的条件判断变量为符号变量,如果存在多个条件判断变量,可根据实际需要定义其中一个为符号变量;

然后,在进行符号执行技术分析的过程中,以控制流语句作为路径分支点,以此获得符号执行路径,其中,符号执行路径以一个或者多个比较表达式的与或非进行表示,比较表达式主要包含“>”、“>=”、“<”、“<=”、“!=”、“==”几种,具体可以依据控制流语句中的表达式来自动生成;

接着,K框架通过连接约束求解器Z3,实现在边搜索执行路径时边对当前获得的执行路径对符号变量进行约束求解,若有解,表示每个比较表达式都满足(同时K会自动获得符号具体值并存储在K内置的环境中)则沿着该分支继续进行路径搜索,若无解,则停止该路径的搜索,切换下一个分支继续搜索路径;当没有路径可查时,表示对程序代码的路径搜索完毕,输出并显示所有符号执行路径,以及每个符号执行路径对应的符号变量的求解值(即符号具体值)。

最后,将自动获得的符号具体值输入到基于K框架执行Go程序的指令中,再次运行同样的程序代码,查看程序运行结果的所有状态信息,包括程序中的每个变量的最终值,若变量没有具体值,则表明该变量的结果是使用符号变量表示的表达式,并显示在变量对应的结果中。若程序存在漏洞,则执行失败,K框架抛出异常,否则,执行成功,K框架则显示具体的程序状态信息。

为了应用GO[KS]语义查找程序漏洞,以如下Go程序代码为例详细说明该过程。程序核心代码见表5。

表5 Go程序代码

在K框架中编译GO[KS]后,执行命令krun if.go–cIN="ListItem(#symInt(n))"–cPC="true"--search,则开始对程序代码进行符号执行分析,其中使用#来指定符号变量,#symInt(n)表示定义程序代码中的变量n为符号变量,最后得到的符号执行路径有三个:

路径1:#symInt(n)>Int 5==Bool false,路径简化后为:非(n>5);

路径2:#symInt(n)>Int 5==Bool true andBool#symInt(n)<=Int 20==Bool false,路径简化后:n>5∩非(n<=20);

路径3:“#symInt(n)>Int 5==Bool true andBool#symInt(n)<=Int 20==Bool true”,路径简化后:n>5∩n<=20。

对于上述符号路径,经过约束求解得到的符号变量的具体值分别为n=4、n=21和n=6。将符号具体值n=4作为具体输入,执行命令为krun if.go–cIN="ListItem(4)"–cPC="true",将触发程序漏洞,K的格单元error结果显示“assert false”。

相对于现有技术,本发明的优点和有益效果是:

1、提出了一种基于重写逻辑的可执行形式化语义,可执行的形式化语义是在K框架中实现的。基于K框架可以模块化地指定语言规则,并准确表示真正的并发编程语言,尤其适合用于对并发语言Go语言的分析,并且K中的语义作为项重写系统具有严格的意义,支持完全的形式化证明。

2、使用kompile工具对语义进行编译,K框架会自动生成相应的解释器,且自动支持对程序进行模型检查和符号执行分析,然后基于该解释器使用krun工具运行程序代码,通过符号执行等技术可以用于程序漏洞分析,从而帮助发现程序中隐藏的各种问题。

以上实施例仅用以说明本发明的技术方案,而非对其限制;尽管参照前述实施例对本发明进行了详细的说明,本领域的普通技术人员应当理解:其依然可以对前述各实施例所记载的技术方案进行修改,或者对其中部分技术特征进行等同替换;而这些修改或者替换,并不使相应技术方案的本质脱离本发明各实施例技术方案的精神和范围。

去获取专利,查看全文>

相似文献

  • 专利
  • 中文文献
  • 外文文献
获取专利

客服邮箱:kefu@zhangqiaokeyan.com

京公网安备:11010802029741号 ICP备案号:京ICP备15016152号-6 六维联合信息科技 (北京) 有限公司©版权所有
  • 客服微信

  • 服务号