打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
linux下的吹毛求疵检查器splint
[c-sharp] view plaincopy
  1. 讲  授  内  容        
  2. splint  
  3. 一.splint介绍  
  4. splint是一个开源的静态代码检测工具,用于动态检查C语言程序安全弱点和编写错误的程序。splint会进行多种常规检查,包括未使用的变量,类型不一致,使用未定义变量,无法执行的代码,忽略返回值,执行路径未返回,无限循环等错误。它以大师级的眼光来审阅你的代码,Splint只能检测标准C代码。  
  5. 'lint-clean' -- 程序能够顺利通过lint程序的检查。这是微软的编码要求。  
  6. 二.splint的安装  
  7. (一)Linux环境下的安装  
  8. 1.rpm安装  
  9. GTES 10.5和11版本已经整合有splint软件包,直接可以使用。  
  10. 2.源代码安装  
  11. 下载地址:  
  12. http://www.splint.org/downloads/splint-3.1.2.src.tgz  
  13. 源码包安装:  
  14. # tar zxvf splint-3.1.2.src.tgz  
  15. # cd splint-3.1.2  
  16. # ./configure  
  17. # make install  
  18. (二)Windows环境下的安装  
  19. 1. 将软件解压到c:/。  
  20. 2.设置用户变量:  
  21. larch_path:安装splint的路径/lib  
  22. lclimprotdir:安装splint的路径/imports  
  23. include:安装tc 的路径/include  
  24. path:安装splint的路径/bin  
  25. 3.注销当前用户后重新登陆,即可使用。  
  26. 三.splint的用法  
  27.    在“开始”-“运行”中输入cmd,进入dos窗口,输入cd/,到C盘根目录,输入splint运行命令。      
  28. 基本用法:splint *.cSplint输出Warnings的格式:<file>:<line>[,<column>]: message[hint]<file>:<line>,<column>: extra location information, if appropriate  1.使用' +/-<flags>'来自定义其输出格式,如'splint -showcol *c',则Splint不会在输出信息中显示'列'信息。  
  29. 使用flags控制splint的检查范围和输出格式'+<flag>' -- 表明某个flag处于打开状态,如'+unixlib''-<flag>' -- 表明某个flag处于关闭状态,如'-weak';   
  30. 2.使用.splintrc环境文件如果不想每次使用splint的时候都手工输入一堆'+/-<flags>',那么你可以把这些'+/-<flags>'预先写到.splintrc文件中,当splint执行的时候它会自动加上这些flags的。默认的flags设置在'~/splintrc'文件中,但是如果一旦splint的当前工作路径下也有.splintrc文件,那么这个.splintrc文件中的flag设置会覆盖'~/splintrc'中的flags设置,但是命令行中的flags设置是具备最高优先级的,它会覆盖前面提到的任何一个文件中的flags设置。   
  31. 3.使用Annotations:  
  32. 对于'Annotations'的作用,Java程序员并不陌生,但是C程序员则对这个不是那么了解。C代码中的Annotations用来指导Splint生成恰当的代码检查报告。  
  33. 下面这个例子对比使用和不使用Annotations,Splint的输出的差别:/* testlint.c */void foo1() {/*@unused@*/int *p = NULL;}   
  34. oid foo2() {int *p = NULL;}   
  35. splint testlint.cSplint 3.1.1 --- 28 Apr 2003   
  36. testlint.c: (in function foo2)testlint.c:6:7: Variable p declared but not usedA variable is declared but never used. Use /*@unused@*/ in front ofdeclaration to suppress message. (Use -varuse to inhibit warning)   
  37. Finished checking --- 1 code warning   
  38. 可以看出没使用Annotation的函数foo2被给出Warning了。Splint的Annotations繁多,我们在平时做lint时可以多多接触。   
  39. '早用lint,勤用lint',这是C专家给我们的建议。'lint-clean'也许离你并不遥远。   
  40. 四.splint的使用  
  41. 1.空引用错误  
  42. 在引用没有指向任何内存地址的指针时,会导致这种错误,也就是使用了一个没有赋值的指针。splint支持一种特别的注释,这种注释写在C程序代码中,用于对程序进行特殊说明。如下面这段程序,使用了/*@null@*/进行了说明,表示说明*s的值可能会是NULL。  
  43. //null.c  
  44. char firstChar1 (/*@null@*/ char *s)  
  45. {  
  46. return *s;  
  47. }  
  48.   
  49. char firstChar2 (/*@null@*/ char *s)  
  50. {  
  51.  if (s ==NULL) return '/0';  
  52.  return *s;  
  53. }  
  54. //END  
  55. 使用splint扫描这个程序时,会输出:   
  56. # splint null.c  
  57. Splint 3.1.1 --- 28 Apr 2005  
  58. null.c: (in function firstChar1)  
  59. null.c:3:11: Dereference of possibly null pointer s: *s  
  60.   null.c:1:35: Storage s may become null  
  61. Finished checking --- 1 code warning found  
  62. 由于firstChar1和firstChar2都使用了null说明,表示指针s可能是个NULL值。所以,splint会对s值的使用情况进行检查。因为firstChar2函数中,对s的值进行了NULL的判断。所以,没有对firstChar2函数的指针s输出警告信息。  
  63. 2.未定义的变量错误  
  64. C语言中,要求先定义变量,而后才可使用。所以,当使用一个没有定义的变量时,编译就会出错。如下例,使用/*@in@*/说明的变量,表示必须进行定义。使用/*@out@*/说明的变量,表示在执行过此函数后,这个变量就进行了定义。  
  65. // usedef.c  
  66. extern void setVal (/*@out@*/ int *x);  
  67. extern int getVal (/*@in@*/ int *x);  
  68. extern int mysteryVal (int *x);  
  69. int dumbfunc (/*@out@*/ int *x, int i)  
  70.  {  
  71.  if (i > 3) return *x;  
  72.  else if (i > 1)  
  73.    return getVal (x);  
  74.  else if (i == 0)  
  75.    return mysteryVal (x);  
  76.  else  
  77.   {  
  78.  setVal (x);  
  79.  return *x;  
  80.   }  
  81.  }  
  82. // END  
  83. 使用splint检查usedef.c:  
  84. $ splint usedef.c  
  85. Splint 3.1.1 --- 28 Apr 2005  
  86.   
  87. usedef.c: (in function dumbfunc)  
  88. usedef.c:7:19: Value *x used before definition  
  89.   An rvalue is used that may not be initialized to a value on some execution  path. (Use -usedef to inhibit warning)  
  90. usedef.c:9:18: Passed storage x not completely defined (*x is undefined):                  getVal (x)  
  91.   Storage derivable from a parameter, return value or global is not defined.  
  92.   Use /*@out@*/ to denote passed or returned storage which need not be defined.  
  93.   (Use -compdef to inhibit warning)  
  94. usedef.c:11:22: Passed storage x not completely defined (*x is undefined):  
  95.                    mysteryVal (x)  
  96.   
  97. Finished checking --- 3 code warnings  
  98. //错误原因: 由于程序中没有对x进行定义,所以报未定义错误。但setVal()使用了/*@out@*/说明,所以在setVal(x);和return x;中,没有报未定义错误。  
  99. 3.类型错误  
  100. C语言中的数据类型较多,各个之间有些细微差别。splint也可以对变量类型进行检查。  
  101. 示例1:  
  102. //bool.c  
  103. int f (int i, char *s,bool b1, bool b2)  
  104. {  
  105. if (i = 3) return b1;  
  106. if (!i || s) return i;  
  107. if (s) return 7;  
  108. if (b1 == b2)  
  109. return 3;  
  110. return 2;  
  111. }  
  112. //END  
  113. 使用splint进行检查:  
  114. $ splint bool.c  
  115. Splint 3.1.1 --- 28 Apr 2005  
  116.   
  117. bool.c: (in function f)  
  118. bool.c:4:5: Test expression for if is assignment expression: i = 3  
  119.   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)  
  120. // 错误原因: if语句中的条件表达式是一个赋值语句。  
  121. bool.c:4:5: Test expression for if not boolean, type int: i = 3  
  122.   Test expression type is not boolean or int. (Use -predboolint to inhibit  
  123.   warning)  
  124. // 错误原因: if语句中的条件表达式的返回值,不是布尔型,而是整型。  
  125. bool.c:4:8: Return value type bool does not match declared type int: b1  
  126.   Types are incompatible. (Use -type to inhibit warning)  
  127. // 错误原因: 返回值是布尔型,而不是整型。  
  128. bool.c:5:6: Operand of ! is non-boolean (int): !i  
  129.   The operand of a boolean operator is not a boolean. Use +ptrnegate to allow !  
  130.   to be used on pointers. (Use -boolops to inhibit warning)  
  131. // 错误原因: "!"操作符的操作数不是布尔型,而是整型i.  
  132. bool.c:5:11: Right operand of || is non-boolean (char *): !i || s  
  133. // 错误原因: "||"操作符的右操作数不是布尔型,而是字符指针.  
  134. bool.c:7:5: Use of == with boolean variables (risks inconsistency because of  
  135.                 multiple true values): b1 == b2  
  136.   Two bool values are compared directly using a C primitive. This may produce  
  137.   unexpected results since all non-zero values are considered true, so  
  138.   different true values may not be equal. The file bool.h (included in  
  139.   splint/lib) provides bool_equal for safe bool comparisons. (Use -boolcompare  
  140.   to inhibit warning)  
  141. // 错误原因: 使用"=="对两个布尔型进行比较.应该使用"&&".  
  142.   
  143. Finished checking --- 6 code warnings  
  144. 示例2:  
  145. //malloc1.c  
  146. #include <stdlib.h>  
  147. #include <stdio.h>  
  148.   
  149. int main(void)  
  150. {  
  151. char *some_mem;  
  152. int size1=1048576;  
  153. some_mem=(char *)malloc(size1);  
  154. printf("Malloed 1M Memory!/n");  
  155. free(some_mem);  
  156. exit(EXIT_SUCCESS);  
  157. }  
  158.   
  159. //END  
  160. 使用splint检查malloc1.c  
  161. $ splint malloc1.c  
  162. Splint 3.1.1 --- 28 Apr 2005  
  163.   
  164. malloc1.c: (in function main)  
  165. malloc1.c:9:25: Function malloc expects arg 1 to be size_t gets int: size1  
  166.   To allow arbitrary integral types to match any integral type, use  
  167.   +matchanyintegral.  
  168.   
  169. Finished checking --- 1 code warning  
  170. 修改变量size1的定义为:  
  171. size_t size1=1048576;  
  172. 再使用splint进行检查。  
  173. $ splint malloc1.c  
  174. Splint 3.1.1 --- 28 Apr 2005  
  175.   
  176. Finished checking --- no warnings  
  177. 没有检查到错误。  
  178. 4.内存检查  
  179. 缓冲区溢出错误是一种非常危险的C语言错误,大部分安全漏洞都与它有关。splint可以对缓冲区的使用进行检查,报告溢出或越界错误。  
  180. 示例1:  
  181. //over.c  
  182. int main()  
  183. {  
  184. int buf[10];  
  185. buf[10] = 3;  
  186. retrun 0;  
  187. }  
  188. //END  
  189. 使用splint进行检查  
  190. $ splint over.c  +bounds +showconstraintlocation  
  191. Splint 3.1.1 --- 21 Apr 2006  
  192.   
  193. Command Line: Setting +showconstraintlocation redundant with current value  
  194. over.c: (in function main)  
  195. over.c:6:3: Likely out-of-bounds store:  
  196.     buf[10]  
  197.     Unable to resolve constraint:  
  198.     requires 9 >= 10  
  199.      needed to satisfy precondition:  
  200.     requires maxSet(buf @ over.c:6:3) >= 10  
  201.   A memory write may write to an address beyond the allocated buffer. (Use  
  202.   -likely-boundswrite to inhibit warning)  
  203.   
  204. Finished checking --- 1 code warning  
  205. 数组buf的大小是10字节,最大可使用的元素位置为buf[9],但程序中使用了buf[10]。所以报错。  
  206. 示例2:  
  207. // bound.c  
  208. void updateEnv(char *str)  
  209. {  
  210.   char *tmp;  
  211.   tmp = getenv("MYENV");  
  212.   if(tmp != NULL) strcpy(str,tmp);  
  213. }  
  214. void updateEnvSafe(char *str, size_t strSize)  
  215.   
  216. {  
  217.   char *tmp;  
  218.   tmp = getenv("MYENV");  
  219.   if(tmp != NULL)  
  220.     {  
  221.       strncpy(str, tmp, strSize -1);  
  222.       str[strSize - 1] = '/0';  
  223.     }  
  224. }  
  225. //END  
  226. 执行splint:  
  227. $ splint bound.c +bounds +showconstraintlocation  
  228.   
  229. Splint 3.1.1 --- 21 Apr 2006  
  230.   
  231. Command Line: Setting +showconstraintlocation redundant with current value  
  232. bound.c: (in function updateEnv)  
  233. bound.c:6:19: Possible out-of-bounds store:  
  234.     strcpy(str, tmp)  
  235.     Unable to resolve constraint:  
  236.     requires maxSet(str @ bound.c:6:26) >= maxRead(getenv("MYENV") @  
  237.     bound.c:5:9)  
  238.      needed to satisfy precondition:  
  239.     requires maxSet(str @ bound.c:6:26) >= maxRead(tmp @ bound.c:6:30)  
  240.      derived from strcpy precondition: requires maxSet(<parameter 1>) >=  
  241.     maxRead(<parameter 2>)  
  242.   A memory write may write to an address beyond the allocated buffer. (Use  
  243.   -boundswrite to inhibit warning)  
  244. 错误原因: 由于使用strcpy函数时,没有指定复制字符串的长度,所以可能导致缓冲区溢出。后面的updateEnvSafe函数,使用了strncpy进行字符串复制,避免了这种情况。  
  245.   
  246.   
  247. Splint的使用练习:  
  248. 1、检测下面程序的错误,并分析改正。  
  249. #include<stdio.h>#include<stdlib.h>int main(int argc, char **argv){printf("Hello,Splint/n");return 0;}这段代码大家肯定很熟悉吧,下面用Splint来检测.下面是检测结果:Splint 3.0.1.6 --- 11 Feb 2002hello.c: (in function main)hello.c(4,14): Parameter argc not usedA function parameter is not used in the body of the function. If the argument is needed for type compatibility or future plans, use /*@unused@*/ in the argument declaration. (Use -paramuse to inhibit warning)hello.c(4,27): Parameter argv not usedFinished checking --- 2 code warnings    从输出结果中可以看出,有两个警告,提示是变量argc和argv没有使用,并且提示可以加入/*@unused@*/忽略这个警告。更改后的代码:#include<stdio.h>#include<stdlib.h>int main(/*@unused@*/int argc, /*@unused@*/char **argv){printf("Hello,Splint/n");return 0;}然后我们再来检测:Splint 3.0.1.6 --- 11 Feb 2002Finished checking --- no warnings提示没有警告,这样也就是lint clean.  
  250. 2、看一下下面的代码(当然包括错误,以检验splint的功能):#include int main(int argc,char* argv[]){ int a=100; /*没有使用的变量*/ int b[8]; printf("Hello c/n"); b[9]=100; /*明显数组越界 */ /* 用到了两个为声明的变量c和d*/ c=100; d=10; return 0; }   
  251. 现在可以用splint来检查一下,为了检验是否可以检测到数组越界,使用+bounds选项。 splint hi.c +bounds 输出结果: hi.c: (in function main) hi.c:9:2: Unrecognized identifier: c Identifier used in code has not been declared. (Use -unrecog to inhibit warning) hi.c:10:2: Unrecognized identifier: d hi.c:4:6: Variable a 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)   
  252. hi.c:7:2: Likely out-of-bounds store: b[9] Unable to resolve constraint: requires 7 >= 9 needed to satisfy precondition: requires maxSet(b @ hi.c:7:2) >= 9 A memory write may write to an address beyond the allocated buffer. (Use -likely-boundswrite to inhibit warning) hi.c:3:14: Parameter argc not used A function parameter is not used in the body of the function. If the argument is needed for type compatibility or future plans, use /*@unused@*/ in the argument declaration. (Use -paramuse to inhibit warning) hi.c:3:25: Parameter argv not used Finished checking --- 6 code warnings   
  253. 现在详细看一下结果: 检查结果1:   
  254. hi.c:9:2: Unrecognized identifier: c Identifier used in code has not been declared. (Use -unrecog to inhibit warning) hi.c:10:2: Unrecognized identifier: d hi.c:4:6: Variable a 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)   
  255. 这些应该是splint检测到变量c和d没有声明。 检查结果2:   
  256. hi.c:7:2: Likely out-of-bounds store: b[9] Unable to resolve constraint: requires 7 >= 9 needed to satisfy precondition: requires maxSet(b @ hi.c:7:2) >= 9 A memory write may write to an address beyond the allocated buffer. (Use -likely-boundswrite to inhibit warning)   
  257. 这些是检查存在数组越界,因为b[8]的最大数组序号应该是7,而不是9,所以出现requires 7 >= 9; 检查结果3:   
  258. hi.c:3:14: Parameter argc not used A function parameter is not used in the body of the function. If the argument is needed for type compatibility or future plans, use /*@unused@*/ in the argument declaration. (Use -paramuse to inhibit warning) hi.c:3:25: Parameter argv not used     这些表明argc和argv变量声明了,但是没有使用。这个可以加入/*@unused@*/忽略这个警告。  小心使用splint,应该对于c语言的程序编写有非常大的辅助作用!     
 
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
splint
代码静态分析工具
错误警告解决办法
GCC警告选项例解
Mysql UDF
数据结构-四叉树
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服