Splint C语言代码检测

Splint C语言代码检测http://blog.chinaunix.net/uid-14833587-id-76561.html在linux下并没有pclint,可以使用splint代替。splint使用一.splint介绍splint是一个静态检查C语言程序安全弱点和编写错误的工具。splint会进行多种常规检查,包括未使用的变量,类型不一致,使用未定义变量,无法执行的代码,忽略返回值,执行路

大家好,欢迎来到IT知识分享网。

http://blog.chinaunix.net/uid-14833587-id-76561.html

在linux下并没有pclint,可以使用splint代替。
splint使用
一.splint介绍
splint是一个静态检查C语言程序安全弱点和编写错误的工具。splint会进行多种常规检查,包括未使 用的变量,类型不一致,使用未定义变量,无法执行的代码,忽略返回值,执行路径未返回,无限循环等错误。同 时通过在源码中添加注记给出的附加信息,使其可以进行功能更加强大的检查。而注记,则是对文件中的函数、变 量、参数以及类型进行假定的一种的程式化的注释。
二.splint的安装
下载地址:

http://www.splint.org/downloads/splint-3.1.2.src.tgz

Splint C语言代码检测
文件: splint-3.1.2-src.tar.gz
大小: 2272KB
下载: 下载



源码包安装:


# tar zxvf splint-3.1.2.src.tgz


# cd splint-3.1.2


# mkdir /usr/local/splint


# ./configure –prefix=/usr/local/splint


# make install


# vi ~/.bashrc


添加:


export LARCH_PATH=/usr/local/splint/share/splint/lib


export LCLIMPORTDIR=/usr/local/splint/share/splint/import


# source ~/.bashrc


# export PATH=/usr/local/splint/bin/splint:$PATH




三.splint的使用


1.空引用错误




在引用没有指向任何内存地址的指针时,会导致这种错误.也就是使用了一个没有赋值的指针.


splint支持一种特别的注释.这种注释写在C程序代码中,用于对程序进行特殊说明.


如下面这段程序.使用了/*@null@*/进行了说明,表示说明*s的值可能会是NULL.




//null.c


char firstChar1 (/*@null@*/ char *s)


{



return *s;


}




char firstChar2 (/*@null@*/ char *s)


{



if (s ==NULL) return ‘\0’;


return *s;


}


//END




使用splint扫描这个程序时,会输出:




# splint null.c


Splint 3.1.1 — 28 Apr 2005


null.c: (in function firstChar1)


null.c:3:11: Dereference of possibly null pointer s: *s


null.c:1:35: Storage s may become null


Finished checking — 1 code warning found


由于firstChar1和firstChar2都使用了null说明,表示指针s可能是个NULL值.


所以,splint会对s值的使用情况进行检查.因为firstChar2函数中,对s的值进行


了NULL的判断.所以,没有对firstChar2函数的指针s输出警告信息.




2.未定义的变量错误




C语言中,要求先定义变量,而后才可使用.所以,当使用一个没有定义的变量时,编译就会出错.


如下例,使用/*@in@*/说明的变量,表示必须进行定义.使用/*@out@*/说明的变量,表示在 执行过此函数后,这个变量就进行了定义.




// usedef.c


extern void setVal (/*@out@*/ int *x);


extern int getVal (/*@in@*/ int *x);


extern int mysteryVal (int *x);


int dumbfunc (/*@out@*/ int *x, int i)


{



if (i > 3) return *x;


else if (i > 1)


return getVal (x);


else if (i == 0)


return mysteryVal (x);


else


{



setVal (x);


return *x;


}


}


// END




使用splint检查usedef.c




$ splint usedef.c


Splint 3.1.1 — 28 Apr 2005




usedef.c: (in function dumbfunc)


usedef.c:7:19: Value *x used before definition


An rvalue is used that may not be initialized to a value on some execution


path. (Use -usedef to inhibit warning)


usedef.c:9:18: Passed storage x not completely defined (*x is undefined):


getVal (x)


Storage derivable from a parameter, return value or global is not defined.


Use /*@out@*/ to denote passed or returned storage which need not be defined.


(Use -compdef to inhibit warning)


usedef.c:11:22: Passed storage x not completely defined (*x is undefined):


mysteryVal (x)




Finished checking — 3 code warnings


//错误原因: 由于程序中没有对x进行定义,所以报未定义错误.但setVal()使用了/*@out@*/ 说明,所以


在setVal(x);和return x;中,没有报未定义错误.




3.类型错误




C语言中的数据类型较多,各个之间有些细微差别.splint也可以对变量类型进行检查.




示例1:




//bool.c


int f (int i, char *s,bool b1, bool b2)


{



if (i = 3) return b1;


if (!i || s) return i;


if (s) return 7;


if (b1 == b2)


return 3;


return 2;


}


