代码静态分析工具splint的学习与使用

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

引言最近在项目中使用了静态程序分析工具PC-Lint,体会到它在项目实施中带给开发人员的方便。PC-Lint是一款针对C/C++语言、windows平台的静态分析工具,FlexeLint是针对其他平台的PC-Lint版本。由于PC-Lint/FlexeLint是商业的程序分析工具,不便于大家对其进行学习和使用,因而下面我将介绍一个针对C语言的开源程序静态分析工具——splint。静态程序分析先来说说什么是“静态程序分析(Staticprogramanalysis)”,静态程序分析是指使用自动化工具软件对程序源代码进行检查,以分析程序行为的技术,应用于程序的正确性检查、安全缺陷检测、程序优化等。它的特点就是不执行程序,相反,通过在真实或模拟环境中执行程序进行分析的方法称为“动态程序分析(Dynamicprogramanalysis)”。那在什么情况下需要进行静态程序分析呢?静态程序分析往往作为一个多人参与的项目中代码审查过程的一个阶段,因编写完一部分代码之后就可以进行静态分析,分析过程不需要执行整个程序,这有助于在项目早期发现以下问题:变量声明了但未使用、变量类型不匹配、变量在使用前未定义、不可达代码、死循环、数组越界、内存泄漏等。下图说明了静态程序分析在进行项目编码过程中所处的位置:从上图可以知道,静态分析工具在代码通过编译之后再对代码进行分析。我们会问:静态分析工具与编译器相比,所做的工作有什么不同?静态分析工具相比编译器,对代码进行了更加严格的检查,像数组越界访问、内存泄漏、使用不当的类型转换等问题,都可以通过静态分析工具检查出来,我们甚至可以在分析工具的分析标准里定义代码的编写规范,在检测到不符合编写规范的代码时抛出告警,这些功能都是编译器没有的。既然静态分析工具发挥了不小的作用,何不在编译器里兼备静态分析的功能?对于这个问题,S.C.Johnson(他是最古老的静态分析工具Lint的作者)在其1978年发表的论文《Lint,aCProgramChecker》中给出了他的答案:“Lint与C编译器在功能上的分离既有历史原因,也有现实的意义。编译器负责把C源程序快速、高效地转变为可执行文件,不对代码做类型检查(特别是对分别编译的程序),有益于做到快速与高效。而Lint没有“高效”的要求,可以花更多时间对代码进行更深入、仔细的检查。”针对空指针提取、未定义变量使用、类型转换、内存管理、函数接口定义等,我们可以在静态分析工具里制定不同的检测标准,以下曲线图说明了在使用splint进行分析时,检测标准与splint运行的开销所对应的关系,从另一个角度看,也说明了静态分析工具与编译器的关系:splint掌握了“静态分析”等概念之后,我们再来看splint。在Linux命令行下,splint的使用很简单,检测文件*.c,只要这样使用就可以了:splint*.c1.splint消息我们通过以下例子来认识典型的splint告警信息:1//splint_msg.c2intfunc_splint_msg1(void)3{4inta;5return0;6}7intfunc_splint_msg2(void)8{9int*a=(int*)malloc(sizeof(int));10a=NULL;11return0;12}运行splintsplint_msg.c之后,我们来看输出的告警信息:splint_msg.c:(infunctionfunc_splint_msg1)splint_msg.c:4:6:VariableadeclaredbutnotusedAvariableisdeclaredbutneverused.Use/*@unused@*/infrontofdeclarationtosuppressmessage.(Use-varusetoinhibitwarning)splint_msg.c:(infunctionfunc_splint_msg2)splint_msg.c:10:2:Freshstoragea(typeint*)notreleasedbeforeassignment:a=NULLAmemoryleakhasbeendetected.Storageallocatedlocallyisnotreleasedbeforethelastreferencetoitislost.(Use-mustfreefreshtoinhibitwarning)splint_msg.c:9:37:FreshstorageacreatedFinishedchecking---2codewarnings蓝色字体部分:给出告警所在函数名,在函数的第一个警告消息报告前打印;红色字体部分:消息的正文,文件名、行号、列号显示在的警告的正文前;黑色字体部分:是有关该可疑错误的详细信息,包含一些怎样去掉这个消息的信息;绿色字体部分:给出格外的位置信息,这里消息给出了是在哪里申请了这个可能泄露的内存。2.检查控制splint提供了三种方式可进行检查的控制,分别是.splintrc配置文件、flags标志和格式化注释。flags:splint支持几百个标志用来控制检查和消息报告,使用时标志前加’+‘或’-’,'+'标志开启这个标志,'-'表示关闭此标志,下面例子展示了flags标志的用法:splint-showcola.c//在检测a.c时,告警消息中列数不被打印splint-varusea.c//在检测a.c时,告警消息中未使用变量告警不被打印.splintrc配置文件:在使用源码安装splint之后,.splintrc文件将被安装在主目录下,.splintrc文件中对一些标志作了默认的设定,命令行中指定的flags标志会覆盖.splintrc文件中的标志。格式化注释:格式化注释提供一个类型、变量或函数的格外的信息,可以控制标志设置,增加检查效果,所有格式化注释都以/*@开始,@*/结束,比如在函数参数前加/*@null@*/,表示该参数可能是NULL,做检测时,splint会加强对该参数的值的检测。3.检测分析内容1.解引用空指针(NullDereferences)在Unix操作系统中,解引用空指针将导致我们在程序运行时产生段错误(Segmentationfault),一个简单的解引用空指针例子如下:1//null_dereferences.c2intfunc_null_dereferences(void)3{4int*a=NULL;5return*a;6}执行splintnull_dereference.c命令,将产生以下告警消息:null_dereference.c:(infunctionfunc_null_dereferences)null_dereference.c:5:10:Dereferenceofnullpointera:*aApossiblynullpointerisdereferenced.Valueiseithertheresultofafunctionwhichmayreturnnull(inwhichcase,codeshouldcheckitisnotnull),oraglobal,parameterorstructurefielddeclaredwiththenullqualifier.(Use-nulldereftoinhibitwarning)null_dereference.c:4:11:StorageabecomesnullFinishedchecking---1codewarnin2.类型(Types)我们在编程中经常用到强制类型转换,将有符号值转换为无符号值、大范围类型值赋值给小范围类型,程序运行的结果会出无我们的预料。1//types.c2voidsplint_types(void)3{4shorta=0;5longb=32768;6a=b;7return;8}执行splinttypes.c命令,将产生以下告警消息:types.c:(infunctionsplint_types)types.c:6:2:Assignmentoflonginttoshortint:a=bToignoretypequalifiersintypecomparisonsuse+ignorequals.Finishedchecking---1codewarning3.内存管理(MemoryManagement)C语言程序中,将近半数的bug归功于内存管理问题,关乎内存的bug难以发现并且会给程序带来致命的破坏。由内存释放所产生的问题,我们可以将其分为两种:当尚有其他指针引用的时候,释放一块空间1//memory_management1.c2voidmemory_management1(void)3{4int*a=(int*)malloc(sizeof(int));5int*b=a;6free(a);7*b=0;8return;9}在上面这个例子中,指针a与b指向同一块内存,但在内存释放之后仍对b指向的内容进行赋值操作,我们来看splintmemory_management1.c的结果:memory_management1.c:(infunctionmemory_management1)memory_management1.c:7:3:VariablebusedafterbeingreleasedMemoryisusedafterithasbeenreleased(eitherbypassingasanonlyparamorassigningtoanonlyglobal).(Use-usereleasedtoinhibitwarning)memory_management1.c:6:7:Storagebreleasedmemory_management1.c:7:3:Dereferenceofpossiblynullpointerb:*bApossiblynullpointerisdereferenced.Valueiseithertheresultofafunctionwhichmayreturnnull(inwhichcase,codeshouldcheckitisnotnull),oraglobal,parameterorstructurefielddeclaredwiththenullqualifier.(Use-nulldereftoinhibitwarning)memory_management1.c:5:11:StoragebmaybecomenullFinishedchecking---2codewarnings检查结果中包含了两个告警,第一个指出我们使用了b指针,而它所指向的内存已被释放;第二个是对解引用空指针的告警。当最后一个指针引用丢失的时候,其指向的空间尚未释放1//memory_management2.c2voidmemory_management2(void)3{4int*a=(int*)malloc(sizeof(int));5a=NULL;6return;7}这个例子中内存尚未释放,就将指向它的唯一指针赋值为NULL,我们来看splintmemory_management2.c的检测结果:memory_management2.c:(infunctionmemory_management2)memory_management2.c:5:2:Freshstoragea(typeint*)notreleasedbeforeassignment:a=NULLAmemoryleakhasbeendetected.Storageallocatedlocallyisnotreleasedbeforethelastreferencetoitislost.(Use-mustfreefreshtoinhibitwarning)memory_management2.c:4:37:Freshstorageacreate

1 / 9
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功