//END




使用splint进行检查:




$ splint bool.c


Splint 3.1.1 — 28 Apr 2005




bool.c: (in function f)


bool.c:4:5: Test expression for if is assignment expression: i = 3


The condition test is an assignment expression. Probably, you mean to use ==


instead of =. If an assignment is intended, add an extra parentheses nesting


(e.g., if ((a = b)) …) to suppress this message. (Use -predassign to


inhibit warning)


// 错误原因: if语句中的条件表达式是一个赋值语句.


bool.c:4:5: Test expression for if not boolean, type int: i = 3


Test expression type is not boolean or int. (Use -predboolint to inhibit


warning)


// 错误原因: if语句中的条件表达式的返回值,不是布尔型,而是整型.


bool.c:4:8: Return value type bool does not match declared type int: b1


Types are incompatible. (Use -type to inhibit warning)


// 错误原因: 返回值是布尔型,而不是整型.


bool.c:5:6: Operand of ! is non-boolean (int): !i


The operand of a boolean operator is not a boolean. Use +ptrnegate to allow !


to be used on pointers. (Use -boolops to inhibit warning)


// 错误原因: “!”操作符的操作数不是布尔型,而是整型i.


bool.c:5:11: Right operand of || is non-boolean (char *): !i || s


// 错误原因: “||”操作符的右操作数不是布尔型,而是字符指针.


bool.c:7:5: Use of == with boolean variables (risks inconsistency because of


multiple true values): b1 == b2


Two bool values are compared directly using a C primitive. This may produce


unexpected results since all non-zero values are considered true, so


different true values may not be equal. The file bool.h (included in


splint/lib) provides bool_equal for safe bool comparisons. (Use -boolcompare


to inhibit warning)


// 错误原因: 使用”==”对两个布尔型进行比较.应该使用”&&”.




Finished checking — 6 code warnings




示例2:




//malloc1.c


#include <stdlib.h>


#include <stdio.h>




int main(void)


{



char *some_mem;


int size1=1048576;


some_mem=(char *)malloc(size1);


printf(“Malloed 1M Memory!\n”);


free(some_mem);


exit(EXIT_SUCCESS);


}




//END




使用splint检查malloc1.c




$ splint malloc1.c


Splint 3.1.1 — 28 Apr 2005




malloc1.c: (in function main)


malloc1.c:9:25: Function malloc expects arg 1 to be size_t gets int: size1


To allow arbitrary integral types to match any integral type, use


+matchanyintegral.




Finished checking — 1 code warning




修改变量size1的定义为:




size_t size1=1048576;




再使用splint进行检查.




$ splint malloc1.c


Splint 3.1.1 — 28 Apr 2005




Finished checking — no warnings




没有检查到错误.


4.内存检查




缓冲区溢出错误是一种非常危险的C语言错误,大部分安全漏洞都与它有关.splint可以


对缓冲区的使用进行检查.报告溢出或越界错误.


示例1:




//over.c


int main()


{



int buf[10];


buf[10] = 3;


retrun 0;


}


//END




使用splint进行检查




$ splint over.c +bounds +showconstraintlocation


Splint 3.1.1 — 21 Apr 2006




Command Line: Setting +showconstraintlocation redundant with current value


over.c: (in function main)


over.c:6:3: Likely out-of-bounds store:


buf[10]


Unable to resolve constraint:


requires 9 >= 10


needed to satisfy precondition:


requires maxSet(buf @ over.c:6:3) >= 10


A memory write may write to an address beyond the allocated buffer. (Use


-likely-boundswrite to inhibit warning)




Finished checking — 1 code warning




数组buf的大小是10字节.最大可使用的元素位置为buf[9],但程序中使用了buf[10].


所以报错.




示例2:




// bound.c


void updateEnv(char *str)


{



char *tmp;


tmp = getenv(“MYENV”);


if(tmp != NULL) strcpy(str,tmp);


}


void updateEnvSafe(char *str, size_t strSize)




{



char *tmp;


tmp = getenv(“MYENV”);


if(tmp != NULL)


{



strncpy(str, tmp, strSize -1);


str[strSize – 1] = ‘/0’;


}


}


//END




$ splint bound.c +bounds +showconstraintlocation




Splint 3.1.1 — 21 Apr 2006




Command Line: Setting +showconstraintlocation redundant with current value


bound.c: (in function updateEnv)


bound.c:6:19: Possible out-of-bounds store:


strcpy(str, tmp)


Unable to resolve constraint:


requires maxSet(str @ bound.c:6:26) >= maxRead(getenv(“MYENV”) @


bound.c:5:9)


needed to satisfy precondition:


requires maxSet(str @ bound.c:6:26) >= maxRead(tmp @ bound.c:6:30)


derived from strcpy precondition: requires maxSet(<parameter 1>) >=


maxRead(<parameter 2>)


A memory write may write to an address beyond the allocated buffer. (Use


-boundswrite to inhibit warning)


错误原因: 由于使用strcpy函数时,没有指定复制字符串的长度,所以可能导致缓冲区


溢出.后面的updateEnvSafe函数,使用了strncpy进行字符串复制,避免了这种情况.








到底需不需要编译器之外的独立的静态代码检查工具呢?这个问题’仁者见仁,智者见智’。但是有一个结论我想大 家都会认可,那就是越是在开发周期早期发现的Bug,修复它所付出的代价就越小。而像lint这样的静态代 码检查程序恰恰是让Bug在早期阶段’显露原型’的绝佳工具,而追求’lint- clean'[注1]境界的 代码也向来是专家级程序员的嗜好。别忘了在’C专家编程’一书中曾经提到Sun OS的内核一直是保持’lint-clean’ 状态的,这就是榜样!还等什么?赶快学呀!^_^




有人抱怨’不敢用lint工具, 太多的Warnings把快屏幕都淹没了!’,不过高手一般不这么想,他会细 心琢磨这些Warnings背后的’暗示’,并和lint工具沟通,利用 lint工具提供的交互方 法屏蔽掉一些经过分析认为不能成为错误的Warnings。久而久之,高手本身就成了一个lint程序, 就能够很快的用肉眼发现代码中的问题,并指出问题所在,如何解决!他还能告知如何嵌入一些Annotations 从而避免让lint程序产生不必要的Warnings,这时这位高手对语言和程序的理解就又提高了一 个档次了。其实使用ling工具不仅仅是为了提早发现程序中的Bug,其使用过程有助于你加深对程序的认识 和理解。的确事实就是这样。




Splint就是一款强大而且应用广泛的开源lint工具。它的强大的代码检查能力固然让人称道,但是让我更 欣赏的却是它提供的’Annotations’机制。 Splint可以让程序员在自己的代码中嵌入相应的 Anotations,这些Anotations作为Splint分析代码时的输入以帮助Splint 产生对程序员更有用的信息。下面是一些Splint的使用入门,更多详细信息请查看’Splint manual’。




1、最简单的Splint使用方法


>> splint *.c




2、Splint输出Warnings的基本格式


<file>:<line>[,<column>]: message


[hint]


<file>:<line>,<column>: extra location information, if appropriate


我们可以使用’+/-<flags>’来自定义其输出格式,如’splint -showcol *c’,则Splint不会在输出信息中显示’列’信息。




3、使用flags控制splint的检查范围和输出格式


‘+<flag>’ — 表明某个flag处于打开状态,如’+unixlib’;


‘-<flag>’ — 表明某个flag处于关闭状态,如’-weak’;




4、使用.splintrc环境文件


如果不想每次使用splint的时候都手工输入一堆’+/-<flags>’,那么你可以把这些 ‘+/-<flags>’预先写到.splintrc文件中,当splint执行的时候它会自动加 上这些flags的。默认的flags设置在’~/splintrc’文件中,但是如果一旦splint的 当前工作路径下也有.splintrc文件,那么这个.splintrc文件中的flag设置会覆盖’~/splintrc’中 的 flags设置,但是命令行中的flags设置是具备最高优先级的,它会覆盖前面提到的任何一个文件中的flags 设置。




5、使用Annotations


对于’Annotations’的作用,Java程序员并不陌生,但是C程序员则对这个不是那么了解。C代码 中的Annotations用来指导Splint生成恰当的代码检查报告。下面这个例子对比使用和不使用Annotations,Splint 的输出的差别:


/* testlint.c */


void foo1() {



/*@unused@*/int *p = NULL;


}




void foo2() {



int *p = NULL;


}




splint testlint.c


Splint 3.1.1 — 28 Apr 2003




testlint.c: (in function foo2)


testlint.c:6:7: Variable p declared but not used


A variable is declared but never used. Use /*@unused@*/ in front of


declaration to suppress message. (Use -varuse to inhibit warning)




Finished checking — 1 code warning




可以看出没使用Annotation的函数foo2被给出Warning了。Splint的Annotations 繁多,我们在平时做lint时可以多多接触。




‘早用lint,勤用lint’,这是C专家给我们的建议。’lint-clean’也许离你并不遥远。




[注1]


‘lint-clean’ — 程序能够顺利通过lint程序的检查。








splint


splint -I /usr/local/mysql/include/mysql/ +gnuextensions +trytorecover *.c

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/11587.html

(0)
上一篇 2024-03-21 14:00
下一篇 2024-03-22 07:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信